зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1770877 - part 27: Make `HTMLEditor::SplitParagraph` stop touching `Selection` directly r=m_kato
Differential Revision: https://phabricator.services.mozilla.com/D149091
This commit is contained in:
Родитель
324edb90ae
Коммит
c77d8d358f
|
@ -539,6 +539,18 @@ class MOZ_STACK_CLASS SplitNodeResult final {
|
|||
SplitNodeResult(SplitNodeResult&&) = default;
|
||||
SplitNodeResult& operator=(SplitNodeResult&&) = default;
|
||||
|
||||
/**
|
||||
* This constructor should be used for setting specific caret point instead of
|
||||
* aSplitResult's one.
|
||||
*/
|
||||
SplitNodeResult(SplitNodeResult&& aSplitResult,
|
||||
const EditorDOMPoint& aNewCaretPoint)
|
||||
: SplitNodeResult(std::move(aSplitResult)) {
|
||||
MOZ_ASSERT(isOk());
|
||||
mCaretPoint = aNewCaretPoint;
|
||||
mHandledCaretPoint = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor shouldn't be used by anybody except methods which
|
||||
* use this as result when it succeeds.
|
||||
|
|
|
@ -7226,43 +7226,45 @@ EditActionResult HTMLEditor::HandleInsertParagraphInParagraph(
|
|||
|
||||
bool doesCRCreateNewP = GetReturnInParagraphCreatesNewParagraph();
|
||||
bool splitAfterNewBR = false;
|
||||
nsCOMPtr<nsIContent> brContent;
|
||||
RefPtr<HTMLBRElement> brElement;
|
||||
|
||||
EditorDOMPoint pointToSplitParentDivOrP(atStartOfSelection);
|
||||
|
||||
EditorDOMPoint pointToInsertBR;
|
||||
if (doesCRCreateNewP && atStartOfSelection.GetContainer() == &aParentDivOrP) {
|
||||
// We are at the edges of the block, so, we don't need to create new <br>.
|
||||
brContent = nullptr;
|
||||
brElement = nullptr;
|
||||
} else if (atStartOfSelection.IsInTextNode()) {
|
||||
// at beginning of text node?
|
||||
if (atStartOfSelection.IsStartOfContainer()) {
|
||||
// is there a BR prior to it?
|
||||
brContent = atStartOfSelection.IsInContentNode()
|
||||
? HTMLEditUtils::GetPreviousSibling(
|
||||
*atStartOfSelection.ContainerAsContent(),
|
||||
{WalkTreeOption::IgnoreNonEditableNode})
|
||||
: nullptr;
|
||||
if (!brContent || !HTMLEditUtils::IsVisibleBRElement(*brContent) ||
|
||||
EditorUtils::IsPaddingBRElementForEmptyLastLine(*brContent)) {
|
||||
brElement = HTMLBRElement::FromNodeOrNull(
|
||||
atStartOfSelection.IsInContentNode()
|
||||
? HTMLEditUtils::GetPreviousSibling(
|
||||
*atStartOfSelection.ContainerAsContent(),
|
||||
{WalkTreeOption::IgnoreNonEditableNode})
|
||||
: nullptr);
|
||||
if (!brElement || HTMLEditUtils::IsInvisibleBRElement(*brElement) ||
|
||||
EditorUtils::IsPaddingBRElementForEmptyLastLine(*brElement)) {
|
||||
pointToInsertBR.Set(atStartOfSelection.GetContainer());
|
||||
brContent = nullptr;
|
||||
brElement = nullptr;
|
||||
}
|
||||
} else if (atStartOfSelection.IsEndOfContainer()) {
|
||||
// we're at the end of text node...
|
||||
// is there a BR after to it?
|
||||
brContent = atStartOfSelection.IsInContentNode()
|
||||
? HTMLEditUtils::GetNextSibling(
|
||||
*atStartOfSelection.ContainerAsContent(),
|
||||
{WalkTreeOption::IgnoreNonEditableNode})
|
||||
: nullptr;
|
||||
if (!brContent || !HTMLEditUtils::IsVisibleBRElement(*brContent) ||
|
||||
EditorUtils::IsPaddingBRElementForEmptyLastLine(*brContent)) {
|
||||
brElement = HTMLBRElement::FromNodeOrNull(
|
||||
atStartOfSelection.IsInContentNode()
|
||||
? HTMLEditUtils::GetNextSibling(
|
||||
*atStartOfSelection.ContainerAsContent(),
|
||||
{WalkTreeOption::IgnoreNonEditableNode})
|
||||
: nullptr);
|
||||
if (!brElement || HTMLEditUtils::IsInvisibleBRElement(*brElement) ||
|
||||
EditorUtils::IsPaddingBRElementForEmptyLastLine(*brElement)) {
|
||||
pointToInsertBR.SetAfter(atStartOfSelection.GetContainer());
|
||||
NS_WARNING_ASSERTION(
|
||||
pointToInsertBR.IsSet(),
|
||||
"Failed to set to after the container of selection start");
|
||||
brContent = nullptr;
|
||||
brElement = nullptr;
|
||||
}
|
||||
} else {
|
||||
if (doesCRCreateNewP) {
|
||||
|
@ -7312,28 +7314,27 @@ EditActionResult HTMLEditor::HandleInsertParagraphInParagraph(
|
|||
// not in a text node.
|
||||
// is there a BR prior to it?
|
||||
Element* editingHost = ComputeEditingHost();
|
||||
nsIContent* nearContent =
|
||||
brElement = HTMLBRElement::FromNodeOrNull(
|
||||
editingHost ? HTMLEditUtils::GetPreviousContent(
|
||||
atStartOfSelection,
|
||||
{WalkTreeOption::IgnoreNonEditableNode}, editingHost)
|
||||
: nullptr;
|
||||
if (!nearContent || !HTMLEditUtils::IsVisibleBRElement(*nearContent) ||
|
||||
EditorUtils::IsPaddingBRElementForEmptyLastLine(*nearContent)) {
|
||||
: nullptr);
|
||||
if (!brElement || HTMLEditUtils::IsInvisibleBRElement(*brElement) ||
|
||||
EditorUtils::IsPaddingBRElementForEmptyLastLine(*brElement)) {
|
||||
// is there a BR after it?
|
||||
nearContent = editingHost ? HTMLEditUtils::GetNextContent(
|
||||
atStartOfSelection,
|
||||
{WalkTreeOption::IgnoreNonEditableNode},
|
||||
editingHost)
|
||||
: nullptr;
|
||||
if (!nearContent || !HTMLEditUtils::IsVisibleBRElement(*nearContent) ||
|
||||
EditorUtils::IsPaddingBRElementForEmptyLastLine(*nearContent)) {
|
||||
brElement = HTMLBRElement::FromNodeOrNull(
|
||||
editingHost
|
||||
? HTMLEditUtils::GetNextContent(
|
||||
atStartOfSelection, {WalkTreeOption::IgnoreNonEditableNode},
|
||||
editingHost)
|
||||
: nullptr);
|
||||
if (!brElement || HTMLEditUtils::IsInvisibleBRElement(*brElement) ||
|
||||
EditorUtils::IsPaddingBRElementForEmptyLastLine(*brElement)) {
|
||||
pointToInsertBR = atStartOfSelection;
|
||||
splitAfterNewBR = true;
|
||||
brElement = nullptr;
|
||||
}
|
||||
}
|
||||
if (!pointToInsertBR.IsSet() && nearContent->IsHTMLElement(nsGkAtoms::br)) {
|
||||
brContent = nearContent;
|
||||
}
|
||||
}
|
||||
if (pointToInsertBR.IsSet()) {
|
||||
// if CR does not create a new P, default to BR creation
|
||||
|
@ -7365,19 +7366,37 @@ EditActionResult HTMLEditor::HandleInsertParagraphInParagraph(
|
|||
NS_WARNING_ASSERTION(pointToSplitParentDivOrP.IsSet(),
|
||||
"Failed to set after the new <br>");
|
||||
}
|
||||
brContent = insertBRElementResult.UnwrapNewNode();
|
||||
brElement =
|
||||
HTMLBRElement::FromNodeOrNull(insertBRElementResult.GetNewNode());
|
||||
}
|
||||
EditActionResult result(
|
||||
SplitParagraph(aParentDivOrP, pointToSplitParentDivOrP, brContent));
|
||||
NS_WARNING_ASSERTION(result.Succeeded(),
|
||||
"HTMLEditor::SplitParagraph() failed");
|
||||
result.MarkAsHandled();
|
||||
return result;
|
||||
const SplitNodeResult splitParagraphResult = SplitParagraphWithTransaction(
|
||||
aParentDivOrP, pointToSplitParentDivOrP, brElement);
|
||||
if (splitParagraphResult.isErr()) {
|
||||
NS_WARNING("HTMLEditor::SplitParagraphWithTransaction() failed");
|
||||
return EditActionResult(splitParagraphResult.inspectErr());
|
||||
}
|
||||
if (MOZ_UNLIKELY(!splitParagraphResult.DidSplit())) {
|
||||
NS_WARNING(
|
||||
"HTMLEditor::SplitParagraphWithTransaction() didn't split the "
|
||||
"paragraph");
|
||||
splitParagraphResult.IgnoreCaretPointSuggestion();
|
||||
return EditActionResult(NS_ERROR_FAILURE);
|
||||
}
|
||||
nsresult rv = splitParagraphResult.SuggestCaretPointTo(
|
||||
*this, {SuggestCaret::AndIgnoreTrivialError});
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("SplitNodeResult::SuggestCaretPointTo() failed");
|
||||
return EditActionResult(rv);
|
||||
}
|
||||
NS_WARNING_ASSERTION(
|
||||
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
|
||||
"SplitNodeResult::SuggestCaretPointTo() failed, but ignored");
|
||||
return EditActionHandled();
|
||||
}
|
||||
|
||||
nsresult HTMLEditor::SplitParagraph(Element& aParentDivOrP,
|
||||
const EditorDOMPoint& aStartOfRightNode,
|
||||
nsIContent* aNextBRNode) {
|
||||
SplitNodeResult HTMLEditor::SplitParagraphWithTransaction(
|
||||
Element& aParentDivOrP, const EditorDOMPoint& aStartOfRightNode,
|
||||
HTMLBRElement* aMayBecomeVisibleBRElement) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
Result<EditorDOMPoint, nsresult> preparationResult =
|
||||
|
@ -7386,31 +7405,27 @@ nsresult HTMLEditor::SplitParagraph(Element& aParentDivOrP,
|
|||
if (MOZ_UNLIKELY(preparationResult.isErr())) {
|
||||
NS_WARNING(
|
||||
"WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement() failed");
|
||||
return preparationResult.unwrapErr();
|
||||
return SplitNodeResult(preparationResult.unwrapErr());
|
||||
}
|
||||
EditorDOMPoint pointToSplit = preparationResult.unwrap();
|
||||
MOZ_ASSERT(pointToSplit.IsInContentNode());
|
||||
|
||||
// Split the paragraph.
|
||||
const SplitNodeResult splitDivOrPResult = SplitNodeDeepWithTransaction(
|
||||
SplitNodeResult splitDivOrPResult = SplitNodeDeepWithTransaction(
|
||||
aParentDivOrP, pointToSplit, SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
if (splitDivOrPResult.isErr()) {
|
||||
NS_WARNING("HTMLEditor::SplitNodeDeepWithTransaction() failed");
|
||||
return splitDivOrPResult.unwrapErr();
|
||||
}
|
||||
nsresult rv = splitDivOrPResult.SuggestCaretPointTo(
|
||||
*this, {SuggestCaret::OnlyIfHasSuggestion,
|
||||
SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("SplitNodeResult::SuggestCaretPointTo() failed");
|
||||
return rv;
|
||||
return splitDivOrPResult;
|
||||
}
|
||||
if (MOZ_UNLIKELY(!splitDivOrPResult.DidSplit())) {
|
||||
NS_WARNING(
|
||||
"HTMLEditor::SplitNodeDeepWithTransaction() didn't split any nodes");
|
||||
return NS_ERROR_FAILURE;
|
||||
return splitDivOrPResult;
|
||||
}
|
||||
|
||||
// We'll compute caret suggestion later. So the simple result is not needed.
|
||||
splitDivOrPResult.IgnoreCaretPointSuggestion();
|
||||
|
||||
Element* leftDivOrParagraphElement =
|
||||
Element::FromNode(splitDivOrPResult.GetPreviousContent());
|
||||
MOZ_ASSERT(leftDivOrParagraphElement,
|
||||
|
@ -7424,26 +7439,27 @@ nsresult HTMLEditor::SplitParagraph(Element& aParentDivOrP,
|
|||
|
||||
// Get rid of the break, if it is visible (otherwise it may be needed to
|
||||
// prevent an empty p).
|
||||
if (aNextBRNode && HTMLEditUtils::IsVisibleBRElement(*aNextBRNode)) {
|
||||
nsresult rv = DeleteNodeWithTransaction(*aNextBRNode);
|
||||
if (aMayBecomeVisibleBRElement &&
|
||||
HTMLEditUtils::IsVisibleBRElement(*aMayBecomeVisibleBRElement)) {
|
||||
nsresult rv = DeleteNodeWithTransaction(*aMayBecomeVisibleBRElement);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
|
||||
return rv;
|
||||
return SplitNodeResult(rv);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove ID attribute on the paragraph from the right node.
|
||||
// MOZ_KnownLive(rightDivOrParagraphElement) because it's grabbed by
|
||||
// splitDivOrPResult.
|
||||
rv = RemoveAttributeWithTransaction(
|
||||
nsresult rv = RemoveAttributeWithTransaction(
|
||||
MOZ_KnownLive(*rightDivOrParagraphElement), *nsGkAtoms::id);
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
return SplitNodeResult(NS_ERROR_EDITOR_DESTROYED);
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING(
|
||||
"EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::id) failed");
|
||||
return rv;
|
||||
return SplitNodeResult(rv);
|
||||
}
|
||||
|
||||
// We need to ensure to both paragraphs visible even if they are empty.
|
||||
|
@ -7470,9 +7486,8 @@ nsresult HTMLEditor::SplitParagraph(Element& aParentDivOrP,
|
|||
"HTMLEditor::InsertBRElement(WithTransaction::Yes) failed");
|
||||
return insertBRElementResult.unwrapErr();
|
||||
}
|
||||
// After this is called twice, selection will be updated by
|
||||
// SplitParagraph itself. Therefore, we don't need to update selection
|
||||
// here.
|
||||
// After this is called twice, we'll compute new caret position.
|
||||
// Therefore, we don't need to update selection here.
|
||||
insertBRElementResult.IgnoreCaretPointSuggestion();
|
||||
return NS_OK;
|
||||
};
|
||||
|
@ -7484,7 +7499,7 @@ nsresult HTMLEditor::SplitParagraph(Element& aParentDivOrP,
|
|||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING(
|
||||
"InsertBRElementIfEmptyBlockElement(leftDivOrParagraphElement) failed");
|
||||
return rv;
|
||||
return SplitNodeResult(rv);
|
||||
}
|
||||
// MOZ_KnownLive(rightDivOrParagraphElement) because it's grabbed by
|
||||
// splitDivOrResult.
|
||||
|
@ -7494,35 +7509,22 @@ nsresult HTMLEditor::SplitParagraph(Element& aParentDivOrP,
|
|||
NS_WARNING(
|
||||
"InsertBRElementIfEmptyBlockElement(rightDivOrParagraphElement) "
|
||||
"failed");
|
||||
return rv;
|
||||
return SplitNodeResult(rv);
|
||||
}
|
||||
|
||||
// selection to beginning of right hand para;
|
||||
// look inside any containers that are up front.
|
||||
nsCOMPtr<nsIContent> child = HTMLEditUtils::GetFirstLeafContent(
|
||||
nsIContent* child = HTMLEditUtils::GetFirstLeafContent(
|
||||
*rightDivOrParagraphElement, {LeafNodeType::LeafNodeOrChildBlock});
|
||||
if (child && (child->IsText() || HTMLEditUtils::IsContainerNode(*child))) {
|
||||
nsresult rv = CollapseSelectionToStartOf(*child);
|
||||
if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
|
||||
NS_WARNING(
|
||||
"EditorBase::CollapseSelectionTo() caused destroying the editor");
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
NS_WARNING_ASSERTION(
|
||||
NS_SUCCEEDED(rv),
|
||||
"EditorBase::CollapseSelectionToStartOf() failed, but ignored");
|
||||
} else {
|
||||
nsresult rv = CollapseSelectionTo(EditorRawDOMPoint(child));
|
||||
if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
|
||||
NS_WARNING(
|
||||
"EditorBase::CollapseSelectionTo() caused destroying the editor");
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
NS_WARNING_ASSERTION(
|
||||
NS_SUCCEEDED(rv),
|
||||
"EditorBase::CollapseSelectionTo() failed, but ignored");
|
||||
if (MOZ_UNLIKELY(!child)) {
|
||||
return SplitNodeResult(std::move(splitDivOrPResult),
|
||||
EditorDOMPoint(rightDivOrParagraphElement, 0u));
|
||||
}
|
||||
return NS_OK;
|
||||
return child->IsText() || HTMLEditUtils::IsContainerNode(*child)
|
||||
? SplitNodeResult(std::move(splitDivOrPResult),
|
||||
EditorDOMPoint(child, 0u))
|
||||
: SplitNodeResult(std::move(splitDivOrPResult),
|
||||
EditorDOMPoint(child));
|
||||
}
|
||||
|
||||
Result<EditorDOMPoint, nsresult>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "mozilla/dom/AbstractRange.h"
|
||||
#include "mozilla/dom/AncestorIterator.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLBRElement.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -332,17 +333,27 @@ class HTMLEditUtils final {
|
|||
* last line in a block element visible, or an invisible <br> element.
|
||||
*/
|
||||
static bool IsVisibleBRElement(const nsIContent& aContent) {
|
||||
if (!aContent.IsHTMLElement(nsGkAtoms::br)) {
|
||||
return false;
|
||||
if (const dom::HTMLBRElement* brElement =
|
||||
dom::HTMLBRElement::FromNode(&aContent)) {
|
||||
return IsVisibleBRElement(*brElement);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool IsVisibleBRElement(const dom::HTMLBRElement& aBRElement) {
|
||||
// If followed by a block boundary without visible content, it's invisible
|
||||
// <br> element.
|
||||
return !HTMLEditUtils::GetElementOfImmediateBlockBoundary(
|
||||
aContent, WalkTreeDirection::Forward);
|
||||
aBRElement, WalkTreeDirection::Forward);
|
||||
}
|
||||
static bool IsInvisibleBRElement(const nsIContent& aContent) {
|
||||
return aContent.IsHTMLElement(nsGkAtoms::br) &&
|
||||
!HTMLEditUtils::IsVisibleBRElement(aContent);
|
||||
if (const dom::HTMLBRElement* brElement =
|
||||
dom::HTMLBRElement::FromNode(&aContent)) {
|
||||
return IsInvisibleBRElement(*brElement);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool IsInvisibleBRElement(const dom::HTMLBRElement& aBRElement) {
|
||||
return !HTMLEditUtils::IsVisibleBRElement(aBRElement);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1473,7 +1473,7 @@ class HTMLEditor final : public EditorBase,
|
|||
MaybeInsertPaddingBRElementForEmptyLastLineAtSelection();
|
||||
|
||||
/**
|
||||
* SplitParagraph() splits the parent block, aParentDivOrP, at
|
||||
* SplitParagraphWithTransaction() splits the parent block, aParentDivOrP, at
|
||||
* aStartOfRightNode.
|
||||
*
|
||||
* @param aParentDivOrP The parent block to be split. This must be <p>
|
||||
|
@ -1481,13 +1481,16 @@ class HTMLEditor final : public EditorBase,
|
|||
* @param aStartOfRightNode The point to be start of right node after
|
||||
* split. This must be descendant of
|
||||
* aParentDivOrP.
|
||||
* @param aNextBRNode Next <br> node if there is. Otherwise, nullptr.
|
||||
* If this is not nullptr, the <br> node may be
|
||||
* removed.
|
||||
* @param aMayBecomeVisibleBRElement
|
||||
* Next <br> element of the split point if there
|
||||
* is. Otherwise, nullptr. If this is not nullptr,
|
||||
* the <br> element may be removed if it becomes
|
||||
* visible.
|
||||
*/
|
||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
|
||||
SplitParagraph(Element& aParentDivOrP,
|
||||
const EditorDOMPoint& aStartOfRightNode, nsIContent* aBRNode);
|
||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitNodeResult
|
||||
SplitParagraphWithTransaction(Element& aParentDivOrP,
|
||||
const EditorDOMPoint& aStartOfRightNode,
|
||||
dom::HTMLBRElement* aMayBecomeVisibleBRElement);
|
||||
|
||||
/**
|
||||
* HandleInsertParagraphInParagraph() does the right thing for Enter key
|
||||
|
|
Загрузка…
Ссылка в новой задаче