Bug 1415062 - part 2: Editor should use Selection::Collapse(const RawRangeBoundary&) as far as possible r=m_kato

In some places, editor computes index from child node for collapsing selection
at the child node.  However, it's expensive.  Therefore, editor should use
Selection::Collapse(const RawRangeBoundary&) as far as possible.

MozReview-Commit-ID: LF2MwASuXzZ

--HG--
extra : rebase_source : b7afc35c0d9d88845391b6f18de57cbff1935ae4
This commit is contained in:
Masayuki Nakano 2017-11-07 19:50:25 +09:00
parent 898bbb3137
commit 806668b21a
10 changed files with 191 additions and 112 deletions

View File

@ -216,9 +216,13 @@ public:
mOffset = mozilla::Some(aOffset); mOffset = mozilla::Some(aOffset);
} }
void void
Set(const nsIContent* aChild) Set(const nsINode* aChild)
{ {
MOZ_ASSERT(aChild); MOZ_ASSERT(aChild);
if (!aChild->IsContent()) {
Clear();
return;
}
mParent = aChild->GetParentNode(); mParent = aChild->GetParentNode();
mRef = aChild->GetPreviousSibling(); mRef = aChild->GetPreviousSibling();
if (!mRef) { if (!mRef) {
@ -246,12 +250,14 @@ public:
* If the container can have children and there is no next sibling, this * If the container can have children and there is no next sibling, this
* outputs warning and does nothing. So, callers need to check if there is * outputs warning and does nothing. So, callers need to check if there is
* next sibling which you need to refer. * next sibling which you need to refer.
*
* @return true if there is a next sibling to refer.
*/ */
void bool
AdvanceOffset() AdvanceOffset()
{ {
if (NS_WARN_IF(!mParent)) { if (NS_WARN_IF(!mParent)) {
return; return false;
} }
EnsureRef(); EnsureRef();
if (!mRef) { if (!mRef) {
@ -260,30 +266,31 @@ public:
MOZ_ASSERT(mOffset.isSome()); MOZ_ASSERT(mOffset.isSome());
if (NS_WARN_IF(mOffset.value() == mParent->Length())) { if (NS_WARN_IF(mOffset.value() == mParent->Length())) {
// Already referring the end of the node. // Already referring the end of the node.
return; return false;
} }
mOffset = mozilla::Some(mOffset.value() + 1); mOffset = mozilla::Some(mOffset.value() + 1);
return; return true;
} }
mRef = mParent->GetFirstChild(); mRef = mParent->GetFirstChild();
if (NS_WARN_IF(!mRef)) { if (NS_WARN_IF(!mRef)) {
// No children in the container. // No children in the container.
mOffset = mozilla::Some(0); mOffset = mozilla::Some(0);
} else { return false;
mOffset = mozilla::Some(1);
} }
return; mOffset = mozilla::Some(1);
return true;
} }
nsIContent* nextSibling = mRef->GetNextSibling(); nsIContent* nextSibling = mRef->GetNextSibling();
if (NS_WARN_IF(!nextSibling)) { if (NS_WARN_IF(!nextSibling)) {
// Already referring the end of the container. // Already referring the end of the container.
return; return false;
} }
mRef = nextSibling; mRef = nextSibling;
if (mOffset.isSome()) { if (mOffset.isSome()) {
mOffset = mozilla::Some(mOffset.value() + 1); mOffset = mozilla::Some(mOffset.value() + 1);
} }
return true;
} }
/** /**
@ -293,34 +300,37 @@ public:
* If the container can have children and there is no next previous, this * If the container can have children and there is no next previous, this
* outputs warning and does nothing. So, callers need to check if there is * outputs warning and does nothing. So, callers need to check if there is
* previous sibling which you need to refer. * previous sibling which you need to refer.
*
* @return true if there is a previous sibling to refer.
*/ */
void bool
RewindOffset() RewindOffset()
{ {
if (NS_WARN_IF(!mParent)) { if (NS_WARN_IF(!mParent)) {
return; return false;
} }
EnsureRef(); EnsureRef();
if (!mRef) { if (!mRef) {
if (NS_WARN_IF(mParent->IsContainerNode())) { if (NS_WARN_IF(mParent->IsContainerNode())) {
// Already referring the start of the container // Already referring the start of the container
mOffset = mozilla::Some(0); mOffset = mozilla::Some(0);
return; return false;
} }
// In text node or something, just decrement the offset. // In text node or something, just decrement the offset.
MOZ_ASSERT(mOffset.isSome()); MOZ_ASSERT(mOffset.isSome());
if (NS_WARN_IF(mOffset.value() == 0)) { if (NS_WARN_IF(mOffset.value() == 0)) {
// Already referring the start of the node. // Already referring the start of the node.
return; return false;
} }
mOffset = mozilla::Some(mOffset.value() - 1); mOffset = mozilla::Some(mOffset.value() - 1);
return; return true;
} }
mRef = mRef->GetPreviousSibling(); mRef = mRef->GetPreviousSibling();
if (mOffset.isSome()) { if (mOffset.isSome()) {
mOffset = mozilla::Some(mOffset.value() - 1); mOffset = mozilla::Some(mOffset.value() - 1);
} }
return true;
} }
void void

View File

@ -13,6 +13,7 @@
#include "mozilla/Casting.h" #include "mozilla/Casting.h"
#include "mozilla/EditorBase.h" #include "mozilla/EditorBase.h"
#include "mozilla/EditorDOMPoint.h"
#include "nsAlgorithm.h" #include "nsAlgorithm.h"
#include "nsAString.h" #include "nsAString.h"
@ -103,7 +104,14 @@ CreateElementTransaction::DoTransaction()
RefPtr<Selection> selection = mEditorBase->GetSelection(); RefPtr<Selection> selection = mEditorBase->GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
rv = selection->Collapse(mParent, mParent->IndexOf(mNewNode) + 1); EditorRawDOMPoint afterNewNode(mNewNode);
if (NS_WARN_IF(!afterNewNode.AdvanceOffset())) {
// If mutation observer or mutation event listener moved or removed the
// new node, we hit this case. Should we use script blocker while we're
// in this method?
return NS_ERROR_FAILURE;
}
rv = selection->Collapse(afterNewNode);
NS_ASSERTION(!rv.Failed(), NS_ASSERTION(!rv.Failed(),
"selection could not be collapsed after insert"); "selection could not be collapsed after insert");
return NS_OK; return NS_OK;

View File

@ -4278,22 +4278,30 @@ EditorBase::DeleteSelectionAndPrepareToCreateNode()
uint32_t offset = selection->AnchorOffset(); uint32_t offset = selection->AnchorOffset();
if (!offset) { if (!offset) {
nsresult rv = selection->Collapse(node->GetParentNode(), EditorRawDOMPoint atNode(node);
node->GetParentNode()->IndexOf(node)); if (NS_WARN_IF(!atNode.IsSetAndValid())) {
return NS_ERROR_FAILURE;
}
nsresult rv = selection->Collapse(atNode);
MOZ_ASSERT(NS_SUCCEEDED(rv)); MOZ_ASSERT(NS_SUCCEEDED(rv));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else if (offset == node->Length()) { } else if (offset == node->Length()) {
nsresult rv = EditorRawDOMPoint afterNode(node);
selection->Collapse(node->GetParentNode(), if (NS_WARN_IF(!afterNode.AdvanceOffset())) {
node->GetParentNode()->IndexOf(node) + 1); return NS_ERROR_FAILURE;
}
nsresult rv = selection->Collapse(afterNode);
MOZ_ASSERT(NS_SUCCEEDED(rv)); MOZ_ASSERT(NS_SUCCEEDED(rv));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else { } else {
nsCOMPtr<nsIDOMNode> tmp; nsCOMPtr<nsIDOMNode> tmp;
nsresult rv = SplitNode(node->AsDOMNode(), offset, getter_AddRefs(tmp)); nsresult rv = SplitNode(node->AsDOMNode(), offset, getter_AddRefs(tmp));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
rv = selection->Collapse(node->GetParentNode(), EditorRawDOMPoint atNode(node);
node->GetParentNode()->IndexOf(node)); if (NS_WARN_IF(!atNode.IsSetAndValid())) {
return NS_ERROR_FAILURE;
}
rv = selection->Collapse(atNode);
MOZ_ASSERT(NS_SUCCEEDED(rv)); MOZ_ASSERT(NS_SUCCEEDED(rv));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }

View File

@ -49,15 +49,17 @@ public:
} }
/** /**
* Different from RangeBoundary, aReferenceChild should be a child node * Different from RangeBoundary, aPointedNode should be a child node
* which you want to refer. So, set non-nullptr if offset is * which you want to refer. So, set non-nullptr if offset is
* 0 - Length() - 1. Otherwise, set nullptr, i.e., if offset is same as * 0 - Length() - 1. Otherwise, set nullptr, i.e., if offset is same as
* Length(). * Length().
*/ */
EditorDOMPointBase(nsINode* aContainer, explicit EditorDOMPointBase(nsINode* aPointedNode)
nsIContent* aPointedNode) : RangeBoundaryBase<ParentType, RefType>(
: RangeBoundaryBase<ParentType, RefType>(aContainer, aPointedNode && aPointedNode->IsContent() ?
GetRef(aPointedNode)) aPointedNode->GetParentNode() : nullptr,
aPointedNode && aPointedNode->IsContent() ?
GetRef(aPointedNode->AsContent()) : nullptr)
{ {
} }

View File

@ -1265,9 +1265,8 @@ HTMLEditRules::WillInsert(Selection& aSelection,
// If we are here then the selection is right after a mozBR that is in // If we are here then the selection is right after a mozBR that is in
// the same block as the selection. We need to move the selection start // the same block as the selection. We need to move the selection start
// to be before the mozBR. // to be before the mozBR.
selNode = priorNode->GetParentNode(); EditorRawDOMPoint point(priorNode);
selOffset = selNode->IndexOf(priorNode); nsresult rv = aSelection.Collapse(point.AsRaw());
nsresult rv = aSelection.Collapse(selNode, selOffset);
NS_ENSURE_SUCCESS_VOID(rv); NS_ENSURE_SUCCESS_VOID(rv);
} }
} }
@ -1493,7 +1492,9 @@ HTMLEditRules::WillInsertText(EditAction aAction,
} }
} }
aSelection->SetInterlinePosition(false); aSelection->SetInterlinePosition(false);
if (curNode) aSelection->Collapse(curNode, curOffset); if (curNode) {
aSelection->Collapse(curNode, curOffset);
}
// manually update the doc changed range so that AfterEdit will clean up // manually update the doc changed range so that AfterEdit will clean up
// the correct portion of the document. // the correct portion of the document.
if (!mDocChangeRange) { if (!mDocChangeRange) {
@ -1802,15 +1803,16 @@ HTMLEditRules::StandardBreakImpl(nsINode& aNode,
} }
node = brNode->GetParentNode(); node = brNode->GetParentNode();
NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
int32_t offset = node->IndexOf(brNode);
if (bAfterBlock && bBeforeBlock) { if (bAfterBlock && bBeforeBlock) {
// We just placed a br between block boundaries. This is the one case // We just placed a br between block boundaries. This is the one case
// where we want the selection to be before the br we just placed, as the // where we want the selection to be before the br we just placed, as the
// br will be on a new line, rather than at end of prior line. // br will be on a new line, rather than at end of prior line.
aSelection.SetInterlinePosition(true); aSelection.SetInterlinePosition(true);
nsresult rv = aSelection.Collapse(node, offset); EditorRawDOMPoint point(brNode);
nsresult rv = aSelection.Collapse(point.AsRaw());
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else { } else {
int32_t offset = node->IndexOf(brNode);
WSRunObject wsObj(htmlEditor, node, offset + 1); WSRunObject wsObj(htmlEditor, node, offset + 1);
nsCOMPtr<nsINode> secondBR; nsCOMPtr<nsINode> secondBR;
int32_t visOffset = 0; int32_t visOffset = 0;
@ -2309,7 +2311,7 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// Fix up selection // Fix up selection
rv = aSelection->Collapse(pt.Container(), pt.Offset()); rv = aSelection->Collapse(pt.AsRaw());
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
rv = InsertBRIfNeeded(aSelection); rv = InsertBRIfNeeded(aSelection);
@ -2380,7 +2382,7 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection,
if (NS_WARN_IF(!newSel.IsSet())) { if (NS_WARN_IF(!newSel.IsSet())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
aSelection->Collapse(newSel.Container(), newSel.Offset()); aSelection->Collapse(newSel.AsRaw());
return NS_OK; return NS_OK;
} }
@ -2572,7 +2574,7 @@ HTMLEditRules::WillDeleteSelection(Selection* aSelection,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// Fix up selection // Fix up selection
rv = aSelection->Collapse(pt.Container(), pt.Offset()); rv = aSelection->Collapse(pt.AsRaw());
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return NS_OK;
} }
@ -2792,7 +2794,7 @@ HTMLEditRules::GetGoodSelPointForNode(nsINode& aNode,
return EditorDOMPoint(); return EditorDOMPoint();
} }
EditorDOMPoint ret(aNode.GetParentNode(), aNode.AsContent()); EditorDOMPoint ret(&aNode);
if ((!aNode.IsHTMLElement(nsGkAtoms::br) || if ((!aNode.IsHTMLElement(nsGkAtoms::br) ||
mHTMLEditor->IsVisibleBRElement(&aNode)) && isPreviousAction) { mHTMLEditor->IsVisibleBRElement(&aNode)) && isPreviousAction) {
ret.AdvanceOffset(); ret.AdvanceOffset();
@ -4552,25 +4554,22 @@ HTMLEditRules::WillOutdent(Selection& aSelection,
NS_ENSURE_TRUE(aSelection.GetRangeAt(0), NS_OK); NS_ENSURE_TRUE(aSelection.GetRangeAt(0), NS_OK);
nsCOMPtr<nsINode> startNode = nsCOMPtr<nsINode> startNode =
aSelection.GetRangeAt(0)->GetStartContainer(); aSelection.GetRangeAt(0)->GetStartContainer();
int32_t startOffset = aSelection.GetRangeAt(0)->StartOffset();
if (rememberedLeftBQ && if (rememberedLeftBQ &&
(startNode == rememberedLeftBQ || (startNode == rememberedLeftBQ ||
EditorUtils::IsDescendantOf(*startNode, *rememberedLeftBQ))) { EditorUtils::IsDescendantOf(*startNode, *rememberedLeftBQ))) {
// Selection is inside rememberedLeftBQ - push it past it. // Selection is inside rememberedLeftBQ - push it past it.
startNode = rememberedLeftBQ->GetParentNode(); EditorRawDOMPoint afterRememberedLeftBQ(rememberedLeftBQ);
startOffset = startNode ? 1 + startNode->IndexOf(rememberedLeftBQ) : 0; afterRememberedLeftBQ.AdvanceOffset();
aSelection.Collapse(startNode, startOffset); aSelection.Collapse(afterRememberedLeftBQ);
} }
// And pull selection before beginning of rememberedRightBQ // And pull selection before beginning of rememberedRightBQ
startNode = aSelection.GetRangeAt(0)->GetStartContainer(); startNode = aSelection.GetRangeAt(0)->GetStartContainer();
startOffset = aSelection.GetRangeAt(0)->StartOffset();
if (rememberedRightBQ && if (rememberedRightBQ &&
(startNode == rememberedRightBQ || (startNode == rememberedRightBQ ||
EditorUtils::IsDescendantOf(*startNode, *rememberedRightBQ))) { EditorUtils::IsDescendantOf(*startNode, *rememberedRightBQ))) {
// Selection is inside rememberedRightBQ - push it before it. // Selection is inside rememberedRightBQ - push it before it.
startNode = rememberedRightBQ->GetParentNode(); EditorRawDOMPoint atRememberedRightBQ(rememberedRightBQ);
startOffset = startNode ? startNode->IndexOf(rememberedRightBQ) : -1; aSelection.Collapse(atRememberedRightBQ);
aSelection.Collapse(startNode, startOffset);
} }
} }
return NS_OK; return NS_OK;
@ -5188,37 +5187,45 @@ HTMLEditRules::CheckForEmptyBlock(nsINode* aStartNode,
// AfterEdit() // AfterEdit()
} }
} else { } else {
int32_t offset = blockParent->IndexOf(emptyBlock);
if (aAction == nsIEditor::eNext || aAction == nsIEditor::eNextWord || if (aAction == nsIEditor::eNext || aAction == nsIEditor::eNextWord ||
aAction == nsIEditor::eToEndOfLine) { aAction == nsIEditor::eToEndOfLine) {
// Move to the start of the next node, if any // Move to the start of the next node, if any
nsINode* child = emptyBlock->GetNextSibling(); nsINode* child = emptyBlock->GetNextSibling();
int32_t offset = blockParent->IndexOf(emptyBlock);
nsCOMPtr<nsIContent> nextNode = nsCOMPtr<nsIContent> nextNode =
htmlEditor->GetNextNode(blockParent, offset + 1, child, true); htmlEditor->GetNextNode(blockParent, offset + 1, child, true);
if (nextNode) { if (nextNode) {
EditorDOMPoint pt = GetGoodSelPointForNode(*nextNode, aAction); EditorDOMPoint pt = GetGoodSelPointForNode(*nextNode, aAction);
nsresult rv = aSelection->Collapse(pt.Container(), pt.Offset()); nsresult rv = aSelection->Collapse(pt.AsRaw());
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else { } else {
// Adjust selection to be right after it. // Adjust selection to be right after it.
nsresult rv = aSelection->Collapse(blockParent, offset + 1); EditorRawDOMPoint afterEmptyBlock(emptyBlock);
if (NS_WARN_IF(!afterEmptyBlock.AdvanceOffset())) {
return NS_ERROR_FAILURE;
}
nsresult rv = aSelection->Collapse(afterEmptyBlock);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
} else if (aAction == nsIEditor::ePrevious || } else if (aAction == nsIEditor::ePrevious ||
aAction == nsIEditor::ePreviousWord || aAction == nsIEditor::ePreviousWord ||
aAction == nsIEditor::eToBeginningOfLine) { aAction == nsIEditor::eToBeginningOfLine) {
// Move to the end of the previous node // Move to the end of the previous node
int32_t offset = blockParent->IndexOf(emptyBlock);
nsCOMPtr<nsIContent> priorNode = htmlEditor->GetPriorNode(blockParent, nsCOMPtr<nsIContent> priorNode = htmlEditor->GetPriorNode(blockParent,
offset, offset,
emptyBlock, emptyBlock,
true); true);
if (priorNode) { if (priorNode) {
EditorDOMPoint pt = GetGoodSelPointForNode(*priorNode, aAction); EditorDOMPoint pt = GetGoodSelPointForNode(*priorNode, aAction);
nsresult rv = aSelection->Collapse(pt.Container(), pt.Offset()); nsresult rv = aSelection->Collapse(pt.AsRaw());
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else { } else {
nsresult rv = aSelection->Collapse(blockParent, offset + 1); EditorRawDOMPoint afterEmptyBlock(emptyBlock);
if (NS_WARN_IF(!afterEmptyBlock.AdvanceOffset())) {
return NS_ERROR_FAILURE;
}
nsresult rv = aSelection->Collapse(afterEmptyBlock);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
} else if (aAction != nsIEditor::eNone) { } else if (aAction != nsIEditor::eNone) {
@ -6625,10 +6632,12 @@ HTMLEditRules::ReturnInHeader(Selection& aSelection,
rv = aSelection.Collapse(pNode, 0); rv = aSelection.Collapse(pNode, 0);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else { } else {
headerParent = sibling->GetParentNode(); EditorRawDOMPoint afterSibling(sibling);
offset = headerParent ? headerParent->IndexOf(sibling) : -1; if (NS_WARN_IF(!afterSibling.AdvanceOffset())) {
return NS_ERROR_FAILURE;
}
// Put selection after break // Put selection after break
rv = aSelection.Collapse(headerParent, offset + 1); rv = aSelection.Collapse(afterSibling);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
} else { } else {
@ -6825,15 +6834,13 @@ HTMLEditRules::SplitParagraph(nsIDOMNode *aPara,
// look inside any containers that are up front. // look inside any containers that are up front.
nsCOMPtr<nsINode> rightParaNode = do_QueryInterface(rightPara); nsCOMPtr<nsINode> rightParaNode = do_QueryInterface(rightPara);
NS_ENSURE_STATE(mHTMLEditor && rightParaNode); NS_ENSURE_STATE(mHTMLEditor && rightParaNode);
nsCOMPtr<nsIDOMNode> child = nsIContent* child = mHTMLEditor->GetLeftmostChild(rightParaNode, true);
GetAsDOMNode(mHTMLEditor->GetLeftmostChild(rightParaNode, true));
if (EditorBase::IsTextNode(child) || if (EditorBase::IsTextNode(child) ||
mHTMLEditor->IsContainer(child)) { mHTMLEditor->IsContainer(child)) {
aSelection->Collapse(child,0); aSelection->Collapse(child,0);
} else { } else {
int32_t offset; EditorRawDOMPoint atChild(child);
nsCOMPtr<nsIDOMNode> parent = EditorBase::GetNodeLocation(child, &offset); aSelection->Collapse(atChild);
aSelection->Collapse(parent,offset);
} }
return NS_OK; return NS_OK;
} }
@ -6959,9 +6966,11 @@ HTMLEditRules::ReturnInListItem(Selection& aSelection,
getter_AddRefs(brNode)); getter_AddRefs(brNode));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (brNode) { if (brNode) {
nsCOMPtr<nsINode> brParent = brNode->GetParentNode(); EditorRawDOMPoint atBrNode(brNode);
int32_t offset = brParent ? brParent->IndexOf(brNode) : -1; if (NS_WARN_IF(!atBrNode.IsSetAndValid())) {
rv = aSelection.Collapse(brParent, offset); return NS_ERROR_FAILURE;
}
rv = aSelection.Collapse(atBrNode);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return NS_OK;
} }
@ -6974,9 +6983,11 @@ HTMLEditRules::ReturnInListItem(Selection& aSelection,
&visOffset, &wsType); &visOffset, &wsType);
if (wsType == WSType::special || wsType == WSType::br || if (wsType == WSType::special || wsType == WSType::br ||
visNode->IsHTMLElement(nsGkAtoms::hr)) { visNode->IsHTMLElement(nsGkAtoms::hr)) {
nsCOMPtr<nsINode> parent = visNode->GetParentNode(); EditorRawDOMPoint atVisNode(visNode);
int32_t offset = parent ? parent->IndexOf(visNode) : -1; if (NS_WARN_IF(!atVisNode.IsSetAndValid())) {
rv = aSelection.Collapse(parent, offset); return NS_ERROR_FAILURE;
}
rv = aSelection.Collapse(atVisNode);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return NS_OK;
} else { } else {
@ -7681,36 +7692,42 @@ HTMLEditRules::PinSelectionToNewBlock(Selection* aSelection)
if (nodeBefore && nodeAfter) { if (nodeBefore && nodeAfter) {
return NS_OK; // selection is inside block return NS_OK; // selection is inside block
} else if (nodeBefore) { }
if (nodeBefore) {
// selection is after block. put at end of block. // selection is after block. put at end of block.
NS_ENSURE_STATE(mHTMLEditor); NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsINode> tmp = mHTMLEditor->GetLastEditableChild(*mNewBlock); nsCOMPtr<nsINode> tmp = mHTMLEditor->GetLastEditableChild(*mNewBlock);
if (!tmp) { if (!tmp) {
tmp = mNewBlock; tmp = mNewBlock;
} }
uint32_t endPoint; EditorRawDOMPoint endPoint;
if (EditorBase::IsTextNode(tmp) || if (EditorBase::IsTextNode(tmp) ||
mHTMLEditor->IsContainer(tmp)) { mHTMLEditor->IsContainer(tmp)) {
endPoint = tmp->Length(); endPoint.Set(tmp, tmp->Length());
} else { } else {
tmp = EditorBase::GetNodeLocation(tmp, (int32_t*)&endPoint); endPoint.Set(tmp);
endPoint++; // want to be after this node if (NS_WARN_IF(!endPoint.AdvanceOffset())) {
return NS_ERROR_FAILURE;
}
} }
return aSelection->Collapse(tmp, (int32_t)endPoint); return aSelection->Collapse(endPoint);
} else {
// selection is before block. put at start of block.
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsINode> tmp = mHTMLEditor->GetFirstEditableChild(*mNewBlock);
if (!tmp) {
tmp = mNewBlock;
}
int32_t offset;
if (EditorBase::IsTextNode(tmp) ||
mHTMLEditor->IsContainer(tmp)) {
tmp = EditorBase::GetNodeLocation(tmp, &offset);
}
return aSelection->Collapse(tmp, 0);
} }
// selection is before block. put at start of block.
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsINode> tmp = mHTMLEditor->GetFirstEditableChild(*mNewBlock);
if (!tmp) {
tmp = mNewBlock;
}
EditorRawDOMPoint atStartOfBlock;
if (EditorBase::IsTextNode(tmp) ||
mHTMLEditor->IsContainer(tmp)) {
atStartOfBlock.Set(tmp);
} else {
atStartOfBlock.Set(tmp, 0);
}
return aSelection->Collapse(atStartOfBlock);
} }
void void
@ -7904,7 +7921,7 @@ HTMLEditRules::AdjustSelection(Selection* aSelection,
return NS_OK; return NS_OK;
} }
EditorDOMPoint pt = GetGoodSelPointForNode(*nearNode, aAction); EditorDOMPoint pt = GetGoodSelPointForNode(*nearNode, aAction);
rv = aSelection->Collapse(pt.Container(), pt.Offset()); rv = aSelection->Collapse(pt.AsRaw());
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }

View File

@ -1086,10 +1086,18 @@ HTMLEditor::InsertBR(nsCOMPtr<nsIDOMNode>* outBRNode)
rv = CreateBR(selNode, selOffset, outBRNode); rv = CreateBR(selNode, selOffset, outBRNode);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// position selection after br
selNode = GetNodeLocation(*outBRNode, &selOffset);
selection->SetInterlinePosition(true); selection->SetInterlinePosition(true);
return selection->Collapse(selNode, selOffset+1);
// position selection after br
nsCOMPtr<nsINode> brNode = do_QueryInterface(*outBRNode);
if (NS_WARN_IF(!brNode)) {
return NS_ERROR_FAILURE;
}
EditorRawDOMPoint afterBrNode(brNode);
if (NS_WARN_IF(!afterBrNode.AdvanceOffset())) {
return NS_ERROR_FAILURE;
}
return selection->Collapse(afterBrNode);
} }
void void
@ -1692,9 +1700,12 @@ HTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement)
nsresult rv = aElement->GetParentNode(getter_AddRefs(parent)); nsresult rv = aElement->GetParentNode(getter_AddRefs(parent));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER); NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
int32_t offsetInParent = GetChildOffset(aElement, parent);
// Collapse selection to just after desired element, // Collapse selection to just after desired element,
return selection->Collapse(parent, offsetInParent + 1); EditorRawDOMPoint afterElement(element);
if (NS_WARN_IF(!afterElement.AdvanceOffset())) {
return NS_ERROR_FAILURE;
}
return selection->Collapse(afterElement);
} }
NS_IMETHODIMP NS_IMETHODIMP

