diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 0256cc9d544f..9559ad01f170 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -2384,23 +2384,18 @@ EditorBase::ScrollSelectionIntoView(bool aScrollToAnchor) return NS_OK; } -void -EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, - int32_t& aOffset) +EditorRawDOMPoint +EditorBase::FindBetterInsertionPoint(const EditorRawDOMPoint& aPoint) { - nsCOMPtr node = do_QueryInterface(aNode); - FindBetterInsertionPoint(node, aOffset, nullptr); - aNode = do_QueryInterface(node); -} + if (NS_WARN_IF(!aPoint.IsSet())) { + return aPoint; + } -void -EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, - int32_t& aOffset, - nsCOMPtr* aSelChild) -{ - if (aNode->IsNodeOfType(nsINode::eTEXT)) { + MOZ_ASSERT(aPoint.IsSetAndValid()); + + if (aPoint.Container()->IsNodeOfType(nsINode::eTEXT)) { // There is no "better" insertion point. - return; + return aPoint; } if (!IsPlaintextEditor()) { @@ -2408,57 +2403,44 @@ EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, // WARNING: When you add some code to find better node in HTML editor, // you need to call this before calling InsertTextImpl() in // HTMLEditRules. - return; + return aPoint; } - nsCOMPtr node = aNode; - int32_t offset = aOffset; - nsCOMPtr root = GetRoot(); - if (aNode == root) { + if (aPoint.Container() == root) { // In some cases, aNode is the anonymous DIV, and offset is 0. To avoid // injecting unneeded text nodes, we first look to see if we have one // available. In that case, we'll just adjust node and offset accordingly. - if (!offset && node->HasChildren() && - node->GetFirstChild()->IsNodeOfType(nsINode::eTEXT)) { - aNode = node->GetFirstChild(); - aOffset = 0; - if (aSelChild) { - *aSelChild = nullptr; - } - return; + if (aPoint.IsStartOfContainer() && + aPoint.Container()->HasChildren() && + aPoint.Container()->GetFirstChild()->IsNodeOfType(nsINode::eTEXT)) { + return EditorRawDOMPoint(aPoint.Container()->GetFirstChild(), 0); } // In some other cases, aNode is the anonymous DIV, and offset points to the // terminating mozBR. In that case, we'll adjust aInOutNode and // aInOutOffset to the preceding text node, if any. - if (offset) { + if (!aPoint.IsStartOfContainer()) { if (AsHTMLEditor()) { // Fall back to a slow path that uses GetChildAt() for Thunderbird's // plaintext editor. - nsIContent* child = node->GetChildAt(offset - 1); + nsIContent* child = aPoint.GetPreviousSiblingOfChildAtOffset(); if (child && child->IsNodeOfType(nsINode::eTEXT)) { - NS_ENSURE_TRUE_VOID(node->Length() <= INT32_MAX); - aNode = child; - aOffset = static_cast(aNode->Length()); - if (aSelChild) { - *aSelChild = nullptr; + if (NS_WARN_IF(child->Length() > INT32_MAX)) { + return aPoint; } - return; + return EditorRawDOMPoint(child, child->Length()); } } else { // If we're in a real plaintext editor, use a fast path that avoids // calling GetChildAt() which may perform a linear search. - nsIContent* child = node->GetLastChild(); + nsIContent* child = aPoint.Container()->GetLastChild(); while (child) { if (child->IsNodeOfType(nsINode::eTEXT)) { - NS_ENSURE_TRUE_VOID(node->Length() <= INT32_MAX); - aNode = child; - aOffset = static_cast(aNode->Length()); - if (aSelChild) { - *aSelChild = nullptr; + if (NS_WARN_IF(child->Length() > INT32_MAX)) { + return aPoint; } - return; + return EditorRawDOMPoint(child, child->Length()); } child = child->GetPreviousSibling(); } @@ -2469,27 +2451,24 @@ EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, // Sometimes, aNode is the mozBR element itself. In that case, we'll adjust // the insertion point to the previous text node, if one exists, or to the // parent anonymous DIV. - if (TextEditUtils::IsMozBR(node) && !offset) { - if (node->GetPreviousSibling() && - node->GetPreviousSibling()->IsNodeOfType(nsINode::eTEXT)) { - NS_ENSURE_TRUE_VOID(node->Length() <= INT32_MAX); - aNode = node->GetPreviousSibling(); - aOffset = static_cast(aNode->Length()); - if (aSelChild) { - *aSelChild = nullptr; + if (TextEditUtils::IsMozBR(aPoint.Container()) && + aPoint.IsStartOfContainer()) { + nsIContent* previousSibling = aPoint.Container()->GetPreviousSibling(); + if (previousSibling && previousSibling->IsNodeOfType(nsINode::eTEXT)) { + if (NS_WARN_IF(previousSibling->Length() > INT32_MAX)) { + return aPoint; } - return; + return EditorRawDOMPoint(previousSibling, previousSibling->Length()); } - if (node->GetParentNode() && node->GetParentNode() == root) { - if (aSelChild) { - *aSelChild = node->AsContent(); - } - aNode = node->GetParentNode(); - aOffset = 0; - return; + nsINode* parentOfContainer = aPoint.Container()->GetParentNode(); + if (parentOfContainer && parentOfContainer == root) { + return EditorRawDOMPoint(parentOfContainer, + aPoint.Container()->AsContent(), 0); } } + + return aPoint; } nsresult @@ -2528,7 +2507,16 @@ EditorBase::InsertTextImpl(const nsAString& aStringToInsert, // In some cases, the node may be the anonymous div elemnt or a mozBR // element. Let's try to look for better insertion point in the nearest // text node if there is. - FindBetterInsertionPoint(node, offset, address_of(child)); + EditorRawDOMPoint insertionPoint; + if (child) { + insertionPoint.Set(child); + } else { + insertionPoint.Set(node, offset); + } + EditorRawDOMPoint betterPoint = FindBetterInsertionPoint(insertionPoint); + node = betterPoint.Container(); + offset = betterPoint.Offset(); + child = betterPoint.GetChildAtOffset(); // If a neighboring text node already exists, use that if (!node->IsNodeOfType(nsINode::eTEXT)) { @@ -3824,20 +3812,35 @@ EditorBase::GetStartNodeAndOffset(Selection* aSelection, *aStartContainer = nullptr; *aStartOffset = 0; - if (!aSelection->RangeCount()) { + EditorRawDOMPoint point = EditorBase::GetStartPoint(aSelection); + if (!point.IsSet()) { return NS_ERROR_FAILURE; } - const nsRange* range = aSelection->GetRangeAt(0); - NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); - - NS_ENSURE_TRUE(range->IsPositioned(), NS_ERROR_FAILURE); - - NS_IF_ADDREF(*aStartContainer = range->GetStartContainer()); - *aStartOffset = range->StartOffset(); + NS_ADDREF(*aStartContainer = point.Container()); + *aStartOffset = point.Offset(); return NS_OK; } +// static +EditorRawDOMPoint +EditorBase::GetStartPoint(Selection* aSelection) +{ + MOZ_ASSERT(aSelection); + + if (NS_WARN_IF(!aSelection->RangeCount())) { + return EditorRawDOMPoint(); + } + + const nsRange* range = aSelection->GetRangeAt(0); + if (NS_WARN_IF(!range) || + NS_WARN_IF(!range->IsPositioned())) { + return EditorRawDOMPoint(); + } + + return EditorRawDOMPoint(range->StartRef()); +} + /** * GetEndNodeAndOffset() returns whatever the end parent & offset is of * the first range in the selection. @@ -4978,10 +4981,10 @@ EditorBase::InitializeSelection(nsIDOMEventTarget* aFocusEventTarget) // XXX If selection is changed during reframe, this doesn't work well! nsRange* firstRange = selection->GetRangeAt(0); NS_ENSURE_TRUE(firstRange, NS_ERROR_FAILURE); - nsCOMPtr startNode = firstRange->GetStartContainer(); - int32_t startOffset = firstRange->StartOffset(); - FindBetterInsertionPoint(startNode, startOffset, nullptr); - Text* textNode = startNode->GetAsText(); + EditorRawDOMPoint atStartOfFirstRange(firstRange->StartRef()); + EditorRawDOMPoint betterInsertionPoint = + FindBetterInsertionPoint(atStartOfFirstRange); + Text* textNode = betterInsertionPoint.Container()->GetAsText(); MOZ_ASSERT(textNode, "There must be text node if mIMETextLength is larger than 0"); if (textNode) { diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index 8995fed7cf20..ba7e74d48c33 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -927,6 +927,7 @@ public: static nsresult GetStartNodeAndOffset(Selection* aSelection, nsINode** aStartContainer, int32_t* aStartOffset); + static EditorRawDOMPoint GetStartPoint(Selection* aSelection); static nsresult GetEndNodeAndOffset(Selection* aSelection, nsIDOMNode** outEndNode, int32_t* outEndOffset); @@ -1258,22 +1259,11 @@ public: * FindBetterInsertionPoint() tries to look for better insertion point which * is typically the nearest text node and offset in it. * - * @param aNode in/out param, on input set to the node to use to start the search, - * on output set to the node found as the better insertion point. - * @param aOffset in/out param, on input set to the offset to use to start the - * search, on putput set to the offset found as the better insertion - * point. - * @param aSelChild in/out param, on input, can be set to nullptr if the caller - * doesn't want to pass this in, or set to a pointer to an nsCOMPtr - * pointing to the child at the input node and offset, and on output - * the method will make it point to the child at the output node and - * offset returned in aNode and aOffset. + * @param aPoint Insertion point which the callers found. + * @return Better insertion point if there is. If not returns + * same point as aPoint. */ - void FindBetterInsertionPoint(nsCOMPtr& aNode, - int32_t& aOffset); - void FindBetterInsertionPoint(nsCOMPtr& aNode, - int32_t& aOffset, - nsCOMPtr* aSelChild); + EditorRawDOMPoint FindBetterInsertionPoint(const EditorRawDOMPoint& aPoint); /** * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent diff --git a/editor/libeditor/TextEditRules.cpp b/editor/libeditor/TextEditRules.cpp index 2d0480e35225..4522ce6be19f 100644 --- a/editor/libeditor/TextEditRules.cpp +++ b/editor/libeditor/TextEditRules.cpp @@ -729,16 +729,16 @@ TextEditRules::WillInsertText(EditAction aAction, // get the (collapsed) selection location NS_ENSURE_STATE(aSelection->GetRangeAt(0)); - nsCOMPtr selNode = aSelection->GetRangeAt(0)->GetStartContainer(); - nsCOMPtr selChild = - aSelection->GetRangeAt(0)->GetChildAtStartOffset(); - int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset(); - NS_ENSURE_STATE(selNode); + EditorRawDOMPoint atStartOfSelection(aSelection->GetRangeAt(0)->StartRef()); + if (NS_WARN_IF(!atStartOfSelection.IsSetAndValid())) { + return NS_ERROR_FAILURE; + } // don't put text in places that can't have it NS_ENSURE_STATE(mTextEditor); - if (!EditorBase::IsTextNode(selNode) && - !mTextEditor->CanContainTag(*selNode, *nsGkAtoms::textTagName)) { + if (!EditorBase::IsTextNode(atStartOfSelection.Container()) && + !mTextEditor->CanContainTag(*atStartOfSelection.Container(), + *nsGkAtoms::textTagName)) { return NS_ERROR_FAILURE; } @@ -750,22 +750,28 @@ TextEditRules::WillInsertText(EditAction aAction, if (aAction == EditAction::insertIMEText) { NS_ENSURE_STATE(mTextEditor); // Find better insertion point to insert text. - mTextEditor->FindBetterInsertionPoint(selNode, selOffset, - address_of(selChild)); + EditorRawDOMPoint betterInsertionPoint = + mTextEditor->FindBetterInsertionPoint(atStartOfSelection); // If there is one or more IME selections, its minimum offset should be // the insertion point. int32_t IMESelectionOffset = - mTextEditor->GetIMESelectionStartOffsetIn(selNode); + mTextEditor->GetIMESelectionStartOffsetIn( + betterInsertionPoint.Container()); if (IMESelectionOffset >= 0) { - selOffset = IMESelectionOffset; + betterInsertionPoint.Set(betterInsertionPoint.Container(), + IMESelectionOffset); } + nsCOMPtr selNode = betterInsertionPoint.Container(); + int32_t selOffset = betterInsertionPoint.Offset(); + nsCOMPtr selChild = betterInsertionPoint.GetChildAtOffset(); rv = mTextEditor->InsertTextImpl(*outString, address_of(selNode), address_of(selChild), &selOffset, doc); NS_ENSURE_SUCCESS(rv, rv); } else { // aAction == EditAction::insertText; find where we are - nsCOMPtr curNode = selNode; - int32_t curOffset = selOffset; + nsCOMPtr curNode = atStartOfSelection.Container(); + int32_t curOffset = atStartOfSelection.Offset(); + nsCOMPtr selChild = atStartOfSelection.GetChildAtOffset(); // don't change my selection in subtransactions NS_ENSURE_STATE(mTextEditor); diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index 57a7e34893fc..dd9d3fad23ff 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -532,63 +532,89 @@ TextEditor::ExtendSelectionForDelete(Selection* aSelection, GetSelectionController(getter_AddRefs(selCont)); NS_ENSURE_TRUE(selCont, NS_ERROR_NO_INTERFACE); - nsresult rv; switch (*aAction) { - case eNextWord: - rv = selCont->WordExtendForDelete(true); + case eNextWord: { + nsresult rv = selCont->WordExtendForDelete(true); // DeleteSelectionImpl doesn't handle these actions // because it's inside batching, so don't confuse it: *aAction = eNone; - break; - case ePreviousWord: - rv = selCont->WordExtendForDelete(false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; + } + case ePreviousWord: { + nsresult rv = selCont->WordExtendForDelete(false); *aAction = eNone; - break; - case eNext: - rv = selCont->CharacterExtendForDelete(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; + } + case eNext: { + nsresult rv = selCont->CharacterExtendForDelete(); // Don't set aAction to eNone (see Bug 502259) - break; + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; + } case ePrevious: { // Only extend the selection where the selection is after a UTF-16 // surrogate pair or a variation selector. // For other cases we don't want to do that, in order // to make sure that pressing backspace will only delete the last // typed character. - nsCOMPtr node; - int32_t offset; - rv = GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); + EditorRawDOMPoint atStartOfSelection = + EditorBase::GetStartPoint(aSelection); + if (NS_WARN_IF(!atStartOfSelection.IsSet())) { + return NS_ERROR_FAILURE; + } // node might be anonymous DIV, so we find better text node - FindBetterInsertionPoint(node, offset, nullptr); + EditorRawDOMPoint insertionPoint = + FindBetterInsertionPoint(atStartOfSelection); - if (IsTextNode(node)) { - const nsTextFragment* data = node->GetAsText()->GetText(); + if (IsTextNode(insertionPoint.Container())) { + const nsTextFragment* data = + insertionPoint.Container()->GetAsText()->GetText(); + uint32_t offset = insertionPoint.Offset(); if ((offset > 1 && NS_IS_LOW_SURROGATE(data->CharAt(offset - 1)) && NS_IS_HIGH_SURROGATE(data->CharAt(offset - 2))) || (offset > 0 && gfxFontUtils::IsVarSelector(data->CharAt(offset - 1)))) { - rv = selCont->CharacterExtendForBackspace(); + nsresult rv = selCont->CharacterExtendForBackspace(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } } - break; + return NS_OK; } - case eToBeginningOfLine: - selCont->IntraLineMove(true, false); // try to move to end - rv = selCont->IntraLineMove(false, true); // select to beginning + case eToBeginningOfLine: { + // Try to move to end + selCont->IntraLineMove(true, false); + // Select to beginning + nsresult rv = selCont->IntraLineMove(false, true); *aAction = eNone; - break; - case eToEndOfLine: - rv = selCont->IntraLineMove(true, true); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; + } + case eToEndOfLine: { + nsresult rv = selCont->IntraLineMove(true, true); *aAction = eNext; - break; - default: // avoid several compiler warnings - rv = NS_OK; - break; + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; + } + // For avoiding several compiler warnings + default: + return NS_OK; } - return rv; } return NS_OK; }