Backed out 2 changesets (bug 1574852) for assertion failures at TextEditRules.cpp

Backed out changeset f54f6af6359d (bug 1574852)
Backed out changeset 2b6968592570 (bug 1574852)
This commit is contained in:
Gurzau Raul 2019-09-15 02:32:09 +03:00
parent bf1c1cfd11
commit ba23dcf114
9 changed files with 227 additions and 409 deletions

View File

@ -2910,23 +2910,19 @@ nsresult EditorBase::NotifyDocumentListeners(
return rv;
}
nsresult EditorBase::SetTextNodeWithoutTransaction(const nsAString& aString,
Text& aTextNode) {
nsresult EditorBase::SetTextImpl(const nsAString& aString, Text& aTextNode) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!AsHTMLEditor());
MOZ_ASSERT(IsPlaintextEditor());
MOZ_ASSERT(!IsUndoRedoEnabled());
const uint32_t length = aTextNode.Length();
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eSetText, nsIEditor::eNext);
// Let listeners know what's up
if (!mActionListeners.IsEmpty() && length) {
AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
listener->WillDeleteText(&aTextNode, 0, length);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
}
}
@ -2940,13 +2936,13 @@ nsresult EditorBase::SetTextNodeWithoutTransaction(const nsAString& aString,
return rv;
}
DebugOnly<nsresult> rvIgnored =
SelectionRefPtr()->Collapse(&aTextNode, aString.Length());
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
{
// Create a nested scope to not overwrite rv from the outer scope.
DebugOnly<nsresult> rv =
SelectionRefPtr()->Collapse(&aTextNode, aString.Length());
NS_ASSERTION(NS_SUCCEEDED(rv),
"Selection could not be collapsed after insert");
}
NS_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Selection::Collapse() failed, but ignored");
RangeUpdaterRef().SelAdjDeleteText(&aTextNode, 0, length);
RangeUpdaterRef().SelAdjInsertText(aTextNode, 0, aString);
@ -2955,11 +2951,9 @@ nsresult EditorBase::SetTextNodeWithoutTransaction(const nsAString& aString,
RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
if (length) {
htmlEditRules->DidDeleteText(aTextNode, 0, length);
MOZ_ASSERT(!Destroyed());
}
if (!aString.IsEmpty()) {
htmlEditRules->DidInsertText(aTextNode, 0, aString);
MOZ_ASSERT(!Destroyed());
}
}
@ -2969,20 +2963,14 @@ nsresult EditorBase::SetTextNodeWithoutTransaction(const nsAString& aString,
for (auto& listener : listeners) {
if (length) {
listener->DidDeleteText(&aTextNode, 0, length, rv);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
}
if (!aString.IsEmpty()) {
listener->DidInsertText(&aTextNode, 0, aString, rv);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
}
}
}
return NS_OK;
return rv;
}
nsresult EditorBase::DeleteTextWithTransaction(Text& aTextNode,

View File

@ -1144,13 +1144,8 @@ class EditorBase : public nsIEditor,
const nsAString& aStringToInsert, Text& aTextNode, int32_t aOffset,
bool aSuppressIME = false);
/**
* SetTextNodeWithoutTransaction() is optimized path to set new value to
* the text node directly and without transaction. This is used when
* setting `<input>.value` and `<textarea>.value`.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
SetTextNodeWithoutTransaction(const nsAString& aString, Text& aTextNode);
MOZ_CAN_RUN_SCRIPT nsresult SetTextImpl(const nsAString& aString,
Text& aTextNode);
/**
* DeleteNodeWithTransaction() removes aNode from the DOM tree.

View File

@ -773,34 +773,11 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
switch (aInfo.mEditSubAction) {
case EditSubAction::eInsertElement:
case EditSubAction::eInsertQuotedText: {
*aCancel = IsReadonly() || IsDisabled();
nsresult rv = MOZ_KnownLive(HTMLEditorRef())
.EnsureNoPaddingBRElementForEmptyEditor();
nsresult rv = MOZ_KnownLive(HTMLEditorRef()).WillInsert(aCancel);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_FAILED(rv)) {
NS_WARNING(
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
return NS_OK;
}
if (!SelectionRefPtr()->IsCollapsed()) {
return NS_OK;
}
rv = MOZ_KnownLive(HTMLEditorRef()).EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_FAILED(rv)) {
NS_WARNING("EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
return NS_OK;
}
rv = MOZ_KnownLive(HTMLEditorRef()).PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
return NS_OK;
}
case EditSubAction::eComputeTextToOutput:
@ -1306,64 +1283,66 @@ nsresult HTMLEditRules::GetFormatString(nsINode* aNode, nsAString& outFormat) {
return NS_OK;
}
nsresult HTMLEditor::EnsureCaretNotAfterPaddingBRElement() {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(SelectionRefPtr()->IsCollapsed());
nsresult HTMLEditor::WillInsert(bool* aCancel) {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
// If we are after a padding `<br>` element for empty last line in the same
// XXX Why don't we stop handling this call if we're readonly or disabled?
if (aCancel && (IsReadonly() || IsDisabled())) {
*aCancel = true;
}
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Adjust selection to prevent insertion after a padding <br> element for
// empty last line. This next only works for collapsed selections right
// now, because selection is a pain to work with when not collapsed. (no
// good way to extend start or end of selection), so we ignore those types
// of selections.
if (!SelectionRefPtr()->IsCollapsed()) {
return NS_OK;
}
// If we are after a padding <br> element for empty last line in the same
// block, then move selection to be before it
nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) {
return NS_ERROR_FAILURE;
}
EditorRawDOMPoint atSelectionStart(firstRange->StartRef());
if (NS_WARN_IF(!atSelectionStart.IsSet())) {
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(atSelectionStart.IsSetAndValid());
MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
nsCOMPtr<nsIContent> previousEditableContent =
GetPreviousEditableHTMLNode(atSelectionStart);
if (!previousEditableContent ||
!EditorBase::IsPaddingBRElementForEmptyLastLine(
*previousEditableContent)) {
return NS_OK;
// Get prior node
nsCOMPtr<nsIContent> priorNode =
GetPreviousEditableHTMLNode(atStartOfSelection);
if (priorNode && EditorBase::IsPaddingBRElementForEmptyLastLine(*priorNode)) {
RefPtr<Element> block1 = GetBlock(*atStartOfSelection.GetContainer());
RefPtr<Element> block2 = GetBlockNodeParent(priorNode);
if (block1 && block1 == block2) {
// If we are here then the selection is right after a padding <br>
// element for empty last line that is in the same block as the
// selection. We need to move the selection start to be before the
// padding <br> element.
EditorRawDOMPoint point(priorNode);
ErrorResult error;
SelectionRefPtr()->Collapse(point, error);
if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
}
}
RefPtr<Element> blockElementAtSelectionStart =
GetBlock(*atSelectionStart.GetContainer());
RefPtr<Element> parentBlockElementOfPreviousEditableContent =
GetBlockNodeParent(previousEditableContent);
if (!blockElementAtSelectionStart ||
blockElementAtSelectionStart !=
parentBlockElementOfPreviousEditableContent) {
return NS_OK;
}
// If we are here then the selection is right after a padding <br>
// element for empty last line that is in the same block as the
// selection. We need to move the selection start to be before the
// padding <br> element.
EditorRawDOMPoint atPreviousEditableContent(previousEditableContent);
ErrorResult error;
SelectionRefPtr()->Collapse(atPreviousEditableContent, error);
if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(!error.Failed(), "Selection::Collapse() failed");
return error.StealNSResult();
}
nsresult HTMLEditor::PrepareInlineStylesForCaret() {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
MOZ_ASSERT(SelectionRefPtr()->IsCollapsed());
// XXX This method works with the top level edit sub-action, but this
// must be wrong if we are handling nested edit action.
if (TopLevelEditSubActionDataRef().mDidDeleteSelection) {
switch (GetTopLevelEditSubAction()) {
case EditSubAction::eInsertText:
@ -1410,31 +1389,12 @@ EditActionResult HTMLEditor::HandleInsertText(
}
}
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
// FYI: Ignore cancel result of WillInsert().
nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
RefPtr<Document> document = GetDocument();
if (NS_WARN_IF(!document)) {
@ -1778,31 +1738,12 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
}
}
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
// FYI: Ignore cancel result of WillInsert().
nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
// Split any mailcites in the way. Should we abort this if we encounter
// table cell boundaries?
@ -2020,8 +1961,7 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
// Fall through, if HandleInsertParagraphInParagraph() didn't handle it.
MOZ_ASSERT(!result.Canceled(),
"HandleInsertParagraphInParagraph() canceled this edit action, "
"InsertParagraphSeparatorAsSubAction() needs to handle this "
"action instead");
"WillInsertBreak() needs to handle such case");
}
// If nobody handles this edit action, let's insert new <br> at the selection.
@ -4232,31 +4172,12 @@ EditActionResult HTMLEditor::MakeOrChangeListAndListItemAsSubAction(
: EditSubAction::eCreateOrChangeList,
nsIEditor::eNext);
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
// FYI: Ignore cancel result of WillInsert().
nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
nsAtom* listTagName = nullptr;
nsAtom* listItemTagName = nullptr;
@ -5037,31 +4958,12 @@ EditActionResult HTMLEditor::IndentAsSubAction() {
EditActionResult HTMLEditor::HandleIndentAtSelection() {
MOZ_ASSERT(IsEditActionDataAvailable());
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
// FYI: Ignore cancel result of WillInsert().
nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed, but ignored");
if (IsCSSEnabled()) {
nsresult rv = HandleCSSIndentAtSelection();
@ -6389,31 +6291,12 @@ EditActionResult HTMLEditor::AlignAsSubAction(const nsAString& aAlignType) {
return result;
}
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
// FYI: Ignore cancel result of WillInsert().
nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed, but ignored");
if (!SelectionRefPtr()->IsCollapsed()) {
nsresult rv = MaybeExtendSelectionToHardLineEdgesForBlockEditAction();
@ -11018,31 +10901,12 @@ EditActionResult HTMLEditor::SetSelectionToAbsoluteAsSubAction() {
return result;
}
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
// FYI: Ignore cancel result of WillInsert().
nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
RefPtr<Element> focusElement = GetSelectionContainerElement();
if (focusElement && HTMLEditUtils::IsImage(focusElement)) {
@ -11367,31 +11231,12 @@ EditActionResult HTMLEditor::SetSelectionToStaticAsSubAction() {
return result;
}
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
// FYI: Ignore cancel result of WillInsert().
nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed, but ignored");
RefPtr<Element> element = GetAbsolutelyPositionedSelectionContainer();
if (NS_WARN_IF(!element)) {
@ -11430,31 +11275,12 @@ EditActionResult HTMLEditor::AddZIndexAsSubAction(int32_t aChange) {
return result;
}
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
// FYI: Ignore cancel result of WillInsert().
nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed, but ignored");
RefPtr<Element> absolutelyPositionedElement =
GetAbsolutelyPositionedSelectionContainer();

View File

@ -2115,31 +2115,13 @@ nsresult HTMLEditor::FormatBlockContainerAsSubAction(nsAtom& aTagName) {
return result.Rv();
}
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
// FYI: Ignore cancel result of WillInsert() since we've already checked
// whether we can or cannot edit current selection range.
nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed, but ignored");
// FormatBlockContainerWithTransaction() creates AutoSelectionRestorer.
// Therefore, even if it returns NS_OK, editor might have been destroyed

View File

@ -1156,24 +1156,14 @@ class HTMLEditor final : public TextEditor,
EditActionResult CanHandleHTMLEditSubAction() const;
/**
* EnsureCaretNotAfterPaddingBRElement() makes sure that caret is NOT after
* padding `<br>` element for preventing insertion after padding `<br>`
* element at empty last line.
* NOTE: This method should be called only when `Selection` is collapsed
* because `Selection` is a pain to work with when not collapsed.
* (no good way to extend start or end of selection), so we need to
* ignore those types of selections.
* Called before inserting something into the editor.
* This method may removes mPaddingBRElementForEmptyEditor if there is.
* Therefore, this method might cause destroying the editor.
*
* @param aCancel Returns true if the operation is canceled.
* This can be nullptr.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
EnsureCaretNotAfterPaddingBRElement();
/**
* PrepareInlineStylesForCaret() consider inline styles from top level edit
* sub-action and setting it to `mTypeInState` and clear inline style cache
* if necessary.
* NOTE: This method should be called only when `Selection` is collapsed.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult PrepareInlineStylesForCaret();
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult WillInsert(bool* aCancel = nullptr);
/**
* HandleInsertText() handles inserting text at selection.

View File

@ -202,6 +202,9 @@ nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
// my kingdom for dynamic cast
switch (aInfo.mEditSubAction) {
case EditSubAction::eSetText:
TextEditorRef().UndefineCaretBidiLevel();
return WillSetText(aCancel, aHandled, aInfo.inString, aInfo.maxLength);
case EditSubAction::eInsertQuotedText: {
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
@ -221,7 +224,6 @@ nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
case EditSubAction::eInsertLineBreak:
case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME:
case EditSubAction::eSetText:
case EditSubAction::eUndo:
case EditSubAction::eRedo:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
@ -244,7 +246,6 @@ nsresult TextEditRules::DidDoAction(EditSubActionInfo& aInfo,
case EditSubAction::eInsertLineBreak:
case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME:
case EditSubAction::eSetText:
case EditSubAction::eUndo:
case EditSubAction::eRedo:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
@ -678,31 +679,41 @@ EditActionResult TextEditor::HandleInsertText(
return EditActionHandled();
}
EditActionResult TextEditor::SetTextWithoutTransaction(
const nsAString& aValue) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!AsTextEditor());
MOZ_ASSERT(IsPlaintextEditor());
MOZ_ASSERT(!IsIMEComposing());
MOZ_ASSERT(!IsUndoRedoEnabled());
MOZ_ASSERT(GetEditAction() != EditAction::eReplaceText);
MOZ_ASSERT(mMaxTextLength < 0);
MOZ_ASSERT(aValue.FindChar(static_cast<char16_t>('\r')) == kNotFound);
UndefineCaretBidiLevel();
nsresult TextEditRules::WillSetText(bool* aCancel, bool* aHandled,
const nsAString* aString,
int32_t aMaxLength) {
MOZ_ASSERT(IsEditorDataAvailable());
MOZ_ASSERT(!mIsHTMLEditRules);
MOZ_ASSERT(aCancel);
MOZ_ASSERT(aHandled);
MOZ_ASSERT(aString);
MOZ_ASSERT(aString->FindChar(static_cast<char16_t>('\r')) == kNotFound);
// XXX If we're setting value, shouldn't we keep setting the new value here?
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
MaybeDoAutoPasswordMasking();
*aHandled = false;
*aCancel = false;
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditActionResult(rv);
if (!IsPlaintextEditor() || TextEditorRef().IsIMEComposing() ||
TextEditorRef().IsUndoRedoEnabled() ||
TextEditorRef().GetEditAction() == EditAction::eReplaceText ||
aMaxLength != -1) {
// SetTextImpl only supports plain text editor without IME and
// when we don't need to make it undoable.
return NS_OK;
}
RefPtr<Element> anonymousDivElement = GetRoot();
nsIContent* firstChild = anonymousDivElement->GetFirstChild();
TextEditorRef().MaybeDoAutoPasswordMasking();
nsresult rv =
MOZ_KnownLive(TextEditorRef()).EnsureNoPaddingBRElementForEmptyEditor();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<Element> rootElement = TextEditorRef().GetRoot();
nsIContent* firstChild = rootElement->GetFirstChild();
// We can use this fast path only when:
// - we need to insert a text node.
@ -715,73 +726,82 @@ EditActionResult TextEditor::SetTextWithoutTransaction(
// that even if there is a padding <br> element for empty editor, it's
// already been removed by `EnsureNoPaddingBRElementForEmptyEditor()`. So,
// at here, there should be only one text node or no children.
if (firstChild && (!firstChild->IsText() || firstChild->GetNextSibling())) {
return EditActionIgnored();
if (firstChild &&
(!EditorBase::IsTextNode(firstChild) || firstChild->GetNextSibling())) {
return NS_OK;
}
} else {
// If we're a multiline text editor, i.e., <textarea>, there is a padding
// <br> element for empty last line followed by scrollbar/resizer elements.
// Otherwise, a text node is followed by them.
if (!firstChild) {
return EditActionIgnored();
return NS_OK;
}
if (firstChild->IsText()) {
if (EditorBase::IsTextNode(firstChild)) {
if (!firstChild->GetNextSibling() ||
!EditorBase::IsPaddingBRElementForEmptyLastLine(
*firstChild->GetNextSibling())) {
return EditActionIgnored();
return NS_OK;
}
} else if (!EditorBase::IsPaddingBRElementForEmptyLastLine(*firstChild)) {
return EditActionIgnored();
return NS_OK;
}
}
// XXX Password fields accept line breaks as normal characters with this code.
// Is this intentional?
nsAutoString sanitizedValue(aValue);
nsAutoString tString(*aString);
if (IsSingleLineEditor() && !IsPasswordEditor()) {
HandleNewLinesInStringForSingleLineEditor(sanitizedValue);
TextEditorRef().HandleNewLinesInStringForSingleLineEditor(tString);
}
if (!firstChild || !firstChild->IsText()) {
if (sanitizedValue.IsEmpty()) {
return EditActionHandled();
if (!firstChild || !EditorBase::IsTextNode(firstChild)) {
if (tString.IsEmpty()) {
*aHandled = true;
return NS_OK;
}
RefPtr<Document> document = GetDocument();
if (NS_WARN_IF(!document)) {
return EditActionIgnored();
RefPtr<Document> doc = TextEditorRef().GetDocument();
if (NS_WARN_IF(!doc)) {
return NS_OK;
}
RefPtr<nsTextNode> newTextNode = CreateTextNode(sanitizedValue);
if (NS_WARN_IF(!newTextNode)) {
return EditActionIgnored();
RefPtr<nsTextNode> newNode = TextEditorRef().CreateTextNode(tString);
if (NS_WARN_IF(!newNode)) {
return NS_OK;
}
nsresult rv = InsertNodeWithTransaction(
*newTextNode, EditorDOMPoint(anonymousDivElement, 0));
if (NS_WARN_IF(Destroyed())) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
nsresult rv = MOZ_KnownLive(TextEditorRef())
.InsertNodeWithTransaction(
*newNode, EditorDOMPoint(rootElement, 0));
if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditActionResult(rv);
return rv;
}
return EditActionHandled();
*aHandled = true;
return NS_OK;
}
// TODO: If new value is empty string, we should only remove it.
// Even if empty text, we don't remove text node and set empty text
// for performance
RefPtr<Text> textNode = firstChild->GetAsText();
if (MOZ_UNLIKELY(NS_WARN_IF(!textNode))) {
return EditActionIgnored();
return NS_OK;
}
rv = MOZ_KnownLive(TextEditorRef()).SetTextImpl(tString, *textNode);
if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED;
}
rv = SetTextNodeWithoutTransaction(sanitizedValue, *textNode);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditActionResult(rv);
return rv;
}
// If we replaced non-empty value with empty string, we need to delete the
// text node.
if (sanitizedValue.IsEmpty() && !textNode->Length()) {
nsresult rv = DeleteNodeWithTransaction(*textNode);
if (tString.IsEmpty() && !textNode->Length()) {
nsresult rv =
MOZ_KnownLive(TextEditorRef()).DeleteNodeWithTransaction(*textNode);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"DeleteNodeWithTransaction() failed, but ignored");
@ -794,7 +814,8 @@ EditActionResult TextEditor::SetTextWithoutTransaction(
"Selection::SetInterlinePoisition() failed");
}
return EditActionHandled();
*aHandled = true;
return NS_OK;
}
EditActionResult TextEditor::HandleDeleteSelection(

View File

@ -106,6 +106,22 @@ class TextEditRules {
// TextEditRules implementation methods
/**
* Called before setting text to the text editor.
* This method may actually set text to it. Therefore, this might cause
* destroying the text editor.
*
* @param aCancel Returns true if the operation is canceled.
* @param aHandled Returns true if the edit action is handled.
* @param inString String to be set.
* @param aMaxLength The maximum string length which the text editor
* allows to set.
*/
MOZ_CAN_RUN_SCRIPT
MOZ_MUST_USE nsresult WillSetText(bool* aCancel, bool* aHandled,
const nsAString* inString,
int32_t aMaxLength);
/**
* Creates a trailing break in the text doc if there is not one already.
*/

