Bug 1415445 - part 1: CreateElementTransaction should use EditorRawDOMPoint and RangeBoundary r=m_kato

The constructor of CreateElementTransaction should use EditorRawDOMPoint to
receive insertion point of the new element.  Then, it should be stored with
RangeBoundary.  With this change, CreateElementTransaction doesn't need to
compute child node at insertion point.

Additionally, this creates InsertNode() method.  Current code works differently
when DoTransaction() is called and RedoTransaction() is called.

MozReview-Commit-ID: 8ujhmzn65Wg

--HG--
extra : rebase_source : 30cc045a30a3836f211d11e5f70a85804f44a72a
This commit is contained in:
Masayuki Nakano 2017-11-08 16:49:06 +09:00
Родитель 4b81416ab1
Коммит ae419aa495
3 изменённых файлов: 84 добавлений и 60 удалений

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

@ -32,17 +32,14 @@ namespace mozilla {
using namespace dom;
CreateElementTransaction::CreateElementTransaction(EditorBase& aEditorBase,
nsAtom& aTag,
nsINode& aParent,
int32_t aOffsetInParent,
nsIContent* aChildAtOffset)
CreateElementTransaction::CreateElementTransaction(
EditorBase& aEditorBase,
nsAtom& aTag,
const EditorRawDOMPoint& aPointToInsert)
: EditTransactionBase()
, mEditorBase(&aEditorBase)
, mTag(&aTag)
, mParent(&aParent)
, mOffsetInParent(aOffsetInParent)
, mRefNode(aChildAtOffset)
, mPointToInsert(aPointToInsert)
{
}
@ -53,9 +50,8 @@ CreateElementTransaction::~CreateElementTransaction()
NS_IMPL_CYCLE_COLLECTION_INHERITED(CreateElementTransaction,
EditTransactionBase,
mEditorBase,
mParent,
mNewNode,
mRefNode)
mPointToInsert,
mNewNode)
NS_IMPL_ADDREF_INHERITED(CreateElementTransaction, EditTransactionBase)
NS_IMPL_RELEASE_INHERITED(CreateElementTransaction, EditTransactionBase)
@ -66,7 +62,8 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
NS_IMETHODIMP
CreateElementTransaction::DoTransaction()
{
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) || NS_WARN_IF(!mParent)) {
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) ||
NS_WARN_IF(!mPointToInsert.IsSet())) {
return NS_ERROR_NOT_INITIALIZED;
}
@ -77,24 +74,12 @@ CreateElementTransaction::DoTransaction()
mEditorBase->MarkNodeDirty(GetAsDOMNode(mNewNode));
// Insert the new node
ErrorResult rv;
if (mOffsetInParent == -1) {
mParent->AppendChild(*mNewNode, rv);
return rv.StealNSResult();
ErrorResult error;
InsertNewNode(error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
mOffsetInParent = std::min(mOffsetInParent,
static_cast<int32_t>(mParent->GetChildCount()));
if (!mRefNode) {
// Note, it's ok for mRefNode to be null. That means append
mRefNode = mParent->GetChildAt(mOffsetInParent);
}
nsCOMPtr<nsIContent> refNode = mRefNode;
mParent->InsertBefore(*mNewNode, refNode, rv);
NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
// Only set selection to insertion point if editor gives permission
if (!mEditorBase->GetShouldTxnSetSelection()) {
// Do nothing - DOM range gravity will adjust selection
@ -111,41 +96,77 @@ CreateElementTransaction::DoTransaction()
// in this method?
return NS_ERROR_FAILURE;
}
rv = selection->Collapse(afterNewNode);
NS_ASSERTION(!rv.Failed(),
"selection could not be collapsed after insert");
selection->Collapse(afterNewNode, error);
if (error.Failed()) {
NS_WARNING("selection could not be collapsed after insert");
error.SuppressException();
}
return NS_OK;
}
void
CreateElementTransaction::InsertNewNode(ErrorResult& aError)
{
if (mPointToInsert.IsSetAndValid()) {
if (mPointToInsert.IsEndOfContainer()) {
mPointToInsert.Container()->AppendChild(*mNewNode, aError);
NS_WARNING_ASSERTION(!aError.Failed(), "Failed to append the new node");
return;
}
mPointToInsert.Container()->InsertBefore(*mNewNode,
mPointToInsert.GetChildAtOffset(),
aError);
NS_WARNING_ASSERTION(!aError.Failed(), "Failed to insert the new node");
return;
}
if (NS_WARN_IF(mPointToInsert.GetChildAtOffset() &&
mPointToInsert.Container() !=
mPointToInsert.GetChildAtOffset()->GetParentNode())) {
aError.Throw(NS_ERROR_FAILURE);
return;
}
// If mPointToInsert has only offset and it's not valid, we need to treat
// it as pointing end of the container.
mPointToInsert.Container()->AppendChild(*mNewNode, aError);
NS_WARNING_ASSERTION(!aError.Failed(), "Failed to append the new node");
}
NS_IMETHODIMP
CreateElementTransaction::UndoTransaction()
{
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) {
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mPointToInsert.IsSet())) {
return NS_ERROR_NOT_INITIALIZED;
}
ErrorResult rv;
mParent->RemoveChild(*mNewNode, rv);
return rv.StealNSResult();
ErrorResult error;
mPointToInsert.Container()->RemoveChild(*mNewNode, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
NS_IMETHODIMP
CreateElementTransaction::RedoTransaction()
{
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) {
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mPointToInsert.IsSet())) {
return NS_ERROR_NOT_INITIALIZED;
}
// First, reset mNewNode so it has no attributes or content
// XXX We never actually did this, we only cleared mNewNode's contents if it
// was a CharacterData node (which it's not, it's an Element)
// XXX Don't we need to set selection like DoTransaction()?
// Now, reinsert mNewNode
ErrorResult rv;
nsCOMPtr<nsIContent> refNode = mRefNode;
mParent->InsertBefore(*mNewNode, refNode, rv);
return rv.StealNSResult();
ErrorResult error;
InsertNewNode(error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
NS_IMETHODIMP

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

@ -6,6 +6,7 @@
#ifndef CreateElementTransaction_h
#define CreateElementTransaction_h
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EditTransactionBase.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
@ -32,17 +33,14 @@ public:
* Initialize the transaction.
* @param aEditorBase The provider of basic editing functionality.
* @param aTag The tag (P, HR, TABLE, etc.) for the new element.
* @param aParent The node into which the new element will be
* inserted.
* @param aOffsetInParent The location in aParent to insert the new element.
* If eAppend, the new element is appended as the last
* child.
* @param aPointToInsert The new node will be inserted before the child at
* aPointToInsert. If this refers end of the container
* or after, the new node will be appended to the
* container.
*/
CreateElementTransaction(EditorBase& aEditorBase,
nsAtom& aTag,
nsINode& aParent,
int32_t aOffsetInParent,
nsIContent* aChildAtOffset);
const EditorRawDOMPoint& aPointToInsert);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CreateElementTransaction,
@ -57,24 +55,22 @@ public:
protected:
virtual ~CreateElementTransaction();
/**
* InsertNewNode() inserts mNewNode before the child node at mPointToInsert.
*/
void InsertNewNode(ErrorResult& aError);
// The document into which the new node will be inserted.
RefPtr<EditorBase> mEditorBase;
// The tag (mapping to object type) for the new element.
RefPtr<nsAtom> mTag;
// The node into which the new node will be inserted.
nsCOMPtr<nsINode> mParent;
// The index in mParent for the new node.
int32_t mOffsetInParent;
// The DOM point we will insert mNewNode.
RangeBoundary mPointToInsert;
// The new node to insert.
nsCOMPtr<dom::Element> mNewNode;
// The node we will insert mNewNode before. We compute this ourselves if it
// is not set by the constructor.
nsCOMPtr<nsIContent> mRefNode;
};
} // namespace mozilla

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

@ -4372,9 +4372,16 @@ EditorBase::CreateTxnForCreateElement(nsAtom& aTag,
int32_t aPosition,
nsIContent* aChildAtPosition)
{
EditorRawDOMPoint pointToInsert;
if (aPosition == -1) {
pointToInsert.Set(&aParent, aParent.Length());
} else if (aChildAtPosition) {
pointToInsert.Set(aChildAtPosition);
} else {
pointToInsert.Set(&aParent, aPosition);
}
RefPtr<CreateElementTransaction> transaction =
new CreateElementTransaction(*this, aTag, aParent, aPosition,
aChildAtPosition);
new CreateElementTransaction(*this, aTag, pointToInsert);
return transaction.forget();
}