From 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:47:29 +0200 Subject: Adding upstream version 115.8.0esr. Signed-off-by: Daniel Baumann --- editor/libeditor/TextEditSubActionHandler.cpp | 817 ++++++++++++++++++++++++++ 1 file changed, 817 insertions(+) create mode 100644 editor/libeditor/TextEditSubActionHandler.cpp (limited to 'editor/libeditor/TextEditSubActionHandler.cpp') diff --git a/editor/libeditor/TextEditSubActionHandler.cpp b/editor/libeditor/TextEditSubActionHandler.cpp new file mode 100644 index 0000000000..17fae74f2a --- /dev/null +++ b/editor/libeditor/TextEditSubActionHandler.cpp @@ -0,0 +1,817 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ErrorList.h" +#include "TextEditor.h" + +#include "AutoRangeArray.h" +#include "EditAction.h" +#include "EditorDOMPoint.h" +#include "EditorUtils.h" +#include "HTMLEditor.h" + +#include "mozilla/Assertions.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_editor.h" +#include "mozilla/TextComposition.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLBRElement.h" +#include "mozilla/dom/NodeFilterBinding.h" +#include "mozilla/dom/NodeIterator.h" +#include "mozilla/dom/Selection.h" + +#include "nsAString.h" +#include "nsCOMPtr.h" +#include "nsCRT.h" +#include "nsCRTGlue.h" +#include "nsComponentManagerUtils.h" +#include "nsContentUtils.h" +#include "nsDebug.h" +#include "nsError.h" +#include "nsGkAtoms.h" +#include "nsIContent.h" +#include "nsIHTMLCollection.h" +#include "nsINode.h" +#include "nsISupports.h" +#include "nsLiteralString.h" +#include "nsNameSpaceManager.h" +#include "nsPrintfCString.h" +#include "nsTextNode.h" +#include "nsUnicharUtils.h" + +namespace mozilla { + +using namespace dom; + +#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY \ + if (IsReadonly()) { \ + return EditActionResult::CanceledResult(); \ + } + +void TextEditor::OnStartToHandleTopLevelEditSubAction( + EditSubAction aTopLevelEditSubAction, + nsIEditor::EDirection aDirectionOfTopLevelEditSubAction, ErrorResult& aRv) { + MOZ_ASSERT(IsEditActionDataAvailable()); + MOZ_ASSERT(!aRv.Failed()); + + EditorBase::OnStartToHandleTopLevelEditSubAction( + aTopLevelEditSubAction, aDirectionOfTopLevelEditSubAction, aRv); + + MOZ_ASSERT(GetTopLevelEditSubAction() == aTopLevelEditSubAction); + MOZ_ASSERT(GetDirectionOfTopLevelEditSubAction() == + aDirectionOfTopLevelEditSubAction); + + if (NS_WARN_IF(Destroyed())) { + aRv.Throw(NS_ERROR_EDITOR_DESTROYED); + return; + } + + if (NS_WARN_IF(!mInitSucceeded)) { + return; + } + + if (aTopLevelEditSubAction == EditSubAction::eSetText) { + // SetText replaces all text, so spell checker handles starting from the + // start of new value. + SetSpellCheckRestartPoint(EditorDOMPoint(mRootElement, 0)); + return; + } + + if (aTopLevelEditSubAction == EditSubAction::eInsertText || + aTopLevelEditSubAction == EditSubAction::eInsertTextComingFromIME) { + // For spell checker, previous selected node should be text node if + // possible. If anchor is root of editor, it may become invalid offset + // after inserting text. + const EditorRawDOMPoint point = + FindBetterInsertionPoint(EditorRawDOMPoint(SelectionRef().AnchorRef())); + NS_WARNING_ASSERTION( + point.IsSet(), + "EditorBase::FindBetterInsertionPoint() failed, but ignored"); + if (point.IsSet()) { + SetSpellCheckRestartPoint(point); + return; + } + } + if (SelectionRef().AnchorRef().IsSet()) { + SetSpellCheckRestartPoint(EditorRawDOMPoint(SelectionRef().AnchorRef())); + } +} + +nsresult TextEditor::OnEndHandlingTopLevelEditSubAction() { + MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable()); + + nsresult rv; + while (true) { + if (NS_WARN_IF(Destroyed())) { + rv = NS_ERROR_EDITOR_DESTROYED; + break; + } + + // XXX Probably, we should spellcheck again after edit action (not top-level + // sub-action) is handled because the ranges can be referred only by + // users. + if (NS_FAILED(rv = HandleInlineSpellCheckAfterEdit())) { + NS_WARNING("TextEditor::HandleInlineSpellCheckAfterEdit() failed"); + break; + } + + if (!IsSingleLineEditor() && + NS_FAILED(rv = EnsurePaddingBRElementInMultilineEditor())) { + NS_WARNING( + "EditorBase::EnsurePaddingBRElementInMultilineEditor() failed"); + break; + } + + rv = EnsureCaretNotAtEndOfTextNode(); + if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { + break; + } + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "TextEditor::EnsureCaretNotAtEndOfTextNode() failed, but ignored"); + rv = NS_OK; + break; + } + DebugOnly rvIgnored = + EditorBase::OnEndHandlingTopLevelEditSubAction(); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rvIgnored), + "EditorBase::OnEndHandlingTopLevelEditSubAction() failed, but ignored"); + MOZ_ASSERT(!GetTopLevelEditSubAction()); + MOZ_ASSERT(GetDirectionOfTopLevelEditSubAction() == eNone); + return rv; +} + +nsresult TextEditor::InsertLineBreakAsSubAction() { + MOZ_ASSERT(IsEditActionDataAvailable()); + + if (NS_WARN_IF(!mInitSucceeded)) { + return NS_ERROR_NOT_INITIALIZED; + } + + IgnoredErrorResult ignoredError; + AutoEditSubActionNotifier startToHandleEditSubAction( + *this, EditSubAction::eInsertLineBreak, nsIEditor::eNext, ignoredError); + if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) { + return ignoredError.StealNSResult(); + } + NS_WARNING_ASSERTION( + !ignoredError.Failed(), + "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored"); + + Result result = + InsertLineFeedCharacterAtSelection(); + if (MOZ_UNLIKELY(result.isErr())) { + NS_WARNING( + "TextEditor::InsertLineFeedCharacterAtSelection() failed, but ignored"); + return result.unwrapErr(); + } + return NS_OK; +} + +Result +TextEditor::InsertLineFeedCharacterAtSelection() { + MOZ_ASSERT(IsEditActionDataAvailable()); + MOZ_ASSERT(!IsSingleLineEditor()); + + UndefineCaretBidiLevel(); + + CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY + + if (mMaxTextLength >= 0) { + nsAutoString insertionString(u"\n"_ns); + Result result = + MaybeTruncateInsertionStringForMaxLength(insertionString); + if (MOZ_UNLIKELY(result.isErr())) { + NS_WARNING( + "TextEditor::MaybeTruncateInsertionStringForMaxLength() failed"); + return result; + } + if (result.inspect().Handled()) { + // Don't return as handled since we stopped inserting the line break. + return EditActionResult::CanceledResult(); + } + } + + // if the selection isn't collapsed, delete it. + if (!SelectionRef().IsCollapsed()) { + nsresult rv = + DeleteSelectionAsSubAction(nsIEditor::eNone, nsIEditor::eNoStrip); + if (NS_FAILED(rv)) { + NS_WARNING( + "EditorBase::DeleteSelectionAsSubAction(eNone, eNoStrip) failed"); + return Err(rv); + } + } + + const auto pointToInsert = GetFirstSelectionStartPoint(); + if (NS_WARN_IF(!pointToInsert.IsSet())) { + return Err(NS_ERROR_FAILURE); + } + MOZ_ASSERT(pointToInsert.IsSetAndValid()); + MOZ_ASSERT(!pointToInsert.IsContainerHTMLElement(nsGkAtoms::br)); + + RefPtr document = GetDocument(); + if (NS_WARN_IF(!document)) { + return Err(NS_ERROR_NOT_INITIALIZED); + } + + // Insert a linefeed character. + Result insertTextResult = + InsertTextWithTransaction(*document, u"\n"_ns, pointToInsert); + if (MOZ_UNLIKELY(insertTextResult.isErr())) { + NS_WARNING("TextEditor::InsertTextWithTransaction(\"\\n\") failed"); + return insertTextResult.propagateErr(); + } + insertTextResult.inspect().IgnoreCaretPointSuggestion(); + EditorDOMPoint pointToPutCaret = insertTextResult.inspect().Handled() + ? insertTextResult.inspect() + .EndOfInsertedTextRef() + .To() + : pointToInsert; + if (NS_WARN_IF(!pointToPutCaret.IsSetAndValid())) { + return Err(NS_ERROR_FAILURE); + } + // XXX I don't think we still need this. This must have been required when + // `