Bug 1655391 - Make new stack only class handle deletion at current block boundary r=m_kato

This patch creates new stack only class, `HTMLEditor::AutoBlockElementsJoiner`
and splits `HTMLEditor::HandleDeleteCollapsedSelectionAtCurrentBlockBoundary()`
to considering the content nodes to be joined part and doing join the nodes
part.

Differential Revision: https://phabricator.services.mozilla.com/D85567
This commit is contained in:
Masayuki Nakano 2020-08-03 09:52:38 +00:00
parent 749a1d0d03
commit 04fa88ff17
2 changed files with 97 additions and 49 deletions

View File

@ -2612,14 +2612,16 @@ EditActionResult HTMLEditor::HandleDeleteAroundCollapsedSelection(
if (NS_WARN_IF(!scanFromStartPointResult.GetContent()->IsElement())) {
return EditActionResult(NS_ERROR_FAILURE);
}
EditActionResult result =
HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(
aDirectionAndAmount,
MOZ_KnownLive(*scanFromStartPointResult.ElementPtr()), startPoint);
NS_WARNING_ASSERTION(
result.Succeeded(),
"HTMLEditor::HandleDeleteCollapsedSelectionAtCurrentBlockBoundary() "
"failed");
AutoBlockElementsJoiner joiner;
if (!joiner.PrepareToDeleteCollapsedSelectionAtCurrentBlockBoundary(
*this, aDirectionAndAmount, *scanFromStartPointResult.ElementPtr(),
startPoint)) {
return EditActionCanceled();
}
EditActionResult result = joiner.Run(*this, startPoint);
NS_WARNING_ASSERTION(result.Succeeded(),
"HTMLEditor::AutoBlockElementsJoiner::Run() failed "
"(current block boundary)");
return result;
}
@ -3157,52 +3159,54 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtOtherBlockBoundary(
return result;
}
EditActionResult
HTMLEditor::HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(
nsIEditor::EDirection aDirectionAndAmount, Element& aCurrentBlockElement,
const EditorDOMPoint& aCaretPoint) {
MOZ_ASSERT(IsEditActionDataAvailable());
bool HTMLEditor::AutoBlockElementsJoiner::
PrepareToDeleteCollapsedSelectionAtCurrentBlockBoundary(
const HTMLEditor& aHTMLEditor,
nsIEditor::EDirection aDirectionAndAmount,
Element& aCurrentBlockElement, const EditorDOMPoint& aCaretPoint) {
MOZ_ASSERT(aHTMLEditor.IsEditActionDataAvailable());
// At edge of our block. Look beside it and see if we can join to an
// adjacent block
mMode = Mode::JoinCurrentBlock;
// Make sure it's not a table element. If so, cancel the operation
// (translation: users cannot backspace or delete across table cells)
if (HTMLEditUtils::IsAnyTableElement(&aCurrentBlockElement)) {
return EditActionCanceled();
return false;
}
// First find the relevant nodes
nsCOMPtr<nsINode> leftNode, rightNode;
if (aDirectionAndAmount == nsIEditor::ePrevious) {
leftNode = GetPreviousEditableHTMLNode(aCurrentBlockElement);
rightNode = aCaretPoint.GetContainer();
mLeftContent =
aHTMLEditor.GetPreviousEditableHTMLNode(aCurrentBlockElement);
mRightContent = aCaretPoint.GetContainerAsContent();
} else {
rightNode = GetNextEditableHTMLNode(aCurrentBlockElement);
leftNode = aCaretPoint.GetContainer();
mRightContent = aHTMLEditor.GetNextEditableHTMLNode(aCurrentBlockElement);
mLeftContent = aCaretPoint.GetContainerAsContent();
}
// Nothing to join
if (!leftNode || !rightNode) {
return EditActionCanceled();
if (!mLeftContent || !mRightContent) {
return false;
}
// Don't cross table boundaries -- cancel it
if (HTMLEditor::NodesInDifferentTableElements(*leftNode, *rightNode)) {
return EditActionCanceled();
}
// Don't cross table boundaries.
return !HTMLEditor::NodesInDifferentTableElements(*mLeftContent,
*mRightContent);
}
EditActionResult HTMLEditor::AutoBlockElementsJoiner::
HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(
HTMLEditor& aHTMLEditor, const EditorDOMPoint& aCaretPoint) {
MOZ_ASSERT(mLeftContent);
MOZ_ASSERT(mRightContent);
EditActionResult result(NS_OK);
EditorDOMPoint pointToPutCaret(aCaretPoint);
{
AutoTrackDOMPoint tracker(RangeUpdaterRef(), &pointToPutCaret);
if (NS_WARN_IF(!leftNode->IsContent()) ||
NS_WARN_IF(!rightNode->IsContent())) {
return EditActionResult(NS_ERROR_FAILURE);
}
result |=
TryToJoinBlocksWithTransaction(MOZ_KnownLive(*leftNode->AsContent()),
MOZ_KnownLive(*rightNode->AsContent()));
AutoTrackDOMPoint tracker(aHTMLEditor.RangeUpdaterRef(), &pointToPutCaret);
result |= aHTMLEditor.TryToJoinBlocksWithTransaction(
MOZ_KnownLive(*mLeftContent), MOZ_KnownLive(*mRightContent));
// 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.
@ -3212,7 +3216,7 @@ HTMLEditor::HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(
return result;
}
}
nsresult rv = CollapseSelectionTo(pointToPutCaret);
nsresult rv = aHTMLEditor.CollapseSelectionTo(pointToPutCaret);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
}

View File

@ -2681,6 +2681,64 @@ class HTMLEditor final : public TextEditor,
nsIEditor::EStripWrappers aStripWrappers, nsIContent& aAtomicContent,
const EditorDOMPoint& aCaretPoint, WSRunScanner& aWSRunScannerAtCaret);
class MOZ_STACK_CLASS AutoBlockElementsJoiner final {
public:
/**
* PrepareToDeleteCollapsedSelectionAtCurrentBlockBoundary() considers
* left content and right content which are joined for handling deletion
* at current block boundary (i.e., at start or end of the current block).
*
* @param aHTMLEditor The HTML editor.
* @param aDirectionAndAmount Direction of the deletion.
* @param aCurrentBlockElement The current block element.
* @param aCaretPoint The caret point (i.e., selection start
* or end).
* @return true if can continue to handle the
* deletion.
*/
bool PrepareToDeleteCollapsedSelectionAtCurrentBlockBoundary(
const HTMLEditor& aHTMLEditor,
nsIEditor::EDirection aDirectionAndAmount,
Element& aCurrentBlockElement, const EditorDOMPoint& aCaretPoint);
/**
* Run() executes the joining.
*
* @param aHTMLEditor The HTML editor.
* @param aCaretPoint The caret point (i.e., selection start or end).
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
Run(HTMLEditor& aHTMLEditor, const EditorDOMPoint& aCaretPoint) {
switch (mMode) {
case Mode::JoinCurrentBlock: {
EditActionResult result =
HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(aHTMLEditor,
aCaretPoint);
NS_WARNING_ASSERTION(
result.Succeeded(),
"AutoBlockElementsJoiner::"
"HandleDeleteCollapsedSelectionAtCurrentBlockBoundary() failed");
return result;
}
case Mode::NotInitialized:
return EditActionIgnored();
}
}
private:
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(
HTMLEditor& aHTMLEditor, const EditorDOMPoint& aCaretPoint);
enum class Mode {
NotInitialized,
JoinCurrentBlock,
};
nsCOMPtr<nsIContent> mLeftContent;
nsCOMPtr<nsIContent> mRightContent;
Mode mMode = Mode::NotInitialized;
};
/**
* HandleDeleteCollapsedSelectionAtOtherBlockBoundary() handles deletion at
* other block boundary (i.e., immediately before or after a block).
@ -2702,20 +2760,6 @@ class HTMLEditor final : public TextEditor,
nsIEditor::EStripWrappers aStripWrappers, Element& aOtherBlockElement,
const EditorDOMPoint& aCaretPoint, WSRunScanner& aWSRunScannerAtCaret);
/**
* HandleDeleteCollapsedSelectionAtCurrentBlockBoundary() handles deletion
* at current block boundary (i.e., at start or end of current block).
*
* @param aDirectionAndAmount Direction of the deletion.
* @param aCurrentBlockElement The current block element.
* @param aCaretPoint The caret point (i.e., selection start
* or end).
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(
nsIEditor::EDirection aDirectionAndAmount, Element& aCurrentBlockElement,
const EditorDOMPoint& aCaretPoint);
/**
* DeleteUnnecessaryNodesAndCollapseSelection() removes unnecessary nodes
* around aSelectionStartPoint and aSelectionEndPoint. Then, collapse