Bug 1408125 - part 4: Redesign HTMLEditor::InsertNodeAtPoint() with EditorRawDOMPoint r?m_kato
HTMLEditor::InsertNodeAtPoint() should take |const EditorRawDOMPoint&| as
an argument which specifies point to insert. Additionally, it should take
|EditorDOMPoint*| to return the next point of actual insertion point.
Additionally, this patch renames it to InsertNodeAtProperAncestor() for
explaining what it will do.
MozReview-Commit-ID: HYUzSlyPxAd
--- a/editor/libeditor/EditorDOMPoint.h
+++ b/editor/libeditor/EditorDOMPoint.h
@@ -114,16 +114,26 @@ public:
{
mIsChildInitialized = aPointedNode && mChild;
NS_WARNING_ASSERTION(IsSet(),
"The child is nullptr or doesn't have its parent");
NS_WARNING_ASSERTION(mChild && mChild->GetParentNode() == mParent,
"Initializing RangeBoundary with invalid value");
}
+ explicit EditorDOMPointBase(nsIDOMNode* aDOMPointedNode)
+ : mIsChildInitialized(false)
+ {
+ nsCOMPtr<nsIContent> child = do_QueryInterface(aDOMPointedNode);
+ if (NS_WARN_IF(!child)) {
+ return;
+ }
+ this->Set(child);
+ }
+
EditorDOMPointBase(nsINode* aContainer,
nsIContent* aPointedNode,
int32_t aOffset)
: mParent(aContainer)
, mChild(aPointedNode)
, mOffset(mozilla::Some(aOffset))
, mIsChildInitialized(true)
{
--- a/editor/libeditor/HTMLEditUtils.cpp
+++ b/editor/libeditor/HTMLEditUtils.cpp
@@ -219,16 +219,22 @@ HTMLEditUtils::IsTable(nsINode* aNode)
* IsTableRow() returns true if aNode is an html tr.
*/
bool
HTMLEditUtils::IsTableRow(nsIDOMNode* aNode)
{
return EditorBase::NodeIsType(aNode, nsGkAtoms::tr);
}
+bool
+HTMLEditUtils::IsTableRow(nsINode* aNode)
+{
+ return aNode && aNode->IsHTMLElement(nsGkAtoms::tr);
+}
+
/**
* IsTableCell() returns true if aNode is an html td or th.
*/
bool
HTMLEditUtils::IsTableCell(nsIDOMNode* aNode)
{
MOZ_ASSERT(aNode);
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
@@ -304,16 +310,22 @@ HTMLEditUtils::IsBlockquote(nsIDOMNode*
* IsPre() returns true if aNode is an html pre node.
*/
bool
HTMLEditUtils::IsPre(nsIDOMNode* aNode)
{
return EditorBase::NodeIsType(aNode, nsGkAtoms::pre);
}
+bool
+HTMLEditUtils::IsPre(nsINode* aNode)
+{
+ return aNode && aNode->IsHTMLElement(nsGkAtoms::pre);
+}
+
/**
* IsImage() returns true if aNode is an html image node.
*/
bool
HTMLEditUtils::IsImage(nsINode* aNode)
{
return aNode && aNode->IsHTMLElement(nsGkAtoms::img);
}
--- a/editor/libeditor/HTMLEditUtils.h
+++ b/editor/libeditor/HTMLEditUtils.h
@@ -24,29 +24,31 @@ public:
static bool IsHeader(nsINode& aNode);
static bool IsHeader(nsIDOMNode* aNode);
static bool IsParagraph(nsIDOMNode* aNode);
static bool IsListItem(nsINode* aNode);
static bool IsListItem(nsIDOMNode* aNode);
static bool IsTable(nsIDOMNode* aNode);
static bool IsTable(nsINode* aNode);
static bool IsTableRow(nsIDOMNode* aNode);
+ static bool IsTableRow(nsINode* aNode);
static bool IsTableElement(nsINode* aNode);
static bool IsTableElement(nsIDOMNode* aNode);
static bool IsTableElementButNotTable(nsINode* aNode);
static bool IsTableElementButNotTable(nsIDOMNode* aNode);
static bool IsTableCell(nsINode* node);
static bool IsTableCell(nsIDOMNode* aNode);
static bool IsTableCellOrCaption(nsINode& aNode);
static bool IsList(nsINode* aNode);
static bool IsList(nsIDOMNode* aNode);
static bool IsOrderedList(nsIDOMNode* aNode);
static bool IsUnorderedList(nsIDOMNode* aNode);
static bool IsBlockquote(nsIDOMNode* aNode);
static bool IsPre(nsIDOMNode* aNode);
+ static bool IsPre(nsINode* aNode);
static bool IsAnchor(nsIDOMNode* aNode);
static bool IsImage(nsINode* aNode);
static bool IsImage(nsIDOMNode* aNode);
static bool IsLink(nsIDOMNode* aNode);
static bool IsLink(nsINode* aNode);
static bool IsNamedAnchor(nsINode* aNode);
static bool IsNamedAnchor(nsIDOMNode* aNode);
static bool IsDiv(nsIDOMNode* aNode);
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1494,137 +1494,114 @@ HTMLEditor::InsertElementAtSelection(nsI
if (parentSelectedNode) {
int32_t offsetForInsert = selection->AnchorOffset();
// Adjust position based on the node we are going to insert.
nsCOMPtr<nsIDOMNode> parentSelectedDOMNode =
GetAsDOMNode(parentSelectedNode);
NormalizeEOLInsertPosition(element, address_of(parentSelectedDOMNode),
&offsetForInsert);
- rv = InsertNodeAtPoint(node, address_of(parentSelectedDOMNode),
- &offsetForInsert,
- SplitAtEdges::eAllowToCreateEmptyContainer);
- NS_ENSURE_SUCCESS(rv, rv);
+ EditorDOMPoint insertedPoint =
+ InsertNodeIntoProperAncestor(
+ *element, EditorRawDOMPoint(parentSelectedDOMNode, offsetForInsert),
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ if (NS_WARN_IF(!insertedPoint.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
// Set caret after element, but check for special case
// of inserting table-related elements: set in first cell instead
if (!SetCaretInTableCell(aElement)) {
rv = SetCaretAfterElement(aElement);
NS_ENSURE_SUCCESS(rv, rv);
}
// check for inserting a whole table at the end of a block. If so insert
// a br after it.
if (HTMLEditUtils::IsTable(node) &&
IsLastEditableChild(element)) {
+ DebugOnly<bool> advanced = insertedPoint.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to advance offset from inserted point");
// Collapse selection to the new <br> element node after creating it.
RefPtr<Element> newBRElement =
- CreateBRImpl(*selection,
- EditorRawDOMPoint(parentSelectedDOMNode,
- offsetForInsert + 1),
- ePrevious);
+ CreateBRImpl(*selection, insertedPoint.AsRaw(), ePrevious);
if (NS_WARN_IF(!newBRElement)) {
return NS_ERROR_FAILURE;
}
}
}
}
rv = rules->DidDoAction(selection, &ruleInfo, rv);
return rv;
}
-
-/**
- * InsertNodeAtPoint() attempts to insert aNode into the document, at a point
- * specified by {*ioParent,*ioOffset}. Checks with strict dtd to see if
- * containment is allowed. If not allowed, will attempt to find a parent in
- * the parent hierarchy of *ioParent that will accept aNode as a child. If
- * such a parent is found, will split the document tree from
- * {*ioParent,*ioOffset} up to parent, and then insert aNode.
- * ioParent & ioOffset are then adjusted to point to the actual location that
- * aNode was inserted at. aNoEmptyNodes specifies if the splitting process
- * is allowed to reslt in empty nodes. ioChildAtOffset, if provided, is the
- * child node at offset if ioParent is non-null, and the function will update
- * *ioChildAtOffset upon returning.
- *
- * @param aNode Node to insert.
- * @param ioParent Insertion parent.
- * @param ioOffset Insertion offset.
- * @param aSplitAtEdges Splitting can result in empty nodes?
- * @param ioChildAtOffset Child node at insertion offset (optional).
- */
-nsresult
-HTMLEditor::InsertNodeAtPoint(nsIDOMNode* aNode,
- nsCOMPtr<nsIDOMNode>* ioParent,
- int32_t* ioOffset,
- SplitAtEdges aSplitAtEdges,
- nsCOMPtr<nsIDOMNode>* ioChildAtOffset)
+EditorDOMPoint
+HTMLEditor::InsertNodeIntoProperAncestor(
+ nsIContent& aNode,
+ const EditorRawDOMPoint& aPointToInsert,
+ SplitAtEdges aSplitAtEdges)
{
- nsCOMPtr<nsIContent> node = do_QueryInterface(aNode);
- NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
- NS_ENSURE_TRUE(ioParent, NS_ERROR_NULL_POINTER);
- NS_ENSURE_TRUE(*ioParent, NS_ERROR_NULL_POINTER);
- NS_ENSURE_TRUE(ioOffset, NS_ERROR_NULL_POINTER);
- bool isDocumentFragment = false;
- if (ioChildAtOffset) {
- *ioChildAtOffset = aNode;
- uint16_t nodeType = 0;
- if (NS_SUCCEEDED(aNode->GetNodeType(&nodeType)) &&
- nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
- // For document fragments, we can't return aNode itself in
- // *ioChildAtOffset, so we have to find out the inserted child after
- // the insertion is successfully finished.
- isDocumentFragment = true;
+ if (NS_WARN_IF(!aPointToInsert.IsSet())) {
+ return EditorDOMPoint();
+ }
+ MOZ_ASSERT(aPointToInsert.IsSetAndValid());
+
+ // Search up the parent chain to find a suitable container.
+ EditorDOMPoint pointToInsert(aPointToInsert);
+ MOZ_ASSERT(pointToInsert.IsSet());
+ while (!CanContain(*pointToInsert.Container(), aNode)) {
+ // If the current parent is a root (body or table element)
+ // then go no further - we can't insert.
+ if (pointToInsert.Container()->IsHTMLElement(nsGkAtoms::body) ||
+ HTMLEditUtils::IsTableElement(pointToInsert.Container())) {
+ return EditorDOMPoint();
}
- }
-
- nsCOMPtr<nsIContent> parent = do_QueryInterface(*ioParent);
- NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
- nsCOMPtr<nsIContent> topChild = parent;
- nsCOMPtr<nsIContent> origParent = parent;
-
- // Search up the parent chain to find a suitable container
- while (!CanContain(*parent, *node)) {
- // If the current parent is a root (body or table element)
- // then go no further - we can't insert
- if (parent->IsHTMLElement(nsGkAtoms::body) ||
- HTMLEditUtils::IsTableElement(parent)) {
- return NS_ERROR_FAILURE;
+
+ // Get the next point.
+ pointToInsert.Set(pointToInsert.Container());
+ if (NS_WARN_IF(!pointToInsert.IsSet())) {
+ return EditorDOMPoint();
}
- // Get the next parent
- NS_ENSURE_TRUE(parent->GetParentNode(), NS_ERROR_FAILURE);
- if (!IsEditable(parent->GetParentNode())) {
+
+ if (!IsEditable(pointToInsert.Container())) {
// There's no suitable place to put the node in this editing host. Maybe
// someone is trying to put block content in a span. So just put it
// where we were originally asked.
- parent = topChild = origParent;
+ pointToInsert = aPointToInsert;
break;
}
- topChild = parent;
- parent = parent->GetParent();
- }
- if (parent != topChild) {
+ }
+
+ if (pointToInsert != aPointToInsert) {
// We need to split some levels above the original selection parent.
+ MOZ_ASSERT(pointToInsert.GetChildAtOffset());
SplitNodeResult splitNodeResult =
- SplitNodeDeep(*topChild, EditorRawDOMPoint(origParent, *ioOffset),
- aSplitAtEdges);
+ SplitNodeDeep(*pointToInsert.GetChildAtOffset(),
+ aPointToInsert, aSplitAtEdges);
if (NS_WARN_IF(splitNodeResult.Failed())) {
- return splitNodeResult.Rv();
+ return EditorDOMPoint();
}
- EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
- *ioParent = GetAsDOMNode(splitPoint.Container());
- *ioOffset = splitPoint.Offset();
- if (ioChildAtOffset) {
- *ioChildAtOffset = GetAsDOMNode(splitPoint.GetChildAtOffset());
+ pointToInsert = splitNodeResult.SplitPoint();
+ MOZ_ASSERT(pointToInsert.IsSet());
+ }
+
+ {
+ // After inserting a node, pointToInsert will refer next sibling of
+ // the new node but keep referring the new node's offset.
+ // This method's result should be the point at insertion, it's useful
+ // even if the new node is moved by mutation observer immediately.
+ // So, let's lock only the offset and child node should be recomputed
+ // when it's necessary.
+ AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
+ // Now we can insert the new node.
+ nsresult rv = InsertNode(aNode, pointToInsert.AsRaw());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return EditorDOMPoint();
}
}
- // Now we can insert the new node
- nsresult rv = InsertNode(*node, EditorRawDOMPoint(parent, *ioOffset));
- if (isDocumentFragment) {
- *ioChildAtOffset = do_QueryInterface(parent->GetChildAt(*ioOffset));
- }
- return rv;
+ return pointToInsert;
}
NS_IMETHODIMP
HTMLEditor::SelectElement(nsIDOMElement* aElement)
{
nsCOMPtr<Element> element = do_QueryInterface(aElement);
NS_ENSURE_STATE(element || !aElement);
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -333,21 +333,37 @@ public:
// nsICSSLoaderObserver
NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet,
bool aWasAlternate, nsresult aStatus) override;
// Utility Routines, not part of public API
NS_IMETHOD TypedText(const nsAString& aString,
ETypingAction aAction) override;
- nsresult InsertNodeAtPoint(nsIDOMNode* aNode,
- nsCOMPtr<nsIDOMNode>* ioParent,
- int32_t* ioOffset,
- SplitAtEdges aSplitAtEdges,
- nsCOMPtr<nsIDOMNode>* ioChildAtOffset = nullptr);
+
+ /**
+ * InsertNodeIntoProperAncestor() attempts to insert aNode into the document,
+ * at aPointToInsert. Checks with strict dtd to see if containment is
+ * allowed. If not allowed, will attempt to find a parent in the parent
+ * hierarchy of aPointToInsert.Container() that will accept aNode as a child.
+ * If such a parent is found, will split the document tree from aPointToInsert
+ * up to parent, and then insert aNode. aPointToInsert is then adjusted to
+ * point to the actual location that aNode was inserted at. aSplitAtEdges
+ * specifies if the splitting process is allowed to result in empty nodes.
+ *
+ * @param aNode Node to insert.
+ * @param aPointToInsert Insertion point.
+ * @param aSplitAtEdges Splitting can result in empty nodes?
+ * @return Returns inserted point if succeeded.
+ * Otherwise, the result is not set.
+ */
+ EditorDOMPoint
+ InsertNodeIntoProperAncestor(nsIContent& aNode,
+ const EditorRawDOMPoint& aPointToInsert,
+ SplitAtEdges aSplitAtEdges);
/**
* Use this to assure that selection is set after attribute nodes when
* trying to collapse selection at begining of a block node
* e.g., when setting at beginning of a table cell
* This will stop at a table, however, since we don't want to
* "drill down" into nested tables.
* @param aSelection Optional. If null, we get current selection.
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -425,260 +425,270 @@ HTMLEditor::DoInsertHTMLWithContext(cons
// don't orphan partial list or table structure
if (highWaterMark >= 0) {
ReplaceOrphanedStructure(StartOrEnd::end, nodeList,
endListAndTableArray, highWaterMark);
}
// Loop over the node list and paste the nodes:
- nsCOMPtr<nsIDOMNode> parentBlock, lastInsertNode, insertedContextParent;
+ nsCOMPtr<nsIDOMNode> parentBlock;
nsCOMPtr<nsINode> parentNodeNode = do_QueryInterface(parentNode);
NS_ENSURE_STATE(parentNodeNode || !parentNode);
if (IsBlockNode(parentNodeNode)) {
parentBlock = parentNode;
} else if (parentNodeNode) {
parentBlock = GetAsDOMNode(GetBlockNodeParent(parentNodeNode));
}
- int32_t listCount = nodeList.Length();
- for (int32_t j = 0; j < listCount; j++) {
- bool bDidInsert = false;
- nsCOMPtr<nsIDOMNode> curNode = nodeList[j]->AsDOMNode();
-
- NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
- NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE);
- NS_ENSURE_TRUE(!TextEditUtils::IsBody(curNode), NS_ERROR_FAILURE);
+ nsCOMPtr<nsIContent> lastInsertNode;
+ nsCOMPtr<nsINode> insertedContextParent;
+ EditorDOMPoint pointToInsert(parentNodeNode, offsetOfNewNode);
+ for (OwningNonNull<nsINode>& curNode : nodeList) {
+ if (NS_WARN_IF(curNode == fragmentAsNodeNode) ||
+ NS_WARN_IF(TextEditUtils::IsBody(curNode))) {
+ return NS_ERROR_FAILURE;
+ }
if (insertedContextParent) {
- // if we had to insert something higher up in the paste hierarchy, we want to
- // skip any further paste nodes that descend from that. Else we will paste twice.
- nsCOMPtr<nsINode> insertedContextParentNode =
- do_QueryInterface(insertedContextParent);
- if (NS_WARN_IF(!insertedContextParentNode) ||
- EditorUtils::IsDescendantOf(*nodeList[j],
- *insertedContextParentNode)) {
+ // If we had to insert something higher up in the paste hierarchy,
+ // we want to skip any further paste nodes that descend from that.
+ // Else we will paste twice.
+ if (EditorUtils::IsDescendantOf(*curNode,
+ *insertedContextParent)) {
continue;
}
}
// give the user a hand on table element insertion. if they have
// a table or table row on the clipboard, and are trying to insert
// into a table or table row, insert the appropriate children instead.
+ bool bDidInsert = false;
if (HTMLEditUtils::IsTableRow(curNode) &&
- HTMLEditUtils::IsTableRow(parentNode) &&
+ HTMLEditUtils::IsTableRow(pointToInsert.Container()) &&
(HTMLEditUtils::IsTable(curNode) ||
- HTMLEditUtils::IsTable(parentNode))) {
- nsCOMPtr<nsIDOMNode> child;
- curNode->GetFirstChild(getter_AddRefs(child));
- while (child) {
- rv = InsertNodeAtPoint(child, address_of(parentNode),
- &offsetOfNewNode,
- SplitAtEdges::eDoNotCreateEmptyContainer);
- if (NS_FAILED(rv)) {
+ HTMLEditUtils::IsTable(pointToInsert.Container()))) {
+ for (nsCOMPtr<nsIContent> firstChild = curNode->GetFirstChild();
+ firstChild;
+ firstChild = curNode->GetFirstChild()) {
+ EditorDOMPoint insertedPoint =
+ InsertNodeIntoProperAncestor(
+ *firstChild, pointToInsert.AsRaw(),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ if (NS_WARN_IF(!insertedPoint.IsSet())) {
break;
}
-
bDidInsert = true;
- lastInsertNode = child;
- offsetOfNewNode++;
-
- curNode->GetFirstChild(getter_AddRefs(child));
+ lastInsertNode = firstChild;
+ pointToInsert = insertedPoint;
+ DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to advance offset from inserted point");
}
}
// give the user a hand on list insertion. if they have
// a list on the clipboard, and are trying to insert
// into a list or list item, insert the appropriate children instead,
// ie, merge the lists instead of pasting in a sublist.
else if (HTMLEditUtils::IsList(curNode) &&
- (HTMLEditUtils::IsList(parentNode) ||
- HTMLEditUtils::IsListItem(parentNode))) {
- nsCOMPtr<nsIDOMNode> child, tmp;
- curNode->GetFirstChild(getter_AddRefs(child));
- while (child) {
- if (HTMLEditUtils::IsListItem(child) ||
- HTMLEditUtils::IsList(child)) {
+ (HTMLEditUtils::IsList(pointToInsert.Container()) ||
+ HTMLEditUtils::IsListItem(pointToInsert.Container()))) {
+ for (nsCOMPtr<nsIContent> firstChild = curNode->GetFirstChild();
+ firstChild;
+ firstChild = curNode->GetFirstChild()) {
+ if (HTMLEditUtils::IsListItem(firstChild) ||
+ HTMLEditUtils::IsList(firstChild)) {
// Check if we are pasting into empty list item. If so
// delete it and paste into parent list instead.
- if (HTMLEditUtils::IsListItem(parentNode)) {
+ if (HTMLEditUtils::IsListItem(pointToInsert.Container())) {
bool isEmpty;
- rv = IsEmptyNode(parentNode, &isEmpty, true);
+ rv = IsEmptyNode(pointToInsert.Container(), &isEmpty, true);
if (NS_SUCCEEDED(rv) && isEmpty) {
- int32_t newOffset;
- nsCOMPtr<nsIDOMNode> listNode = GetNodeLocation(parentNode, &newOffset);
- if (listNode) {
- DeleteNode(parentNode);
- parentNode = listNode;
- offsetOfNewNode = newOffset;
+ if (NS_WARN_IF(!pointToInsert.Container()->GetParentNode())) {
+ // Is it an orphan node?
+ } else {
+ DeleteNode(pointToInsert.Container());
+ pointToInsert.Set(pointToInsert.Container());
}
}
}
- rv = InsertNodeAtPoint(child, address_of(parentNode),
- &offsetOfNewNode,
- SplitAtEdges::eDoNotCreateEmptyContainer);
- if (NS_FAILED(rv)) {
+ EditorDOMPoint insertedPoint =
+ InsertNodeIntoProperAncestor(
+ *firstChild, pointToInsert.AsRaw(),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ if (NS_WARN_IF(!insertedPoint.IsSet())) {
break;
}
bDidInsert = true;
- lastInsertNode = child;
- offsetOfNewNode++;
+ lastInsertNode = firstChild;
+ pointToInsert = insertedPoint;
+ DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to advance offset from inserted point");
} else {
- curNode->RemoveChild(child, getter_AddRefs(tmp));
+ AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
+ ErrorResult error;
+ curNode->RemoveChild(*firstChild, error);
+ if (NS_WARN_IF(error.Failed())) {
+ error.SuppressException();
+ }
}
- curNode->GetFirstChild(getter_AddRefs(child));
}
} else if (parentBlock && HTMLEditUtils::IsPre(parentBlock) &&
HTMLEditUtils::IsPre(curNode)) {
// Check for pre's going into pre's.
- nsCOMPtr<nsIDOMNode> child;
- curNode->GetFirstChild(getter_AddRefs(child));
- while (child) {
- rv = InsertNodeAtPoint(child, address_of(parentNode),
- &offsetOfNewNode,
- SplitAtEdges::eDoNotCreateEmptyContainer);
- if (NS_FAILED(rv)) {
+ for (nsCOMPtr<nsIContent> firstChild = curNode->GetFirstChild();
+ firstChild;
+ firstChild = curNode->GetFirstChild()) {
+ EditorDOMPoint insertedPoint =
+ InsertNodeIntoProperAncestor(
+ *firstChild, pointToInsert.AsRaw(),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ if (NS_WARN_IF(!insertedPoint.IsSet())) {
break;
}
bDidInsert = true;
- lastInsertNode = child;
- offsetOfNewNode++;
-
- curNode->GetFirstChild(getter_AddRefs(child));
+ lastInsertNode = firstChild;
+ pointToInsert = insertedPoint;
+ DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to advance offset from inserted point");
}
}
if (!bDidInsert || NS_FAILED(rv)) {
- // try to insert
- rv = InsertNodeAtPoint(curNode, address_of(parentNode),
- &offsetOfNewNode,
- SplitAtEdges::eDoNotCreateEmptyContainer);
- if (NS_SUCCEEDED(rv)) {
- bDidInsert = true;
- lastInsertNode = curNode;
+ // Try to insert.
+ EditorDOMPoint insertedPoint =
+ InsertNodeIntoProperAncestor(
+ *curNode->AsContent(), pointToInsert.AsRaw(),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ if (insertedPoint.IsSet()) {
+ lastInsertNode = curNode->AsContent();
+ pointToInsert = insertedPoint;
}
// Assume failure means no legal parent in the document hierarchy,
// try again with the parent of curNode in the paste hierarchy.
- nsCOMPtr<nsIDOMNode> parent;
- while (NS_FAILED(rv) && curNode) {
- curNode->GetParentNode(getter_AddRefs(parent));
- if (parent && !TextEditUtils::IsBody(parent)) {
- rv = InsertNodeAtPoint(parent, address_of(parentNode),
- &offsetOfNewNode,
- SplitAtEdges::eDoNotCreateEmptyContainer,
- address_of(lastInsertNode));
- if (NS_SUCCEEDED(rv)) {
- bDidInsert = true;
- insertedContextParent = parent;
-#ifdef DEBUG
- nsCOMPtr<nsINode> node = do_QueryInterface(parentNode);
- MOZ_ASSERT(lastInsertNode == GetAsDOMNode(node->GetChildAt(offsetOfNewNode)));
-#endif
- }
+ for (nsCOMPtr<nsIContent> content =
+ curNode->IsContent() ? curNode->AsContent() : nullptr;
+ content && !insertedPoint.IsSet();
+ content = content->GetParent()) {
+ if (NS_WARN_IF(!content->GetParent()) ||
+ NS_WARN_IF(TextEditUtils::IsBody(content->GetParent()))) {
+ continue;
}
- curNode = parent;
+ nsCOMPtr<nsINode> oldParent = content->GetParentNode();
+ insertedPoint =
+ InsertNodeIntoProperAncestor(
+ *content->GetParent(), pointToInsert.AsRaw(),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ if (insertedPoint.IsSet()) {
+ insertedContextParent = oldParent;
+ pointToInsert = insertedPoint;
+ }
}
}
if (lastInsertNode) {
- parentNode = GetNodeLocation(lastInsertNode, &offsetOfNewNode);
- offsetOfNewNode++;
+ pointToInsert.Set(lastInsertNode);
+ DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to advance offset from inserted point");
}
}
// Now collapse the selection to the end of what we just inserted:
if (lastInsertNode) {
// set selection to the end of what we just pasted.
- nsCOMPtr<nsIDOMNode> selNode, tmp, highTable;
+ nsCOMPtr<nsINode> selNode;
int32_t selOffset;
// but don't cross tables
if (!HTMLEditUtils::IsTable(lastInsertNode)) {
- nsCOMPtr<nsINode> lastInsertNode_ = do_QueryInterface(lastInsertNode);
- NS_ENSURE_STATE(lastInsertNode_ || !lastInsertNode);
- selNode = GetAsDOMNode(GetLastEditableLeaf(*lastInsertNode_));
- tmp = selNode;
- while (tmp && tmp != lastInsertNode) {
- if (HTMLEditUtils::IsTable(tmp)) {
- highTable = tmp;
+ selNode = GetLastEditableLeaf(*lastInsertNode);
+ nsINode* highTable = nullptr;
+ for (nsINode* parent = selNode;
+ parent && parent != lastInsertNode;
+ parent = parent->GetParentNode()) {
+ if (HTMLEditUtils::IsTable(parent)) {
+ highTable = parent;
}
- nsCOMPtr<nsIDOMNode> parent = tmp;
- tmp->GetParentNode(getter_AddRefs(parent));
- tmp = parent;
}
if (highTable) {
selNode = highTable;
}
}
if (!selNode) {
selNode = lastInsertNode;
}
- if (IsTextNode(selNode) ||
+ if (EditorBase::IsTextNode(selNode) ||
(IsContainer(selNode) && !HTMLEditUtils::IsTable(selNode))) {
- rv = GetLengthOfDOMNode(selNode, (uint32_t&)selOffset);
- NS_ENSURE_SUCCESS(rv, rv);
+ selOffset = selNode->Length();
} else {
// We need to find a container for selection. Look up.
- tmp = selNode;
- selNode = GetNodeLocation(tmp, &selOffset);
- // selNode might be null in case a mutation listener removed
+ EditorRawDOMPoint pointAtContainer(selNode);
+ if (NS_WARN_IF(!pointAtContainer.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
+ // The container might be null in case a mutation listener removed
// the stuff we just inserted from the DOM.
- NS_ENSURE_STATE(selNode);
- ++selOffset; // want to be *after* last leaf node in paste
+ selNode = pointAtContainer.Container();
+ // Want to be *after* last leaf node in paste.
+ selOffset = pointAtContainer.Offset() + 1;
}
// make sure we don't end up with selection collapsed after an invisible break node
WSRunObject wsRunObj(this, selNode, selOffset);
nsCOMPtr<nsINode> visNode;
int32_t outVisOffset=0;
WSType visType;
- nsCOMPtr<nsINode> selNode_(do_QueryInterface(selNode));
- wsRunObj.PriorVisibleNode(selNode_, selOffset, address_of(visNode),
+ wsRunObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
&outVisOffset, &visType);
if (visType == WSType::br) {
// we are after a break. Is it visible? Despite the name,
// PriorVisibleNode does not make that determination for breaks.
// It also may not return the break in visNode. We have to pull it
// out of the WSRunObject's state.
if (!IsVisibleBRElement(wsRunObj.mStartReasonNode)) {
// don't leave selection past an invisible break;
// reset {selNode,selOffset} to point before break
- selNode = GetNodeLocation(GetAsDOMNode(wsRunObj.mStartReasonNode), &selOffset);
+ EditorRawDOMPoint atStartReasonNode(wsRunObj.mStartReasonNode);
+ selNode = atStartReasonNode.Container();
+ selOffset = atStartReasonNode.Offset();
// we want to be inside any inline style prior to break
WSRunObject wsRunObj(this, selNode, selOffset);
- selNode_ = do_QueryInterface(selNode);
- wsRunObj.PriorVisibleNode(selNode_, selOffset, address_of(visNode),
+ wsRunObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
&outVisOffset, &visType);
if (visType == WSType::text || visType == WSType::normalWS) {
- selNode = GetAsDOMNode(visNode);
+ selNode = visNode;
selOffset = outVisOffset; // PriorVisibleNode already set offset to _after_ the text or ws
} else if (visType == WSType::special) {
// prior visible thing is an image or some other non-text thingy.
// We want to be right after it.
- selNode = GetNodeLocation(GetAsDOMNode(wsRunObj.mStartReasonNode), &selOffset);
- ++selOffset;
+ atStartReasonNode.Set(wsRunObj.mStartReasonNode);
+ selNode = atStartReasonNode.Container();
+ selOffset = atStartReasonNode.Offset() + 1;
}
}
}
selection->Collapse(selNode, selOffset);
// if we just pasted a link, discontinue link style
nsCOMPtr<nsIDOMNode> link;
- if (!bStartedInLink && IsInLink(selNode, address_of(link))) {
+ if (!bStartedInLink &&
+ IsInLink(GetAsDOMNode(selNode), address_of(link))) {
// so, if we just pasted a link, I split it. Why do that instead of just
// nudging selection point beyond it? Because it might have ended in a BR
// that is not visible. If so, the code above just placed selection
// inside that. So I split it instead.
nsCOMPtr<nsIContent> linkContent = do_QueryInterface(link);
NS_ENSURE_STATE(linkContent || !link);
- nsCOMPtr<nsIContent> selContent = do_QueryInterface(selNode);
- NS_ENSURE_STATE(selContent || !selNode);
SplitNodeResult splitLinkResult =
- SplitNodeDeep(*linkContent, EditorRawDOMPoint(selContent, selOffset),
+ SplitNodeDeep(*linkContent, EditorRawDOMPoint(selNode, selOffset),
SplitAtEdges::eDoNotCreateEmptyContainer);
NS_WARNING_ASSERTION(splitLinkResult.Succeeded(),
"Failed to split the link");
if (splitLinkResult.GetPreviousNode()) {
EditorRawDOMPoint afterLeftLink(splitLinkResult.GetPreviousNode());
if (afterLeftLink.AdvanceOffset()) {
selection->Collapse(afterLeftLink);
}
--- a/editor/libeditor/TextEditUtils.cpp
+++ b/editor/libeditor/TextEditUtils.cpp
@@ -30,16 +30,23 @@ namespace mozilla {
* IsBody() returns true if aNode is an html body node.
*/
bool
TextEditUtils::IsBody(nsIDOMNode* aNode)
{
return EditorBase::NodeIsType(aNode, nsGkAtoms::body);
}
+bool
+TextEditUtils::IsBody(nsINode* aNode)
+{
+ MOZ_ASSERT(aNode);
+ return aNode->IsHTMLElement(nsGkAtoms::body);
+}
+
/**
* IsBreak() returns true if aNode is an html break node.
*/
bool
TextEditUtils::IsBreak(nsIDOMNode* aNode)
{
return EditorBase::NodeIsType(aNode, nsGkAtoms::br);
}
--- a/editor/libeditor/TextEditUtils.h
+++ b/editor/libeditor/TextEditUtils.h
@@ -15,16 +15,17 @@ namespace mozilla {
class TextEditor;
class TextEditUtils final
{
public:
// from TextEditRules:
static bool IsBody(nsIDOMNode* aNode);
+ static bool IsBody(nsINode* aNode);
static bool IsBreak(nsIDOMNode* aNode);
static bool IsBreak(nsINode* aNode);
static bool IsMozBR(nsIDOMNode* aNode);
static bool IsMozBR(nsINode* aNode);
static bool HasMozAttr(nsIDOMNode* aNode);
};
/***************************************************************************