Bug 1478564 - part 1: Optimize TextEditRules::HandleNewLines() r=m_kato

TextEditRules::HandleNewLines() is expensive since it may scan all of given
string twice and more.  On the other hand, in most cases, given string does
not contain \n, \r nor \r\n.

First, for avoid using nsTString::FindCharInSet(), HandleNewLine() should
receive string which never contains \r nor \r\n.  Then, it always can use
nsTSubstring::FindChar() instead.

Next, HandleNewLines() should do nothing if given string does not contain \n.

Finally, because of unused, this removes unnecessary HandleNewLines() argument
which can specify the way to handle new lines.

MozReview-Commit-ID: 8WSfxfkuFgN

--HG--
extra : rebase_source : 1c05721162a30288929d030c0a15fe83a50fe9d2
This commit is contained in:
Masayuki Nakano 2018-07-24 17:46:12 +09:00
parent 58cbd9a01e
commit a9d57dd8a8
3 changed files with 58 additions and 44 deletions

View File

@ -596,37 +596,40 @@ TextEditRules::GetTextNodeAroundSelectionStartContainer()
#define ASSERT_PASSWORD_LENGTHS_EQUAL()
#endif
// static
void
TextEditRules::HandleNewLines(nsString& aString,
int32_t aNewlineHandling)
TextEditRules::HandleNewLines(nsString& aString)
{
if (aNewlineHandling < 0) {
int32_t caretStyle;
TextEditor::GetDefaultEditorPrefs(aNewlineHandling, caretStyle);
static const char16_t kLF = static_cast<char16_t>('\n');
MOZ_ASSERT(IsEditorDataAvailable());
MOZ_ASSERT(aString.FindChar(static_cast<uint16_t>('\r')) == kNotFound);
// First of all, check if aString contains '\n' since if the string
// does not include it, we don't need to do nothing here.
int32_t firstLF = aString.FindChar(kLF, 0);
if (firstLF == kNotFound) {
return;
}
switch(aNewlineHandling) {
switch(TextEditorRef().mNewlineHandling) {
case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
// Default of Firefox:
// Strip trailing newlines first so we don't wind up with trailing spaces
aString.Trim(CRLF, false, true);
aString.ReplaceChar(CRLF, ' ');
aString.Trim(LFSTR, false, true);
aString.ReplaceChar(kLF, ' ');
break;
case nsIPlaintextEditor::eNewlinesStrip:
aString.StripCRLF();
aString.StripChar(kLF);
break;
case nsIPlaintextEditor::eNewlinesPasteToFirst:
default: {
int32_t firstCRLF = aString.FindCharInSet(CRLF);
// we get first *non-empty* line.
int32_t offset = 0;
while (firstCRLF == offset) {
while (firstLF == offset) {
offset++;
firstCRLF = aString.FindCharInSet(CRLF, offset);
firstLF = aString.FindChar(kLF, offset);
}
if (firstCRLF > 0) {
aString.Truncate(firstCRLF);
if (firstLF > 0) {
aString.Truncate(firstLF);
}
if (offset > 0) {
aString.Cut(0, offset);
@ -634,25 +637,27 @@ TextEditRules::HandleNewLines(nsString& aString,
break;
}
case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
aString.Trim(CRLF, true, true);
aString.ReplaceChar(CRLF, ',');
// Default of Thunderbird:
aString.Trim(LFSTR, true, true);
aString.ReplaceChar(kLF, ',');
break;
case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace: {
nsAutoString result;
uint32_t offset = 0;
while (offset < aString.Length()) {
int32_t nextCRLF = aString.FindCharInSet(CRLF, offset);
if (nextCRLF < 0) {
int32_t nextLF =
!offset ? firstLF : aString.FindChar(kLF, offset);
if (nextLF < 0) {
result.Append(nsDependentSubstring(aString, offset));
break;
}
uint32_t wsBegin = nextCRLF;
uint32_t wsBegin = nextLF;
// look backwards for the first non-whitespace char
while (wsBegin > offset && NS_IS_SPACE(aString[wsBegin - 1])) {
--wsBegin;
}
result.Append(nsDependentSubstring(aString, offset, wsBegin - offset));
offset = nextCRLF + 1;
offset = nextLF + 1;
while (offset < aString.Length() && NS_IS_SPACE(aString[offset])) {
++offset;
}
@ -662,7 +667,7 @@ TextEditRules::HandleNewLines(nsString& aString,
}
case nsIPlaintextEditor::eNewlinesPasteIntact:
// even if we're pasting newlines, don't paste leading/trailing ones
aString.Trim(CRLF, true, true);
aString.Trim(LFSTR, true, true);
break;
}
}
@ -760,7 +765,15 @@ TextEditRules::WillInsertText(EditSubAction aEditSubAction,
// So find out what we're expected to do:
if (IsSingleLineEditor()) {
nsAutoString tString(*outString);
HandleNewLines(tString, TextEditorRef().mNewlineHandling);
// XXX Some callers of TextEditor::InsertTextAsAction() already make the
// string use only \n as a linebreaker. However, they are not hot
// path and nsContentUtils::PlatformToDOMLineBreaks() does nothing
// if the string doesn't include \r. So, let's convert linebreakers
// here. Note that there are too many callers of
// TextEditor::InsertTextAsAction(). So, it's difficult to keep
// maintaining all of them won't reach here without \r nor \r\n.
nsContentUtils::PlatformToDOMLineBreaks(tString);
HandleNewLines(tString);
outString->Assign(tString);
}
@ -885,6 +898,7 @@ TextEditRules::WillSetText(bool* aCancel,
MOZ_ASSERT(aCancel);
MOZ_ASSERT(aHandled);
MOZ_ASSERT(aString);
MOZ_ASSERT(aString->FindChar(static_cast<char16_t>('\r')) == kNotFound);
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
@ -923,7 +937,7 @@ TextEditRules::WillSetText(bool* aCancel,
mPasswordText.Assign(tString);
FillBufWithPWChars(&tString, tString.Length());
} else if (IsSingleLineEditor()) {
HandleNewLines(tString, TextEditorRef().mNewlineHandling);
HandleNewLines(tString);
}
if (!count) {

View File

@ -104,28 +104,26 @@ public:
void ResetIMETextPWBuf();
/**
* Handles the newline characters either according to aNewLineHandling
* or to the default system prefs if aNewLineHandling is negative.
* Handles the newline characters according to the default system prefs
* (editor.singleLine.pasteNewlines).
* Each value means:
* nsIPlaintextEditor::eNewlinesReplaceWithSpaces (2, Firefox default):
* replace newlines with spaces.
* nsIPlaintextEditor::eNewlinesStrip (3):
* remove newlines from the string.
* nsIPlaintextEditor::eNewlinesReplaceWithCommas (4, Thunderbird default):
* replace newlines with commas.
* nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace (5):
* collapse newlines and surrounding whitespace characters and
* remove them from the string.
* nsIPlaintextEditor::eNewlinesPasteIntact (0):
* only remove the leading and trailing newlines.
* nsIPlaintextEditor::eNewlinesPasteToFirst (1) or any other value:
* remove the first newline and all characters following it.
*
* @param aString the string to be modified in place.
* @param aNewLineHandling determine the desired type of newline handling:
* * negative values:
* handle newlines according to platform defaults.
* * nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
* replace newlines with spaces.
* * nsIPlaintextEditor::eNewlinesStrip:
* remove newlines from the string.
* * nsIPlaintextEditor::eNewlinesReplaceWithCommas:
* replace newlines with commas.
* * nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace:
* collapse newlines and surrounding whitespace characters and
* remove them from the string.
* * nsIPlaintextEditor::eNewlinesPasteIntact:
* only remove the leading and trailing newlines.
* * nsIPlaintextEditor::eNewlinesPasteToFirst or any other value:
* remove the first newline and all characters following it.
*/
static void HandleNewLines(nsString& aString, int32_t aNewLineHandling);
void HandleNewLines(nsString& aString);
/**
* Prepare a string buffer for being displayed as the contents of a password

View File

@ -1114,6 +1114,8 @@ TextEditor::InsertParagraphSeparatorAsAction()
nsresult
TextEditor::SetText(const nsAString& aString)
{
MOZ_ASSERT(aString.FindChar(static_cast<char16_t>('\r')) == kNotFound);
if (NS_WARN_IF(!mRules)) {
return NS_ERROR_NOT_INITIALIZED;
}