mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
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:
parent
f6e37d5fe3
commit
36d48397f0
@ -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)
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user