diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index 371075976ac4..7bc8032a2345 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -3955,53 +3955,74 @@ class HTMLEditor final : public TextEditor, nsTArray>& aOutArrayOfNodes); /** - * CollectListAndTableRelatedElementsAt() collects list elements and - * table related elements from aNode (meaning aNode may be in the first of - * the result) to the root element. + * AutoHTMLFragmentBoundariesFixer fixes both edges of topmost child nodes + * which are created with SubtreeContentIterator. */ - static void CollectListAndTableRelatedElementsAt( - nsINode& aNode, - nsTArray>& aOutArrayOfListAndTableElements); + class MOZ_STACK_CLASS AutoHTMLFragmentBoundariesFixer final { + public: + /** + * @param aArrayOfTopMostChildNodes + * [in/out] The topmost child nodes which will be + * inserted into the DOM tree. Both edges, i.e., + * first node and last node in this array will be + * checked whether they can be insertted into + * another DOM tree. If not, it'll replaces some + * orphan nodes around nodes with proper parent. + */ + explicit AutoHTMLFragmentBoundariesFixer( + nsTArray>& aArrayOfTopMostChildNodes); - /** - * TODO: Document what this does. - */ - static Element* DiscoverPartialListsAndTables( - const nsTArray>& aArrayOfNodes, - const nsTArray>& - aArrayOfListAndTableRelatedElements); + private: + /** + * CollectListAndTableRelatedElementsAt() collects list elements and + * table related elements from aNode (meaning aNode may be in the first of + * the result) to the root element. + */ + void CollectListAndTableRelatedElementsAt( + nsINode& aNode, + nsTArray>& aOutArrayOfListAndTableElements) + const; - /** - * TODO: Document what this does. - */ - enum class StartOrEnd { start, end }; - static void ReplaceOrphanedStructure( - StartOrEnd aStartOrEnd, nsTArray>& aArrayOfNodes, - Element& aListOrTableElement); + /** + * TODO: Document what this does. + */ + Element* DiscoverPartialListsAndTables( + const nsTArray>& aArrayOfNodes, + const nsTArray>& + aArrayOfListAndTableRelatedElements) const; - /** - * FindReplaceableTableElement() is a helper method of - * ReplaceOrphanedStructure(). If aNodeMaybeInTableElement is a descendant - * of aTableElement, returns aNodeMaybeInTableElement or its nearest ancestor - * whose tag name is ``, ``, ``, ``, ``, `` - * or ``. - * - * @param aTableElement Must be a `` element. - * @param aNodeMaybeInTableElement A node which may be in aTableElement. - */ - static Element* FindReplaceableTableElement( - Element& aTableElement, nsINode& aNodeMaybeInTableElement); + /** + * TODO: Document what this does. + */ + enum class StartOrEnd { start, end }; + void ReplaceOrphanedStructure( + StartOrEnd aStartOrEnd, nsTArray>& aArrayOfNodes, + Element& aListOrTableElement) const; - /** - * IsReplaceableListElement() is a helper method of - * ReplaceOrphanedStructure(). If aNodeMaybeInListElement is a descendant - * of aListElement, returns true. Otherwise, false. - * - * @param aListElement Must be a list element. - * @param aNodeMaybeInListElement A node which may be in aListElement. - */ - static bool IsReplaceableListElement(Element& aListElement, - nsINode& aNodeMaybeInListElement); + /** + * FindReplaceableTableElement() is a helper method of + * ReplaceOrphanedStructure(). If aNodeMaybeInTableElement is a descendant + * of aTableElement, returns aNodeMaybeInTableElement or its nearest + * ancestor whose tag name is ``, ``, ``, + * `` or `
`, ``, `
`. + * + * @param aTableElement Must be a `` element. + * @param aNodeMaybeInTableElement A node which may be in aTableElement. + */ + Element* FindReplaceableTableElement( + Element& aTableElement, nsINode& aNodeMaybeInTableElement) const; + + /** + * IsReplaceableListElement() is a helper method of + * ReplaceOrphanedStructure(). If aNodeMaybeInListElement is a descendant + * of aListElement, returns true. Otherwise, false. + * + * @param aListElement Must be a list element. + * @param aNodeMaybeInListElement A node which may be in aListElement. + */ + bool IsReplaceableListElement(Element& aListElement, + nsINode& aNodeMaybeInListElement) const; + }; /** * GetBetterInsertionPointFor() returns better insertion point to insert diff --git a/editor/libeditor/HTMLEditorDataTransfer.cpp b/editor/libeditor/HTMLEditorDataTransfer.cpp index fdd3f61b5dc9..9ab36efd9283 100644 --- a/editor/libeditor/HTMLEditorDataTransfer.cpp +++ b/editor/libeditor/HTMLEditorDataTransfer.cpp @@ -396,36 +396,9 @@ nsresult HTMLEditor::DoInsertHTMLWithContext( } } - // build up list of parents of first node in list that are either - // lists or tables. First examine front of paste node list. - AutoTArray, 4> - arrayOfListAndTableRelatedElementsAtStart; - HTMLEditor::CollectListAndTableRelatedElementsAt( - nodeList[0], arrayOfListAndTableRelatedElementsAtStart); - if (!arrayOfListAndTableRelatedElementsAtStart.IsEmpty()) { - Element* listOrTableElement = HTMLEditor::DiscoverPartialListsAndTables( - nodeList, arrayOfListAndTableRelatedElementsAtStart); - // if we have pieces of tables or lists to be inserted, let's force the - // paste to deal with table elements right away, so that it doesn't orphan - // some table or list contents outside the table or list. - if (listOrTableElement) { - HTMLEditor::ReplaceOrphanedStructure(StartOrEnd::start, nodeList, - *listOrTableElement); - } - } - - // Now go through the same process again for the end of the paste node list. - AutoTArray, 4> arrayOfListAndTableRelatedElementsAtEnd; - HTMLEditor::CollectListAndTableRelatedElementsAt( - nodeList.LastElement(), arrayOfListAndTableRelatedElementsAtEnd); - if (!arrayOfListAndTableRelatedElementsAtEnd.IsEmpty()) { - Element* listOrTableElement = HTMLEditor::DiscoverPartialListsAndTables( - nodeList, arrayOfListAndTableRelatedElementsAtEnd); - // don't orphan partial list or table structure - if (listOrTableElement) { - HTMLEditor::ReplaceOrphanedStructure(StartOrEnd::end, nodeList, - *listOrTableElement); - } + { // Block only for AutoHTMLFragmentBoundariesFixer to hide it from the + // following code. Note that it may modify nodeList. + AutoHTMLFragmentBoundariesFixer fixPiecesOfTablesAndLists(nodeList); } MOZ_ASSERT(pointToInsert.GetContainer()->GetChildAt_Deprecated( @@ -2740,10 +2713,50 @@ void HTMLEditor::CollectTopMostChildNodesCompletelyInRange( iter.AppendAllNodesToArray(aOutArrayOfNodes); } -// static -void HTMLEditor::CollectListAndTableRelatedElementsAt( - nsINode& aNode, - nsTArray>& aOutArrayOfListAndTableElements) { +/****************************************************************************** + * HTMLEditor::AutoHTMLFragmentBoundariesFixer + ******************************************************************************/ + +HTMLEditor::AutoHTMLFragmentBoundariesFixer::AutoHTMLFragmentBoundariesFixer( + nsTArray>& aArrayOfTopMostChildNodes) { + // build up list of parents of first node in list that are either + // lists or tables. First examine front of paste node list. + AutoTArray, 4> + arrayOfListAndTableRelatedElementsAtStart; + CollectListAndTableRelatedElementsAt( + aArrayOfTopMostChildNodes[0], arrayOfListAndTableRelatedElementsAtStart); + if (!arrayOfListAndTableRelatedElementsAtStart.IsEmpty()) { + Element* listOrTableElement = DiscoverPartialListsAndTables( + aArrayOfTopMostChildNodes, arrayOfListAndTableRelatedElementsAtStart); + // if we have pieces of tables or lists to be inserted, let's force the + // paste to deal with table elements right away, so that it doesn't orphan + // some table or list contents outside the table or list. + if (listOrTableElement) { + ReplaceOrphanedStructure(StartOrEnd::start, aArrayOfTopMostChildNodes, + *listOrTableElement); + } + } + + // Now go through the same process again for the end of the paste node list. + AutoTArray, 4> arrayOfListAndTableRelatedElementsAtEnd; + CollectListAndTableRelatedElementsAt(aArrayOfTopMostChildNodes.LastElement(), + arrayOfListAndTableRelatedElementsAtEnd); + if (!arrayOfListAndTableRelatedElementsAtEnd.IsEmpty()) { + Element* listOrTableElement = DiscoverPartialListsAndTables( + aArrayOfTopMostChildNodes, arrayOfListAndTableRelatedElementsAtEnd); + // don't orphan partial list or table structure + if (listOrTableElement) { + ReplaceOrphanedStructure(StartOrEnd::end, aArrayOfTopMostChildNodes, + *listOrTableElement); + } + } +} + +void HTMLEditor::AutoHTMLFragmentBoundariesFixer:: + CollectListAndTableRelatedElementsAt( + nsINode& aNode, + nsTArray>& aOutArrayOfListAndTableElements) + const { for (nsIContent* content = nsIContent::FromNode(&aNode); content; content = content->GetParentElement()) { if (HTMLEditUtils::IsList(content) || HTMLEditUtils::IsTable(content)) { @@ -2752,11 +2765,11 @@ void HTMLEditor::CollectListAndTableRelatedElementsAt( } } -// static -Element* HTMLEditor::DiscoverPartialListsAndTables( +Element* +HTMLEditor::AutoHTMLFragmentBoundariesFixer::DiscoverPartialListsAndTables( const nsTArray>& aArrayOfNodes, - const nsTArray>& - aArrayOfListAndTableRelatedElements) { + const nsTArray>& aArrayOfListAndTableRelatedElements) + const { Element* lastFoundAncestorListOrTableElement = nullptr; for (auto& node : aArrayOfNodes) { if (HTMLEditUtils::IsTableElement(node) && @@ -2831,9 +2844,9 @@ Element* HTMLEditor::DiscoverPartialListsAndTables( return lastFoundAncestorListOrTableElement; } -// static -Element* HTMLEditor::FindReplaceableTableElement( - Element& aTableElement, nsINode& aNodeMaybeInTableElement) { +Element* +HTMLEditor::AutoHTMLFragmentBoundariesFixer::FindReplaceableTableElement( + Element& aTableElement, nsINode& aNodeMaybeInTableElement) const { MOZ_ASSERT(aTableElement.IsHTMLElement(nsGkAtoms::table)); // Perhaps, this is designed for climbing up the DOM tree from // aNodeMaybeInTableElement to aTableElement and making sure that @@ -2873,9 +2886,8 @@ Element* HTMLEditor::FindReplaceableTableElement( return nullptr; } -// static -bool HTMLEditor::IsReplaceableListElement(Element& aListElement, - nsINode& aNodeMaybeInListElement) { +bool HTMLEditor::AutoHTMLFragmentBoundariesFixer::IsReplaceableListElement( + Element& aListElement, nsINode& aNodeMaybeInListElement) const { MOZ_ASSERT(HTMLEditUtils::IsList(&aListElement)); // Perhaps, this is designed for climbing up the DOM tree from // aNodeMaybeInListElement to aListElement and making sure that @@ -2913,10 +2925,9 @@ bool HTMLEditor::IsReplaceableListElement(Element& aListElement, return false; } -// static -void HTMLEditor::ReplaceOrphanedStructure( +void HTMLEditor::AutoHTMLFragmentBoundariesFixer::ReplaceOrphanedStructure( StartOrEnd aStartOrEnd, nsTArray>& aArrayOfNodes, - Element& aListOrTableElement) { + Element& aListOrTableElement) const { MOZ_ASSERT(!aArrayOfNodes.IsEmpty()); OwningNonNull& firstOrLastChildNode = @@ -2926,15 +2937,14 @@ void HTMLEditor::ReplaceOrphanedStructure( // Find substructure of list or table that must be included in paste. Element* replaceElement; if (HTMLEditUtils::IsList(&aListOrTableElement)) { - if (!HTMLEditor::IsReplaceableListElement(aListOrTableElement, - firstOrLastChildNode)) { + if (!IsReplaceableListElement(aListOrTableElement, firstOrLastChildNode)) { return; } replaceElement = &aListOrTableElement; } else { MOZ_ASSERT(aListOrTableElement.IsHTMLElement(nsGkAtoms::table)); - replaceElement = HTMLEditor::FindReplaceableTableElement( - aListOrTableElement, firstOrLastChildNode); + replaceElement = + FindReplaceableTableElement(aListOrTableElement, firstOrLastChildNode); if (!replaceElement) { return; }