Bug 1415414 - Make DeleteRangeTransaction::CreateTxnsToDeleteBetween() and DeleteRangeTransaction::CreateTxnsToDeleteContent() use RawRangeBoundary as their arguments r=m_kato

By the fix of bug 1407352, DeleteRangeTransaction::CreateTxnsToDeleteBetween()
starts to use child node instead of a set of container node and offset in it.
However, this is ugly and using RawRangeBoundary may reduce computing offset
more.  So, they should use RawRangeBoundary to refer delete points.

MozReview-Commit-ID: pwDHOxpz0E

--HG--
extra : rebase_source : 3871ba4fa3cfb174d5dac57822ae69c40a305323
This commit is contained in:
Masayuki Nakano 2017-11-08 15:01:23 +09:00
parent 223e6dec30
commit 7c53b094a1
2 changed files with 131 additions and 73 deletions

View File

@ -11,6 +11,7 @@
#include "mozilla/EditorBase.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/mozalloc.h"
#include "mozilla/RangeBoundary.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsError.h"
@ -56,46 +57,54 @@ DeleteRangeTransaction::DoTransaction()
rangeToDelete.swap(mRangeToDelete);
// build the child transactions
nsCOMPtr<nsINode> startContainer = rangeToDelete->GetStartContainer();
int32_t startOffset = rangeToDelete->StartOffset();
nsCOMPtr<nsINode> endContainer = rangeToDelete->GetEndContainer();
int32_t endOffset = rangeToDelete->EndOffset();
MOZ_ASSERT(startContainer && endContainer);
const RangeBoundary& startRef = rangeToDelete->StartRef();
const RangeBoundary& endRef = rangeToDelete->EndRef();
MOZ_ASSERT(startRef.IsSetAndValid());
MOZ_ASSERT(endRef.IsSetAndValid());
if (startContainer == endContainer) {
if (startRef.Container() == endRef.Container()) {
// the selection begins and ends in the same node
nsIContent* startChild = rangeToDelete->GetChildAtStartOffset();
nsresult rv =
CreateTxnsToDeleteBetween(startContainer, startOffset,
startChild, endOffset);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = CreateTxnsToDeleteBetween(startRef.AsRaw(), endRef.AsRaw());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
// the selection ends in a different node from where it started. delete
// the relevant content in the start node
nsresult rv =
CreateTxnsToDeleteContent(startContainer, startOffset, nsIEditor::eNext);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = CreateTxnsToDeleteContent(startRef.AsRaw(), nsIEditor::eNext);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// delete the intervening nodes
rv = CreateTxnsToDeleteNodesBetween(rangeToDelete);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// delete the relevant content in the end node
rv = CreateTxnsToDeleteContent(endContainer, endOffset,
nsIEditor::ePrevious);
NS_ENSURE_SUCCESS(rv, rv);
rv = CreateTxnsToDeleteContent(endRef.AsRaw(), nsIEditor::ePrevious);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// if we've successfully built this aggregate transaction, then do it.
nsresult rv = EditAggregateTransaction::DoTransaction();
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// only set selection to deletion point if editor gives permission
bool bAdjustSelection;
mEditorBase->ShouldTxnSetSelection(&bAdjustSelection);
if (bAdjustSelection) {
RefPtr<Selection> selection = mEditorBase->GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
rv = selection->Collapse(startContainer, startOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(!selection)) {
return NS_ERROR_NULL_POINTER;
}
rv = selection->Collapse(startRef.AsRaw());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// else do nothing - dom range gravity will adjust selection
@ -122,30 +131,36 @@ DeleteRangeTransaction::GetTxnDescription(nsAString& aString)
}
nsresult
DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode,
int32_t aStartOffset,
nsIContent* aChildAtStartOffset,
int32_t aEndOffset)
DeleteRangeTransaction::CreateTxnsToDeleteBetween(
const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd)
{
if (NS_WARN_IF(!aStart.IsSetAndValid()) ||
NS_WARN_IF(!aEnd.IsSetAndValid()) ||
NS_WARN_IF(aStart.Container() != aEnd.Container())) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(!mEditorBase)) {
return NS_ERROR_NOT_AVAILABLE;
}
// see what kind of node we have
if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
if (aStart.Container()->IsNodeOfType(nsINode::eDATA_NODE)) {
// if the node is a chardata node, then delete chardata content
int32_t numToDel;
if (aStartOffset == aEndOffset) {
if (aStart == aEnd) {
numToDel = 1;
} else {
numToDel = aEndOffset - aStartOffset;
numToDel = aEnd.Offset() - aStart.Offset();
MOZ_DIAGNOSTIC_ASSERT(numToDel > 0);
}
RefPtr<nsGenericDOMDataNode> charDataNode =
static_cast<nsGenericDOMDataNode*>(aNode);
static_cast<nsGenericDOMDataNode*>(aStart.Container());
RefPtr<DeleteTextTransaction> deleteTextTransaction =
new DeleteTextTransaction(*mEditorBase, *charDataNode, aStartOffset,
new DeleteTextTransaction(*mEditorBase, *charDataNode, aStart.Offset(),
numToDel, mRangeUpdater);
// If the text node isn't editable, it should be never undone/redone.
// So, the transaction shouldn't be recorded.
@ -156,13 +171,11 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode,
return NS_OK;
}
nsIContent* child = aChildAtStartOffset;
for (int32_t i = aStartOffset; i < aEndOffset; ++i) {
// Even if we detect invalid range, we should ignore it for removing
// specified range's nodes as far as possible.
if (NS_WARN_IF(!child)) {
break;
}
// Even if we detect invalid range, we should ignore it for removing
// specified range's nodes as far as possible.
for (nsIContent* child = aStart.GetChildAtOffset();
child && child != aEnd.GetChildAtOffset();
child = child->GetNextSibling()) {
RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
new DeleteNodeTransaction(*mEditorBase, *child, mRangeUpdater);
// XXX This is odd handling. Even if some children are not editable,
@ -172,48 +185,54 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode,
if (deleteNodeTransaction->CanDoIt()) {
AppendChild(deleteNodeTransaction);
}
child = child->GetNextSibling();
}
return NS_OK;
}
nsresult
DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode,
int32_t aOffset,
nsIEditor::EDirection aAction)
DeleteRangeTransaction::CreateTxnsToDeleteContent(
const RawRangeBoundary& aPoint,
nsIEditor::EDirection aAction)
{
if (NS_WARN_IF(!aPoint.IsSetAndValid())) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(!mEditorBase)) {
return NS_ERROR_NOT_AVAILABLE;
}
// see what kind of node we have
if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
// if the node is a chardata node, then delete chardata content
uint32_t start, numToDelete;
if (nsIEditor::eNext == aAction) {
start = aOffset;
numToDelete = aNode->Length() - aOffset;
} else {
start = 0;
numToDelete = aOffset;
}
if (numToDelete) {
RefPtr<nsGenericDOMDataNode> dataNode =
static_cast<nsGenericDOMDataNode*>(aNode);
RefPtr<DeleteTextTransaction> deleteTextTransaction =
new DeleteTextTransaction(*mEditorBase, *dataNode, start, numToDelete,
mRangeUpdater);
// If the text node isn't editable, it should be never undone/redone.
// So, the transaction shouldn't be recorded.
if (NS_WARN_IF(!deleteTextTransaction->CanDoIt())) {
return NS_ERROR_FAILURE;
}
AppendChild(deleteTextTransaction);
}
if (!aPoint.Container()->IsNodeOfType(nsINode::eDATA_NODE)) {
return NS_OK;
}
// If the node is a chardata node, then delete chardata content
uint32_t startOffset, numToDelete;
if (nsIEditor::eNext == aAction) {
startOffset = aPoint.Offset();
numToDelete = aPoint.Container()->Length() - aPoint.Offset();
} else {
startOffset = 0;
numToDelete = aPoint.Offset();
}
if (!numToDelete) {
return NS_OK;
}
RefPtr<nsGenericDOMDataNode> dataNode =
static_cast<nsGenericDOMDataNode*>(aPoint.Container());
RefPtr<DeleteTextTransaction> deleteTextTransaction =
new DeleteTextTransaction(*mEditorBase, *dataNode, startOffset, numToDelete,
mRangeUpdater);
// If the text node isn't editable, it should be never undone/redone.
// So, the transaction shouldn't be recorded.
if (NS_WARN_IF(!deleteTextTransaction->CanDoIt())) {
return NS_ERROR_FAILURE;
}
AppendChild(deleteTextTransaction);
return NS_OK;
}

View File

@ -7,6 +7,7 @@
#define DeleteRangeTransaction_h
#include "EditAggregateTransaction.h"
#include "mozilla/RangeBoundary.h"
#include "nsCycleCollectionParticipant.h"
#include "nsID.h"
#include "nsIEditor.h"
@ -50,15 +51,53 @@ public:
}
protected:
nsresult CreateTxnsToDeleteBetween(nsINode* aNode,
int32_t aStartOffset,
nsIContent* aChildAtStartOffset,
int32_t aEndOffset);
/**
* CreateTxnsToDeleteBetween() creates a DeleteTextTransaction or some
* DeleteNodeTransactions to remove text or nodes between aStart and aEnd
* and appends the created transactions to the array.
*
* @param aStart Must be set and valid point.
* @param aEnd Must be set and valid point. Additionally, the
* container must be same as aStart's container.
* And of course, this must not be before aStart in
* the DOM tree order.
* @return Returns NS_OK in most cases.
* When the arguments are invalid, returns
* NS_ERROR_INVALID_ARG.
* When mEditorBase isn't available, returns
* NS_ERROR_NOT_AVAIALBLE.
* When created DeleteTextTransaction cannot do its
* transaction, returns NS_ERROR_FAILURE.
* Note that even if one of created DeleteNodeTransaction
* cannot do its transaction, this returns NS_OK.
*/
nsresult CreateTxnsToDeleteBetween(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd);
nsresult CreateTxnsToDeleteNodesBetween(nsRange* aRangeToDelete);
nsresult CreateTxnsToDeleteContent(nsINode* aParent,
int32_t aOffset,
/**
* CreateTxnsToDeleteContent() creates a DeleteTextTransaction to delete
* text between start of aPoint.Container() and aPoint or aPoint and end of
* aPoint.Container() and appends the created transaction to the array.
*
* @param aPoint Must be set and valid point. If the container is not
* a data node, this method does nothing.
* @param aAction If nsIEditor::eNext, this method creates a transaction
* to delete text from aPoint to the end of the data node.
* Otherwise, this method creates a transaction to delete
* text from start of the data node to aPoint.
* @return Returns NS_OK in most cases.
* When the arguments are invalid, returns
* NS_ERROR_INVALID_ARG.
* When mEditorBase isn't available, returns
* NS_ERROR_NOT_AVAIALBLE.
* When created DeleteTextTransaction cannot do its
* transaction, returns NS_ERROR_FAILURE.
* Note that even if no character will be deleted,
* this returns NS_OK.
*/
nsresult CreateTxnsToDeleteContent(const RawRangeBoundary& aPoint,
nsIEditor::EDirection aAction);
// The editor for this transaction.