Bug 1655388 - part 5: Split the special handling for <hr> element off from HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent() r=m_kato

Depends on D87032

Differential Revision: https://phabricator.services.mozilla.com/D87033
This commit is contained in:
Masayuki Nakano 2020-08-19 02:15:01 +00:00
parent 416bf2f5ad
commit c9bee9126b
2 changed files with 145 additions and 90 deletions

View File

@ -2678,8 +2678,7 @@ EditActionResult HTMLEditor::HandleDeleteAroundCollapsedRanges(
}
if (aScanFromCaretPointResult.ReachedSpecialContent() ||
aScanFromCaretPointResult.ReachedBRElement() ||
aScanFromCaretPointResult.ReachedHRElement()) {
aScanFromCaretPointResult.ReachedBRElement()) {
if (aScanFromCaretPointResult.GetContent() ==
aWSRunScannerAtCaret.GetEditingHost()) {
return EditActionHandled();
@ -2694,6 +2693,21 @@ EditActionResult HTMLEditor::HandleDeleteAroundCollapsedRanges(
return result;
}
if (aScanFromCaretPointResult.ReachedHRElement()) {
if (aScanFromCaretPointResult.GetContent() ==
aWSRunScannerAtCaret.GetEditingHost()) {
return EditActionHandled();
}
EditActionResult result = HandleDeleteCollapsedSelectionAtHRElement(
aDirectionAndAmount, aStripWrappers,
MOZ_KnownLive(*aScanFromCaretPointResult.ElementPtr()),
aWSRunScannerAtCaret.ScanStartRef(), aWSRunScannerAtCaret);
NS_WARNING_ASSERTION(
result.Succeeded(),
"HTMLEditor::HandleDeleteCollapsedSelectionAtHRElement() failed");
return result;
}
if (aScanFromCaretPointResult.ReachedOtherBlockElement()) {
if (NS_WARN_IF(!aScanFromCaretPointResult.GetContent()->IsElement())) {
return EditActionResult(NS_ERROR_FAILURE);
@ -2978,6 +2992,107 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtVisibleChar(
return EditActionHandled();
}
EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtHRElement(
nsIEditor::EDirection aDirectionAndAmount,
nsIEditor::EStripWrappers aStripWrappers, Element& aHRElement,
const EditorDOMPoint& aCaretPoint,
const WSRunScanner& aWSRunScannerAtCaret) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aHRElement.IsHTMLElement(nsGkAtoms::hr));
MOZ_ASSERT(&aHRElement != aWSRunScannerAtCaret.GetEditingHost());
if (aDirectionAndAmount != nsIEditor::ePrevious) {
EditActionResult result = HandleDeleteCollapsedSelectionAtAtomicContent(
aDirectionAndAmount, aStripWrappers, aHRElement, aCaretPoint,
aWSRunScannerAtCaret);
NS_WARNING_ASSERTION(
result.Succeeded(),
"HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent() failed");
return result;
}
// Only if the caret is positioned at the end-of-hr-line position, we
// want to delete the <hr>.
//
// In other words, we only want to delete, if our selection position
// (indicated by aCaretPoint) is the position directly
// after the <hr>, on the same line as the <hr>.
//
// To detect this case we check:
// aCaretPoint's container == parent of `<hr>` element
// and
// aCaretPoint's offset -1 == `<hr>` element offset
// and
// interline position is false (left)
//
// In any other case we set the position to aCaretPoint's container -1
// and interlineposition to false, only moving the caret to the
// end-of-hr-line position.
EditorRawDOMPoint atHRElement(&aHRElement);
ErrorResult error;
bool interLineIsRight = SelectionRefPtr()->GetInterlinePosition(error);
if (error.Failed()) {
NS_WARNING("Selection::GetInterlinePosition() failed");
return EditActionResult(error.StealNSResult());
}
if (aCaretPoint.GetContainer() == atHRElement.GetContainer() &&
aCaretPoint.Offset() - 1 == atHRElement.Offset() && !interLineIsRight) {
EditActionResult result = HandleDeleteCollapsedSelectionAtAtomicContent(
aDirectionAndAmount, aStripWrappers, aHRElement, aCaretPoint,
aWSRunScannerAtCaret);
NS_WARNING_ASSERTION(
result.Succeeded(),
"HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent() failed");
return result;
}
// Go to the position after the <hr>, but to the end of the <hr> line
// by setting the interline position to left.
EditorDOMPoint atNextOfHRElement(EditorDOMPoint::After(aHRElement));
NS_WARNING_ASSERTION(atNextOfHRElement.IsSet(),
"Failed to set after <hr> element");
{
AutoEditorDOMPointChildInvalidator lockOffset(atNextOfHRElement);
nsresult rv = CollapseSelectionTo(atNextOfHRElement);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"HTMLEditor::CollapseSelectionTo() failed, but ignored");
}
IgnoredErrorResult ignoredError;
SelectionRefPtr()->SetInterlinePosition(false, ignoredError);
NS_WARNING_ASSERTION(
!ignoredError.Failed(),
"Selection::SetInterlinePosition(false) failed, but ignored");
TopLevelEditSubActionDataRef().mDidExplicitlySetInterLine = true;
// There is one exception to the move only case. If the <hr> is
// followed by a <br> we want to delete the <br>.
WSScanResult forwardScanFromCaretResult =
aWSRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(aCaretPoint);
if (!forwardScanFromCaretResult.ReachedBRElement()) {
return EditActionHandled();
}
// Delete the <br>
nsresult rv =
WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt(
*this, MOZ_KnownLive(*forwardScanFromCaretResult.BRElementPtr()),
aCaretPoint);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"WhiteSpaceVisibilityKeeper::"
"DeleteContentNodeAndJoinTextNodesAroundIt() failed");
return EditActionHandled(rv);
}
EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent(
nsIEditor::EDirection aDirectionAndAmount,
nsIEditor::EStripWrappers aStripWrappers, nsIContent& aAtomicContent,
@ -2988,92 +3103,6 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent(
IsVisibleBRElement(&aAtomicContent));
MOZ_ASSERT(&aAtomicContent != aWSRunScannerAtCaret.GetEditingHost());
// Special handling for backspace when positioned after <hr>
if (aDirectionAndAmount == nsIEditor::ePrevious &&
aAtomicContent.IsHTMLElement(nsGkAtoms::hr)) {
// Only if the caret is positioned at the end-of-hr-line position, we
// want to delete the <hr>.
//
// In other words, we only want to delete, if our selection position
// (indicated by aCaretPoint) is the position directly
// after the <hr>, on the same line as the <hr>.
//
// To detect this case we check:
// aCaretPoint's container == parent of `<hr>` element
// and
// aCaretPoint's offset -1 == `<hr>` element offset
// and
// interline position is false (left)
//
// In any other case we set the position to aCaretPoint's container -1
// and interlineposition to false, only moving the caret to the
// end-of-hr-line position.
bool moveOnly = true;
EditorRawDOMPoint atHRElement(&aAtomicContent);
ErrorResult error;
bool interLineIsRight = SelectionRefPtr()->GetInterlinePosition(error);
if (error.Failed()) {
NS_WARNING("Selection::GetInterlinePosition() failed");
return EditActionResult(error.StealNSResult());
}
if (aCaretPoint.GetContainer() == atHRElement.GetContainer() &&
aCaretPoint.Offset() - 1 == atHRElement.Offset() && !interLineIsRight) {
moveOnly = false;
}
if (moveOnly) {
// Go to the position after the <hr>, but to the end of the <hr> line
// by setting the interline position to left.
EditorDOMPoint atNextOfHRElement(EditorDOMPoint::After(aAtomicContent));
NS_WARNING_ASSERTION(atNextOfHRElement.IsSet(),
"Failed to set after <hr> element");
{
AutoEditorDOMPointChildInvalidator lockOffset(atNextOfHRElement);
nsresult rv = CollapseSelectionTo(atNextOfHRElement);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"HTMLEditor::CollapseSelectionTo() failed, but ignored");
}
IgnoredErrorResult ignoredError;
SelectionRefPtr()->SetInterlinePosition(false, ignoredError);
NS_WARNING_ASSERTION(
!ignoredError.Failed(),
"Selection::SetInterlinePosition(false) failed, but ignored");
TopLevelEditSubActionDataRef().mDidExplicitlySetInterLine = true;
// There is one exception to the move only case. If the <hr> is
// followed by a <br> we want to delete the <br>.
WSScanResult forwardScanFromCaretResult =
aWSRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(
aCaretPoint);
if (!forwardScanFromCaretResult.ReachedBRElement()) {
return EditActionHandled();
}
// Delete the <br>
nsresult rv =
WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt(
*this, MOZ_KnownLive(*forwardScanFromCaretResult.BRElementPtr()),
aCaretPoint);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"WhiteSpaceVisibilityKeeper::"
"DeleteContentNodeAndJoinTextNodesAroundIt() failed");
return EditActionHandled(rv);
}
// Else continue with normal delete code
}
nsresult rv =
WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt(
*this, aAtomicContent, aCaretPoint);