View File

@ -991,8 +991,10 @@ nsresult TextEditor::SetTextAsAction(const nsAString& aString,
AutoPlaceholderBatch treatAsOneTransaction(*this);
nsresult rv = SetTextAsSubAction(aString);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetTextAsSubAction() failed");
return EditorBase::ToGenericNSResult(rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditorBase::ToGenericNSResult(rv);
}
return NS_OK;
}
nsresult TextEditor::ReplaceTextAsAction(const nsAString& aString,
@ -1017,8 +1019,10 @@ nsresult TextEditor::ReplaceTextAsAction(const nsAString& aString,
if (!aReplaceRange) {
nsresult rv = SetTextAsSubAction(aString);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetTextAsSubAction() failed");
return EditorBase::ToGenericNSResult(rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditorBase::ToGenericNSResult(rv);
}
return NS_OK;
}
if (NS_WARN_IF(aString.IsEmpty() && aReplaceRange->Collapsed())) {
@ -1058,18 +1062,26 @@ nsresult TextEditor::SetTextAsSubAction(const nsAString& aString) {
return NS_ERROR_NOT_INITIALIZED;
}
// Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules);
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eSetText, nsIEditor::eNext);
if (IsPlaintextEditor() && !IsIMEComposing() && !IsUndoRedoEnabled() &&
GetEditAction() != EditAction::eReplaceText && mMaxTextLength < 0) {
EditActionResult result = SetTextWithoutTransaction(aString);
if (NS_WARN_IF(result.Failed()) || result.Canceled() || result.Handled()) {
return result.Rv();
}
}
EditSubActionInfo subActionInfo(EditSubAction::eSetText);
subActionInfo.inString = &aString;
subActionInfo.maxLength = mMaxTextLength;
{
bool cancel;
bool handled;
nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (cancel) {
return NS_OK;
}
if (!handled) {
// Note that do not notify selectionchange caused by selecting all text
// because it's preparation of our delete implementation so web apps
// shouldn't receive such selectionchange before the first mutation.
@ -1080,34 +1092,31 @@ nsresult TextEditor::SetTextAsSubAction(const nsAString& aString) {
return NS_ERROR_FAILURE;
}
// We want to select trailing `<br>` element to remove all nodes to replace
// all, but TextEditor::SelectEntireDocument() doesn't select such `<br>`
// elements.
// XXX We should make ReplaceSelectionAsSubAction() take range. Then,
// we can saving the expensive cost of modifying `Selection` here.
nsresult rv;
if (mRules && mRules->DocumentIsEmpty()) {
// We want to select trailing BR node to remove all nodes to replace all,
// but TextEditor::SelectEntireDocument doesn't select that BR node.
if (rules->DocumentIsEmpty()) {
rv = SelectionRefPtr()->Collapse(rootElement, 0);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Selection::Collapse() failed, but ignored");
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"Failed to move caret to start of the editor root element");
} else {
ErrorResult error;
SelectionRefPtr()->SelectAllChildren(*rootElement, error);
NS_WARNING_ASSERTION(
!error.Failed(),
"Selection::SelectAllChildren() failed, but ignored");
"Failed to select all children of the editor root element");
rv = error.StealNSResult();
}
if (NS_SUCCEEDED(rv)) {
DebugOnly<nsresult> rvIgnored = ReplaceSelectionAsSubAction(aString);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"ReplaceSelectionAsSubAction() failed, but ignored");
rv = ReplaceSelectionAsSubAction(aString);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to replace selection with new string");
}
}
// Destroying AutoUpdateViewBatch may cause destroying us.
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
// post-process
rv = rules->DidDoAction(subActionInfo, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}

View File

@ -457,8 +457,7 @@ class TextEditor : public EditorBase,
*
* @ param aString The string to be set.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
SetTextAsSubAction(const nsAString& aString);
MOZ_CAN_RUN_SCRIPT nsresult SetTextAsSubAction(const nsAString& aString);
/**
* ReplaceSelectionAsSubAction() replaces selection with aString.
@ -661,14 +660,6 @@ class TextEditor : public EditorBase,
EditActionResult ComputeValueFromTextNodeAndPaddingBRElement(
nsAString& aValue) const;
/**
* SetTextWithoutTransaction() is optimized method to set `<input>.value`
* and `<textarea>.value` to aValue without transaction. This must be
* called only when it's not `HTMLEditor` and undo/redo is disabled.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE EditActionResult
SetTextWithoutTransaction(const nsAString& aValue);
protected: // Called by helper classes.
virtual void OnStartToHandleTopLevelEditSubAction(
EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;