Node* DOMPatchSupport::patchNode(Node* node, const String& markup, ExceptionState& exceptionState) { // Don't parse <html> as a fragment. if (node->isDocumentNode() || (node->parentNode() && node->parentNode()->isDocumentNode())) { patchDocument(markup); return 0; } Node* previousSibling = node->previousSibling(); RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(m_document); Node* targetNode = node->parentElementOrShadowRoot() ? node->parentElementOrShadowRoot() : m_document.documentElement(); // Use the document BODY as the context element when editing immediate shadow root children, // as it provides an equivalent parsing context. if (targetNode->isShadowRoot()) targetNode = m_document.body(); Element* targetElement = toElement(targetNode); // FIXME: This code should use one of createFragment* in markup.h if (m_document.isHTMLDocument()) fragment->parseHTML(markup, targetElement); else fragment->parseXML(markup, targetElement); // Compose the old list. ContainerNode* parentNode = node->parentNode(); Vector<OwnPtr<Digest> > oldList; for (Node* child = parentNode->firstChild(); child; child = child->nextSibling()) oldList.append(createDigest(child, 0)); // Compose the new list. String markupCopy = markup.lower(); Vector<OwnPtr<Digest> > newList; for (Node* child = parentNode->firstChild(); child != node; child = child->nextSibling()) newList.append(createDigest(child, 0)); for (Node* child = fragment->firstChild(); child; child = child->nextSibling()) { if (isHTMLHeadElement(*child) && !child->firstChild() && markupCopy.find("</head>") == kNotFound) continue; // HTML5 parser inserts empty <head> tag whenever it parses <body> if (isHTMLBodyElement(*child) && !child->firstChild() && markupCopy.find("</body>") == kNotFound) continue; // HTML5 parser inserts empty <body> tag whenever it parses </head> newList.append(createDigest(child, &m_unusedNodesMap)); } for (Node* child = node->nextSibling(); child; child = child->nextSibling()) newList.append(createDigest(child, 0)); if (!innerPatchChildren(parentNode, oldList, newList, exceptionState)) { // Fall back to total replace. if (!m_domEditor->replaceChild(parentNode, fragment.release(), node, exceptionState)) return 0; } return previousSibling ? previousSibling->nextSibling() : parentNode->firstChild(); }
Node* DOMPatchSupport::patchNode(Node* node, const String& markup, ExceptionCode& ec) { // Don't parse <html> as a fragment. if (node->isDocumentNode() || (node->parentNode() && node->parentNode()->isDocumentNode())) { patchDocument(markup); return 0; } Node* previousSibling = node->previousSibling(); // FIXME: This code should use one of createFragment* in markup.h RefPtr<DocumentFragment> fragment = DocumentFragment::create(m_document); if (m_document->isHTMLDocument()) fragment->parseHTML(markup, node->parentElement() ? node->parentElement() : m_document->documentElement()); else fragment->parseXML(markup, node->parentElement() ? node->parentElement() : m_document->documentElement()); // Compose the old list. ContainerNode* parentNode = node->parentNode(); Vector<OwnPtr<Digest> > oldList; for (Node* child = parentNode->firstChild(); child; child = child->nextSibling()) oldList.append(createDigest(child, 0)); // Compose the new list. String markupCopy = markup; markupCopy.makeLower(); Vector<OwnPtr<Digest> > newList; for (Node* child = parentNode->firstChild(); child != node; child = child->nextSibling()) newList.append(createDigest(child, 0)); for (Node* child = fragment->firstChild(); child; child = child->nextSibling()) { if (child->hasTagName(headTag) && !child->firstChild() && markupCopy.find("</head>") == notFound) continue; // HTML5 parser inserts empty <head> tag whenever it parses <body> if (child->hasTagName(bodyTag) && !child->firstChild() && markupCopy.find("</body>") == notFound) continue; // HTML5 parser inserts empty <body> tag whenever it parses </head> newList.append(createDigest(child, &m_unusedNodesMap)); } for (Node* child = node->nextSibling(); child; child = child->nextSibling()) newList.append(createDigest(child, 0)); if (!innerPatchChildren(parentNode, oldList, newList, ec)) { // Fall back to total replace. ec = 0; if (!m_domEditor->replaceChild(parentNode, fragment.release(), node, ec)) return 0; } return previousSibling ? previousSibling->nextSibling() : parentNode->firstChild(); }
void SimplifyMarkupCommand::doApply() { ContainerNode* rootNode = m_firstNode->parentNode(); WillBeHeapVector<RefPtrWillBeMember<ContainerNode>> nodesToRemove; // Walk through the inserted nodes, to see if there are elements that could be removed // without affecting the style. The goal is to produce leaner markup even when starting // from a verbose fragment. // We look at inline elements as well as non top level divs that don't have attributes. for (Node* node = m_firstNode.get(); node && node != m_nodeAfterLast; node = NodeTraversal::next(*node)) { if (node->hasChildren() || (node->isTextNode() && node->nextSibling())) continue; ContainerNode* startingNode = node->parentNode(); if (!startingNode) continue; RenderStyle* startingStyle = startingNode->renderStyle(); if (!startingStyle) continue; ContainerNode* currentNode = startingNode; ContainerNode* topNodeWithStartingStyle = nullptr; while (currentNode != rootNode) { if (currentNode->parentNode() != rootNode && isRemovableBlock(currentNode)) nodesToRemove.append(currentNode); currentNode = currentNode->parentNode(); if (!currentNode) break; if (!currentNode->renderer() || !currentNode->renderer()->isRenderInline() || toRenderInline(currentNode->renderer())->alwaysCreateLineBoxes()) continue; if (currentNode->firstChild() != currentNode->lastChild()) { topNodeWithStartingStyle = 0; break; } if (!currentNode->renderStyle()->visualInvalidationDiff(*startingStyle).hasDifference()) topNodeWithStartingStyle = currentNode; } if (topNodeWithStartingStyle) { for (ContainerNode* node = startingNode; node != topNodeWithStartingStyle; node = node->parentNode()) nodesToRemove.append(node); } } // we perform all the DOM mutations at once. for (size_t i = 0; i < nodesToRemove.size(); ++i) { // FIXME: We can do better by directly moving children from nodesToRemove[i]. int numPrunedAncestors = pruneSubsequentAncestorsToRemove(nodesToRemove, i); if (numPrunedAncestors < 0) continue; removeNodePreservingChildren(nodesToRemove[i], AssumeContentIsAlwaysEditable); i += numPrunedAncestors; } }
static void notifyDescendantInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets) { for (Node* child = node.firstChild(); child; child = child->nextSibling()) { if (is<ContainerNode>(*child)) notifyNodeInsertedIntoTree(insertionPoint, downcast<ContainerNode>(*child), postInsertionNotificationTargets); } if (ShadowRoot* root = node.shadowRoot()) notifyNodeInsertedIntoTree(insertionPoint, *root, postInsertionNotificationTargets); }
static void detachChildren(ContainerNode& current, DetachType detachType) { for (Node* child = current.firstChild(); child; child = child->nextSibling()) { if (child->isTextNode()) { Style::detachTextRenderer(*toText(child)); continue; } if (child->isElementNode()) detachRenderTree(*toElement(child), detachType); } current.clearChildNeedsStyleRecalc(); }
static void attachChildren(ContainerNode& current) { for (Node* child = current.firstChild(); child; child = child->nextSibling()) { ASSERT(!child->attached() || childAttachedAllowedWhenAttachingChildren(current)); if (child->attached()) continue; if (child->isTextNode()) { attachTextRenderer(*toText(child)); continue; } if (child->isElementNode()) attachRenderTree(*toElement(child), nullptr); } }
int HTMLTableRowElement::rowIndex() const { ContainerNode* table = parentNode(); if (!table) return -1; table = table->parentNode(); if (!table || !table->hasTagName(tableTag)) return -1; // To match Firefox, the row indices work like this: // Rows from the first <thead> are numbered before all <tbody> rows. // Rows from the first <tfoot> are numbered after all <tbody> rows. // Rows from other <thead> and <tfoot> elements don't get row indices at all. int rIndex = 0; if (HTMLTableSectionElement* head = static_cast<HTMLTableElement*>(table)->tHead()) { for (Node *row = head->firstChild(); row; row = row->nextSibling()) { if (row == this) return rIndex; if (row->hasTagName(trTag)) ++rIndex; } } for (Node *node = table->firstChild(); node; node = node->nextSibling()) { if (node->hasTagName(tbodyTag)) { HTMLTableSectionElement* section = static_cast<HTMLTableSectionElement*>(node); for (Node* row = section->firstChild(); row; row = row->nextSibling()) { if (row == this) return rIndex; if (row->hasTagName(trTag)) ++rIndex; } } } if (HTMLTableSectionElement* foot = static_cast<HTMLTableElement*>(table)->tFoot()) { for (Node *row = foot->firstChild(); row; row = row->nextSibling()) { if (row == this) return rIndex; if (row->hasTagName(trTag)) ++rIndex; } } // We get here for rows that are in <thead> or <tfoot> sections other than the main header and footer. return -1; }
// FIXME: It is silly that these functions are in HTMLCollection.cpp. Node* LiveNodeListBase::item(unsigned offset) const { if (isItemCacheValid() && cachedItemOffset() == offset) return cachedItem(); if (isLengthCacheValid() && cachedLength() <= offset) return 0; #if ENABLE(MICRODATA) if (type() == ItemProperties) static_cast<const HTMLPropertiesCollection*>(this)->updateRefElements(); else if (type() == PropertyNodeListType) static_cast<const PropertyNodeList*>(this)->updateRefElements(); #endif ContainerNode* root = rootContainerNode(); if (!root) { // FIMXE: In someTextNode.childNodes case the root is Text. We shouldn't even make a LiveNodeList for that. setLengthCache(0); return 0; } if (isLengthCacheValid() && !overridesItemAfter() && isLastItemCloserThanLastOrCachedItem(offset)) { Node* lastItem = itemBefore(0); ASSERT(lastItem); setItemCache(lastItem, cachedLength() - 1, 0); } else if (!isItemCacheValid() || isFirstItemCloserThanCachedItem(offset) || (overridesItemAfter() && offset < cachedItemOffset())) { unsigned offsetInArray = 0; Node* firstItem; if (type() == ChildNodeListType) firstItem = root->firstChild(); else if (isNodeList(type())) firstItem = traverseLiveNodeListFirstElement(root); else firstItem = static_cast<const HTMLCollection*>(this)->traverseFirstElement(offsetInArray, root); if (!firstItem) { setLengthCache(0); return 0; } setItemCache(firstItem, 0, offsetInArray); ASSERT(!cachedItemOffset()); } if (cachedItemOffset() == offset) return cachedItem(); return itemBeforeOrAfterCachedItem(offset, root); }
inline void DistributionPool::populateChildren(const ContainerNode& parent) { clear(); for (Node* child = parent.firstChild(); child; child = child->nextSibling()) { if (isActiveInsertionPoint(*child)) { InsertionPoint* insertionPoint = toInsertionPoint(child); for (size_t i = 0; i < insertionPoint->size(); ++i) m_nodes.append(insertionPoint->at(i)); } else { m_nodes.append(child); } } m_distributed.resize(m_nodes.size()); m_distributed.fill(false); }
inline void DistributionPool::populateChildren(const ContainerNode& parent) { clear(); for (Node* child = parent.firstChild(); child; child = child->nextSibling()) { if (isHTMLSlotElement(child)) { // TODO(hayato): Support re-distribution across v0 and v1 shadow trees continue; } if (isActiveInsertionPoint(*child)) { InsertionPoint* insertionPoint = toInsertionPoint(child); for (size_t i = 0; i < insertionPoint->distributedNodesSize(); ++i) m_nodes.append(insertionPoint->distributedNodeAt(i)); } else { m_nodes.append(child); } } m_distributed.resize(m_nodes.size()); m_distributed.fill(false); }
void notifyNodeRemovedFromTree(ContainerNode& insertionPoint, ContainerNode& node) { NoEventDispatchAssertion assertNoEventDispatch; ASSERT(!insertionPoint.inDocument()); node.removedFrom(insertionPoint); for (Node* child = node.firstChild(); child; child = child->nextSibling()) { if (is<ContainerNode>(*child)) notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(*child)); } if (!is<Element>(node)) return; if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) notifyNodeRemovedFromTree(insertionPoint, *root.get()); }
void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode& container) { // We have to tell all children that their parent has died. Node* next = nullptr; for (auto* node = container.firstChild(); node; node = next) { ASSERT(!node->m_deletionHasBegun); next = node->nextSibling(); node->setNextSibling(nullptr); node->setParentNode(nullptr); container.setFirstChild(next); if (next) next->setPreviousSibling(nullptr); if (!node->refCount()) { #ifndef NDEBUG node->m_deletionHasBegun = true; #endif // Add the node to the list of nodes to be deleted. // Reuse the nextSibling pointer for this purpose. if (tail) tail->setNextSibling(node); else head = node; tail = node; } else { Ref<Node> protect(*node); // removedFromDocument may remove remove all references to this node. if (Document* containerDocument = container.ownerDocument()) containerDocument->adoptIfNeeded(*node); if (node->isInTreeScope()) notifyChildNodeRemoved(container, *node); } } container.setLastChild(nullptr); }
static inline bool hasOneTextChild(ContainerNode& node) { return hasOneChild(node) && node.firstChild()->isTextNode(); }
static inline bool hasOneChild(ContainerNode& node) { Node* firstChild = node.firstChild(); return firstChild && !firstChild->nextSibling(); }