View File

@ -2603,8 +2603,9 @@ class HTMLEditor final : public TextEditor,
* HandleDeleteCollapsedSelectionAtAtomicContent() handles deletion of
* atomic elements like `<br>`, `<hr>`, `<img>`, `<input>`, etc and
* data nodes except text node (e.g., comment node).
* If aAtomicContent is a invisible `<br>` element, this will call
* `HandleDeleteSelectionInternal()` recursively after deleting it.
* Note that don't call this directly with `<hr>` element. Instead, call
* `HandleDeleteCollapsedSelectionAtHRElement()`.
* Note that don't call this for invisible `<br>` element.
*
* @param aDirectionAndAmount Direction of the deletion.
* @param aStripWrappers Must be eStrip or eNoStrip.
@ -2621,6 +2622,31 @@ class HTMLEditor final : public TextEditor,
const EditorDOMPoint& aCaretPoint,
const WSRunScanner& aWSRunScannerAtCaret);
/**
* HandleDeleteCollapsedSelectionAtHRElement() handles deletion around
* `<hr>` element. If aDirectionAndAmount is nsIEditor::ePrevious,
* aHTElement is removed only when caret is at next sibling of the `<hr>`
* element and inter line position is "left". Otherwise, caret is moved
* and does not remove the `<hr>` elemnent.
* XXX Perhaps, we can get rid of this special handling because the other
* browsers don't do this, and our `<hr>` element handling is really
* odd.
*
* @param aDirectionAndAmount Direction of the deletion.
* @param aStripWrappers Must be eStrip or eNoStrip.
* @param aHRElement The `<hr>` element to be removed.
* @param aCaretPoint The caret point (i.e., selection start or
* end).
* @param aWSRunScannerAtCaret WSRunScanner instance which was initialized
* with the caret point.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
HandleDeleteCollapsedSelectionAtHRElement(
nsIEditor::EDirection aDirectionAndAmount,
nsIEditor::EStripWrappers aStripWrappers, Element& aHRElement,
const EditorDOMPoint& aCaretPoint,
const WSRunScanner& aWSRunScannerAtCaret);
class MOZ_STACK_CLASS AutoEmptyBlockAncestorDeleter final {
public:
/**