Bug 1774704 - part 5-2: Make `HTMLEditor::IndentListChild` stop touching `Selection` directly r=m_kato

Differential Revision: https://phabricator.services.mozilla.com/D152971
This commit is contained in:
Masayuki Nakano 2022-08-04 03:00:31 +00:00
Родитель 49869c2825
Коммит 2296d580e7
2 изменённых файлов: 126 добавлений и 109 удалений

Просмотреть файл

@ -4154,101 +4154,77 @@ EditActionResult HTMLEditor::IndentAsSubAction(const Element& aEditingHost) {
return result.SetResult(rv);
}
// Helper for Handle[CSS|HTML]IndentAtSelectionInternal
nsresult HTMLEditor::IndentListChild(RefPtr<Element>* aCurList,
const EditorDOMPoint& aCurPoint,
nsIContent& aContent) {
MOZ_ASSERT(HTMLEditUtils::IsAnyListElement(aCurPoint.GetContainer()),
"unexpected container");
Result<EditorDOMPoint, nsresult> HTMLEditor::IndentListChildWithTransaction(
RefPtr<Element>* aSubListElement, const EditorDOMPoint& aPointInListElement,
nsIContent& aContentMovingToSubList, const Element& aEditingHost) {
MOZ_ASSERT(
HTMLEditUtils::IsAnyListElement(aPointInListElement.GetContainer()),
"unexpected container");
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
RefPtr<Element> editingHost = ComputeEditingHost();
if (MOZ_UNLIKELY(NS_WARN_IF(!editingHost))) {
return NS_ERROR_FAILURE;
}
// some logic for putting list items into nested lists...
// Check for whether we should join a list that follows aContent.
// We do this if the next element is a list, and the list is of the
// same type (li/ol) as aContent was a part it.
// If aContentMovingToSubList is followed by a sub-list element whose tag is
// same as the parent list element's tag, we can move it to start of the
// sub-list.
if (nsIContent* nextEditableSibling = HTMLEditUtils::GetNextSibling(
aContent, {WalkTreeOption::IgnoreWhiteSpaceOnlyText,
WalkTreeOption::IgnoreNonEditableNode})) {
aContentMovingToSubList, {WalkTreeOption::IgnoreWhiteSpaceOnlyText,
WalkTreeOption::IgnoreNonEditableNode})) {
if (HTMLEditUtils::IsAnyListElement(nextEditableSibling) &&
aCurPoint.GetContainer()->NodeInfo()->NameAtom() ==
aPointInListElement.GetContainer()->NodeInfo()->NameAtom() ==
nextEditableSibling->NodeInfo()->NameAtom() &&
aCurPoint.GetContainer()->NodeInfo()->NamespaceID() ==
aPointInListElement.GetContainer()->NodeInfo()->NamespaceID() ==
nextEditableSibling->NodeInfo()->NamespaceID()) {
const MoveNodeResult moveListElementResult = MoveNodeWithTransaction(
aContent, EditorDOMPoint(nextEditableSibling, 0u));
MoveNodeResult moveListElementResult = MoveNodeWithTransaction(
aContentMovingToSubList, EditorDOMPoint(nextEditableSibling, 0u));
if (moveListElementResult.isErr()) {
NS_WARNING("HTMLEditor::MoveNodeWithTransaction() failed");
return moveListElementResult.unwrapErr();
return Err(moveListElementResult.unwrapErr());
}
nsresult rv = moveListElementResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("MoveNodeResult::SuggestCaretPointTo() failed");
return rv;
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"MoveNodeResult::SuggestCaretPointTo() failed, but ignored");
return NS_OK;
return moveListElementResult.UnwrapCaretPoint();
}
}
// Check for whether we should join a list that preceeds aContent.
// We do this if the previous element is a list, and the list is of
// the same type (li/ol) as aContent was a part of.
// If aContentMovingToSubList follows a sub-list element whose tag is same
// as the parent list element's tag, we can move it to end of the sub-list.
if (nsCOMPtr<nsIContent> previousEditableSibling =
HTMLEditUtils::GetPreviousSibling(
aContent, {WalkTreeOption::IgnoreWhiteSpaceOnlyText,
WalkTreeOption::IgnoreNonEditableNode})) {
aContentMovingToSubList,
{WalkTreeOption::IgnoreWhiteSpaceOnlyText,
WalkTreeOption::IgnoreNonEditableNode})) {
if (HTMLEditUtils::IsAnyListElement(previousEditableSibling) &&
aCurPoint.GetContainer()->NodeInfo()->NameAtom() ==
aPointInListElement.GetContainer()->NodeInfo()->NameAtom() ==
previousEditableSibling->NodeInfo()->NameAtom() &&
aCurPoint.GetContainer()->NodeInfo()->NamespaceID() ==
aPointInListElement.GetContainer()->NodeInfo()->NamespaceID() ==
previousEditableSibling->NodeInfo()->NamespaceID()) {
const MoveNodeResult moveListElementResult =
MoveNodeToEndWithTransaction(aContent, *previousEditableSibling);
MoveNodeResult moveListElementResult = MoveNodeToEndWithTransaction(
aContentMovingToSubList, *previousEditableSibling);
if (moveListElementResult.isErr()) {
NS_WARNING("HTMLEditor::MoveNodeToEndWithTransaction() failed");
return moveListElementResult.unwrapErr();
return Err(moveListElementResult.unwrapErr());
}
nsresult rv = moveListElementResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("MoveNodeResult::SuggestCaretPointTo() failed");
return rv;
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"MoveNodeResult::SuggestCaretPointTo() failed, but ignored");
return NS_OK;
return moveListElementResult.UnwrapCaretPoint();
}
}
// check to see if aCurList is still appropriate. Which it is if
// aContent is still right after it in the same list.
// If aContentMovingToSubList does not follow aSubListElement, we need
// to create new sub-list element.
EditorDOMPoint pointToPutCaret;
nsIContent* previousEditableSibling =
*aCurList ? HTMLEditUtils::GetPreviousSibling(
aContent, {WalkTreeOption::IgnoreWhiteSpaceOnlyText,
WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!*aCurList ||
(previousEditableSibling && previousEditableSibling != *aCurList)) {
nsAtom* containerName = aCurPoint.GetContainer()->NodeInfo()->NameAtom();
*aSubListElement ? HTMLEditUtils::GetPreviousSibling(
aContentMovingToSubList,
{WalkTreeOption::IgnoreWhiteSpaceOnlyText,
WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!*aSubListElement || (previousEditableSibling &&
previousEditableSibling != *aSubListElement)) {
nsAtom* containerName =
aPointInListElement.GetContainer()->NodeInfo()->NameAtom();
// Create a new nested list of correct type.
CreateElementResult createNewListElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
MOZ_KnownLive(*containerName), aCurPoint,
BRElementNextToSplitPoint::Keep, *editingHost);
MOZ_KnownLive(*containerName), aPointInListElement,
BRElementNextToSplitPoint::Keep, aEditingHost);
if (createNewListElementResult.isErr()) {
NS_WARNING(
nsPrintfCString(
@ -4256,42 +4232,25 @@ nsresult HTMLEditor::IndentListChild(RefPtr<Element>* aCurList,
"%s) failed",
nsAtomCString(containerName).get())
.get());
return createNewListElementResult.unwrapErr();
}
nsresult rv = createNewListElementResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
if (NS_FAILED(rv)) {
NS_WARNING("CreateElementResult::SuggestCaretPointTo() failed");
return rv;
return Err(createNewListElementResult.unwrapErr());
}
MOZ_ASSERT(createNewListElementResult.GetNewNode());
// aCurList is now the correct thing to put aContent in
// remember our new block for postprocessing
TopLevelEditSubActionDataRef().mNewBlockElement =
createNewListElementResult.GetNewNode();
*aCurList = createNewListElementResult.UnwrapNewNode();
pointToPutCaret = createNewListElementResult.UnwrapCaretPoint();
*aSubListElement = createNewListElementResult.UnwrapNewNode();
}
// tuck the node into the end of the active list
RefPtr<nsINode> container = *aCurList;
const MoveNodeResult moveNodeResult =
MoveNodeToEndWithTransaction(aContent, *container);
// Finally, we should move aContentMovingToSubList into aSubListElement.
const RefPtr<Element> subListElement = *aSubListElement;
MoveNodeResult moveNodeResult =
MoveNodeToEndWithTransaction(aContentMovingToSubList, *subListElement);
if (moveNodeResult.isErr()) {
NS_WARNING("HTMLEditor::MoveNodeToEndWithTransaction() failed");
return moveNodeResult.unwrapErr();
return Err(moveNodeResult.unwrapErr());
}
nsresult rv = moveNodeResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("MoveNodeResult::SuggestCaretPointTo() failed");
return rv;
if (moveNodeResult.HasCaretPointSuggestion()) {
pointToPutCaret = moveNodeResult.UnwrapCaretPoint();
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"MoveNodeResult::SuggestCaretPointTo() failed, but ignored");
return NS_OK;
return pointToPutCaret;
}
EditActionResult HTMLEditor::HandleIndentAtSelection(
@ -4551,13 +4510,31 @@ nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal(
}
if (HTMLEditUtils::IsAnyListElement(atContent.GetContainer())) {
const RefPtr<Element> listElement = curList;
// MOZ_KnownLive because 'arrayOfContents' is guaranteed to
// keep it alive.
nsresult rv =
IndentListChild(&curList, atContent, MOZ_KnownLive(content));
if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::IndentListChild() failed");
return rv;
Result<EditorDOMPoint, nsresult> pointToPutCaretOrError =
IndentListChildWithTransaction(&curList, atContent,
MOZ_KnownLive(content), aEditingHost);
if (MOZ_UNLIKELY(pointToPutCaretOrError.isErr())) {
NS_WARNING("HTMLEditor::IndentListChildWithTransaction() failed");
return pointToPutCaretOrError.unwrapErr();
}
if (curList != listElement) {
// New list element is created, so we should put caret into the new list
// element.
TopLevelEditSubActionDataRef().mNewBlockElement = curList;
}
if (AllowsTransactionsToChangeSelection() &&
pointToPutCaretOrError.inspect().IsSet()) {
nsresult rv = CollapseSelectionTo(pointToPutCaretOrError.inspect());
if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
NS_WARNING("EditorBase::CollapseSelectionTo() failed");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::CollapseSelectionTo() failed, but ignored");
}
continue;
}
@ -4836,13 +4813,31 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal(
}
if (HTMLEditUtils::IsAnyListElement(atContent.GetContainer())) {
const RefPtr<Element> listElement = curList;
// MOZ_KnownLive because 'arrayOfContents' is guaranteed to
// keep it alive.
nsresult rv =
IndentListChild(&curList, atContent, MOZ_KnownLive(content));
if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::IndentListChild() failed");
return rv;
Result<EditorDOMPoint, nsresult> pointToPutCaretOrError =
IndentListChildWithTransaction(&curList, atContent,
MOZ_KnownLive(content), aEditingHost);
if (MOZ_UNLIKELY(pointToPutCaretOrError.isErr())) {
NS_WARNING("HTMLEditor::IndentListChildWithTransaction() failed");
return pointToPutCaretOrError.unwrapErr();
}
if (listElement != curList) {
// New list element is created, so we should put caret into the new list
// element.
TopLevelEditSubActionDataRef().mNewBlockElement = curList;
}
if (AllowsTransactionsToChangeSelection() &&
pointToPutCaretOrError.inspect().IsSet()) {
nsresult rv = CollapseSelectionTo(pointToPutCaretOrError.inspect());
if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
NS_WARNING("EditorBase::CollapseSelectionTo() failed");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::CollapseSelectionTo() failed, but ignored");
}
// forget curQuote, if any
curQuote = nullptr;

Просмотреть файл

@ -4321,10 +4321,32 @@ class HTMLEditor final : public EditorBase,
}
protected:
// Helper for Handle[CSS|HTML]IndentAtSelectionInternal
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
IndentListChild(RefPtr<Element>* aCurList, const EditorDOMPoint& aCurPoint,
nsIContent& aContent);
/**
* IndentListChildWithTransaction() is a helper method of
* Handle(CSS|HTML)IndentAtSelectionInternal().
*
* @param aSubListElement [in/out] Specify a sub-list element of the
* container of aPointInListElement or nullptr.
* When there is no proper sub-list element to
* move aContentMovingToSubList, this method
* inserts a new sub-list element and update this
* to it.
* @param aPointInListElement A point in a list element whose child should
* be indented. If this method creates new list
* element into the list element, this inserts
* the new list element to this point.
* @param aContentMovingToSubList
* A content node which is a child of a list
* element and should be moved into a sub-list
* element.
* @param aEditingHost The editing host.
* @return A candidate caret position.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
IndentListChildWithTransaction(RefPtr<Element>* aSubListElement,
const EditorDOMPoint& aPointInListElement,
nsIContent& aContentMovingToSubList,
const Element& aEditingHost);
/**
* Stack based helper class for saving/restoring selection. Note that this