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

--HG--
extra : rebase_source : 57033cdc4458b45a5fe9d6aa642c185133a79304
This commit is contained in:
Masayuki Nakano 2017-11-28 22:28:07 +09:00
parent f6e37d5fe3
commit 36d48397f0
8 changed files with 243 additions and 208 deletions

View File

@ -119,6 +119,16 @@ public:
"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)

View File

@ -224,6 +224,12 @@ 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.
*/
@ -309,6 +315,12 @@ 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.
*/

View File

@ -29,6 +29,7 @@ public:
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);
@ -42,6 +43,7 @@ public:
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);

View File

@ -1499,10 +1499,13 @@ HTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement,
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)) {
@ -1513,12 +1516,12 @@ HTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement,
// 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;
}
@ -1529,97 +1532,71 @@ HTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement,
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());
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)) {
// 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 (parent->IsHTMLElement(nsGkAtoms::body) ||
HTMLEditUtils::IsTableElement(parent)) {
return NS_ERROR_FAILURE;
// then go no further - we can't insert.
if (pointToInsert.Container()->IsHTMLElement(nsGkAtoms::body) ||
HTMLEditUtils::IsTableElement(pointToInsert.Container())) {
return EditorDOMPoint();
}
// Get the next parent
NS_ENSURE_TRUE(parent->GetParentNode(), NS_ERROR_FAILURE);
if (!IsEditable(parent->GetParentNode())) {
// Get the next point.
pointToInsert.Set(pointToInsert.Container());
if (NS_WARN_IF(!pointToInsert.IsSet())) {
return EditorDOMPoint();
}
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

View File

@ -338,11 +338,27 @@ public:
// 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

View File

@ -430,7 +430,7 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
}
// 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)) {
@ -439,23 +439,21 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
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;
}
}
@ -463,25 +461,27 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
// 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
@ -489,121 +489,129 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
// 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;
}
nsCOMPtr<nsINode> oldParent = content->GetParentNode();
insertedPoint =
InsertNodeIntoProperAncestor(
*content->GetParent(), pointToInsert.AsRaw(),
SplitAtEdges::eDoNotCreateEmptyContainer);
if (insertedPoint.IsSet()) {
insertedContextParent = oldParent;
pointToInsert = insertedPoint;
}
curNode = parent;
}
}
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;
@ -612,18 +620,20 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
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
@ -631,8 +641,7 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
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,
@ -642,20 +651,22 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
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;
}
}
}
@ -663,17 +674,16 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
// 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");

View File

@ -35,6 +35,13 @@ 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.
*/

View File

@ -20,6 +20,7 @@ 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);