Bug 1533293 - part 2: Rewrite EditorBase::SelectEntireDocument() and its overrides r=m_kato

`EditorBase::SelectEntierDocument()` uses `Selection::Extend()` but it's too
slow.  It should use `Selection::SetStartAndEndInLimiter()` instead.

Additionally, `TextEditor::SelectEntierDocument()` shrink the result of
`EditorBase::SelectEntierDocument()` with `Selection::Extend()` if there is
a `moz-<br>` element.  So, `TextEditor::SelectEntinerDocument()` should set
its expected selection with a call for saving the runtime cost.

Then, we don't need to make `EditorBase::SelectEntierDocument()` as non-pure
virtual method.  So, this patch makes each its callers call
`Selection->SelectAllChildren()` directly.

Differential Revision: https://phabricator.services.mozilla.com/D23461

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2019-03-18 01:51:53 +00:00
Родитель d4cbc09db8
Коммит 448571fd81
13 изменённых файлов: 77 добавлений и 55 удалений

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

@ -2301,7 +2301,9 @@ bool nsTextEditorState::SetValue(const nsAString& aValue,
// transactions typed by user shouldn't be merged with this). // transactions typed by user shouldn't be merged with this).
// In this case, we need to dispatch "input" event because // In this case, we need to dispatch "input" event because
// web apps may need to know the user's operation. // web apps may need to know the user's operation.
DebugOnly<nsresult> rv = textEditor->ReplaceTextAsAction(newValue); RefPtr<nsRange> range; // See bug 1506439
DebugOnly<nsresult> rv =
textEditor->ReplaceTextAsAction(newValue, range);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to set the new value"); "Failed to set the new value");
} else if (aFlags & eSetValue_ForXUL) { } else if (aFlags & eSetValue_ForXUL) {

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

@ -2660,19 +2660,6 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction(
return rv; return rv;
} }
nsresult EditorBase::SelectEntireDocument() {
MOZ_ASSERT(IsEditActionDataAvailable());
Element* rootElement = GetRoot();
if (!rootElement) {
return NS_ERROR_NOT_INITIALIZED;
}
ErrorResult errorResult;
SelectionRefPtr()->SelectAllChildren(*rootElement, errorResult);
return errorResult.StealNSResult();
}
nsINode* EditorBase::GetFirstEditableNode(nsINode* aRoot) { nsINode* EditorBase::GetFirstEditableNode(nsINode* aRoot) {
MOZ_ASSERT(aRoot); MOZ_ASSERT(aRoot);

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

@ -1816,6 +1816,7 @@ class EditorBase : public nsIEditor,
* because SelectAll() creates AutoEditActionSetter but we should avoid * because SelectAll() creates AutoEditActionSetter but we should avoid
* to create it as far as possible. * to create it as far as possible.
*/ */
MOZ_CAN_RUN_SCRIPT
virtual nsresult SelectAllInternal(); virtual nsresult SelectAllInternal();
nsresult DetermineCurrentDirection(); nsresult DetermineCurrentDirection();
@ -1860,7 +1861,8 @@ class EditorBase : public nsIEditor,
/** /**
* Make the given selection span the entire document. * Make the given selection span the entire document.
*/ */
virtual nsresult SelectEntireDocument(); MOZ_CAN_RUN_SCRIPT
virtual nsresult SelectEntireDocument() = 0;
/** /**
* Helper method for scrolling the selection into view after * Helper method for scrolling the selection into view after

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

@ -695,7 +695,7 @@ SelectAllCommand::DoCommand(const char* aCommandName,
} }
TextEditor* textEditor = editor->AsTextEditor(); TextEditor* textEditor = editor->AsTextEditor();
MOZ_ASSERT(textEditor); MOZ_ASSERT(textEditor);
return textEditor->SelectAll(); return MOZ_KnownLive(textEditor)->SelectAll();
} }
NS_IMETHODIMP NS_IMETHODIMP

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

@ -3603,23 +3603,31 @@ nsresult HTMLEditor::SelectEntireDocument() {
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
} }
RefPtr<Element> rootElement = GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_NOT_INITIALIZED;
}
// Protect the edit rules object from dying // Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules); RefPtr<TextEditRules> rules(mRules);
// is doc empty? // If we're empty, don't select all children because that would select the
// bogus node.
if (rules->DocumentIsEmpty()) { if (rules->DocumentIsEmpty()) {
// get editor root node
Element* rootElement = GetRoot();
// if its empty dont select entire doc - that would select the bogus node
nsresult rv = SelectionRefPtr()->Collapse(rootElement, 0); nsresult rv = SelectionRefPtr()->Collapse(rootElement, 0);
if (NS_WARN_IF(NS_FAILED(rv))) { NS_WARNING_ASSERTION(
return rv; NS_SUCCEEDED(rv),
} "Failed to move caret to start of the editor root element");
return NS_OK; return rv;
} }
return EditorBase::SelectEntireDocument(); // Otherwise, select all children.
ErrorResult error;
SelectionRefPtr()->SelectAllChildren(*rootElement, error);
NS_WARNING_ASSERTION(
!error.Failed(),
"Failed to select all children of the editor root element");
return error.StealNSResult();
} }
nsresult HTMLEditor::SelectAllInternal() { nsresult HTMLEditor::SelectAllInternal() {

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

@ -955,6 +955,7 @@ class HTMLEditor final : public TextEditor,
*/ */
nsresult InsertParagraphSeparatorAsSubAction(); nsresult InsertParagraphSeparatorAsSubAction();
MOZ_CAN_RUN_SCRIPT
virtual nsresult SelectAllInternal() override; virtual nsresult SelectAllInternal() override;
/** /**
@ -1521,6 +1522,7 @@ class HTMLEditor final : public TextEditor,
/** /**
* Make the given selection span the entire document. * Make the given selection span the entire document.
*/ */
MOZ_CAN_RUN_SCRIPT
virtual nsresult SelectEntireDocument() override; virtual nsresult SelectEntireDocument() override;
/** /**

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

@ -1198,18 +1198,25 @@ nsresult TextEditor::SetTextAsSubAction(const nsAString& aString) {
// shouldn't receive such selectionchange before the first mutation. // shouldn't receive such selectionchange before the first mutation.
AutoUpdateViewBatch preventSelectionChangeEvent(*this); AutoUpdateViewBatch preventSelectionChangeEvent(*this);
Element* rootElement = GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_FAILURE;
}
// We want to select trailing BR node to remove all nodes to replace all, // We want to select trailing BR node to remove all nodes to replace all,
// but TextEditor::SelectEntireDocument doesn't select that BR node. // but TextEditor::SelectEntireDocument doesn't select that BR node.
if (rules->DocumentIsEmpty()) { if (rules->DocumentIsEmpty()) {
// if it's empty, don't select entire doc - that would select
// the bogus node
Element* rootElement = GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_FAILURE;
}
rv = SelectionRefPtr()->Collapse(rootElement, 0); rv = SelectionRefPtr()->Collapse(rootElement, 0);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"Failed to move caret to start of the editor root element");
} else { } else {
rv = EditorBase::SelectEntireDocument(); ErrorResult error;
SelectionRefPtr()->SelectAllChildren(*rootElement, error);
NS_WARNING_ASSERTION(
!error.Failed(),
"Failed to select all children of the editor root element");
rv = error.StealNSResult();
} }
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
rv = ReplaceSelectionAsSubAction(aString); rv = ReplaceSelectionAsSubAction(aString);
@ -2143,31 +2150,28 @@ nsresult TextEditor::SelectEntireDocument() {
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
} }
Element* rootElement = GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_NOT_INITIALIZED;
}
// Protect the edit rules object from dying // Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules); RefPtr<TextEditRules> rules(mRules);
// is doc empty? // If we're empty, don't select all children because that would select the
// bogus node.
if (rules->DocumentIsEmpty()) { if (rules->DocumentIsEmpty()) {
// get root node nsresult rv = SelectionRefPtr()->Collapse(rootElement, 0);
Element* rootElement = GetRoot(); NS_WARNING_ASSERTION(
if (NS_WARN_IF(!rootElement)) { NS_SUCCEEDED(rv),
return NS_ERROR_FAILURE; "Failed to move caret to start of the editor root element");
}
// if it's empty don't select entire doc - that would select the bogus node
return SelectionRefPtr()->Collapse(rootElement, 0);
}
SelectionBatcher selectionBatcher(SelectionRefPtr());
nsresult rv = EditorBase::SelectEntireDocument();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
// Don't select the trailing BR node if we have one // Don't select the trailing BR node if we have one
nsCOMPtr<nsIContent> childNode; nsCOMPtr<nsIContent> childNode;
rv = EditorBase::GetEndChildNode(*SelectionRefPtr(), nsresult rv = EditorBase::GetEndChildNode(*SelectionRefPtr(),
getter_AddRefs(childNode)); getter_AddRefs(childNode));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -2176,13 +2180,22 @@ nsresult TextEditor::SelectEntireDocument() {
} }
if (childNode && TextEditUtils::IsMozBR(childNode)) { if (childNode && TextEditUtils::IsMozBR(childNode)) {
int32_t parentOffset; ErrorResult error;
nsINode* parentNode = GetNodeLocation(childNode, &parentOffset); MOZ_KnownLive(SelectionRefPtr())
->SetStartAndEndInLimiter(RawRangeBoundary(rootElement, 0),
return SelectionRefPtr()->Extend(parentNode, parentOffset); EditorRawDOMPoint(childNode), error);
NS_WARNING_ASSERTION(!error.Failed(),
"Failed to select all children of the editor root "
"element except the moz-<br> element");
return error.StealNSResult();
} }
return NS_OK; ErrorResult error;
SelectionRefPtr()->SelectAllChildren(*rootElement, error);
NS_WARNING_ASSERTION(
!error.Failed(),
"Failed to select all children of the editor root element");
return error.StealNSResult();
} }
EventTarget* TextEditor::GetDOMEventTarget() { return mEventTarget; } EventTarget* TextEditor::GetDOMEventTarget() { return mEventTarget; }

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

@ -173,6 +173,7 @@ class TextEditor : public EditorBase, public nsIPlaintextEditor {
* *
* @ param aString the string to be set * @ param aString the string to be set
*/ */
MOZ_CAN_RUN_SCRIPT
nsresult SetText(const nsAString& aString); nsresult SetText(const nsAString& aString);
/** /**
@ -183,6 +184,7 @@ class TextEditor : public EditorBase, public nsIPlaintextEditor {
* @param aReplaceRange The range to be replaced. * @param aReplaceRange The range to be replaced.
* If nullptr, all contents will be replaced. * If nullptr, all contents will be replaced.
*/ */
MOZ_CAN_RUN_SCRIPT
nsresult ReplaceTextAsAction(const nsAString& aString, nsresult ReplaceTextAsAction(const nsAString& aString,
nsRange* aReplaceRange = nullptr); nsRange* aReplaceRange = nullptr);
@ -365,6 +367,7 @@ class TextEditor : public EditorBase, public nsIPlaintextEditor {
/** /**
* Make the given selection span the entire document. * Make the given selection span the entire document.
*/ */
MOZ_CAN_RUN_SCRIPT
virtual nsresult SelectEntireDocument() override; virtual nsresult SelectEntireDocument() override;
/** /**

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

@ -305,6 +305,7 @@ interface nsIEditor : nsISupports
/* ------------ Selection methods -------------- */ /* ------------ Selection methods -------------- */
/** sets the document selection to the entire contents of the document */ /** sets the document selection to the entire contents of the document */
[can_run_script]
void selectAll(); void selectAll();
/** /**

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

@ -30,6 +30,7 @@ interface nsIEditorMailSupport : nsISupports
* Rewrap the selected part of the document, re-quoting if necessary. * Rewrap the selected part of the document, re-quoting if necessary.
* @param aRespectNewlines Try to maintain newlines in the original? * @param aRespectNewlines Try to maintain newlines in the original?
*/ */
[can_run_script]
void rewrap(in boolean aRespectNewlines); void rewrap(in boolean aRespectNewlines);
}; };

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

@ -147,6 +147,7 @@ interface nsIHTMLEditor : nsISupports
* *
* @param aSourceString HTML source string of the entire new document * @param aSourceString HTML source string of the entire new document
*/ */
[can_run_script]
void rebuildDocumentFromSource(in AString aSourceString); void rebuildDocumentFromSource(in AString aSourceString);
/** /**

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

@ -25,6 +25,7 @@ interface nsIInlineSpellChecker : nsISupports
void spellCheckRange(in Range aSelection); void spellCheckRange(in Range aSelection);
Range getMisspelledWord(in Node aNode, in long aOffset); Range getMisspelledWord(in Node aNode, in long aOffset);
[can_run_script]
void replaceWord(in Node aNode, in long aOffset, in AString aNewword); void replaceWord(in Node aNode, in long aOffset, in AString aNewword);
void addWordToDictionary(in AString aWord); void addWordToDictionary(in AString aWord);
void removeWordFromDictionary(in AString aWord); void removeWordFromDictionary(in AString aWord);

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

@ -869,7 +869,8 @@ mozInlineSpellChecker::ReplaceWord(nsINode* aNode, int32_t aOffset,
return NS_OK; return NS_OK;
} }
DebugOnly<nsresult> rv = mTextEditor->ReplaceTextAsAction(newword, range); RefPtr<TextEditor> textEditor(mTextEditor);
DebugOnly<nsresult> rv = textEditor->ReplaceTextAsAction(newword, range);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert the new word"); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert the new word");
return NS_OK; return NS_OK;
} }