nsresult TextEditRules::WillDeleteSelection(Selection* aSelection, nsIEditor::EDirection aCollapsedAction, bool* aCancel, bool* aHandled) { if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } CANCEL_OPERATION_IF_READONLY_OR_DISABLED // initialize out param *aCancel = false; *aHandled = false; // if there is only bogus content, cancel the operation if (mBogusNode) { *aCancel = true; return NS_OK; } // If the current selection is empty (e.g the user presses backspace with // a collapsed selection), then we want to avoid sending the selectstart // event to the user, so we hide selection changes. However, we still // want to send a single selectionchange event to the document, so we // batch the selectionchange events, such that a single event fires after // the AutoHideSelectionChanges destructor has been run. SelectionBatcher selectionBatcher(aSelection); AutoHideSelectionChanges hideSelection(aSelection); nsAutoScriptBlocker scriptBlocker; if (IsPasswordEditor()) { NS_ENSURE_STATE(mTextEditor); nsresult rv = mTextEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction); NS_ENSURE_SUCCESS(rv, rv); // manage the password buffer uint32_t start, end; nsContentUtils::GetSelectionInTextControl(aSelection, mTextEditor->GetRoot(), start, end); if (LookAndFeel::GetEchoPassword()) { HideLastPWInput(); mLastStart = start; mLastLength = 0; if (mTimer) { mTimer->Cancel(); } } // Collapsed selection. if (end == start) { // Deleting back. if (nsIEditor::ePrevious == aCollapsedAction && start > 0) { mPasswordText.Cut(start-1, 1); } // Deleting forward. else if (nsIEditor::eNext == aCollapsedAction) { mPasswordText.Cut(start, 1); } // Otherwise nothing to do for this collapsed selection. } // Extended selection. else { mPasswordText.Cut(start, end-start); } } else { nsCOMPtr<nsIDOMNode> startNode; int32_t startOffset; nsresult rv = EditorBase::GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); bool bCollapsed; rv = aSelection->GetIsCollapsed(&bCollapsed); NS_ENSURE_SUCCESS(rv, rv); if (!bCollapsed) { return NS_OK; } // Test for distance between caret and text that will be deleted rv = CheckBidiLevelForDeletion(aSelection, startNode, startOffset, aCollapsedAction, aCancel); NS_ENSURE_SUCCESS(rv, rv); if (*aCancel) { return NS_OK; } NS_ENSURE_STATE(mTextEditor); rv = mTextEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction); NS_ENSURE_SUCCESS(rv, rv); } NS_ENSURE_STATE(mTextEditor); nsresult rv = mTextEditor->DeleteSelectionImpl(aCollapsedAction, nsIEditor::eStrip); NS_ENSURE_SUCCESS(rv, rv); *aHandled = true; ASSERT_PASSWORD_LENGTHS_EQUAL() return NS_OK; }
// static nsresult CompositionTransaction::SetIMESelection( EditorBase& aEditorBase, Text* aTextNode, uint32_t aOffsetInNode, uint32_t aLengthOfCompositionString, const TextRangeArray* aRanges) { RefPtr<Selection> selection = aEditorBase.GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED); SelectionBatcher selectionBatcher(selection); // First, remove all selections of IME composition. static const RawSelectionType kIMESelections[] = { nsISelectionController::SELECTION_IME_RAWINPUT, nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT, nsISelectionController::SELECTION_IME_CONVERTEDTEXT, nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT}; nsCOMPtr<nsISelectionController> selCon; aEditorBase.GetSelectionController(getter_AddRefs(selCon)); NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED); nsresult rv = NS_OK; for (uint32_t i = 0; i < ArrayLength(kIMESelections); ++i) { RefPtr<Selection> selectionOfIME = selCon->GetSelection(kIMESelections[i]); if (!selectionOfIME) { continue; } selectionOfIME->RemoveAllRanges(IgnoreErrors()); } // Set caret position and selection of IME composition with TextRangeArray. bool setCaret = false; uint32_t countOfRanges = aRanges ? aRanges->Length() : 0; #ifdef DEBUG // Bounds-checking on debug builds uint32_t maxOffset = aTextNode->Length(); #endif // NOTE: composition string may be truncated when it's committed and // maxlength attribute value doesn't allow input of all text of this // composition. for (uint32_t i = 0; i < countOfRanges; ++i) { const TextRange& textRange = aRanges->ElementAt(i); // Caret needs special handling since its length may be 0 and if it's not // specified explicitly, we need to handle it ourselves later. if (textRange.mRangeType == TextRangeType::eCaret) { NS_ASSERTION(!setCaret, "The ranges already has caret position"); NS_ASSERTION(!textRange.Length(), "EditorBase doesn't support wide caret"); int32_t caretOffset = static_cast<int32_t>( aOffsetInNode + std::min(textRange.mStartOffset, aLengthOfCompositionString)); MOZ_ASSERT(caretOffset >= 0 && static_cast<uint32_t>(caretOffset) <= maxOffset); rv = selection->Collapse(aTextNode, caretOffset); setCaret = setCaret || NS_SUCCEEDED(rv); if (NS_WARN_IF(!setCaret)) { continue; } // If caret range is specified explicitly, we should show the caret if // it should be so. aEditorBase.HideCaret(false); continue; } // If the clause length is 0, it should be a bug. if (!textRange.Length()) { NS_WARNING("Any clauses must not be empty"); continue; } RefPtr<nsRange> clauseRange; int32_t startOffset = static_cast<int32_t>( aOffsetInNode + std::min(textRange.mStartOffset, aLengthOfCompositionString)); MOZ_ASSERT(startOffset >= 0 && static_cast<uint32_t>(startOffset) <= maxOffset); int32_t endOffset = static_cast<int32_t>( aOffsetInNode + std::min(textRange.mEndOffset, aLengthOfCompositionString)); MOZ_ASSERT(endOffset >= startOffset && static_cast<uint32_t>(endOffset) <= maxOffset); rv = nsRange::CreateRange(aTextNode, startOffset, aTextNode, endOffset, getter_AddRefs(clauseRange)); if (NS_FAILED(rv)) { NS_WARNING("Failed to create a DOM range for a clause of composition"); break; } // Set the range of the clause to selection. RefPtr<Selection> selectionOfIME = selCon->GetSelection(ToRawSelectionType(textRange.mRangeType)); if (!selectionOfIME) { NS_WARNING("Failed to get IME selection"); break; } IgnoredErrorResult err; selectionOfIME->AddRange(*clauseRange, err); if (err.Failed()) { NS_WARNING("Failed to add selection range for a clause of composition"); break; } // Set the style of the clause. rv = selectionOfIME->SetTextRangeStyle(clauseRange, textRange.mRangeStyle); if (NS_FAILED(rv)) { NS_WARNING("Failed to set selection style"); break; // but this is unexpected... } } // If the ranges doesn't include explicit caret position, let's set the // caret to the end of composition string. if (!setCaret) { int32_t caretOffset = static_cast<int32_t>(aOffsetInNode + aLengthOfCompositionString); MOZ_ASSERT(caretOffset >= 0 && static_cast<uint32_t>(caretOffset) <= maxOffset); rv = selection->Collapse(aTextNode, caretOffset); NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to set caret at the end of composition string"); // If caret range isn't specified explicitly, we should hide the caret. // Hiding the caret benefits a Windows build (see bug 555642 comment #6). // However, when there is no range, we should keep showing caret. if (countOfRanges) { aEditorBase.HideCaret(true); } } return rv; }