View File

@ -18,6 +18,7 @@
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "mozilla/Base64.h" #include "mozilla/Base64.h"
#include "mozilla/BasicEvents.h" #include "mozilla/BasicEvents.h"
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EditorUtils.h" #include "mozilla/EditorUtils.h"
#include "mozilla/OwningNonNull.h" #include "mozilla/OwningNonNull.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
@ -657,8 +658,10 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
SplitNodeDeep(*linkContent, *selContent, selOffset, SplitNodeDeep(*linkContent, *selContent, selOffset,
EmptyContainers::no, getter_AddRefs(leftLink)); EmptyContainers::no, getter_AddRefs(leftLink));
if (leftLink) { if (leftLink) {
selNode = GetNodeLocation(GetAsDOMNode(leftLink), &selOffset); EditorRawDOMPoint afterLeftLink(leftLink);
selection->Collapse(selNode, selOffset+1); if (afterLeftLink.AdvanceOffset()) {
selection->Collapse(afterLeftLink);
}
} }
} }
} }
@ -1898,10 +1901,9 @@ HTMLEditor::InsertAsPlaintextQuotation(const nsAString& aQuotedText,
// Set the selection to just after the inserted node: // Set the selection to just after the inserted node:
if (NS_SUCCEEDED(rv) && newNode) { if (NS_SUCCEEDED(rv) && newNode) {
nsCOMPtr<nsINode> parent = newNode->GetParentNode(); EditorRawDOMPoint afterNewNode(newNode);
int32_t offset = parent ? parent->IndexOf(newNode) : -1; if (afterNewNode.AdvanceOffset()) {
if (parent) { selection->Collapse(afterNewNode);
selection->Collapse(parent, offset + 1);
} }
} }
return rv; return rv;
@ -1978,10 +1980,9 @@ HTMLEditor::InsertAsCitedQuotation(const nsAString& aQuotedText,
// Set the selection to just after the inserted node: // Set the selection to just after the inserted node:
if (NS_SUCCEEDED(rv) && newNode) { if (NS_SUCCEEDED(rv) && newNode) {
nsCOMPtr<nsINode> parent = newNode->GetParentNode(); EditorRawDOMPoint afterNewNode(newNode);
int32_t offset = parent ? parent->IndexOf(newNode) : -1; if (afterNewNode.AdvanceOffset()) {
if (parent) { selection->Collapse(afterNewNode);
selection->Collapse(parent, offset + 1);
} }
} }
return rv; return rv;

View File

@ -9,6 +9,7 @@
#include "HTMLEditUtils.h" #include "HTMLEditUtils.h"
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EditorUtils.h" #include "mozilla/EditorUtils.h"
#include "mozilla/FlushType.h" #include "mozilla/FlushType.h"
#include "mozilla/dom/Selection.h" #include "mozilla/dom/Selection.h"
@ -3157,8 +3158,15 @@ HTMLEditor::SetSelectionAfterTableEdit(nsIDOMElement* aTable,
nsCOMPtr<nsIDOMNode> tableParent; nsCOMPtr<nsIDOMNode> tableParent;
nsresult rv = aTable->GetParentNode(getter_AddRefs(tableParent)); nsresult rv = aTable->GetParentNode(getter_AddRefs(tableParent));
if (NS_SUCCEEDED(rv) && tableParent) { if (NS_SUCCEEDED(rv) && tableParent) {
int32_t tableOffset = GetChildOffset(aTable, tableParent); nsCOMPtr<nsIContent> table = do_QueryInterface(aTable);
selection->Collapse(tableParent, tableOffset); if (NS_WARN_IF(!table)) {
return;
}
EditorRawDOMPoint atTable(table);
if (NS_WARN_IF(!atTable.IsSetAndValid())) {
return;
}
selection->Collapse(atTable);
return; return;
} }
// Last resort: Set selection to start of doc // Last resort: Set selection to start of doc

View File

@ -7,6 +7,7 @@
#include "TextEditUtils.h" #include "TextEditUtils.h"
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EditorUtils.h" #include "mozilla/EditorUtils.h"
#include "mozilla/LookAndFeel.h" #include "mozilla/LookAndFeel.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
@ -494,8 +495,11 @@ TextEditRules::CollapseSelectionToTrailingBRIfNeeded(Selection* aSelection)
nsINode* nextNode = selNode->GetNextSibling(); nsINode* nextNode = selNode->GetNextSibling();
if (nextNode && TextEditUtils::IsMozBR(nextNode)) { if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
int32_t offsetInParent = EditorBase::GetChildOffset(selNode, parentNode); EditorRawDOMPoint afterSelNode(selNode);
rv = aSelection->Collapse(parentNode, offsetInParent + 1); if (NS_WARN_IF(!afterSelNode.AdvanceOffset())) {
return NS_ERROR_FAILURE;
}
rv = aSelection->Collapse(afterSelNode);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -778,6 +782,8 @@ TextEditRules::WillInsertText(EditAction aAction,
!outString->IsEmpty() && outString->Last() == nsCRT::LF; !outString->IsEmpty() && outString->Last() == nsCRT::LF;
aSelection->SetInterlinePosition(endsWithLF); aSelection->SetInterlinePosition(endsWithLF);
MOZ_ASSERT(!selChild,
"After inserting text into a text node, selChild should be nullptr");
aSelection->Collapse(curNode, curOffset); aSelection->Collapse(curNode, curOffset);
} }
} }

View File

@ -9,6 +9,7 @@
#include "TextEditUtils.h" #include "TextEditUtils.h"
#include "gfxFontUtils.h" #include "gfxFontUtils.h"
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EditorUtils.h" // AutoPlaceholderBatch, AutoRules #include "mozilla/EditorUtils.h" // AutoPlaceholderBatch, AutoRules
#include "mozilla/HTMLEditor.h" #include "mozilla/HTMLEditor.h"
#include "mozilla/mozalloc.h" #include "mozilla/mozalloc.h"
@ -476,19 +477,24 @@ TextEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent,
*outBRNode = GetAsDOMNode(brNode); *outBRNode = GetAsDOMNode(brNode);
if (*outBRNode && (aSelect != eNone)) { if (*outBRNode && (aSelect != eNone)) {
int32_t offset;
nsCOMPtr<nsINode> parent = GetNodeLocation(brNode, &offset);
RefPtr<Selection> selection = GetSelection(); RefPtr<Selection> selection = GetSelection();
NS_ENSURE_STATE(selection); NS_ENSURE_STATE(selection);
if (aSelect == eNext) { if (aSelect == eNext) {
selection->SetInterlinePosition(true);
// position selection after br // position selection after br
selection->SetInterlinePosition(true); EditorRawDOMPoint afterBrNode(brNode);
selection->Collapse(parent, offset + 1); if (NS_WARN_IF(!afterBrNode.AdvanceOffset())) {
return NS_OK;
}
selection->Collapse(afterBrNode);
} else if (aSelect == ePrevious) { } else if (aSelect == ePrevious) {
// position selection before br
selection->SetInterlinePosition(true); selection->SetInterlinePosition(true);
selection->Collapse(parent, offset); // position selection before br
EditorRawDOMPoint atBrNode(brNode);
if (NS_WARN_IF(!atBrNode.IsSetAndValid())) {
return NS_OK;
}
selection->Collapse(atBrNode);
} }
} }
return NS_OK; return NS_OK;
@ -729,7 +735,9 @@ TextEditor::InsertLineBreak()
} }
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
// set the selection to the correct location // set the selection to the correct location
rv = selection->Collapse(selNode, selOffset); MOZ_ASSERT(!selChild,
"After inserting text into a text node, selChild should be nullptr");
rv = selection->Collapse(EditorRawDOMPoint(selNode, selOffset));
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
// see if we're at the end of the editor range // see if we're at the end of the editor range
nsCOMPtr<nsIDOMNode> endNode; nsCOMPtr<nsIDOMNode> endNode;