Bug 1588688 - part 7-8: Encapsulate the first and last node fixer into a stack only class r=m_kato

In these patches, we know that there are a lot of helper methods to fix node
list which is created by `SubtreeContentIterator` and will be inserted into
the editor's DOM tree, and they are not used by other places.  So, we can
encapsulate them into a stack only class for making their usage clearer.

Differential Revision: https://phabricator.services.mozilla.com/D61981

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2020-02-14 02:59:32 +00:00
parent 06359fff27
commit 7656ed2a64
2 changed files with 124 additions and 93 deletions

View File

@ -3955,53 +3955,74 @@ class HTMLEditor final : public TextEditor,
nsTArray<OwningNonNull<nsINode>>& 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<OwningNonNull<Element>>& 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<OwningNonNull<nsINode>>& aArrayOfTopMostChildNodes);
/**
* TODO: Document what this does.
*/
static Element* DiscoverPartialListsAndTables(
const nsTArray<OwningNonNull<nsINode>>& aArrayOfNodes,
const nsTArray<OwningNonNull<Element>>&
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<OwningNonNull<Element>>& aOutArrayOfListAndTableElements)
const;
/**
* TODO: Document what this does.
*/
enum class StartOrEnd { start, end };
static void ReplaceOrphanedStructure(
StartOrEnd aStartOrEnd, nsTArray<OwningNonNull<nsINode>>& aArrayOfNodes,
Element& aListOrTableElement);
/**
* TODO: Document what this does.
*/
Element* DiscoverPartialListsAndTables(
const nsTArray<OwningNonNull<nsINode>>& aArrayOfNodes,
const nsTArray<OwningNonNull<Element>>&
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 `<td>`, `<th>`, `<tr>`, `<thead>`, `<tfoot>`, `<tbody>`
* or `<caption>`.
*
* @param aTableElement Must be a `<table>` 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<OwningNonNull<nsINode>>& 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 `<td>`, `<th>`, `<tr>`, `<thead>`, `<tfoot>`,
* `<tbody>` or `<caption>`.
*
* @param aTableElement Must be a `<table>` 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

View File

@ -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<OwningNonNull<Element>, 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<OwningNonNull<Element>, 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<OwningNonNull<Element>>& aOutArrayOfListAndTableElements) {
/******************************************************************************
* HTMLEditor::AutoHTMLFragmentBoundariesFixer
******************************************************************************/
HTMLEditor::AutoHTMLFragmentBoundariesFixer::AutoHTMLFragmentBoundariesFixer(
nsTArray<OwningNonNull<nsINode>>& 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<OwningNonNull<Element>, 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<OwningNonNull<Element>, 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<OwningNonNull<Element>>& 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<OwningNonNull<nsINode>>& aArrayOfNodes,
const nsTArray<OwningNonNull<Element>>&
aArrayOfListAndTableRelatedElements) {
const nsTArray<OwningNonNull<Element>>& 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<OwningNonNull<nsINode>>& aArrayOfNodes,
Element& aListOrTableElement) {
Element& aListOrTableElement) const {
MOZ_ASSERT(!aArrayOfNodes.IsEmpty());
OwningNonNull<nsINode>& 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;
}