diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 08d869c3d3e8..53b1a76fb33b 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -1543,12 +1543,12 @@ EditorBase::JoinNodes(nsIDOMNode* aLeftNode, nsCOMPtr leftNode = do_QueryInterface(aLeftNode); nsCOMPtr rightNode = do_QueryInterface(aRightNode); NS_ENSURE_STATE(leftNode && rightNode && leftNode->GetParentNode()); - return JoinNodes(*leftNode, *rightNode); + return JoinNodesWithTransaction(*leftNode, *rightNode); } nsresult -EditorBase::JoinNodes(nsINode& aLeftNode, - nsINode& aRightNode) +EditorBase::JoinNodesWithTransaction(nsINode& aLeftNode, + nsINode& aRightNode) { nsCOMPtr parent = aLeftNode.GetParentNode(); MOZ_ASSERT(parent); @@ -4146,13 +4146,9 @@ EditorBase::SplitNodeDeepWithTransaction( return SplitNodeResult(NS_ERROR_FAILURE); } -/** - * This joins two like nodes "deeply", joining children as appropriate. - * Returns the point of the join, or (nullptr, -1) in case of error. - */ EditorDOMPoint -EditorBase::JoinNodeDeep(nsIContent& aLeftNode, - nsIContent& aRightNode) +EditorBase::JoinNodesDeepWithTransaction(nsIContent& aLeftNode, + nsIContent& aRightNode) { // While the rightmost children and their descendants of the left node match // the leftmost children and their descendants of the right node, join them @@ -4168,7 +4164,7 @@ EditorBase::JoinNodeDeep(nsIContent& aLeftNode, uint32_t length = leftNodeToJoin->Length(); // Do the join - nsresult rv = JoinNodes(*leftNodeToJoin, *rightNodeToJoin); + nsresult rv = JoinNodesWithTransaction(*leftNodeToJoin, *rightNodeToJoin); if (NS_WARN_IF(NS_FAILED(rv))) { return EditorDOMPoint(); } diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index 747a975aa6db..0fed144bf243 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -389,7 +389,17 @@ public: SplitNodeWithTransaction(const EditorDOMPointBase& aStartOfRightNode, ErrorResult& aResult); - nsresult JoinNodes(nsINode& aLeftNode, nsINode& aRightNode); + /** + * JoinNodesWithTransaction() joins aLeftNode and aRightNode. Content of + * aLeftNode will be merged into aRightNode. Actual implemenation of this + * method is JoinNodesImpl(). So, see its explanation for the detail. + * + * @param aLeftNode Will be removed from the DOM tree. + * @param aRightNode The node which will be new container of the content of + * aLeftNode. + */ + nsresult JoinNodesWithTransaction(nsINode& aLeftNode, nsINode& aRightNode); + nsresult MoveNode(nsIContent* aNode, nsINode* aParent, int32_t aOffset); /** @@ -803,7 +813,11 @@ public: ErrorResult& aError); /** - * JoinNodes() takes 2 nodes and merge their content|children. + * JoinNodesImpl() merges contents in aNodeToJoin to aNodeToKeep and remove + * aNodeToJoin from the DOM tree. aNodeToJoin and aNodeToKeep must have + * same parent, aParent. Additionally, if one of aNodeToJoin or aNodeToKeep + * is a text node, the other must be a text node. + * * @param aNodeToKeep The node that will remain after the join. * @param aNodeToJoin The node that will be joined with aNodeToKeep. * There is no requirement that the two nodes be of the @@ -1275,8 +1289,20 @@ public: const EditorDOMPointBase& aDeepestStartOfRightNode, SplitAtEdges aSplitAtEdges); - EditorDOMPoint JoinNodeDeep(nsIContent& aLeftNode, - nsIContent& aRightNode); + /** + * JoinNodesDeepWithTransaction() joins aLeftNode and aRightNode "deeply". + * First, they are joined simply, then, new right node is assumed as the + * child at length of the left node before joined and new left node is + * assumed as its previous sibling. Then, they will be joined again. + * And then, these steps are repeated. + * + * @param aLeftNode The node which will be removed form the tree. + * @param aRightNode The node which will be inserted the contents of + * aLeftNode. + * @return The point of the first child of the last right node. + */ + EditorDOMPoint JoinNodesDeepWithTransaction(nsIContent& aLeftNode, + nsIContent& aRightNode); nsresult GetString(const nsAString& name, nsAString& value); diff --git a/editor/libeditor/HTMLEditRules.cpp b/editor/libeditor/HTMLEditRules.cpp index 9eebc9071f19..ac8894f8d0e9 100644 --- a/editor/libeditor/HTMLEditRules.cpp +++ b/editor/libeditor/HTMLEditRules.cpp @@ -2198,10 +2198,10 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection, // origCollapsed is used later to determine whether we should join blocks. We // don't really care about bCollapsed because it will be modified by - // ExtendSelectionForDelete later. TryToJoinBlocks() should happen if the - // original selection is collapsed and the cursor is at the end of a block - // element, in which case ExtendSelectionForDelete would always make the - // selection not collapsed. + // ExtendSelectionForDelete later. TryToJoinBlocksWithTransaction() should + // happen if the original selection is collapsed and the cursor is at the end + // of a block element, in which case ExtendSelectionForDelete would always + // make the selection not collapsed. bool bCollapsed = aSelection->Collapsed(); bool join = false; bool origCollapsed = bCollapsed; @@ -2468,7 +2468,9 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection, // Are they both text nodes? If so, join them! if (startNode == stepbrother && startNode->GetAsText() && sibling->GetAsText()) { - EditorDOMPoint pt = JoinNodesSmart(*sibling, *startNode->AsContent()); + EditorDOMPoint pt = + JoinNearestEditableNodesWithTransaction(*sibling, + *startNode->AsContent()); if (NS_WARN_IF(!pt.IsSet())) { return NS_ERROR_FAILURE; } @@ -2564,7 +2566,8 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection, NS_ENSURE_STATE(leftNode && leftNode->IsContent() && rightNode && rightNode->IsContent()); EditActionResult ret = - TryToJoinBlocks(*leftNode->AsContent(), *rightNode->AsContent()); + TryToJoinBlocksWithTransaction(*leftNode->AsContent(), + *rightNode->AsContent()); *aHandled |= ret.Handled(); *aCancel |= ret.Canceled(); if (NS_WARN_IF(ret.Failed())) { @@ -2572,9 +2575,9 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection, } } - // If TryToJoinBlocks() didn't handle it and it's not canceled, - // user may want to modify the start leaf node or the last leaf node - // of the block. + // If TryToJoinBlocksWithTransaction() didn't handle it and it's not + // canceled, user may want to modify the start leaf node or the last leaf + // node of the block. if (!*aHandled && !*aCancel && leafNode != startNode) { int32_t offset = aAction == nsIEditor::ePrevious ? @@ -2632,7 +2635,8 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection, address_of(selPointNode), &selPointOffset); NS_ENSURE_STATE(leftNode->IsContent() && rightNode->IsContent()); EditActionResult ret = - TryToJoinBlocks(*leftNode->AsContent(), *rightNode->AsContent()); + TryToJoinBlocksWithTransaction(*leftNode->AsContent(), + *rightNode->AsContent()); // This should claim that trying to join the block means that // this handles the action because the caller shouldn't do anything // anymore in this case. @@ -2735,7 +2739,8 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection, // Join blocks NS_ENSURE_STATE(mHTMLEditor); EditorDOMPoint pt = - mHTMLEditor->JoinNodeDeep(*leftParent, *rightParent); + mHTMLEditor->JoinNodesDeepWithTransaction(*leftParent, + *rightParent); if (NS_WARN_IF(!pt.IsSet())) { return NS_ERROR_FAILURE; } @@ -2813,7 +2818,8 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection, } if (join) { - EditActionResult ret = TryToJoinBlocks(*leftParent, *rightParent); + EditActionResult ret = + TryToJoinBlocksWithTransaction(*leftParent, *rightParent); MOZ_ASSERT(*aHandled); *aCancel |= ret.Canceled(); if (NS_WARN_IF(ret.Failed())) { @@ -2980,8 +2986,8 @@ HTMLEditRules::GetGoodSelPointForNode(nsINode& aNode, } EditActionResult -HTMLEditRules::TryToJoinBlocks(nsIContent& aLeftNode, - nsIContent& aRightNode) +HTMLEditRules::TryToJoinBlocksWithTransaction(nsIContent& aLeftNode, + nsIContent& aRightNode) { if (NS_WARN_IF(!mHTMLEditor)) { return EditActionIgnored(NS_ERROR_UNEXPECTED); @@ -3282,7 +3288,8 @@ HTMLEditRules::TryToJoinBlocks(nsIContent& aLeftNode, if (mergeLists || leftBlock->NodeInfo()->NameAtom() == rightBlock->NodeInfo()->NameAtom()) { // Nodes are same type. merge them. - EditorDOMPoint pt = JoinNodesSmart(*leftBlock, *rightBlock); + EditorDOMPoint pt = + JoinNearestEditableNodesWithTransaction(*leftBlock, *rightBlock); if (pt.IsSet() && mergeLists) { RefPtr newBlock = ConvertListType(rightBlock, existingList, nsGkAtoms::li); @@ -7830,16 +7837,9 @@ HTMLEditRules::MaybeSplitAncestorsForInsertWithTransaction( return splitNodeResult; } -/** - * JoinNodesSmart: Join two nodes, doing whatever makes sense for their - * children (which often means joining them, too). aNodeLeft & aNodeRight must - * be same type of node. - * - * Returns the point where they're merged, or (nullptr, -1) on failure. - */ EditorDOMPoint -HTMLEditRules::JoinNodesSmart(nsIContent& aNodeLeft, - nsIContent& aNodeRight) +HTMLEditRules::JoinNearestEditableNodesWithTransaction(nsIContent& aNodeLeft, + nsIContent& aNodeRight) { // Caller responsible for left and right node being the same type nsCOMPtr parent = aNodeLeft.GetParentNode(); @@ -7866,7 +7866,7 @@ HTMLEditRules::JoinNodesSmart(nsIContent& aNodeLeft, // Separate join rules for differing blocks if (HTMLEditUtils::IsList(&aNodeLeft) || aNodeLeft.GetAsText()) { // For lists, merge shallow (wouldn't want to combine list items) - nsresult rv = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight); + nsresult rv = mHTMLEditor->JoinNodesWithTransaction(aNodeLeft, aNodeRight); if (NS_WARN_IF(NS_FAILED(rv))) { return EditorDOMPoint(); } @@ -7894,7 +7894,7 @@ HTMLEditRules::JoinNodesSmart(nsIContent& aNodeLeft, if (NS_WARN_IF(!mHTMLEditor)) { return EditorDOMPoint(); } - nsresult rv = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight); + nsresult rv = mHTMLEditor->JoinNodesWithTransaction(aNodeLeft, aNodeRight); if (NS_WARN_IF(NS_FAILED(rv))) { return EditorDOMPoint(); } @@ -7908,7 +7908,7 @@ HTMLEditRules::JoinNodesSmart(nsIContent& aNodeLeft, if (NS_WARN_IF(!mHTMLEditor)) { return EditorDOMPoint(); } - return JoinNodesSmart(*lastLeft, *firstRight); + return JoinNearestEditableNodesWithTransaction(*lastLeft, *firstRight); } return ret; } diff --git a/editor/libeditor/HTMLEditRules.h b/editor/libeditor/HTMLEditRules.h index d6323567a75a..71834948192c 100644 --- a/editor/libeditor/HTMLEditRules.h +++ b/editor/libeditor/HTMLEditRules.h @@ -186,12 +186,13 @@ protected: nsIEditor::EDirection aAction); /** - * TryToJoinBlocks() tries to join two block elements. The right element is - * always joined to the left element. If the elements are the same type and - * not nested within each other, JoinNodesSmart() is called (example, joining - * two list items together into one). If the elements are not the same type, - * or one is a descendant of the other, we instead destroy the right block - * placing its children into leftblock. DTD containment rules are followed + * TryToJoinBlocksWithTransaction() tries to join two block elements. The + * right element is always joined to the left element. If the elements are + * the same type and not nested within each other, + * JoinEditableNodesWithTransaction() is called (example, joining two list + * items together into one). If the elements are not the same type, or one + * is a descendant of the other, we instead destroy the right block placing + * its children into leftblock. DTD containment rules are followed * throughout. * * @return Sets canceled to true if the operation should do @@ -202,8 +203,8 @@ protected: * be joined or it's impossible to join them but it's not * unexpected case, this returns true with this. */ - EditActionResult TryToJoinBlocks(nsIContent& aLeftNode, - nsIContent& aRightNode); + EditActionResult TryToJoinBlocksWithTransaction(nsIContent& aLeftNode, + nsIContent& aRightNode); /** * MoveBlock() moves the content from aRightBlock starting from aRightOffset @@ -459,8 +460,30 @@ protected: nsAtom& aTag, const EditorDOMPointBase& aStartOfDeepestRightNode); - EditorDOMPoint JoinNodesSmart(nsIContent& aNodeLeft, - nsIContent& aNodeRight); + /** + * JoinNearestEditableNodesWithTransaction() joins two editable nodes which + * are themselves or the nearest editable node of aLeftNode and aRightNode. + * XXX This method's behavior is odd. For example, if user types Backspace + * key at the second editable paragraph in this case: + *
+ *

first editable paragraph

+ *

non-editable paragraph

+ *

second editable paragraph

+ *
+ * The first editable paragraph's content will be moved into the second + * editable paragraph and the non-editable paragraph becomes the first + * paragraph of the editor. I don't think that it's expected behavior of + * any users... + * + * @param aLeftNode The node which will be removed. + * @param aRightNode The node which will be inserted the content of + * aLeftNode. + * @return The point at the first child of aRightNode. + */ + EditorDOMPoint + JoinNearestEditableNodesWithTransaction(nsIContent& aLeftNode, + nsIContent& aRightNode); + Element* GetTopEnclosingMailCite(nsINode& aNode); nsresult PopListItem(nsIContent& aListItem, bool* aOutOfList = nullptr); nsresult RemoveListStructure(Element& aList); diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 841fd70aaf63..fa295a2bbd5c 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -3625,7 +3625,7 @@ HTMLEditor::GetEnclosingTable(nsIDOMNode* aNode) * This method scans the selection for adjacent text nodes * and collapses them into a single text node. * "adjacent" means literally adjacent siblings of the same parent. - * Uses EditorBase::JoinNodes so action is undoable. + * Uses EditorBase::JoinNodesWithTransaction() so action is undoable. * Should be called within the context of a batch transaction. */ nsresult @@ -3668,8 +3668,10 @@ HTMLEditor::CollapseAdjacentTextNodes(nsRange* aInRange) // get the prev sibling of the right node, and see if its leftTextNode nsCOMPtr prevSibOfRightNode = rightTextNode->GetPreviousSibling(); if (prevSibOfRightNode && prevSibOfRightNode == leftTextNode) { - rv = JoinNodes(*leftTextNode, *rightTextNode); - NS_ENSURE_SUCCESS(rv, rv); + rv = JoinNodesWithTransaction(*leftTextNode, *rightTextNode); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } textNodes.RemoveElementAt(0); // remove the leftmost text node from the list diff --git a/editor/libeditor/HTMLStyleEditor.cpp b/editor/libeditor/HTMLStyleEditor.cpp index e1e6d9f79ebd..9d44df125fa1 100644 --- a/editor/libeditor/HTMLStyleEditor.cpp +++ b/editor/libeditor/HTMLStyleEditor.cpp @@ -366,8 +366,10 @@ HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aNode, nsresult rv = MoveNode(&aNode, previousSibling, -1); NS_ENSURE_SUCCESS(rv, rv); if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) { - rv = JoinNodes(*previousSibling, *nextSibling); - NS_ENSURE_SUCCESS(rv, rv); + rv = JoinNodesWithTransaction(*previousSibling, *nextSibling); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } return NS_OK; }