Bug 1630168 - Make HTMLEditor stop adding same runnable method into the queue r=m_kato

When `HTMLEditor` is notified of content changes, it may add a runnable method
`HTMLEditor::OnModifyDocument` or `HTMLEditor::NotifyRootChanged` for each
notification. However, their code do not need running twice nor more. This
could cause performance issues on complicated web apps which sets `innerHTML`
at every key press.

Differential Revision: https://phabricator.services.mozilla.com/D71001
This commit is contained in:
Masayuki Nakano 2020-04-16 15:15:26 +00:00
parent 3ef497088e
commit 9f7fb5bb0f
3 changed files with 41 additions and 10 deletions

View File

@ -11848,8 +11848,12 @@ EditActionResult HTMLEditor::AddZIndexAsSubAction(int32_t aChange) {
}
nsresult HTMLEditor::OnDocumentModified() {
nsContentUtils::AddScriptRunner(NewRunnableMethod(
"HTMLEditor::OnModifyDocument", this, &HTMLEditor::OnModifyDocument));
if (mPendingDocumentModifiedRunner) {
return NS_OK; // We've already posted same runnable into the queue.
}
mPendingDocumentModifiedRunner = NewRunnableMethod(
"HTMLEditor::OnModifyDocument", this, &HTMLEditor::OnModifyDocument);
nsContentUtils::AddScriptRunner(do_AddRef(mPendingDocumentModifiedRunner));
// Be aware, if OnModifyDocument() may be called synchronously, the
// editor might have been destroyed here.
return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;

View File

@ -3650,11 +3650,18 @@ void HTMLEditor::DoContentInserted(nsIContent* aChild,
if (ShouldReplaceRootElement()) {
UpdateRootElement();
nsContentUtils::AddScriptRunner(NewRunnableMethod(
"HTMLEditor::NotifyRootChanged", this, &HTMLEditor::NotifyRootChanged));
if (mPendingRootElementUpdatedRunner) {
return;
}
mPendingRootElementUpdatedRunner = NewRunnableMethod(
"HTMLEditor::NotifyRootChanged", this, &HTMLEditor::NotifyRootChanged);
nsContentUtils::AddScriptRunner(
do_AddRef(mPendingRootElementUpdatedRunner));
return;
}
// We don't need to handle our own modifications
else if (!GetTopLevelEditSubAction() && container->IsEditable()) {
if (!GetTopLevelEditSubAction() && container->IsEditable()) {
if (EditorUtils::IsPaddingBRElementForEmptyEditor(*aChild)) {
// Ignore insertion of the padding <br> element.
return;
@ -3703,11 +3710,18 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY void HTMLEditor::ContentRemoved(
if (SameCOMIdentity(aChild, mRootElement)) {
mRootElement = nullptr;
nsContentUtils::AddScriptRunner(NewRunnableMethod(
"HTMLEditor::NotifyRootChanged", this, &HTMLEditor::NotifyRootChanged));
// We don't need to handle our own modifications
} else if (!GetTopLevelEditSubAction() &&
aChild->GetParentNode()->IsEditable()) {
if (mPendingRootElementUpdatedRunner) {
return;
}
mPendingRootElementUpdatedRunner = NewRunnableMethod(
"HTMLEditor::NotifyRootChanged", this, &HTMLEditor::NotifyRootChanged);
nsContentUtils::AddScriptRunner(
do_AddRef(mPendingRootElementUpdatedRunner));
return;
}
// We don't need to handle our own modifications
if (!GetTopLevelEditSubAction() && aChild->GetParentNode()->IsEditable()) {
if (aChild && EditorUtils::IsPaddingBRElementForEmptyEditor(*aChild)) {
// Ignore removal of the padding <br> element for empty editor.
return;
@ -5124,6 +5138,10 @@ bool HTMLEditor::ShouldReplaceRootElement() const {
}
void HTMLEditor::NotifyRootChanged() {
MOZ_ASSERT(mPendingRootElementUpdatedRunner,
"HTMLEditor::NotifyRootChanged() should be called via a runner");
mPendingRootElementUpdatedRunner = nullptr;
nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
@ -5339,6 +5357,10 @@ nsHTMLDocument* HTMLEditor::GetHTMLDocument() const {
}
nsresult HTMLEditor::OnModifyDocument() {
MOZ_ASSERT(mPendingDocumentModifiedRunner,
"HTMLEditor::OnModifyDocument() should be called via a runner");
mPendingDocumentModifiedRunner = nullptr;
if (IsEditActionDataAvailable()) {
return OnModifyDocumentInternal();
}
@ -5357,6 +5379,7 @@ nsresult HTMLEditor::OnModifyDocument() {
nsresult HTMLEditor::OnModifyDocumentInternal() {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!mPendingDocumentModifiedRunner);
// EnsureNoPaddingBRElementForEmptyEditor() below may cause a flush, which
// could destroy the editor

View File

@ -54,6 +54,7 @@ class ListItemElementSelectionState;
class MoveNodeResult;
class ParagraphStateAtSelection;
class ResizerSelectionListener;
class Runnable;
class SplitRangeOffFromNodeResult;
class SplitRangeOffResult;
class WSRunObject;
@ -4442,6 +4443,9 @@ class HTMLEditor final : public TextEditor,
// Used by TopLevelEditSubActionData::mChangedRange.
mutable RefPtr<nsRange> mChangedRangeForTopLevelEditSubAction;
RefPtr<Runnable> mPendingRootElementUpdatedRunner;
RefPtr<Runnable> mPendingDocumentModifiedRunner;
bool mCRInParagraphCreatesParagraph;
bool mCSSAware;