Bug 1940653 - Make HTMLEditor::HandlerInsertText insert one Text when linefeeds are preformatted r=m_kato
The form of <https://discussions.apple.com/> handles newly inserted `Text` nodes asynchronously after pasted. Then, the text will be wrapped into `<p>` elements. However, the handler does not assume that multiple and consecutive `Text` nodes are inserted by a pasting. Therefore, they fail handling our pasted text which was split to each line and each preformatted linefeed. Chrome puts only one `Text` node at pasting multiline plaintext and following this behavior fixes the issue in the form. Therefore, we should follow the behavior at least for now. Differential Revision: https://phabricator.services.mozilla.com/D233619
This commit is contained in:
@@ -1357,67 +1357,98 @@ Result<EditActionResult, nsresult> HTMLEditor::HandleInsertText(
|
||||
// its a lot cheaper to search the input string for only newlines than
|
||||
// it is to search for both tabs and newlines.
|
||||
if (!isWhiteSpaceCollapsible || IsPlaintextMailComposer()) {
|
||||
uint32_t nextOffset = 0;
|
||||
while (nextOffset < aInsertionString.Length()) {
|
||||
const uint32_t lineStartOffset = nextOffset;
|
||||
const int32_t inclusiveNextLinefeedOffset =
|
||||
aInsertionString.FindChar(nsCRT::LF, lineStartOffset);
|
||||
const uint32_t lineLength =
|
||||
inclusiveNextLinefeedOffset != -1
|
||||
? static_cast<uint32_t>(inclusiveNextLinefeedOffset) -
|
||||
lineStartOffset
|
||||
: aInsertionString.Length() - lineStartOffset;
|
||||
if (lineLength) {
|
||||
// lineText does not include the preformatted line break.
|
||||
const nsDependentSubstring lineText(aInsertionString, lineStartOffset,
|
||||
lineLength);
|
||||
Result<InsertTextResult, nsresult> insertTextResult =
|
||||
InsertTextWithTransaction(
|
||||
*document, lineText, currentPoint,
|
||||
GetInsertTextTo(inclusiveNextLinefeedOffset,
|
||||
lineStartOffset));
|
||||
if (MOZ_UNLIKELY(insertTextResult.isErr())) {
|
||||
NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed");
|
||||
return insertTextResult.propagateErr();
|
||||
}
|
||||
// Ignore the caret suggestion because of `dontChangeMySelection`
|
||||
// above.
|
||||
insertTextResult.inspect().IgnoreCaretPointSuggestion();
|
||||
if (insertTextResult.inspect().Handled()) {
|
||||
pointToInsert = currentPoint = insertTextResult.unwrap()
|
||||
.EndOfInsertedTextRef()
|
||||
.To<EditorDOMPoint>();
|
||||
} else {
|
||||
pointToInsert = currentPoint;
|
||||
}
|
||||
if (inclusiveNextLinefeedOffset < 0) {
|
||||
break; // We reached the last line
|
||||
}
|
||||
if (*lineBreakType == LineBreakType::Linefeed) {
|
||||
// Both Chrome and us inserts a preformatted linefeed with its own
|
||||
// `Text` node in various cases. However, when inserting multiline
|
||||
// text, we should insert a `Text` because Chrome does so and the
|
||||
// comment field in https://discussions.apple.com/ handles the new
|
||||
// `Text` to split each line into a paragraph. At that time, it's
|
||||
// not assumed that inserted text is split at every linefeed.
|
||||
MOZ_ASSERT(*lineBreakType == LineBreakType::Linefeed);
|
||||
Result<InsertTextResult, nsresult> insertTextResult =
|
||||
InsertTextWithTransaction(
|
||||
*document, aInsertionString, currentPoint,
|
||||
InsertTextTo::ExistingTextNodeIfAvailable);
|
||||
if (MOZ_UNLIKELY(insertTextResult.isErr())) {
|
||||
NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed");
|
||||
return insertTextResult.propagateErr();
|
||||
}
|
||||
MOZ_ASSERT(inclusiveNextLinefeedOffset >= 0);
|
||||
Result<CreateLineBreakResult, nsresult> insertLineBreakResultOrError =
|
||||
InsertLineBreak(WithTransaction::Yes, *lineBreakType, currentPoint);
|
||||
if (MOZ_UNLIKELY(insertLineBreakResultOrError.isErr())) {
|
||||
NS_WARNING(nsPrintfCString("HTMLEditor::InsertLineBreak("
|
||||
"WithTransaction::Yes, %s) failed",
|
||||
ToString(*lineBreakType).c_str())
|
||||
.get());
|
||||
return insertLineBreakResultOrError.propagateErr();
|
||||
// Ignore the caret suggestion because of `dontChangeMySelection`
|
||||
// above.
|
||||
insertTextResult.inspect().IgnoreCaretPointSuggestion();
|
||||
if (insertTextResult.inspect().Handled()) {
|
||||
pointToInsert = currentPoint = insertTextResult.unwrap()
|
||||
.EndOfInsertedTextRef()
|
||||
.To<EditorDOMPoint>();
|
||||
} else {
|
||||
pointToInsert = currentPoint;
|
||||
}
|
||||
CreateLineBreakResult insertLineBreakResult =
|
||||
insertLineBreakResultOrError.unwrap();
|
||||
// We don't want to update selection here because we've blocked
|
||||
// InsertNodeTransaction updating selection with
|
||||
// dontChangeMySelection.
|
||||
insertLineBreakResult.IgnoreCaretPointSuggestion();
|
||||
MOZ_ASSERT(!AllowsTransactionsToChangeSelection());
|
||||
} else {
|
||||
MOZ_ASSERT(*lineBreakType == LineBreakType::BRElement);
|
||||
uint32_t nextOffset = 0;
|
||||
while (nextOffset < aInsertionString.Length()) {
|
||||
const uint32_t lineStartOffset = nextOffset;
|
||||
const int32_t inclusiveNextLinefeedOffset =
|
||||
aInsertionString.FindChar(nsCRT::LF, lineStartOffset);
|
||||
const uint32_t lineLength =
|
||||
inclusiveNextLinefeedOffset != -1
|
||||
? static_cast<uint32_t>(inclusiveNextLinefeedOffset) -
|
||||
lineStartOffset
|
||||
: aInsertionString.Length() - lineStartOffset;
|
||||
if (lineLength) {
|
||||
// lineText does not include the preformatted line break.
|
||||
const nsDependentSubstring lineText(aInsertionString,
|
||||
lineStartOffset, lineLength);
|
||||
Result<InsertTextResult, nsresult> insertTextResult =
|
||||
InsertTextWithTransaction(
|
||||
*document, lineText, currentPoint,
|
||||
GetInsertTextTo(inclusiveNextLinefeedOffset,
|
||||
lineStartOffset));
|
||||
if (MOZ_UNLIKELY(insertTextResult.isErr())) {
|
||||
NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed");
|
||||
return insertTextResult.propagateErr();
|
||||
}
|
||||
// Ignore the caret suggestion because of `dontChangeMySelection`
|
||||
// above.
|
||||
insertTextResult.inspect().IgnoreCaretPointSuggestion();
|
||||
if (insertTextResult.inspect().Handled()) {
|
||||
pointToInsert = currentPoint = insertTextResult.unwrap()
|
||||
.EndOfInsertedTextRef()
|
||||
.To<EditorDOMPoint>();
|
||||
} else {
|
||||
pointToInsert = currentPoint;
|
||||
}
|
||||
if (inclusiveNextLinefeedOffset < 0) {
|
||||
break; // We reached the last line
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(inclusiveNextLinefeedOffset >= 0);
|
||||
Result<CreateLineBreakResult, nsresult> insertLineBreakResultOrError =
|
||||
InsertLineBreak(WithTransaction::Yes, *lineBreakType,
|
||||
currentPoint);
|
||||
if (MOZ_UNLIKELY(insertLineBreakResultOrError.isErr())) {
|
||||
NS_WARNING(nsPrintfCString("HTMLEditor::InsertLineBreak("
|
||||
"WithTransaction::Yes, %s) failed",
|
||||
ToString(*lineBreakType).c_str())
|
||||
.get());
|
||||
return insertLineBreakResultOrError.propagateErr();
|
||||
}
|
||||
CreateLineBreakResult insertLineBreakResult =
|
||||
insertLineBreakResultOrError.unwrap();
|
||||
// We don't want to update selection here because we've blocked
|
||||
// InsertNodeTransaction updating selection with
|
||||
// dontChangeMySelection.
|
||||
insertLineBreakResult.IgnoreCaretPointSuggestion();
|
||||
MOZ_ASSERT(!AllowsTransactionsToChangeSelection());
|
||||
|
||||
nextOffset = inclusiveNextLinefeedOffset + 1;
|
||||
pointToInsert = insertLineBreakResult.AfterLineBreak<EditorDOMPoint>();
|
||||
currentPoint.SetAfter(&insertLineBreakResult.LineBreakContentRef());
|
||||
if (NS_WARN_IF(!pointToInsert.IsSetAndValidInComposedDoc()) ||
|
||||
NS_WARN_IF(!currentPoint.IsSetAndValidInComposedDoc())) {
|
||||
return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
|
||||
nextOffset = inclusiveNextLinefeedOffset + 1;
|
||||
pointToInsert =
|
||||
insertLineBreakResult.AfterLineBreak<EditorDOMPoint>();
|
||||
currentPoint.SetAfter(&insertLineBreakResult.LineBreakContentRef());
|
||||
if (NS_WARN_IF(!pointToInsert.IsSetAndValidInComposedDoc()) ||
|
||||
NS_WARN_IF(!currentPoint.IsSetAndValidInComposedDoc())) {
|
||||
return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user