author | Masayuki Nakano <masayuki@d-toybox.com> |
Tue, 04 Aug 2020 11:42:55 +0000 | |
changeset 543350 | 4e9b12c286f4738718cc510b3bf5632f76db6f46 |
parent 543349 | ac83b0fec5119b240b4de9935e1774b8a89da5f7 |
child 543351 | 1e439a68f0e49fb2c1bec0169564a5d69222790d |
push id | 123402 |
push user | masayuki@d-toybox.com |
push date | Wed, 05 Aug 2020 06:52:10 +0000 |
treeherder | autoland@de60b2f1987f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | m_kato |
bugs | 1655389, 1655391 |
milestone | 81.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/editor/libeditor/HTMLEditSubActionHandler.cpp +++ b/editor/libeditor/HTMLEditSubActionHandler.cpp @@ -2591,39 +2591,42 @@ EditActionResult HTMLEditor::HandleDelet "HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent() failed"); return result; } if (scanFromStartPointResult.ReachedOtherBlockElement()) { if (NS_WARN_IF(!scanFromStartPointResult.GetContent()->IsElement())) { return EditActionResult(NS_ERROR_FAILURE); } + AutoBlockElementsJoiner joiner; + if (!joiner.PrepareToDeleteCollapsedSelectionAtOtherBlockBoundary( + *this, aDirectionAndAmount, *scanFromStartPointResult.ElementPtr(), + startPoint, wsRunScanner)) { + return EditActionCanceled(); + } EditActionResult result = - HandleDeleteCollapsedSelectionAtOtherBlockBoundary( - aDirectionAndAmount, aStripWrappers, - MOZ_KnownLive(*scanFromStartPointResult.ElementPtr()), startPoint, - wsRunScanner); - NS_WARNING_ASSERTION( - result.Succeeded(), - "HTMLEditor::HandleDeleteCollapsedSelectionAtOtherBlockBoundary() " - "failed"); + joiner.Run(*this, aDirectionAndAmount, aStripWrappers, startPoint); + NS_WARNING_ASSERTION(result.Succeeded(), + "HTMLEditor::AutoBlockElementsJoiner::Run() failed " + "(other block boundary)"); return result; } if (scanFromStartPointResult.ReachedCurrentBlockBoundary()) { if (NS_WARN_IF(!scanFromStartPointResult.GetContent()->IsElement())) { return EditActionResult(NS_ERROR_FAILURE); } AutoBlockElementsJoiner joiner; if (!joiner.PrepareToDeleteCollapsedSelectionAtCurrentBlockBoundary( *this, aDirectionAndAmount, *scanFromStartPointResult.ElementPtr(), startPoint)) { return EditActionCanceled(); } - EditActionResult result = joiner.Run(*this, startPoint); + EditActionResult result = + joiner.Run(*this, aDirectionAndAmount, aStripWrappers, startPoint); NS_WARNING_ASSERTION(result.Succeeded(), "HTMLEditor::AutoBlockElementsJoiner::Run() failed " "(current block boundary)"); return result; } MOZ_ASSERT_UNREACHABLE("New type of reached content hasn't been handled yet"); return EditActionIgnored(); @@ -3022,140 +3025,166 @@ EditActionResult HTMLEditor::HandleDelet EditorBase::GetStartPoint(*SelectionRefPtr())); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "HTMLEditor::InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() " "failed"); return EditActionHandled(rv); } -EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtOtherBlockBoundary( - nsIEditor::EDirection aDirectionAndAmount, - nsIEditor::EStripWrappers aStripWrappers, Element& aOtherBlockElement, - const EditorDOMPoint& aCaretPoint, WSRunScanner& aWSRunScannerAtCaret) { - MOZ_ASSERT(IsEditActionDataAvailable()); - MOZ_ASSERT(aCaretPoint.IsSet()); +bool HTMLEditor::AutoBlockElementsJoiner:: + PrepareToDeleteCollapsedSelectionAtOtherBlockBoundary( + const HTMLEditor& aHTMLEditor, + nsIEditor::EDirection aDirectionAndAmount, Element& aOtherBlockElement, + const EditorDOMPoint& aCaretPoint, + const WSRunScanner& aWSRunScannerAtCaret) { + MOZ_ASSERT(aHTMLEditor.IsEditActionDataAvailable()); + MOZ_ASSERT(aCaretPoint.IsSetAndValid()); + + mMode = Mode::JoinOtherBlock; // 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(&aOtherBlockElement)) { - return EditActionCanceled(); + return false; } // Next to a block. See if we are between a block and a br. If so, we // really want to delete the br. Else join content at selection to the // block. WSScanResult scanFromCaretResult = aDirectionAndAmount == nsIEditor::eNext ? aWSRunScannerAtCaret.ScanPreviousVisibleNodeOrBlockBoundaryFrom( aCaretPoint) : aWSRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom( aCaretPoint); // First find the adjacent node in the block - nsCOMPtr<nsIContent> leafNode; - nsCOMPtr<nsINode> leftNode, rightNode; if (aDirectionAndAmount == nsIEditor::ePrevious) { - leafNode = GetLastEditableLeaf(aOtherBlockElement); - leftNode = leafNode; - rightNode = aCaretPoint.GetContainer(); + mLeafContentInOtherBlock = + aHTMLEditor.GetLastEditableLeaf(aOtherBlockElement); + mLeftContent = mLeafContentInOtherBlock; + mRightContent = aCaretPoint.GetContainerAsContent(); } else { - leafNode = GetFirstEditableLeaf(aOtherBlockElement); - leftNode = aCaretPoint.GetContainer(); - rightNode = leafNode; - } - - bool didBRElementDeleted = false; + mLeafContentInOtherBlock = + aHTMLEditor.GetFirstEditableLeaf(aOtherBlockElement); + mLeftContent = aCaretPoint.GetContainerAsContent(); + mRightContent = mLeafContentInOtherBlock; + } + + // If we found a `<br>` element, we need to delete it instead of joining the + // contents. if (scanFromCaretResult.ReachedBRElement()) { - nsresult rv = DeleteNodeWithTransaction( - MOZ_KnownLive(*scanFromCaretResult.BRElementPtr())); - if (NS_WARN_IF(Destroyed())) { + mBRElement = scanFromCaretResult.BRElementPtr(); + return true; + } + + return mLeftContent && mRightContent; +} + +EditActionResult HTMLEditor::AutoBlockElementsJoiner:: + HandleDeleteCollapsedSelectionAtOtherBlockBoundary( + HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount, + nsIEditor::EStripWrappers aStripWrappers, + const EditorDOMPoint& aCaretPoint) { + MOZ_ASSERT(aHTMLEditor.IsEditActionDataAvailable()); + MOZ_ASSERT(aCaretPoint.IsSetAndValid()); + + // If we found a `<br>` element, we should delete it instead of joinning the + // contents. + if (mBRElement) { + nsresult rv = + aHTMLEditor.DeleteNodeWithTransaction(MOZ_KnownLive(*mBRElement)); + if (NS_WARN_IF(aHTMLEditor.Destroyed())) { return EditActionResult(NS_ERROR_EDITOR_DESTROYED); } if (NS_FAILED(rv)) { NS_WARNING("HTMLEditor::DeleteNodeWithTransaction() failed"); return EditActionResult(rv); } - didBRElementDeleted = true; - } - - // Don't cross table boundaries - if (leftNode && rightNode && - HTMLEditor::NodesInDifferentTableElements(*leftNode, *rightNode)) { - // If we have not deleted `<br>` element and are not called recursively, - // we should call `DeleteSelectionWithTransaction()` here, but we cannot - // detect it for now. Therefore, we should just tell the caller of that - // we does nothing. - return didBRElementDeleted ? EditActionHandled() : EditActionIgnored(); - } - - if (didBRElementDeleted) { + + if (mLeftContent && mRightContent && + HTMLEditor::NodesInDifferentTableElements(*mLeftContent, + *mRightContent)) { + return EditActionHandled(); + } + // Put selection at edge of block and we are done. - if (NS_WARN_IF(!leafNode)) { + if (NS_WARN_IF(!mLeafContentInOtherBlock)) { + // XXX This must be odd case. The other block can be empty. return EditActionHandled(NS_ERROR_FAILURE); } - EditorDOMPoint newSel = - GetGoodCaretPointFor(*leafNode, aDirectionAndAmount); - if (!newSel.IsSet()) { + EditorDOMPoint newCaretPosition = aHTMLEditor.GetGoodCaretPointFor( + *mLeafContentInOtherBlock, aDirectionAndAmount); + if (!newCaretPosition.IsSet()) { NS_WARNING("HTMLEditor::GetGoodCaretPointFor() failed"); return EditActionHandled(NS_ERROR_FAILURE); } - nsresult rv = CollapseSelectionTo(newSel); + rv = aHTMLEditor.CollapseSelectionTo(newCaretPosition); if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "HTMLEditor::CollapseSelectionTo() failed, but ignored"); return EditActionHandled(); } + MOZ_ASSERT(mLeftContent); + MOZ_ASSERT(mRightContent); + + if (HTMLEditor::NodesInDifferentTableElements(*mLeftContent, + *mRightContent)) { + // If we have not deleted `<br>` element and are not called recursively, + // we should call `DeleteSelectionWithTransaction()` here, but we cannot + // detect it for now. Therefore, we should just tell the caller of that + // we does nothing. + return EditActionIgnored(); + } + // Else we are joining content to block EditActionResult result(NS_OK); EditorDOMPoint pointToPutCaret(aCaretPoint); { - AutoTrackDOMPoint tracker(RangeUpdaterRef(), &pointToPutCaret); - if (NS_WARN_IF(!leftNode) || NS_WARN_IF(!leftNode->IsContent()) || - NS_WARN_IF(!rightNode) || 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)); if (result.Failed()) { NS_WARNING("HTMLEditor::TryToJoinBlocksWithTransaction() failed"); return result; } } // 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 (!result.Handled() && !result.Canceled() && - leafNode != aCaretPoint.GetContainer()) { - int32_t offset = aDirectionAndAmount == nsIEditor::ePrevious - ? static_cast<int32_t>(leafNode->Length()) - : 0; - nsresult rv = CollapseSelectionTo(EditorRawDOMPoint(leafNode, offset)); + mLeafContentInOtherBlock != aCaretPoint.GetContainer()) { + int32_t offset = + aDirectionAndAmount == nsIEditor::ePrevious + ? static_cast<int32_t>(mLeafContentInOtherBlock->Length()) + : 0; + nsresult rv = aHTMLEditor.CollapseSelectionTo( + EditorRawDOMPoint(mLeafContentInOtherBlock, offset)); if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { return result.SetResult(NS_ERROR_EDITOR_DESTROYED); } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "HTMLEditor::CollapseSelectionTo() failed, but ignored"); - EditActionResult result = - HandleDeleteSelectionInternal(aDirectionAndAmount, aStripWrappers); + EditActionResult result = aHTMLEditor.HandleDeleteSelectionInternal( + aDirectionAndAmount, aStripWrappers); NS_WARNING_ASSERTION( result.Succeeded(), "Recursive HTMLEditor::HandleDeleteSelectionInternal() failed"); return result; } // Otherwise, we must have deleted the selection as user expected. - 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); } NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::CollapseSelectionTo() failed, but ignored"); return result; }
--- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -2697,51 +2697,94 @@ class HTMLEditor final : public TextEdit * deletion. */ bool PrepareToDeleteCollapsedSelectionAtCurrentBlockBoundary( const HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount, Element& aCurrentBlockElement, const EditorDOMPoint& aCaretPoint); /** + * PrepareToDeleteCollapsedSelectionAtOtherBlockBoundary() considers + * left content and right content which are joined for handling deletion at + * other block boundary (i.e., immediately before or after a block). + * + * @param aHTMLEditor The HTML editor. + * @param aDirectionAndAmount Direction of the deletion. + * @param aOtherBlockElement The block element which follows the + * caret or is followed by caret. + * @param aCaretPoint The caret point (i.e., selection start + * or end). + * @param aWSRunScannerAtCaret WSRunScanner instance which was + * initialized with the caret point. + * @return true if can continue to handle the + * deletion. + */ + bool PrepareToDeleteCollapsedSelectionAtOtherBlockBoundary( + const HTMLEditor& aHTMLEditor, + nsIEditor::EDirection aDirectionAndAmount, Element& aOtherBlockElement, + const EditorDOMPoint& aCaretPoint, + const WSRunScanner& aWSRunScannerAtCaret); + + /** * 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) { + Run(HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount, + nsIEditor::EStripWrappers aStripWrappers, + 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::JoinOtherBlock: { + EditActionResult result = + HandleDeleteCollapsedSelectionAtOtherBlockBoundary( + aHTMLEditor, aDirectionAndAmount, aStripWrappers, + aCaretPoint); + NS_WARNING_ASSERTION( + result.Succeeded(), + "AutoBlockElementsJoiner::" + "HandleDeleteCollapsedSelectionAtOtherBlockBoundary() failed"); + return result; + } case Mode::NotInitialized: return EditActionIgnored(); } return EditActionResult(NS_ERROR_NOT_INITIALIZED); } private: [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult HandleDeleteCollapsedSelectionAtCurrentBlockBoundary( HTMLEditor& aHTMLEditor, const EditorDOMPoint& aCaretPoint); + [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult + HandleDeleteCollapsedSelectionAtOtherBlockBoundary( + HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount, + nsIEditor::EStripWrappers aStripWrappersconst, + const EditorDOMPoint& aCaretPoint); enum class Mode { NotInitialized, JoinCurrentBlock, + JoinOtherBlock, }; nsCOMPtr<nsIContent> mLeftContent; nsCOMPtr<nsIContent> mRightContent; + nsCOMPtr<nsIContent> mLeafContentInOtherBlock; + RefPtr<dom::HTMLBRElement> mBRElement; Mode mMode = Mode::NotInitialized; }; /** * HandleDeleteCollapsedSelectionAtOtherBlockBoundary() handles deletion at * other block boundary (i.e., immediately before or after a block). * If this does not join blocks, `WillDeleteSelection()` may be called * recursively.
--- a/editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js +++ b/editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js @@ -1,15 +1,15 @@ /** * The current status of the test suite. * * See README.Mozilla for details on how to generate this. */ const knownFailures = { - "value": { + value: { "A-Proposed-FS:18px_TEXT-1_SI-dM": true, "A-Proposed-FS:18px_TEXT-1_SI-body": true, "A-Proposed-FS:18px_TEXT-1_SI-div": true, "A-Proposed-FS:large_TEXT-1_SI-dM": true, "A-Proposed-FS:large_TEXT-1_SI-body": true, "A-Proposed-FS:large_TEXT-1_SI-div": true, "A-Proposed-CB:name_TEXT-1_SI-dM": true, "A-Proposed-CB:name_TEXT-1_SI-body": true, @@ -214,18 +214,16 @@ const knownFailures = { "D-Proposed-TR3rs:3-1_SO1-div": true, "D-Proposed-TR3rs:3-1_SO2-dM": true, "D-Proposed-TR3rs:3-1_SO2-body": true, "D-Proposed-TR3rs:3-1_SO2-div": true, "D-Proposed-TR3rs:3-1_SO3-dM": true, "D-Proposed-TR3rs:3-1_SO3-body": true, "D-Proposed-TR3rs:3-1_SO3-div": true, "D-Proposed-DIV:ce:false-1_SB-dM": true, - "D-Proposed-DIV:ce:false-1_SB-body": true, - "D-Proposed-DIV:ce:false-1_SB-div": true, "D-Proposed-DIV:ce:false-1_SL-dM": true, "D-Proposed-DIV:ce:false-1_SL-body": true, "D-Proposed-DIV:ce:false-1_SL-div": true, "D-Proposed-DIV:ce:false-1_SR-dM": true, "D-Proposed-DIV:ce:false-1_SR-body": true, "D-Proposed-DIV:ce:false-1_SR-div": true, "D-Proposed-DIV:ce:false-1_SI-dM": true, "FD-Proposed-OL-LI-1_SW-dM": true, @@ -457,19 +455,19 @@ const knownFailures = { "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-dM": true, "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-body": true, "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-div": true, "QV-Proposed-HC_SPAN.bcred-1_SI-dM": true, "QV-Proposed-HC_SPAN.bcred-1_SI-body": true, "QV-Proposed-HC_SPAN.bcred-1_SI-div": true, "QV-Proposed-HC_MYBCRED-1-SI-dM": true, "QV-Proposed-HC_MYBCRED-1-SI-body": true, - "QV-Proposed-HC_MYBCRED-1-SI-div": true + "QV-Proposed-HC_MYBCRED-1-SI-div": true, }, - "select": { + select: { "S-Proposed-UNSEL_TEXT-1_SI-dM": true, "S-Proposed-UNSEL_TEXT-1_SI-body": true, "S-Proposed-UNSEL_TEXT-1_SI-div": true, "S-Proposed-SM:m.f.c_TEXT-1_SI-1-dM": true, "S-Proposed-SM:m.f.c_TEXT-1_SI-1-body": true, "S-Proposed-SM:m.f.c_TEXT-1_SI-1-div": true, "S-Proposed-SM:m.b.c_TEXT-1_SI-1-dM": true, "S-Proposed-SM:m.b.c_TEXT-1_SI-1-body": true, @@ -858,18 +856,16 @@ const knownFailures = { "D-Proposed-TR3rs:3-1_SO1-div": true, "D-Proposed-TR3rs:3-1_SO2-dM": true, "D-Proposed-TR3rs:3-1_SO2-body": true, "D-Proposed-TR3rs:3-1_SO2-div": true, "D-Proposed-TR3rs:3-1_SO3-dM": true, "D-Proposed-TR3rs:3-1_SO3-body": true, "D-Proposed-TR3rs:3-1_SO3-div": true, "D-Proposed-DIV:ce:false-1_SB-dM": true, - "D-Proposed-DIV:ce:false-1_SB-body": true, - "D-Proposed-DIV:ce:false-1_SB-div": true, "D-Proposed-DIV:ce:false-1_SL-dM": true, "D-Proposed-DIV:ce:false-1_SL-body": true, "D-Proposed-DIV:ce:false-1_SL-div": true, "D-Proposed-DIV:ce:false-1_SR-dM": true, "D-Proposed-DIV:ce:false-1_SR-body": true, "D-Proposed-DIV:ce:false-1_SR-div": true, "D-Proposed-DIV:ce:false-1_SI-dM": true, "D-Proposed-SPAN:d:ib-2_SL-dM": true, @@ -1837,11 +1833,11 @@ const knownFailures = { "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-dM": true, "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-body": true, "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-div": true, "QV-Proposed-HC_SPAN.bcred-1_SI-dM": true, "QV-Proposed-HC_SPAN.bcred-1_SI-body": true, "QV-Proposed-HC_SPAN.bcred-1_SI-div": true, "QV-Proposed-HC_MYBCRED-1-SI-dM": true, "QV-Proposed-HC_MYBCRED-1-SI-body": true, - "QV-Proposed-HC_MYBCRED-1-SI-div": true - } -} + "QV-Proposed-HC_MYBCRED-1-SI-div": true, + }, +};