--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -1464,52 +1464,67 @@ EditorBase::CreateNode(nsAtom* aTag,
GetAsDOMNode(ret), rv);
}
}
return ret.forget();
}
NS_IMETHODIMP
-EditorBase::InsertNode(nsIDOMNode* aNode,
- nsIDOMNode* aParent,
- int32_t aPosition)
-{
- nsCOMPtr<nsIContent> node = do_QueryInterface(aNode);
- nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
- NS_ENSURE_TRUE(node && parent, NS_ERROR_NULL_POINTER);
-
- return InsertNode(*node, *parent, aPosition);
+EditorBase::InsertNode(nsIDOMNode* aNodeToInsert,
+ nsIDOMNode* aContainer,
+ int32_t aOffset)
+{
+ nsCOMPtr<nsIContent> contentToInsert = do_QueryInterface(aNodeToInsert);
+ if (NS_WARN_IF(!contentToInsert)) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
+ if (NS_WARN_IF(!container)) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ int32_t offset =
+ aOffset < 0 ? static_cast<int32_t>(container->Length()) :
+ std::min(aOffset, static_cast<int32_t>(container->Length()));
+ return InsertNode(*contentToInsert, EditorRawDOMPoint(container, offset));
}
nsresult
-EditorBase::InsertNode(nsIContent& aNode,
- nsINode& aParent,
- int32_t aPosition)
-{
+EditorBase::InsertNode(nsIContent& aContentToInsert,
+ const EditorRawDOMPoint& aPointToInsert)
+{
+ if (NS_WARN_IF(!aPointToInsert.IsSet())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ MOZ_ASSERT(aPointToInsert.IsSetAndValid());
+
AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
{
AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
- listener->WillInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(),
- aPosition);
+ listener->WillInsertNode(aContentToInsert.AsDOMNode(),
+ aPointToInsert.Container()->AsDOMNode(),
+ aPointToInsert.Offset());
}
}
RefPtr<InsertNodeTransaction> transaction =
- CreateTxnForInsertNode(aNode, aParent, aPosition);
+ CreateTxnForInsertNode(aContentToInsert, aPointToInsert);
nsresult rv = DoTransaction(transaction);
- mRangeUpdater.SelAdjInsertNode(&aParent, aPosition);
+ mRangeUpdater.SelAdjInsertNode(aPointToInsert.Container(),
+ aPointToInsert.Offset());
{
AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
- listener->DidInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(), aPosition,
+ listener->DidInsertNode(aContentToInsert.AsDOMNode(),
+ aPointToInsert.Container()->AsDOMNode(),
+ aPointToInsert.Offset(),
rv);
}
}
return rv;
}
NS_IMETHODIMP
@@ -1689,91 +1704,115 @@ already_AddRefed<Element>
EditorBase::ReplaceContainer(Element* aOldContainer,
nsAtom* aNodeType,
nsAtom* aAttribute,
const nsAString* aValue,
ECloneAttributes aCloneAttributes)
{
MOZ_ASSERT(aOldContainer && aNodeType);
- nsCOMPtr<nsIContent> parent = aOldContainer->GetParent();
- NS_ENSURE_TRUE(parent, nullptr);
-
- int32_t offset = parent->IndexOf(aOldContainer);
-
- // create new container
- nsCOMPtr<Element> ret = CreateHTMLContent(aNodeType);
- NS_ENSURE_TRUE(ret, nullptr);
-
- // set attribute if needed
+ EditorDOMPoint atOldContainer(aOldContainer);
+ if (NS_WARN_IF(!atOldContainer.IsSet())) {
+ return nullptr;
+ }
+
+ RefPtr<Element> newContainer = CreateHTMLContent(aNodeType);
+ if (NS_WARN_IF(!newContainer)) {
+ return nullptr;
+ }
+
+ // Set attribute if needed.
if (aAttribute && aValue && aAttribute != nsGkAtoms::_empty) {
- nsresult rv = ret->SetAttr(kNameSpaceID_None, aAttribute, *aValue, true);
- NS_ENSURE_SUCCESS(rv, nullptr);
+ nsresult rv =
+ newContainer->SetAttr(kNameSpaceID_None, aAttribute, *aValue, true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
}
if (aCloneAttributes == eCloneAttributes) {
- CloneAttributes(ret, aOldContainer);
- }
-
- // notify our internal selection state listener
- // (Note: An AutoSelectionRestorer object must be created
- // before calling this to initialize mRangeUpdater)
+ CloneAttributes(newContainer, aOldContainer);
+ }
+
+ // Notify our internal selection state listener.
+ // Note: An AutoSelectionRestorer object must be created before calling this
+ // to initialize mRangeUpdater.
AutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, aOldContainer,
- ret);
+ newContainer);
{
AutoTransactionsConserveSelection conserveSelection(this);
+ // Move all children from the old container to the new container.
while (aOldContainer->HasChildren()) {
nsCOMPtr<nsIContent> child = aOldContainer->GetFirstChild();
nsresult rv = DeleteNode(child);
- NS_ENSURE_SUCCESS(rv, nullptr);
-
- rv = InsertNode(*child, *ret, -1);
- NS_ENSURE_SUCCESS(rv, nullptr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ rv = InsertNode(*child, EditorRawDOMPoint(newContainer,
+ newContainer->Length()));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
}
}
- // insert new container into tree
- nsresult rv = InsertNode(*ret, *parent, offset);
- NS_ENSURE_SUCCESS(rv, nullptr);
-
- // delete old container
+ // Insert new container into tree.
+ NS_WARNING_ASSERTION(atOldContainer.IsSetAndValid(),
+ "The old container might be moved by mutation observer");
+ nsresult rv = InsertNode(*newContainer, atOldContainer.AsRaw());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ // Delete old container.
rv = DeleteNode(aOldContainer);
- NS_ENSURE_SUCCESS(rv, nullptr);
-
- return ret.forget();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ return newContainer.forget();
}
/**
* RemoveContainer() removes inNode, reparenting its children (if any) into the
* parent of inNode.
*/
nsresult
EditorBase::RemoveContainer(nsIContent* aNode)
{
MOZ_ASSERT(aNode);
- nsCOMPtr<nsINode> parent = aNode->GetParentNode();
- NS_ENSURE_STATE(parent);
-
- int32_t offset = parent->IndexOf(aNode);
-
- // Loop through the children of inNode and promote them into inNode's parent
- uint32_t nodeOrigLen = aNode->GetChildCount();
-
- // notify our internal selection state listener
- AutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode, parent,
- offset, nodeOrigLen);
-
+ EditorDOMPoint pointToInsertChildren(aNode);
+ if (NS_WARN_IF(!pointToInsertChildren.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Notify our internal selection state listener.
+ AutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode,
+ pointToInsertChildren.Container(),
+ pointToInsertChildren.Offset(),
+ aNode->GetChildCount());
+
+ // Move all children from aNode to its parent.
while (aNode->HasChildren()) {
nsCOMPtr<nsIContent> child = aNode->GetLastChild();
nsresult rv = DeleteNode(child);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = InsertNode(*child, *parent, offset);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Insert the last child before the previous last child. So, we need to
+ // use offset here because previous child might have been moved to
+ // container.
+ rv = InsertNode(*child, EditorRawDOMPoint(pointToInsertChildren.Container(),
+ pointToInsertChildren.Offset()));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
return DeleteNode(aNode);
}
/**
* InsertContainerAbove() inserts a new parent for inNode, which is contructed
* to be of type aNodeType. outNode becomes a child of inNode's earlier
@@ -1783,49 +1822,66 @@ EditorBase::RemoveContainer(nsIContent*
already_AddRefed<Element>
EditorBase::InsertContainerAbove(nsIContent* aNode,
nsAtom* aNodeType,
nsAtom* aAttribute,
const nsAString* aValue)
{
MOZ_ASSERT(aNode && aNodeType);
- nsCOMPtr<nsIContent> parent = aNode->GetParent();
- NS_ENSURE_TRUE(parent, nullptr);
- int32_t offset = parent->IndexOf(aNode);
+ EditorDOMPoint pointToInsertNewContainer(aNode);
+ if (NS_WARN_IF(!pointToInsertNewContainer.IsSet())) {
+ return nullptr;
+ }
+ // aNode will be moved to the new container before inserting the new
+ // container. So, when we insert the container, the insertion point
+ // is before the next sibling of aNode.
+ DebugOnly<bool> advanced = pointToInsertNewContainer.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to advance offset to after aNode");
// Create new container
- nsCOMPtr<Element> newContent = CreateHTMLContent(aNodeType);
- NS_ENSURE_TRUE(newContent, nullptr);
+ RefPtr<Element> newContainer = CreateHTMLContent(aNodeType);
+ if (NS_WARN_IF(!newContainer)) {
+ return nullptr;
+ }
// Set attribute if needed
if (aAttribute && aValue && aAttribute != nsGkAtoms::_empty) {
nsresult rv =
- newContent->SetAttr(kNameSpaceID_None, aAttribute, *aValue, true);
- NS_ENSURE_SUCCESS(rv, nullptr);
+ newContainer->SetAttr(kNameSpaceID_None, aAttribute, *aValue, true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
}
// Notify our internal selection state listener
AutoInsertContainerSelNotify selNotify(mRangeUpdater);
- // Put inNode in new parent, outNode
+ // Put aNode in the new container, first.
nsresult rv = DeleteNode(aNode);
- NS_ENSURE_SUCCESS(rv, nullptr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
{
AutoTransactionsConserveSelection conserveSelection(this);
- rv = InsertNode(*aNode, *newContent, 0);
- NS_ENSURE_SUCCESS(rv, nullptr);
- }
-
- // Put new parent in doc
- rv = InsertNode(*newContent, *parent, offset);
- NS_ENSURE_SUCCESS(rv, nullptr);
-
- return newContent.forget();
+ rv = InsertNode(*aNode, EditorRawDOMPoint(newContainer, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+ }
+
+ // Put the new container where aNode was.
+ rv = InsertNode(*newContainer, pointToInsertNewContainer.AsRaw());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ return newContainer.forget();
}
/**
* MoveNode() moves aNode to {aParent,aOffset}.
*/
nsresult
EditorBase::MoveNode(nsIContent* aNode,
nsINode* aParent,
@@ -1859,19 +1915,25 @@ EditorBase::MoveNode(nsIContent* aNode,
// When we delete aNode, it will make the offsets after it off by one
aOffset--;
}
// Hold a reference so aNode doesn't go away when we remove it (bug 772282)
nsCOMPtr<nsINode> kungFuDeathGrip = aNode;
nsresult rv = DeleteNode(aNode);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return InsertNode(*aNode, *aParent, aOffset);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = InsertNode(*aNode, EditorRawDOMPoint(aParent, aOffset));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
}
void
EditorBase::MoveAllChildren(nsINode& aContainer,
const EditorRawDOMPoint& aPointToInsert,
ErrorResult& aError)
{
if (!aContainer.HasChildren()) {
@@ -2660,18 +2722,17 @@ EditorBase::InsertTextImpl(nsIDocument&
if (ShouldHandleIMEComposition()) {
CheckedInt<int32_t> newOffset;
if (!pointToInsert.Container()->IsNodeOfType(nsINode::eTEXT)) {
// create a text node
RefPtr<nsTextNode> newNode =
EditorBase::CreateTextNode(aDocument, EmptyString());
// then we insert it into the dom tree
- nsresult rv = InsertNode(*newNode, *pointToInsert.Container(),
- pointToInsert.Offset());
+ nsresult rv = InsertNode(*newNode, pointToInsert);
NS_ENSURE_SUCCESS(rv, rv);
pointToInsert.Set(newNode, 0);
newOffset = lengthToInsert;
} else {
newOffset = lengthToInsert + pointToInsert.Offset();
NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
}
nsresult rv =
@@ -2702,18 +2763,17 @@ EditorBase::InsertTextImpl(nsIDocument&
return NS_OK;
}
// we are inserting text into a non-text node. first we have to create a
// textnode (this also populates it with the text)
RefPtr<nsTextNode> newNode =
EditorBase::CreateTextNode(aDocument, aStringToInsert);
// then we insert it into the dom tree
- nsresult rv = InsertNode(*newNode, *pointToInsert.Container(),
- pointToInsert.Offset());
+ nsresult rv = InsertNode(*newNode, pointToInsert);
NS_ENSURE_SUCCESS(rv, rv);
if (aPointAfterInsertedString) {
aPointAfterInsertedString->Set(newNode, lengthToInsert.value());
}
return NS_OK;
}
nsresult
@@ -4617,25 +4677,20 @@ EditorBase::CreateTxnForCreateElement(ns
new CreateElementTransaction(*this, aTag, aPointToInsert);
return transaction.forget();
}
already_AddRefed<InsertNodeTransaction>
EditorBase::CreateTxnForInsertNode(nsIContent& aNode,
- nsINode& aParent,
- int32_t aPosition)
-{
- int32_t offset =
- aPosition < 0 ? static_cast<int32_t>(aParent.Length()) :
- std::min(aPosition, static_cast<int32_t>(aParent.Length()));
+ const EditorRawDOMPoint& aPointToInsert)
+{
RefPtr<InsertNodeTransaction> transaction =
- new InsertNodeTransaction(*this, aNode,
- EditorRawDOMPoint(&aParent, offset));
+ new InsertNodeTransaction(*this, aNode, aPointToInsert);
return transaction.forget();
}
already_AddRefed<DeleteNodeTransaction>
EditorBase::CreateTxnForDeleteNode(nsINode* aNode)
{
if (NS_WARN_IF(!aNode)) {
return nullptr;
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -341,17 +341,31 @@ public:
EStripWrappers aStripWrappers);
already_AddRefed<Element> DeleteSelectionAndCreateElement(nsAtom& aTag);
/**
* Helper routines for node/parent manipulations.
*/
nsresult DeleteNode(nsINode* aNode);
- nsresult InsertNode(nsIContent& aNode, nsINode& aParent, int32_t aPosition);
+
+ /**
+ * InsertNode() inserts aContentToInsert before the child specified by
+ * aPointToInsert.
+ *
+ * @param aContentToInsert The node to be inserted.
+ * @param aPointToInsert The insertion point of aContentToInsert.
+ * If this refers end of the container, the
+ * transaction will append the node to the
+ * container. Otherwise, will insert the node
+ * before child node referred by this.
+ */
+ nsresult InsertNode(nsIContent& aContentToInsert,
+ const EditorRawDOMPoint& aPointToInsert);
+
enum ECloneAttributes { eDontCloneAttributes, eCloneAttributes };
already_AddRefed<Element> ReplaceContainer(Element* aOldContainer,
nsAtom* aNodeType,
nsAtom* aAttribute = nullptr,
const nsAString* aValue = nullptr,
ECloneAttributes aCloneAttributes
= eDontCloneAttributes);
void CloneAttributes(Element* aDest, Element* aSource);
@@ -507,17 +521,17 @@ protected:
* aPointToInsert of type aTag.
*
* @param aTag The element name to create.
* @param aPointToInsert The insertion point of new element. If this refers
* end of the container or after, the transaction
* will append the element to the container.
* Otherwise, will insert the element before the
* child node referred by this.
- * @return A CreateElementTransaction which are initialized
+ * @return A CreateElementTransaction which was initialized
* with the arguments.
*/
already_AddRefed<CreateElementTransaction>
CreateTxnForCreateElement(nsAtom& aTag,
const EditorRawDOMPoint& aPointToInsert);
/**
* Create an element node whose name is aTag at before aPointToInsert. When
@@ -533,21 +547,31 @@ protected:
* Otherwise, will insert the element before the
* child node referred by this.
* @return The created new element node.
*/
already_AddRefed<Element> CreateNode(nsAtom* aTag,
const EditorRawDOMPoint& aPointToInsert);
/**
- * Create a transaction for inserting aNode as a child of aParent.
+ * Create a transaction for inserting aContentToInsert before the child
+ * at aPointToInsert.
+ *
+ * @param aContentToInsert The node to be inserted.
+ * @param aPointToInsert The insertion point of aContentToInsert.
+ * If this refers end of the container, the
+ * transaction will append the node to the
+ * container. Otherwise, will insert the node
+ * before child node referred by this.
+ * @return A InsertNodeTranaction which was initialized
+ * with the arguments.
*/
already_AddRefed<InsertNodeTransaction>
- CreateTxnForInsertNode(nsIContent& aNode, nsINode& aParent,
- int32_t aOffset);
+ CreateTxnForInsertNode(nsIContent& aContentToInsert,
+ const EditorRawDOMPoint& aPointToInsert);
/**
* Create a transaction for removing aNode from its parent.
*/
already_AddRefed<DeleteNodeTransaction>
CreateTxnForDeleteNode(nsINode* aNode);
/**
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -4859,16 +4859,18 @@ HTMLEditRules::CreateStyleForInsertText(
nsCOMPtr<Element> rootElement = aDoc.GetRootElement();
NS_ENSURE_STATE(rootElement);
// process clearing any styles first
UniquePtr<PropItem> item =
Move(mHTMLEditor->mTypeInState->TakeClearProperty());
while (item && node != rootElement) {
NS_ENSURE_STATE(mHTMLEditor);
+ // XXX If we redesign ClearStyle(), we can use EditorDOMPoint in this
+ // method.
nsresult rv =
mHTMLEditor->ClearStyle(address_of(node), &offset,
item->tag, &item->attr);
NS_ENSURE_SUCCESS(rv, rv);
item = Move(mHTMLEditor->mTypeInState->TakeClearProperty());
weDidSomething = true;
}
@@ -4895,17 +4897,18 @@ HTMLEditRules::CreateStyleForInsertText(
offset = splitPoint.Offset();
}
if (!mHTMLEditor->IsContainer(node)) {
return NS_OK;
}
OwningNonNull<Text> newNode =
EditorBase::CreateTextNode(aDoc, EmptyString());
NS_ENSURE_STATE(mHTMLEditor);
- nsresult rv = mHTMLEditor->InsertNode(*newNode, *node, offset);
+ nsresult rv =
+ mHTMLEditor->InsertNode(*newNode, EditorRawDOMPoint(node, offset));
NS_ENSURE_SUCCESS(rv, rv);
node = newNode;
offset = 0;
weDidSomething = true;
if (relFontSize) {
// dir indicated bigger versus smaller. 1 = bigger, -1 = smaller
HTMLEditor::FontSize dir = relFontSize > 0 ?
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1162,18 +1162,21 @@ HTMLEditor::ReplaceHeadContentsWithHTML(
NS_ENSURE_SUCCESS(rv, rv);
}
// Now insert the new nodes
int32_t offsetOfNewNode = 0;
// Loop over the contents of the fragment and move into the document
while (nsCOMPtr<nsIContent> child = docfrag->GetFirstChild()) {
- nsresult rv = InsertNode(*child, *headNode, offsetOfNewNode++);
- NS_ENSURE_SUCCESS(rv, rv);
+ nsresult rv =
+ InsertNode(*child, EditorRawDOMPoint(headNode, offsetOfNewNode++));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
return NS_OK;
}
NS_IMETHODIMP
HTMLEditor::RebuildDocumentFromSource(const nsAString& aSourceString)
{
@@ -1607,17 +1610,17 @@ HTMLEditor::InsertNodeAtPoint(nsIDOMNode
EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
*ioParent = GetAsDOMNode(splitPoint.Container());
*ioOffset = splitPoint.Offset();
if (ioChildAtOffset) {
*ioChildAtOffset = GetAsDOMNode(splitPoint.GetChildAtOffset());
}
}
// Now we can insert the new node
- nsresult rv = InsertNode(*node, *parent, *ioOffset);
+ nsresult rv = InsertNode(*node, EditorRawDOMPoint(parent, *ioOffset));
if (isDocumentFragment) {
*ioChildAtOffset = do_QueryInterface(parent->GetChildAt(*ioOffset));
}
return rv;
}
NS_IMETHODIMP
HTMLEditor::SelectElement(nsIDOMElement* aElement)
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -119,32 +119,50 @@ HTMLEditor::LoadHTML(const nsAString& aI
rv = DeleteSelection(eNone, eStrip);
NS_ENSURE_SUCCESS(rv, rv);
}
// Get the first range in the selection, for context:
RefPtr<nsRange> range = selection->GetRangeAt(0);
NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
- // create fragment for pasted html
- nsCOMPtr<nsIDOMDocumentFragment> docfrag;
- rv = range->CreateContextualFragment(aInputString, getter_AddRefs(docfrag));
- NS_ENSURE_SUCCESS(rv, rv);
- // put the fragment into the document
- nsCOMPtr<nsINode> parent = range->GetStartContainer();
- NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
- uint32_t childOffset = range->StartOffset();
+ // Create fragment for pasted HTML.
+ ErrorResult error;
+ RefPtr<DocumentFragment> documentFragment =
+ range->CreateContextualFragment(aInputString, error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
- nsCOMPtr<nsIDOMNode> nodeToInsert;
- docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
- while (nodeToInsert) {
- rv = InsertNode(nodeToInsert, GetAsDOMNode(parent),
- static_cast<int32_t>(childOffset++));
- NS_ENSURE_SUCCESS(rv, rv);
- docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
+ // Put the fragment into the document at start of selection.
+ EditorDOMPoint pointToInsert(range->StartRef());
+ // XXX We need to make pointToInsert store offset for keeping traditional
+ // behavior since using only child node to pointing insertion point
+ // changes the behavior when inserted child is moved by mutation
+ // observer. We need to investigate what we should do here.
+ Unused << pointToInsert.Offset();
+ for (nsCOMPtr<nsIContent> contentToInsert =
+ documentFragment->GetFirstChild();
+ contentToInsert;
+ contentToInsert = documentFragment->GetFirstChild()) {
+ rv = InsertNode(*contentToInsert, pointToInsert.AsRaw());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ // XXX If the inserted node has been moved by mutation observer,
+ // incrementing offset will cause odd result. Next new node
+ // will be inserted after existing node and the offset will be
+ // overflown from the container node.
+ pointToInsert.Set(pointToInsert.Container(), pointToInsert.Offset() + 1);
+ if (NS_WARN_IF(!pointToInsert.Offset())) {
+ // Append the remaining children to the container if offset is
+ // overflown.
+ pointToInsert.Set(pointToInsert.Container(),
+ pointToInsert.Container()->Length());
+ }
}
}
return rules->DidDoAction(selection, &ruleInfo, rv);
}
NS_IMETHODIMP
HTMLEditor::InsertHTML(const nsAString& aInString)
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -82,72 +82,80 @@ public:
void CancelSetCaret()
{
mHTMLEditor = nullptr;
mTable = nullptr;
}
};
NS_IMETHODIMP
-HTMLEditor::InsertCell(nsIDOMElement* aCell,
+HTMLEditor::InsertCell(nsIDOMElement* aDOMCell,
int32_t aRowSpan,
int32_t aColSpan,
bool aAfter,
bool aIsHeader,
- nsIDOMElement** aNewCell)
+ nsIDOMElement** aNewDOMCell)
{
- NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
- if (aNewCell) {
- *aNewCell = nullptr;
+ if (aNewDOMCell) {
+ *aNewDOMCell = nullptr;
+ }
+
+ if (NS_WARN_IF(!aDOMCell)) {
+ return NS_ERROR_NULL_POINTER;
}
// And the parent and offsets needed to do an insert
- nsCOMPtr<nsIDOMNode> cellParent;
- nsresult rv = aCell->GetParentNode(getter_AddRefs(cellParent));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(cellParent, NS_ERROR_NULL_POINTER);
-
- int32_t cellOffset = GetChildOffset(aCell, cellParent);
-
- nsCOMPtr<nsIDOMElement> newCell;
- rv = CreateElementWithDefaults(aIsHeader ? NS_LITERAL_STRING("th") :
- NS_LITERAL_STRING("tb"),
- getter_AddRefs(newCell));
- if (NS_FAILED(rv)) {
+ nsCOMPtr<nsIContent> cell = do_QueryInterface(aDOMCell);
+ if (NS_WARN_IF(!cell)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ EditorDOMPoint pointToInsert(cell);
+ if (NS_WARN_IF(!pointToInsert.IsSet())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIDOMElement> newDOMCell;
+ nsresult rv =
+ CreateElementWithDefaults(aIsHeader ? NS_LITERAL_STRING("th") :
+ NS_LITERAL_STRING("tb"),
+ getter_AddRefs(newDOMCell));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- if (!newCell) {
+ nsCOMPtr<Element> newCell = do_QueryInterface(newDOMCell);
+ if (NS_WARN_IF(!newCell)) {
return NS_ERROR_FAILURE;
}
//Optional: return new cell created
- if (aNewCell) {
- *aNewCell = newCell.get();
- NS_ADDREF(*aNewCell);
+ if (aNewDOMCell) {
+ newDOMCell.forget(aNewDOMCell);
}
if (aRowSpan > 1) {
// Note: Do NOT use editor transaction for this
nsAutoString newRowSpan;
newRowSpan.AppendInt(aRowSpan, 10);
- newCell->SetAttribute(NS_LITERAL_STRING("rowspan"), newRowSpan);
+ newCell->SetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, newRowSpan, true);
}
if (aColSpan > 1) {
// Note: Do NOT use editor transaction for this
nsAutoString newColSpan;
newColSpan.AppendInt(aColSpan, 10);
- newCell->SetAttribute(NS_LITERAL_STRING("colspan"), newColSpan);
+ newCell->SetAttr(kNameSpaceID_None, nsGkAtoms::colspan, newColSpan, true);
}
if (aAfter) {
- cellOffset++;
+ DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to advance offset to after the old cell");
}
- //Don't let Rules System change the selection
+ // Don't let Rules System change the selection.
AutoTransactionsConserveSelection dontChangeSelection(this);
- return InsertNode(newCell, cellParent, cellOffset);
+ return InsertNode(*newCell, pointToInsert.AsRaw());
}
NS_IMETHODIMP
HTMLEditor::SetColSpan(nsIDOMElement* aCell,
int32_t aColSpan)
{
NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
nsAutoString newSpan;
@@ -202,17 +210,21 @@ HTMLEditor::InsertTableCell(int32_t aNum
for (int32_t i = 0; i < aNumber; i++) {
nsCOMPtr<nsIDOMElement> newCell;
rv = CreateElementWithDefaults(NS_LITERAL_STRING("td"),
getter_AddRefs(newCell));
if (NS_SUCCEEDED(rv) && newCell) {
if (aAfter) {
cellOffset++;
}
- rv = InsertNode(newCell, cellParent, cellOffset);
+ nsCOMPtr<nsIContent> cell = do_QueryInterface(newCell);
+ if (NS_WARN_IF(!cell)) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = InsertNode(*cell, EditorRawDOMPoint(cellParent, cellOffset));
if (NS_FAILED(rv)) {
break;
}
}
}
// XXX This is perhaps the result of the last call of InsertNode() or
// CreateElementWithDefaults().
return rv;
@@ -682,17 +694,17 @@ HTMLEditor::InsertTableRow(int32_t aNumb
newRow->AppendChild(*newCell, result);
if (NS_WARN_IF(result.Failed())) {
return result.StealNSResult();
}
}
// Use transaction system to insert the entire row+cells
// (Note that rows are inserted at same childoffset each time)
- rv = InsertNode(*newRow, *parentOfRow, newRowOffset);
+ rv = InsertNode(*newRow, EditorRawDOMPoint(parentOfRow, newRowOffset));
NS_ENSURE_SUCCESS(rv, rv);
}
}
// SetSelectionAfterTableEdit from AutoSelectionSetterAfterTableEdit will
// access frame selection, so we need reframe.
// Because GetCellAt depends on frame.
nsCOMPtr<nsIPresShell> ps = GetPresShell();
@@ -2323,21 +2335,22 @@ HTMLEditor::MergeCells(nsCOMPtr<nsIDOMEl
NS_ENSURE_SUCCESS(rv, rv);
insertIndex = 0;
} else {
insertIndex = (int32_t)len;
}
// Move the contents
while (cellToMerge->HasChildren()) {
- nsCOMPtr<nsIDOMNode> cellChild = cellToMerge->GetLastChild()->AsDOMNode();
- nsresult rv = DeleteNode(cellChild);
+ nsCOMPtr<nsIContent> cellChild = cellToMerge->GetLastChild();
+ // XXX We need HTMLEditor::DeleteNode(nsINode&).
+ nsresult rv = DeleteNode(cellChild->AsDOMNode());
NS_ENSURE_SUCCESS(rv, rv);
- rv = InsertNode(cellChild, aTargetCell, insertIndex);
+ rv = InsertNode(*cellChild, EditorRawDOMPoint(aTargetCell, insertIndex));
NS_ENSURE_SUCCESS(rv, rv);
}
}
// Delete cells whose contents were moved
if (aDeleteCellToMerge) {
return DeleteNode(aCellToMerge);
}
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -866,17 +866,18 @@ TextEditRules::WillSetText(Selection& aS
RefPtr<nsIDocument> doc = textEditor->GetDocument();
if (NS_WARN_IF(!doc)) {
return NS_OK;
}
RefPtr<nsTextNode> newNode = EditorBase::CreateTextNode(*doc, tString);
if (NS_WARN_IF(!newNode)) {
return NS_OK;
}
- nsresult rv = textEditor->InsertNode(*newNode, *rootElement, 0);
+ nsresult rv =
+ textEditor->InsertNode(*newNode, EditorRawDOMPoint(rootElement, 0));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
*aHandled = true;
ASSERT_PASSWORD_LENGTHS_EQUAL();
return NS_OK;
@@ -1443,21 +1444,28 @@ TextEditRules::CreateBogusNodeIfNeeded(S
// set mBogusNode to be the newly created <br>
mBogusNode = newContent;
// Give it a special attribute.
newContent->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
kMOZEditorBogusNodeValue, false);
// Put the node in the document.
- nsresult rv = mTextEditor->InsertNode(*mBogusNode, *body, 0);
- NS_ENSURE_SUCCESS(rv, rv);
+ nsresult rv =
+ mTextEditor->InsertNode(*mBogusNode, EditorRawDOMPoint(body, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// Set selection.
- aSelection->Collapse(body, 0);
+ ErrorResult error;
+ aSelection->Collapse(EditorRawDOMPoint(body, 0), error);
+ if (NS_WARN_IF(error.Failed())) {
+ error.SuppressException();
+ }
return NS_OK;
}
nsresult
TextEditRules::TruncateInsertionIfNeeded(Selection* aSelection,
const nsAString* aInString,
nsAString* aOutString,