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

View File

@ -13,6 +13,7 @@
#include "mozilla/Casting.h"
#include "mozilla/EditorBase.h"
#include "mozilla/EditorDOMPoint.h"
#include "nsAlgorithm.h"
#include "nsAString.h"
@ -103,7 +104,14 @@ CreateElementTransaction::DoTransaction()
RefPtr<Selection> selection = mEditorBase->GetSelection();
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(),
"selection could not be collapsed after insert");
return NS_OK;

View File

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

View File

@ -1086,10 +1086,18 @@ HTMLEditor::InsertBR(nsCOMPtr<nsIDOMNode>* outBRNode)
rv = CreateBR(selNode, selOffset, outBRNode);
NS_ENSURE_SUCCESS(rv, rv);
// position selection after br
selNode = GetNodeLocation(*outBRNode, &selOffset);
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
@ -1692,9 +1700,12 @@ HTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement)
nsresult rv = aElement->GetParentNode(getter_AddRefs(parent));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
int32_t offsetInParent = GetChildOffset(aElement, parent);
// 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

View File

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

View File

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

View File

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

View File

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