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
Родитель 3ef497088e
Коммит 9f7fb5bb0f
3 изменённых файлов: 41 добавлений и 10 удалений

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

@ -11848,8 +11848,12 @@ EditActionResult HTMLEditor::AddZIndexAsSubAction(int32_t aChange) {
} }
nsresult HTMLEditor::OnDocumentModified() { nsresult HTMLEditor::OnDocumentModified() {
nsContentUtils::AddScriptRunner(NewRunnableMethod( if (mPendingDocumentModifiedRunner) {
"HTMLEditor::OnModifyDocument", this, &HTMLEditor::OnModifyDocument)); 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 // Be aware, if OnModifyDocument() may be called synchronously, the
// editor might have been destroyed here. // editor might have been destroyed here.
return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK; return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;

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

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

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

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