Bug 1336011 - Fix Crash in InvalidArrayIndex_CRASH in mozilla::EditorBase::DeleteSelectionImpl

* EditorBase shouldn't refer mActionListeners directly in loops because it might be removed during a loop
* Create an alias of the type of mEditorObservers
* Create an alias of the type of mDocStateListeners

Tag #1375
This commit is contained in:
Matt A. Tobin 2020-04-15 01:55:25 -04:00 • committed by Roy Tam
commit 31048968fd
2 changed files with 124 additions and 71 deletions

View file

@ -1385,9 +1385,12 @@ EditorBase::CreateNode(nsIAtom* aTag,
AutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext); AutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
for (auto& listener : mActionListeners) { {
listener->WillCreateNode(nsDependentAtomString(aTag), AutoActionListenerArray listeners(mActionListeners);
GetAsDOMNode(aParent), aPosition); for (auto& listener : listeners) {
listener->WillCreateNode(nsDependentAtomString(aTag),
GetAsDOMNode(aParent), aPosition);
}
} }
nsCOMPtr<Element> ret; nsCOMPtr<Element> ret;
@ -1402,9 +1405,12 @@ EditorBase::CreateNode(nsIAtom* aTag,
mRangeUpdater.SelAdjCreateNode(aParent, aPosition); mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
for (auto& listener : mActionListeners) { {
listener->DidCreateNode(nsDependentAtomString(aTag), GetAsDOMNode(ret), AutoActionListenerArray listeners(mActionListeners);
GetAsDOMNode(aParent), aPosition, rv); for (auto& listener : listeners) {
listener->DidCreateNode(nsDependentAtomString(aTag), GetAsDOMNode(ret),
GetAsDOMNode(aParent), aPosition, rv);
}
} }
return ret.forget(); return ret.forget();
@ -1429,9 +1435,12 @@ EditorBase::InsertNode(nsIContent& aNode,
{ {
AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
for (auto& listener : mActionListeners) { {
listener->WillInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(), AutoActionListenerArray listeners(mActionListeners);
aPosition); for (auto& listener : listeners) {
listener->WillInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(),
aPosition);
}
} }
RefPtr<InsertNodeTransaction> transaction = RefPtr<InsertNodeTransaction> transaction =
@ -1440,9 +1449,12 @@ EditorBase::InsertNode(nsIContent& aNode,
mRangeUpdater.SelAdjInsertNode(aParent.AsDOMNode(), aPosition); mRangeUpdater.SelAdjInsertNode(aParent.AsDOMNode(), aPosition);
for (auto& listener : mActionListeners) { {
listener->DidInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(), aPosition, AutoActionListenerArray listeners(mActionListeners);
rv); for (auto& listener : listeners) {
listener->DidInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(), aPosition,
rv);
}
} }
return rv; return rv;
@ -1468,8 +1480,11 @@ EditorBase::SplitNode(nsIContent& aNode,
{ {
AutoRules beginRulesSniffing(this, EditAction::splitNode, nsIEditor::eNext); AutoRules beginRulesSniffing(this, EditAction::splitNode, nsIEditor::eNext);
for (auto& listener : mActionListeners) { {
listener->WillSplitNode(aNode.AsDOMNode(), aOffset); AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
listener->WillSplitNode(aNode.AsDOMNode(), aOffset);
}
} }
RefPtr<SplitNodeTransaction> transaction = RefPtr<SplitNodeTransaction> transaction =
@ -1482,9 +1497,12 @@ EditorBase::SplitNode(nsIContent& aNode,
mRangeUpdater.SelAdjSplitNode(aNode, aOffset, newNode); mRangeUpdater.SelAdjSplitNode(aNode, aOffset, newNode);
nsresult rv = aResult.StealNSResult(); nsresult rv = aResult.StealNSResult();
for (auto& listener : mActionListeners) { {
listener->DidSplitNode(aNode.AsDOMNode(), aOffset, GetAsDOMNode(newNode), AutoActionListenerArray listeners(mActionListeners);
rv); for (auto& listener : listeners) {
listener->DidSplitNode(aNode.AsDOMNode(), aOffset, GetAsDOMNode(newNode),
rv);
}
} }
// Note: result might be a success code, so we can't use Throw() to // Note: result might be a success code, so we can't use Throw() to
// set it on aResult. // set it on aResult.
@ -1520,9 +1538,12 @@ EditorBase::JoinNodes(nsINode& aLeftNode,
// Find the number of children of the lefthand node // Find the number of children of the lefthand node
uint32_t oldLeftNodeLen = aLeftNode.Length(); uint32_t oldLeftNodeLen = aLeftNode.Length();
for (auto& listener : mActionListeners) { {
listener->WillJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(), AutoActionListenerArray listeners(mActionListeners);
parent->AsDOMNode()); for (auto& listener : listeners) {
listener->WillJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(),
parent->AsDOMNode());
}
} }
nsresult rv = NS_OK; nsresult rv = NS_OK;
@ -1535,9 +1556,12 @@ EditorBase::JoinNodes(nsINode& aLeftNode,
mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, *parent, offset, mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, *parent, offset,
(int32_t)oldLeftNodeLen); (int32_t)oldLeftNodeLen);
for (auto& listener : mActionListeners) { {
listener->DidJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(), AutoActionListenerArray listeners(mActionListeners);
parent->AsDOMNode(), rv); for (auto& listener : listeners) {
listener->DidJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(),
parent->AsDOMNode(), rv);
}
} }
return rv; return rv;
@ -1558,8 +1582,11 @@ EditorBase::DeleteNode(nsINode* aNode)
nsIEditor::ePrevious); nsIEditor::ePrevious);
// save node location for selection updating code. // save node location for selection updating code.
for (auto& listener : mActionListeners) { {
listener->WillDeleteNode(aNode->AsDOMNode()); AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
listener->WillDeleteNode(aNode->AsDOMNode());
}
} }
RefPtr<DeleteNodeTransaction> transaction; RefPtr<DeleteNodeTransaction> transaction;
@ -1568,8 +1595,11 @@ EditorBase::DeleteNode(nsINode* aNode)
rv = DoTransaction(transaction); rv = DoTransaction(transaction);
} }
for (auto& listener : mActionListeners) { {
listener->DidDeleteNode(aNode->AsDOMNode(), rv); AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
listener->DidDeleteNode(aNode->AsDOMNode(), rv);
}
} }
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
@ -1844,7 +1874,7 @@ void
EditorBase::NotifyEditorObservers(NotificationForEditorObservers aNotification) EditorBase::NotifyEditorObservers(NotificationForEditorObservers aNotification)
{ {
// Copy the observers since EditAction()s can modify mEditorObservers. // Copy the observers since EditAction()s can modify mEditorObservers.
nsTArray<mozilla::OwningNonNull<nsIEditorObserver>> observers(mEditorObservers); AutoEditorObserverArray observers(mEditorObservers);
switch (aNotification) { switch (aNotification) {
case eNotifyEditorObserversOfEnd: case eNotifyEditorObserversOfEnd:
mIsInEditAction = false; mIsInEditAction = false;
@ -2505,10 +2535,13 @@ EditorBase::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
} }
// Let listeners know what's up // Let listeners know what's up
for (auto& listener : mActionListeners) { {
listener->WillInsertText( AutoActionListenerArray listeners(mActionListeners);
static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()), for (auto& listener : listeners) {
insertedOffset, aStringToInsert); listener->WillInsertText(
static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()),
insertedOffset, aStringToInsert);
}
} }
// XXX We may not need these view batches anymore. This is handled at a // XXX We may not need these view batches anymore. This is handled at a
@ -2518,10 +2551,13 @@ EditorBase::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
EndUpdateViewBatch(); EndUpdateViewBatch();
// let listeners know what happened // let listeners know what happened
for (auto& listener : mActionListeners) { {
listener->DidInsertText( AutoActionListenerArray listeners(mActionListeners);
static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()), for (auto& listener : listeners) {
insertedOffset, aStringToInsert, rv); listener->DidInsertText(
static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()),
insertedOffset, aStringToInsert, rv);
}
} }
// Added some cruft here for bug 43366. Layout was crashing because we left // Added some cruft here for bug 43366. Layout was crashing because we left
@ -2583,8 +2619,7 @@ EditorBase::NotifyDocumentListeners(
return NS_OK; return NS_OK;
} }
nsTArray<OwningNonNull<nsIDocumentStateListener>> AutoDocumentStateListenerArray listeners(mDocStateListeners);
listeners(mDocStateListeners);
nsresult rv = NS_OK; nsresult rv = NS_OK;
switch (aNotificationType) { switch (aNotificationType) {
@ -2656,19 +2691,25 @@ EditorBase::DeleteText(nsGenericDOMDataNode& aCharData,
nsIEditor::ePrevious); nsIEditor::ePrevious);
// Let listeners know what's up // Let listeners know what's up
for (auto& listener : mActionListeners) { {
listener->WillDeleteText( AutoActionListenerArray listeners(mActionListeners);
static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset, for (auto& listener : listeners) {
aLength); listener->WillDeleteText(
static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset,
aLength);
}
} }
nsresult rv = DoTransaction(transaction); nsresult rv = DoTransaction(transaction);
// Let listeners know what happened // Let listeners know what happened
for (auto& listener : mActionListeners) { {
listener->DidDeleteText( AutoActionListenerArray listeners(mActionListeners);
static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset, for (auto& listener : listeners) {
aLength, rv); listener->DidDeleteText(
static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset,
aLength, rv);
}
} }
return rv; return rv;
@ -4034,17 +4075,20 @@ EditorBase::DeleteSelectionImpl(EDirection aAction,
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction); AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction);
// Notify nsIEditActionListener::WillDelete[Selection|Text|Node] // Notify nsIEditActionListener::WillDelete[Selection|Text|Node]
if (!deleteNode) { {
for (auto& listener : mActionListeners) { AutoActionListenerArray listeners(mActionListeners);
listener->WillDeleteSelection(selection); if (!deleteNode) {
} for (auto& listener : listeners) {
} else if (deleteCharData) { listener->WillDeleteSelection(selection);
for (auto& listener : mActionListeners) { }
listener->WillDeleteText(deleteCharData, deleteCharOffset, 1); } else if (deleteCharData) {
} for (auto& listener : listeners) {
} else { listener->WillDeleteText(deleteCharData, deleteCharOffset, 1);
for (auto& listener : mActionListeners) { }
listener->WillDeleteNode(deleteNode->AsDOMNode()); } else {
for (auto& listener : listeners) {
listener->WillDeleteNode(deleteNode->AsDOMNode());
}
} }
} }
@ -4052,17 +4096,20 @@ EditorBase::DeleteSelectionImpl(EDirection aAction,
rv = DoTransaction(transaction); rv = DoTransaction(transaction);
// Notify nsIEditActionListener::DidDelete[Selection|Text|Node] // Notify nsIEditActionListener::DidDelete[Selection|Text|Node]
if (!deleteNode) { {
for (auto& listener : mActionListeners) { AutoActionListenerArray listeners(mActionListeners);
listener->DidDeleteSelection(selection); if (!deleteNode) {
} for (auto& listener : mActionListeners) {
} else if (deleteCharData) { listener->DidDeleteSelection(selection);
for (auto& listener : mActionListeners) { }
listener->DidDeleteText(deleteCharData, deleteCharOffset, 1, rv); } else if (deleteCharData) {
} for (auto& listener : mActionListeners) {
} else { listener->DidDeleteText(deleteCharData, deleteCharOffset, 1, rv);
for (auto& listener : mActionListeners) { }
listener->DidDeleteNode(deleteNode->AsDOMNode(), rv); } else {
for (auto& listener : mActionListeners) {
listener->DidDeleteNode(deleteNode->AsDOMNode(), rv);
}
} }
} }
} }

