Bug 1425412 - part 5: Create some factory methods of DeleteTextTransaction and remove EditorBase::CreateTxnForDeleteText() and EditorBase::CreateTxnForDeleteCharacter() r=m_kato

DeleteTextTransaction should have 3 factory methods.  One is, simply to create
an instance with a pair of offset and length.  The others are, to create an
instance for deleting a previous or next character at offset.

The former was EditorBase::CreateTxnForDeleteText() and the latter was
EditorBase::CreateTxnForDeleteCharacter(), but this patch creates
DeleteTextTransaction::MaybeCreate() for the former,
DeleteTextTransaction::MaybeCreateForPreviousCharacter() and
DeleteTextTransaction::MaybeCreateForNextCharacter() for the latter.

MozReview-Commit-ID: DFELbmAJDo3

--HG--
extra : rebase_source : 1600984c704b460e1cc09777b81df2906c154cce
This commit is contained in:
Masayuki Nakano 2017-12-15 20:43:26 +09:00
Родитель a5bd93a76e
Коммит 6b8e286ddc
6 изменённых файлов: 163 добавлений и 116 удалений

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

@ -160,11 +160,11 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(
static_cast<nsGenericDOMDataNode*>(aStart.Container());
RefPtr<DeleteTextTransaction> deleteTextTransaction =
new DeleteTextTransaction(*mEditorBase, *charDataNode, aStart.Offset(),
numToDel, mRangeUpdater);
DeleteTextTransaction::MaybeCreate(*mEditorBase, *charDataNode,
aStart.Offset(), numToDel);
// 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())) {
if (NS_WARN_IF(!deleteTextTransaction)) {
return NS_ERROR_FAILURE;
}
AppendChild(deleteTextTransaction);
@ -224,11 +224,11 @@ DeleteRangeTransaction::CreateTxnsToDeleteContent(
RefPtr<nsGenericDOMDataNode> dataNode =
static_cast<nsGenericDOMDataNode*>(aPoint.Container());
RefPtr<DeleteTextTransaction> deleteTextTransaction =
new DeleteTextTransaction(*mEditorBase, *dataNode, startOffset, numToDelete,
mRangeUpdater);
DeleteTextTransaction::MaybeCreate(*mEditorBase, *dataNode,
startOffset, numToDelete);
// 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())) {
if (NS_WARN_IF(!deleteTextTransaction)) {
return NS_ERROR_FAILURE;
}
AppendChild(deleteTextTransaction);

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

@ -7,6 +7,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/EditorBase.h"
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/SelectionState.h"
#include "mozilla/dom/Selection.h"
#include "nsDebug.h"
@ -19,19 +20,82 @@ namespace mozilla {
using namespace dom;
// static
already_AddRefed<DeleteTextTransaction>
DeleteTextTransaction::MaybeCreate(EditorBase& aEditorBase,
nsGenericDOMDataNode& aCharData,
uint32_t aOffset,
uint32_t aLengthToDelete)
{
RefPtr<DeleteTextTransaction> transaction =
new DeleteTextTransaction(aEditorBase, aCharData, aOffset, aLengthToDelete);
return transaction.forget();
}
// static
already_AddRefed<DeleteTextTransaction>
DeleteTextTransaction::MaybeCreateForPreviousCharacter(
EditorBase& aEditorBase,
nsGenericDOMDataNode& aCharData,
uint32_t aOffset)
{
if (NS_WARN_IF(!aOffset)) {
return nullptr;
}
nsAutoString data;
aCharData.GetData(data);
if (NS_WARN_IF(data.IsEmpty())) {
return nullptr;
}
uint32_t length = 1;
uint32_t offset = aOffset - 1;
if (offset &&
NS_IS_LOW_SURROGATE(data[offset]) &&
NS_IS_HIGH_SURROGATE(data[offset - 1])) {
++length;
--offset;
}
return DeleteTextTransaction::MaybeCreate(aEditorBase, aCharData,
offset, length);
}
// static
already_AddRefed<DeleteTextTransaction>
DeleteTextTransaction::MaybeCreateForNextCharacter(
EditorBase& aEditorBase,
nsGenericDOMDataNode& aCharData,
uint32_t aOffset)
{
nsAutoString data;
aCharData.GetData(data);
if (NS_WARN_IF(aOffset >= data.Length()) ||
NS_WARN_IF(data.IsEmpty())) {
return nullptr;
}
uint32_t length = 1;
if (aOffset + 1 < data.Length() &&
NS_IS_HIGH_SURROGATE(data[aOffset]) &&
NS_IS_LOW_SURROGATE(data[aOffset + 1])) {
++length;
}
return DeleteTextTransaction::MaybeCreate(aEditorBase, aCharData,
aOffset, length);
}
DeleteTextTransaction::DeleteTextTransaction(
EditorBase& aEditorBase,
nsGenericDOMDataNode& aCharData,
uint32_t aOffset,
uint32_t aNumCharsToDelete,
RangeUpdater* aRangeUpdater)
uint32_t aLengthToDelete)
: mEditorBase(&aEditorBase)
, mCharData(&aCharData)
, mOffset(aOffset)
, mNumCharsToDelete(aNumCharsToDelete)
, mRangeUpdater(aRangeUpdater)
, mLengthToDelete(aLengthToDelete)
{
NS_ASSERTION(mCharData->Length() >= aOffset + aNumCharsToDelete,
NS_ASSERTION(mCharData->Length() >= aOffset + aLengthToDelete,
"Trying to delete more characters than in node");
}
@ -59,24 +123,28 @@ DeleteTextTransaction::DoTransaction()
}
// Get the text that we're about to delete
nsresult rv = mCharData->SubstringData(mOffset, mNumCharsToDelete,
nsresult rv = mCharData->SubstringData(mOffset, mLengthToDelete,
mDeletedText);
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = mCharData->DeleteData(mOffset, mNumCharsToDelete);
NS_ENSURE_SUCCESS(rv, rv);
if (mRangeUpdater) {
mRangeUpdater->SelAdjDeleteText(mCharData, mOffset, mNumCharsToDelete);
rv = mCharData->DeleteData(mOffset, mLengthToDelete);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mEditorBase->RangeUpdaterRef().
SelAdjDeleteText(mCharData, mOffset, mLengthToDelete);
// Only set selection to deletion point if editor gives permission
if (mEditorBase->GetShouldTxnSetSelection()) {
RefPtr<Selection> selection = mEditorBase->GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
rv = selection->Collapse(mCharData, mOffset);
NS_ASSERTION(NS_SUCCEEDED(rv),
"Selection could not be collapsed after undo of deletetext");
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
ErrorResult error;
selection->Collapse(EditorRawDOMPoint(mCharData, mOffset), error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
}
// Else do nothing - DOM Range gravity will adjust selection
return NS_OK;

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

@ -24,20 +24,45 @@ class RangeUpdater;
*/
class DeleteTextTransaction final : public EditTransactionBase
{
public:
/**
* Initialize the transaction.
* @param aEditorBase The provider of basic editing operations.
* @param aElement The content node to remove text from.
* @param aOffset The location in aElement to begin the deletion.
* @param aNumCharsToDelete The number of characters to delete. Not the
* number of bytes!
*/
protected:
DeleteTextTransaction(EditorBase& aEditorBase,
nsGenericDOMDataNode& aCharData,
uint32_t aOffset,
uint32_t aNumCharsToDelete,
RangeUpdater* aRangeUpdater);
uint32_t aLengthToDelete);
public:
/**
* Creates a delete text transaction to remove given range. This returns
* nullptr if it cannot modify the text node.
*
* @param aEditorBase The provider of basic editing operations.
* @param aCharData The content node to remove text from.
* @param aOffset The location in aElement to begin the deletion.
* @param aLenthToDelete The length to delete.
*/
static already_AddRefed<DeleteTextTransaction>
MaybeCreate(EditorBase& aEditorBase,
nsGenericDOMDataNode& aCharData,
uint32_t aOffset,
uint32_t aLengthToDelete);
/**
* Creates a delete text transaction to remove a previous or next character.
* Those methods MAY return nullptr.
*
* @param aEditorBase The provider of basic editing operations.
* @param aCharData The content node to remove text from.
* @param aOffset The location in aElement to begin the deletion.
*/
static already_AddRefed<DeleteTextTransaction>
MaybeCreateForPreviousCharacter(EditorBase& aEditorBase,
nsGenericDOMDataNode& aCharData,
uint32_t aOffset);
static already_AddRefed<DeleteTextTransaction>
MaybeCreateForNextCharacter(EditorBase& aEditorBase,
nsGenericDOMDataNode& aCharData,
uint32_t aOffset);
/**
* CanDoIt() returns true if there are enough members and can modify the
@ -51,9 +76,9 @@ public:
NS_DECL_EDITTRANSACTIONBASE
uint32_t GetOffset() { return mOffset; }
uint32_t Offset() { return mOffset; }
uint32_t GetNumCharsToDelete() { return mNumCharsToDelete; }
uint32_t LengthToDelete() { return mLengthToDelete; }
protected:
// The provider of basic editing operations.
@ -65,14 +90,11 @@ protected:
// The offset into mCharData where the deletion is to take place.
uint32_t mOffset;
// The number of characters to delete.
uint32_t mNumCharsToDelete;
// The length to delete.
uint32_t mLengthToDelete;
// The text that was deleted.
nsString mDeletedText;
// Range updater object.
RangeUpdater* mRangeUpdater;
};
} // namespace mozilla

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

@ -3006,8 +3006,10 @@ EditorBase::DeleteText(nsGenericDOMDataNode& aCharData,
uint32_t aLength)
{
RefPtr<DeleteTextTransaction> transaction =
CreateTxnForDeleteText(aCharData, aOffset, aLength);
NS_ENSURE_STATE(transaction);
DeleteTextTransaction::MaybeCreate(*this, aCharData, aOffset, aLength);
if (NS_WARN_IF(!transaction)) {
return NS_ERROR_FAILURE;
}
AutoRules beginRulesSniffing(this, EditAction::deleteText,
nsIEditor::ePrevious);
@ -3037,22 +3039,6 @@ EditorBase::DeleteText(nsGenericDOMDataNode& aCharData,
return rv;
}
already_AddRefed<DeleteTextTransaction>
EditorBase::CreateTxnForDeleteText(nsGenericDOMDataNode& aCharData,
uint32_t aOffset,
uint32_t aLength)
{
RefPtr<DeleteTextTransaction> deleteTextTransaction =
new DeleteTextTransaction(*this, aCharData, aOffset, aLength,
&mRangeUpdater);
// If it's not editable, the transaction shouldn't be recorded since it
// should never be undone/redone.
if (NS_WARN_IF(!deleteTextTransaction->CanDoIt())) {
return nullptr;
}
return deleteTextTransaction.forget();
}
already_AddRefed<SplitNodeTransaction>
EditorBase::CreateTxnForSplitNode(const EditorRawDOMPoint& aStartOfRightNode)
{
@ -4722,40 +4708,6 @@ EditorBase::CreateTxnForDeleteSelection(EDirection aAction,
return aggregateTransaction.forget();
}
already_AddRefed<DeleteTextTransaction>
EditorBase::CreateTxnForDeleteCharacter(nsGenericDOMDataNode& aData,
uint32_t aOffset,
EDirection aDirection)
{
NS_ASSERTION(aDirection == eNext || aDirection == ePrevious,
"Invalid direction");
nsAutoString data;
aData.GetData(data);
NS_ASSERTION(data.Length(), "Trying to delete from a zero-length node");
NS_ENSURE_TRUE(data.Length(), nullptr);
uint32_t segOffset = aOffset, segLength = 1;
if (aDirection == eNext) {
if (segOffset + 1 < data.Length() &&
NS_IS_HIGH_SURROGATE(data[segOffset]) &&
NS_IS_LOW_SURROGATE(data[segOffset+1])) {
// Delete both halves of the surrogate pair
++segLength;
}
} else if (aOffset > 0) {
--segOffset;
if (segOffset > 0 &&
NS_IS_LOW_SURROGATE(data[segOffset]) &&
NS_IS_HIGH_SURROGATE(data[segOffset-1])) {
++segLength;
--segOffset;
}
} else {
return nullptr;
}
return CreateTxnForDeleteText(aData, segOffset, segLength);
}
//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior
//are not implemented
already_AddRefed<EditTransactionBase>
@ -4808,12 +4760,13 @@ EditorBase::CreateTxnForDeleteRange(nsRange* aRangeToDelete,
return nullptr;
}
RefPtr<DeleteTextTransaction> deleteTextTransaction =
CreateTxnForDeleteCharacter(*priorNodeAsCharData, length, ePrevious);
DeleteTextTransaction::MaybeCreateForPreviousCharacter(
*this, *priorNodeAsCharData, length);
if (NS_WARN_IF(!deleteTextTransaction)) {
return nullptr;
}
*aOffset = deleteTextTransaction->GetOffset();
*aLength = deleteTextTransaction->GetNumCharsToDelete();
*aOffset = deleteTextTransaction->Offset();
*aLength = deleteTextTransaction->LengthToDelete();
priorNode.forget(aRemovingNode);
return deleteTextTransaction.forget();
}
@ -4847,12 +4800,13 @@ EditorBase::CreateTxnForDeleteRange(nsRange* aRangeToDelete,
return nullptr;
}
RefPtr<DeleteTextTransaction> deleteTextTransaction =
CreateTxnForDeleteCharacter(*nextNodeAsCharData, 0, eNext);
DeleteTextTransaction::MaybeCreateForNextCharacter(
*this, *nextNodeAsCharData, 0);
if (NS_WARN_IF(!deleteTextTransaction)) {
return nullptr;
}
*aOffset = deleteTextTransaction->GetOffset();
*aLength = deleteTextTransaction->GetNumCharsToDelete();
*aOffset = deleteTextTransaction->Offset();
*aLength = deleteTextTransaction->LengthToDelete();
nextNode.forget(aRemovingNode);
return deleteTextTransaction.forget();
}
@ -4868,16 +4822,23 @@ EditorBase::CreateTxnForDeleteRange(nsRange* aRangeToDelete,
}
if (node->IsNodeOfType(nsINode::eDATA_NODE)) {
if (NS_WARN_IF(aAction != ePrevious && aAction != eNext)) {
return nullptr;
}
RefPtr<nsGenericDOMDataNode> nodeAsCharData =
static_cast<nsGenericDOMDataNode*>(node.get());
// we have chardata, so delete a char at the proper offset
// We have chardata, so delete a char at the proper offset
RefPtr<DeleteTextTransaction> deleteTextTransaction =
CreateTxnForDeleteCharacter(*nodeAsCharData, offset, aAction);
aAction == ePrevious ?
DeleteTextTransaction::MaybeCreateForPreviousCharacter(
*this, *nodeAsCharData, offset) :
DeleteTextTransaction::MaybeCreateForNextCharacter(
*this, *nodeAsCharData, offset);
if (NS_WARN_IF(!deleteTextTransaction)) {
return nullptr;
}
*aOffset = deleteTextTransaction->GetOffset();
*aLength = deleteTextTransaction->GetNumCharsToDelete();
*aOffset = deleteTextTransaction->Offset();
*aLength = deleteTextTransaction->LengthToDelete();
node.forget(aRemovingNode);
return deleteTextTransaction.forget();
}
@ -4908,6 +4869,9 @@ EditorBase::CreateTxnForDeleteRange(nsRange* aRangeToDelete,
}
if (selectedNode->IsNodeOfType(nsINode::eDATA_NODE)) {
if (NS_WARN_IF(aAction != ePrevious && aAction != eNext)) {
return nullptr;
}
RefPtr<nsGenericDOMDataNode> selectedNodeAsCharData =
static_cast<nsGenericDOMDataNode*>(selectedNode.get());
// we are deleting from a chardata node, so do a character deletion
@ -4916,13 +4880,16 @@ EditorBase::CreateTxnForDeleteRange(nsRange* aRangeToDelete,
position = selectedNode->Length();
}
RefPtr<DeleteTextTransaction> deleteTextTransaction =
CreateTxnForDeleteCharacter(*selectedNodeAsCharData, position,
aAction);
aAction == ePrevious ?
DeleteTextTransaction::MaybeCreateForPreviousCharacter(
*this, *selectedNodeAsCharData, position) :
DeleteTextTransaction::MaybeCreateForNextCharacter(
*this, *selectedNodeAsCharData, position);
if (NS_WARN_IF(!deleteTextTransaction)) {
return nullptr;
}
*aOffset = deleteTextTransaction->GetOffset();
*aLength = deleteTextTransaction->GetNumCharsToDelete();
*aOffset = deleteTextTransaction->Offset();
*aLength = deleteTextTransaction->LengthToDelete();
selectedNode.forget(aRemovingNode);
return deleteTextTransaction.forget();
}

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

@ -596,14 +596,6 @@ protected:
nsresult DeleteText(nsGenericDOMDataNode& aElement,
uint32_t aOffset, uint32_t aLength);
already_AddRefed<DeleteTextTransaction>
CreateTxnForDeleteText(nsGenericDOMDataNode& aElement,
uint32_t aOffset, uint32_t aLength);
already_AddRefed<DeleteTextTransaction>
CreateTxnForDeleteCharacter(nsGenericDOMDataNode& aData, uint32_t aOffset,
EDirection aDirection);
/**
* CreateTxnForSplitNode() creates a transaction to create a new node
* (left node) identical to an existing node (right node), and split the

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

@ -6,8 +6,6 @@
target="_blank">Mozilla Bug 646194</a>
<iframe id="i" srcdoc="&lt;div contenteditable=true id=t&gt;test me now&lt;/div&gt;"></iframe>
<script>
SimpleTest.expectAssertions(1);
function runTest() {
var i = document.getElementById("i");
i.focus();