Bug 1656430 - part 3: Create EditorBase::DeleteRangesWithTransaction() r=m_kato

This patch makes `EditorBase::DeleteSelectionWithTransaction()` a wrapper of
`EditorBase::DeleteRangesWithTransaction()`.

Differential Revision: https://phabricator.services.mozilla.com/D85686
This commit is contained in:
Masayuki Nakano 2020-08-05 07:13:40 +00:00
parent 65db771e7f
commit e12a28bb16
3 changed files with 100 additions and 43 deletions

View File

@ -3344,12 +3344,13 @@ void EditorBase::DoAfterRedoTransaction() {
already_AddRefed<EditAggregateTransaction>
EditorBase::CreateTransactionForDeleteSelection(
HowToHandleCollapsedRange aHowToHandleCollapsedRange) {
HowToHandleCollapsedRange aHowToHandleCollapsedRange,
const AutoRangeArray& aRangesToDelete) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(SelectionRefPtr()->RangeCount());
MOZ_ASSERT(!aRangesToDelete.Ranges().IsEmpty());
// Check whether the selection is collapsed and we should do nothing:
if (NS_WARN_IF(SelectionRefPtr()->IsCollapsed() &&
if (NS_WARN_IF(aRangesToDelete.IsCollapsed() &&
aHowToHandleCollapsedRange ==
HowToHandleCollapsedRange::Ignore)) {
return nullptr;
@ -3358,18 +3359,12 @@ EditorBase::CreateTransactionForDeleteSelection(
// allocate the out-param transaction
RefPtr<EditAggregateTransaction> aggregateTransaction =
EditAggregateTransaction::Create();
for (uint32_t rangeIdx = 0; rangeIdx < SelectionRefPtr()->RangeCount();
++rangeIdx) {
const nsRange* range = SelectionRefPtr()->GetRangeAt(rangeIdx);
if (NS_WARN_IF(!range)) {
return nullptr;
}
for (const OwningNonNull<nsRange>& range : aRangesToDelete.Ranges()) {
// Same with range as with selection; if it is collapsed and action
// is eNone, do nothing.
if (!range->Collapsed()) {
RefPtr<DeleteRangeTransaction> deleteRangeTransaction =
DeleteRangeTransaction::Create(*this, *range);
DeleteRangeTransaction::Create(*this, range);
// XXX Oh, not checking if deleteRangeTransaction can modify the range...
DebugOnly<nsresult> rvIgnored =
aggregateTransaction->AppendChild(deleteRangeTransaction);
@ -3385,7 +3380,7 @@ EditorBase::CreateTransactionForDeleteSelection(
// Let's extend the collapsed range to delete content around it.
RefPtr<EditTransactionBase> deleteNodeOrTextTransaction =
CreateTransactionForCollapsedRange(*range, aHowToHandleCollapsedRange);
CreateTransactionForCollapsedRange(range, aHowToHandleCollapsedRange);
// XXX When there are two or more ranges and at least one of them is
// not editable, deleteNodeOrTextTransaction may be nullptr.
// In such case, should we stop removing other ranges too?
@ -3948,15 +3943,38 @@ nsresult EditorBase::DeleteSelectionWithTransaction(
nsIEditor::EStripWrappers aStripWrappers) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
AutoRangeArray rangesToDelete(*SelectionRefPtr());
if (NS_WARN_IF(rangesToDelete.Ranges().IsEmpty())) {
NS_ASSERTION(
false,
"For avoiding to throw incompatible exception for `execCommand`, fix "
"the caller");
return NS_ERROR_FAILURE;
}
nsresult rv = DeleteRangesWithTransaction(aDirectionAndAmount, aStripWrappers,
rangesToDelete);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::DeleteRangesWithTransaction() failed");
return rv;
}
nsresult EditorBase::DeleteRangesWithTransaction(
nsIEditor::EDirection aDirectionAndAmount,
nsIEditor::EStripWrappers aStripWrappers,
const AutoRangeArray& aRangesToDelete) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!Destroyed());
MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
MOZ_ASSERT(!aRangesToDelete.Ranges().IsEmpty());
HowToHandleCollapsedRange howToHandleCollapsedRange =
EditorBase::HowToHandleCollapsedRangeFor(aDirectionAndAmount);
if (NS_WARN_IF(!SelectionRefPtr()->RangeCount()) ||
NS_WARN_IF(SelectionRefPtr()->IsCollapsed() &&
if (NS_WARN_IF(aRangesToDelete.IsCollapsed() &&
howToHandleCollapsedRange ==
HowToHandleCollapsedRange::Ignore)) {
NS_ASSERTION(
@ -3967,7 +3985,8 @@ nsresult EditorBase::DeleteSelectionWithTransaction(
}
RefPtr<EditAggregateTransaction> deleteSelectionTransaction =
CreateTransactionForDeleteSelection(howToHandleCollapsedRange);
CreateTransactionForDeleteSelection(howToHandleCollapsedRange,
aRangesToDelete);
if (!deleteSelectionTransaction) {
NS_WARNING("EditorBase::CreateTransactionForDeleteSelection() failed");
return NS_ERROR_FAILURE;
@ -4013,8 +4032,8 @@ nsresult EditorBase::DeleteSelectionWithTransaction(
// this must have a bug since we only add the first range into
// the changed range.
TopLevelEditSubActionDataRef().WillDeleteRange(
*this, EditorBase::GetStartPoint(*SelectionRefPtr()),
EditorBase::GetEndPoint(*SelectionRefPtr()));
*this, aRangesToDelete.GetStartPointOfFirstRange(),
aRangesToDelete.GetEndPointOfFirstRange());
} else if (!deleteCharData) {
TopLevelEditSubActionDataRef().WillDeleteContent(*this, *deleteContent);
}
@ -4023,25 +4042,19 @@ nsresult EditorBase::DeleteSelectionWithTransaction(
// Notify nsIEditActionListener::WillDelete[Selection|Text]
if (!mActionListeners.IsEmpty()) {
if (!deleteContent) {
if (SelectionRefPtr()->RangeCount()) {
AutoActionListenerArray listeners(mActionListeners.Clone());
for (auto& listener : listeners) {
// FYI: Currently, there should be only one listener at most.
// Therefore, retrieving the lastest ranges everytime before
// calling `WillDeleteRanges()` must be fine.
AutoTArray<RefPtr<nsRange>, 8> ranges;
for (uint32_t i = 0; i < SelectionRefPtr()->RangeCount(); i++) {
ranges.AppendElement(
SelectionRefPtr()->GetRangeAt(i)->CloneRange());
}
DebugOnly<nsresult> rvIgnored = listener->WillDeleteRanges(ranges);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsIEditActionListener::WillDeleteRanges() failed, but ignored");
MOZ_DIAGNOSTIC_ASSERT(!Destroyed(),
"nsIEditActionListener::WillDeleteRanges() "
"must not destroy the editor");
}
MOZ_ASSERT(!aRangesToDelete.Ranges().IsEmpty());
AutoTArray<RefPtr<nsRange>, 8> rangesToDelete(
aRangesToDelete.CloneRanges<RefPtr>());
AutoActionListenerArray listeners(mActionListeners.Clone());
for (auto& listener : listeners) {
DebugOnly<nsresult> rvIgnored =
listener->WillDeleteRanges(rangesToDelete);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsIEditActionListener::WillDeleteRanges() failed, but ignored");
MOZ_DIAGNOSTIC_ASSERT(!Destroyed(),
"nsIEditActionListener::WillDeleteRanges() "
"must not destroy the editor");
}
} else if (deleteCharData) {
AutoActionListenerArray listeners(mActionListeners.Clone());

View File

@ -59,6 +59,7 @@ class nsRange;
namespace mozilla {
class AlignStateAtSelection;
class AutoRangeArray;
class AutoSelectionRestorer;
class AutoTopLevelEditSubActionNotifier;
class AutoTransactionBatch;
@ -2227,20 +2228,41 @@ class EditorBase : public nsIEditor,
nsIEditor::EStripWrappers aStripWrappers);
/**
* Create an aggregate transaction for delete selection. The result may
* include DeleteNodeTransactions and/or DeleteTextTransactions as its
* children.
* DeleteRangesWithTransaction() removes content in aRangesToDelete or content
* around collapsed ranges in aRangesToDelete with transactions and remove
* empty inclusive ancestor inline elements of collapsed ranges after
* removing the contents.
*
* @param aDirectionAndAmount How much range should be removed.
* @param aStripWrappers Whether the parent blocks should be removed
* when they become empty.
* Note that this must be `nsIEditor::eNoStrip`
* if this is a TextEditor because anyway it'll
* be ignored.
* @param aRangesToDelete The ranges to delete content.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
DeleteRangesWithTransaction(nsIEditor::EDirection aDirectionAndAmount,
nsIEditor::EStripWrappers aStripWrappers,
const AutoRangeArray& aRangesToDelete);
/**
* Create an aggregate transaction for delete the content in aRangesToDelete.
* The result may include DeleteNodeTransactions and/or DeleteTextTransactions
* as its children.
*
* @param aHowToHandleCollapsedRange
* How to handle collapsed ranges.
* @return If it can remove the selection, returns an
* aggregate transaction which has some
* @param aRangesToDelete The ranges to delete content.
* @return If it can remove the content in ranges, returns
* an aggregate transaction which has some
* DeleteNodeTransactions and/or
* DeleteTextTransactions as its children.
*/
already_AddRefed<EditAggregateTransaction>
CreateTransactionForDeleteSelection(
HowToHandleCollapsedRange aHowToHandleCollapsedRange);
HowToHandleCollapsedRange aHowToHandleCollapsedRange,
const AutoRangeArray& aRangesToDelete);
/**
* Create a transaction for removing the nodes and/or text around

View File

@ -753,6 +753,28 @@ class MOZ_STACK_CLASS AutoRangeArray final {
auto& Ranges() { return mRanges; }
const auto& Ranges() const { return mRanges; }
template <template <typename> typename StrongPtrType>
AutoTArray<StrongPtrType<nsRange>, 8> CloneRanges() const {
AutoTArray<StrongPtrType<nsRange>, 8> ranges;
for (const auto& range : mRanges) {
ranges.AppendElement(range->CloneRange());
}
return ranges;
}
EditorDOMPoint GetStartPointOfFirstRange() const {
if (mRanges.IsEmpty() || !mRanges[0]->IsPositioned()) {
return EditorDOMPoint();
}
return EditorDOMPoint(mRanges[0]->StartRef());
}
EditorDOMPoint GetEndPointOfFirstRange() const {
if (mRanges.IsEmpty() || !mRanges[0]->IsPositioned()) {
return EditorDOMPoint();
}
return EditorDOMPoint(mRanges[0]->EndRef());
}
/**
* The following methods are same as `Selection`'s methods.
*/