View file

@ -987,11 +987,17 @@ protected:
RefPtr<TextComposition> mComposition; RefPtr<TextComposition> mComposition;
// Listens to all low level actions on the doc. // Listens to all low level actions on the doc.
nsTArray<OwningNonNull<nsIEditActionListener>> mActionListeners; typedef AutoTArray<OwningNonNull<nsIEditActionListener>, 5>
AutoActionListenerArray;
AutoActionListenerArray mActionListeners;
// Just notify once per high level change. // Just notify once per high level change.
nsTArray<OwningNonNull<nsIEditorObserver>> mEditorObservers; typedef AutoTArray<OwningNonNull<nsIEditorObserver>, 3>
AutoEditorObserverArray;
AutoEditorObserverArray mEditorObservers;
// Listen to overall doc state (dirty or not, just created, etc.). // Listen to overall doc state (dirty or not, just created, etc.).
nsTArray<OwningNonNull<nsIDocumentStateListener>> mDocStateListeners; typedef AutoTArray<OwningNonNull<nsIDocumentStateListener>, 1>
AutoDocumentStateListenerArray;
AutoDocumentStateListenerArray mDocStateListeners;
// Cached selection for AutoSelectionRestorer. // Cached selection for AutoSelectionRestorer.
SelectionState mSavedSel; SelectionState mSavedSel;