void
IMEContentObserver::Destroy()
{
  // If CreateTextStateManager failed, mRootContent will be null,
  // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
  if (mRootContent) {
    if (IMEStateManager::IsTestingIME() && mEditableNode) {
      nsIDocument* doc = mEditableNode->OwnerDoc();
      (new AsyncEventDispatcher(doc, NS_LITERAL_STRING("MozIMEFocusOut"),
                                false, false))->RunDOMEventWhenSafe();
    }
    mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
  }
  // Even if there are some pending notification, it'll never notify the widget.
  mWidget = nullptr;
  if (mUpdatePreference.WantSelectionChange() && mSelection) {
    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
    if (selPrivate) {
      selPrivate->RemoveSelectionListener(this);
    }
  }
  mSelection = nullptr;
  if (mUpdatePreference.WantTextChange() && mRootContent) {
    mRootContent->RemoveMutationObserver(this);
  }
  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
    mDocShell->RemoveWeakScrollObserver(this);
    mDocShell->RemoveWeakReflowObserver(this);
  }
  mRootContent = nullptr;
  mEditableNode = nullptr;
  mDocShell = nullptr;
  mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
}
void
IMEContentObserver::UnregisterObservers()
{
  if (!mIsObserving) {
    return;
  }
  mIsObserving = false;

  if (mEditor) {
    mEditor->RemoveEditorObserver(this);
  }

  if (mUpdatePreference.WantSelectionChange() && mSelection) {
    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
    if (selPrivate) {
      selPrivate->RemoveSelectionListener(this);
    }
  }

  if (mUpdatePreference.WantTextChange() && mRootContent) {
    mRootContent->RemoveMutationObserver(this);
  }

  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
    mDocShell->RemoveWeakScrollObserver(this);
    mDocShell->RemoveWeakReflowObserver(this);
  }
}
void
IMEContentObserver::ObserveEditableNode()
{
  MOZ_ASSERT(mSelection);
  MOZ_ASSERT(mRootContent);

  mUpdatePreference = mWidget->GetIMEUpdatePreference();
  if (mUpdatePreference.WantSelectionChange()) {
    // add selection change listener
    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
    NS_ENSURE_TRUE_VOID(selPrivate);
    nsresult rv = selPrivate->AddSelectionListener(this);
    NS_ENSURE_SUCCESS_VOID(rv);
  }

  if (mUpdatePreference.WantTextChange()) {
    // add text change observer
    mRootContent->AddMutationObserver(this);
  }

  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
    // Add scroll position listener and reflow observer to detect position and
    // size changes
    mDocShell->AddWeakScrollObserver(this);
    mDocShell->AddWeakReflowObserver(this);
  }
}
nsresult nsCaretAccessible::ClearControlSelectionListener()
{
  nsCOMPtr<nsISelectionController> controller =
    GetSelectionControllerForNode(mCurrentControl);

  mCurrentControl = nsnull;

  if (!controller)
    return NS_OK;

  // Remove 'this' registered as selection listener for the normal selection.
  nsCOMPtr<nsISelection> normalSel;
  controller->GetSelection(nsISelectionController::SELECTION_NORMAL,
                           getter_AddRefs(normalSel));
  nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(normalSel));
  NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);

  nsresult rv = selPrivate->RemoveSelectionListener(this);
  NS_ENSURE_SUCCESS(rv, rv);

  // Remove 'this' registered as selection listener for the spellcheck
  // selection.
  nsCOMPtr<nsISelection> spellcheckSel;
  controller->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
                           getter_AddRefs(spellcheckSel));
  selPrivate = do_QueryInterface(spellcheckSel);
  NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);

  return selPrivate->RemoveSelectionListener(this);
}
Beispiel #5
0
nsresult
nsTextEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
{
    // we only need to execute the stuff below if we are a plaintext editor.
    // html editors have a different mechanism for putting in mozBR's
    // (because there are a bunch more places you have to worry about it in html)
    if (!IsPlaintextEditor()) {
        return NS_OK;
    }

    // if we are at the end of the document, we need to insert
    // a special mozBR following the normal br, and then set the
    // selection to stick to the mozBR.
    PRInt32 selOffset;
    nsCOMPtr<nsIDOMNode> selNode;
    nsresult res;
    res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
    NS_ENSURE_SUCCESS(res, res);
    // confirm we are at end of document
    if (selOffset == 0) return NS_OK;  // can't be after a br if we are at offset 0
    nsIDOMElement *rootElem = mEditor->GetRoot();

    nsCOMPtr<nsIDOMNode> root = do_QueryInterface(rootElem);
    NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
    if (selNode != root) return NS_OK; // must be inside text node or somewhere other than end of root

    nsCOMPtr<nsIDOMNode> temp = mEditor->GetChildAt(selNode, selOffset);
    if (temp) return NS_OK; // can't be at end if there is a node after us.

    nsCOMPtr<nsIDOMNode> nearNode = mEditor->GetChildAt(selNode, selOffset-1);
    if (nearNode && nsTextEditUtils::IsBreak(nearNode) && !nsTextEditUtils::IsMozBR(nearNode))
    {
        nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection));
        // need to insert special moz BR. Why?  Because if we don't
        // the user will see no new line for the break.  Also, things
        // like table cells won't grow in height.
        nsCOMPtr<nsIDOMNode> brNode;
        res = CreateMozBR(selNode, selOffset, address_of(brNode));
        NS_ENSURE_SUCCESS(res, res);

        res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
        NS_ENSURE_SUCCESS(res, res);
        selPrivate->SetInterlinePosition(PR_TRUE);
        res = aSelection->Collapse(selNode, selOffset);
        NS_ENSURE_SUCCESS(res, res);
    }
    return res;
}
void
nsTextStateManager::Destroy(void)
{
  if (mSel) {
    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
    if (selPrivate)
      selPrivate->RemoveSelectionListener(this);
    mSel = nsnull;
  }
  if (mRootContent) {
    mRootContent->RemoveMutationObserver(this);
    mRootContent = nsnull;
  }
  mEditableNode = nsnull;
  mWidget = nsnull;
}
nsresult
nsCaretAccessible::SetControlSelectionListener(nsIContent *aCurrentNode)
{
  NS_ENSURE_TRUE(mRootAccessible, NS_ERROR_FAILURE);

  ClearControlSelectionListener();

  mCurrentControl = aCurrentNode;
  mLastTextAccessible = nsnull;

  // When focus moves such that the caret is part of a new frame selection
  // this removes the old selection listener and attaches a new one for
  // the current focus.

  nsCOMPtr<nsISelectionController> controller =
    GetSelectionControllerForNode(mCurrentControl);
#ifdef DEBUG
  NS_ASSERTION(controller || aCurrentNode->IsNodeOfType(nsINode::eDOCUMENT),
               "No selection controller for non document node!");
#endif
  if (!controller)
    return NS_OK;

  // Register 'this' as selection listener for the normal selection.
  nsCOMPtr<nsISelection> normalSel;
  controller->GetSelection(nsISelectionController::SELECTION_NORMAL,
                           getter_AddRefs(normalSel));
  nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(normalSel));
  NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);

  nsresult rv = selPrivate->AddSelectionListener(this);
  NS_ENSURE_SUCCESS(rv, rv);

  // Register 'this' as selection listener for the spellcheck selection.
  nsCOMPtr<nsISelection> spellcheckSel;
  controller->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
                           getter_AddRefs(spellcheckSel));
  selPrivate = do_QueryInterface(spellcheckSel);
  NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
  
  return selPrivate->AddSelectionListener(this);
}
nsresult
nsContentEventHandler::OnSelectionEvent(nsSelectionEvent* aEvent)
{
  aEvent->mSucceeded = false;

  // Get selection to manipulate
  // XXX why do we need to get them from ISM? This method should work fine
  //     without ISM.
  nsresult rv = nsIMEStateManager::
      GetFocusSelectionAndRoot(getter_AddRefs(mSelection),
                               getter_AddRefs(mRootContent));
  if (rv != NS_ERROR_NOT_AVAILABLE) {
    NS_ENSURE_SUCCESS(rv, rv);
  } else {
    rv = Init(aEvent);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // Get range from offset and length
  nsRefPtr<nsRange> range = new nsRange();
  NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
  rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset, aEvent->mLength,
                                  aEvent->mExpandToClusterBoundary);
  NS_ENSURE_SUCCESS(rv, rv);

  nsINode* startNode = range->GetStartParent();
  nsINode* endNode = range->GetEndParent();
  PRInt32 startOffset = range->StartOffset();
  PRInt32 endOffset = range->EndOffset();
  AdjustRangeForSelection(mRootContent, &startNode, &startOffset);
  AdjustRangeForSelection(mRootContent, &endNode, &endOffset);

  nsCOMPtr<nsIDOMNode> startDomNode(do_QueryInterface(startNode));
  nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode));
  NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED);

  nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
  selPrivate->StartBatchChanges();

  // Clear selection first before setting
  rv = mSelection->RemoveAllRanges();
  // Need to call EndBatchChanges at the end even if call failed
  if (NS_SUCCEEDED(rv)) {
    if (aEvent->mReversed) {
      rv = mSelection->Collapse(endDomNode, endOffset);
    } else {
      rv = mSelection->Collapse(startDomNode, startOffset);
    }
    if (NS_SUCCEEDED(rv) &&
        (startDomNode != endDomNode || startOffset != endOffset)) {
      if (aEvent->mReversed) {
        rv = mSelection->Extend(startDomNode, startOffset);
      } else {
        rv = mSelection->Extend(endDomNode, endOffset);
      }
    }
  }
  selPrivate->EndBatchChanges();
  NS_ENSURE_SUCCESS(rv, rv);

  selPrivate->ScrollIntoViewInternal(
    nsISelectionController::SELECTION_FOCUS_REGION,
    false, nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis());
  aEvent->mSucceeded = true;
  return NS_OK;
}
nsresult
nsTextEditRules::WillInsertText(PRInt32          aAction,
                                nsISelection *aSelection, 
                                PRBool          *aCancel,
                                PRBool          *aHandled,
                                const nsAString *inString,
                                nsAString *outString,
                                PRInt32          aMaxLength)
{  
  if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }

  if (inString->IsEmpty() && (aAction != kInsertTextIME))
  {
    // HACK: this is a fix for bug 19395
    // I can't outlaw all empty insertions
    // because IME transaction depend on them
    // There is more work to do to make the 
    // world safe for IME.
    *aCancel = PR_TRUE;
    *aHandled = PR_FALSE;
    return NS_OK;
  }
  
  // initialize out param
  *aCancel = PR_FALSE;
  *aHandled = PR_TRUE;

  // handle docs with a max length
  // NOTE, this function copies inString into outString for us.
  PRBool truncated = PR_FALSE;
  nsresult res = TruncateInsertionIfNeeded(aSelection, inString, outString,
                                           aMaxLength, &truncated);
  NS_ENSURE_SUCCESS(res, res);
  if (truncated && outString->IsEmpty()) {
    *aCancel = PR_TRUE;
    return NS_OK;
  }
  
  PRUint32 start = 0;
  PRUint32 end = 0;  

  // handle password field docs
  if (IsPasswordEditor())
  {
    res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
    NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!");
    NS_ENSURE_SUCCESS(res, res);
  }

  // if the selection isn't collapsed, delete it.
  PRBool bCollapsed;
  res = aSelection->GetIsCollapsed(&bCollapsed);
  NS_ENSURE_SUCCESS(res, res);
  if (!bCollapsed)
  {
    res = mEditor->DeleteSelection(nsIEditor::eNone);
    NS_ENSURE_SUCCESS(res, res);
  }

  res = WillInsert(aSelection, aCancel);
  NS_ENSURE_SUCCESS(res, res);
  // initialize out param
  // we want to ignore result of WillInsert()
  *aCancel = PR_FALSE;
  
  // handle password field data
  // this has the side effect of changing all the characters in aOutString
  // to the replacement character
  if (IsPasswordEditor())
  {
    if (aAction == kInsertTextIME)  {
      res = RemoveIMETextFromPWBuf(start, outString);
      NS_ENSURE_SUCCESS(res, res);
    }
  }

  // People have lots of different ideas about what text fields
  // should do with multiline pastes.  See bugs 21032, 23485, 23485, 50935.
  // The six possible options are:
  // 0. paste newlines intact
  // 1. paste up to the first newline (default)
  // 2. replace newlines with spaces
  // 3. strip newlines
  // 4. replace with commas
  // 5. strip newlines and surrounding whitespace
  // So find out what we're expected to do:
  if (IsSingleLineEditor())
  {
    nsAutoString tString(*outString);

    HandleNewLines(tString, mEditor->mNewlineHandling);

    outString->Assign(tString);
  }

  if (IsPasswordEditor())
  {
    // manage the password buffer
    mPasswordText.Insert(*outString, start);

    nsCOMPtr<nsILookAndFeel> lookAndFeel = do_GetService(kLookAndFeelCID);
    if (lookAndFeel->GetEchoPassword() && !DontEchoPassword()) {
      HideLastPWInput();
      mLastStart = start;
      mLastLength = outString->Length();
      if (mTimer)
      {
        mTimer->Cancel();
      }
      else
      {
        mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
        NS_ENSURE_SUCCESS(res, res);
      }
      mTimer->InitWithCallback(this, 600, nsITimer::TYPE_ONE_SHOT);
    } 
    else 
    {
      res = FillBufWithPWChars(outString, outString->Length());
      NS_ENSURE_SUCCESS(res, res);
    }
  }

  // get the (collapsed) selection location
  nsCOMPtr<nsIDOMNode> selNode;
  PRInt32 selOffset;
  res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  NS_ENSURE_SUCCESS(res, res);

  // don't put text in places that can't have it
  if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, NS_LITERAL_STRING("#text")))
    return NS_ERROR_FAILURE;

  // we need to get the doc
  nsCOMPtr<nsIDOMDocument>doc;
  res = mEditor->GetDocument(getter_AddRefs(doc));
  NS_ENSURE_SUCCESS(res, res);
  NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
    
  if (aAction == kInsertTextIME) 
  { 
    res = mEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc);
    NS_ENSURE_SUCCESS(res, res);
  }
  else // aAction == kInsertText
  {
    // find where we are
    nsCOMPtr<nsIDOMNode> curNode = selNode;
    PRInt32 curOffset = selOffset;

    // don't spaz my selection in subtransactions
    nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);

    res = mEditor->InsertTextImpl(*outString, address_of(curNode),
                                  &curOffset, doc);
    NS_ENSURE_SUCCESS(res, res);

    if (curNode) 
    {
      // Make the caret attach to the inserted text, unless this text ends with a LF, 
      // in which case make the caret attach to the next line.
      PRBool endsWithLF =
        !outString->IsEmpty() && outString->Last() == nsCRT::LF;
      nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection));
      selPrivate->SetInterlinePosition(endsWithLF);

      aSelection->Collapse(curNode, curOffset);
    }
  }
  ASSERT_PASSWORD_LENGTHS_EQUAL()
  return res;
}
Beispiel #10
0
nsresult
nsTextEditRules::WillInsertText(PRInt32          aAction,
                                nsISelection *aSelection,
                                PRBool          *aCancel,
                                PRBool          *aHandled,
                                const nsAString *inString,
                                nsAString *outString,
                                PRInt32          aMaxLength)
{
    if (!aSelection || !aCancel || !aHandled) {
        return NS_ERROR_NULL_POINTER;
    }

    if (inString->IsEmpty() && (aAction != kInsertTextIME))
    {
        // HACK: this is a fix for bug 19395
        // I can't outlaw all empty insertions
        // because IME transaction depend on them
        // There is more work to do to make the
        // world safe for IME.
        *aCancel = PR_TRUE;
        *aHandled = PR_FALSE;
        return NS_OK;
    }

    // initialize out param
    *aCancel = PR_FALSE;
    *aHandled = PR_TRUE;

    // handle docs with a max length
    // NOTE, this function copies inString into outString for us.
    nsresult res = TruncateInsertionIfNeeded(aSelection, inString, outString, aMaxLength);
    NS_ENSURE_SUCCESS(res, res);

    PRUint32 start = 0;
    PRUint32 end = 0;

    // handle password field docs
    if (IsPasswordEditor())
    {
        res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
        NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!");
        NS_ENSURE_SUCCESS(res, res);
    }

    // if the selection isn't collapsed, delete it.
    PRBool bCollapsed;
    res = aSelection->GetIsCollapsed(&bCollapsed);
    NS_ENSURE_SUCCESS(res, res);
    if (!bCollapsed)
    {
        res = mEditor->DeleteSelection(nsIEditor::eNone);
        NS_ENSURE_SUCCESS(res, res);
    }

    res = WillInsert(aSelection, aCancel);
    NS_ENSURE_SUCCESS(res, res);
    // initialize out param
    // we want to ignore result of WillInsert()
    *aCancel = PR_FALSE;

    // handle password field data
    // this has the side effect of changing all the characters in aOutString
    // to the replacement character
    if (IsPasswordEditor())
    {
        if (aAction == kInsertTextIME)  {
            res = RemoveIMETextFromPWBuf(start, outString);
            NS_ENSURE_SUCCESS(res, res);
        }
    }

    // People have lots of different ideas about what text fields
    // should do with multiline pastes.  See bugs 21032, 23485, 23485, 50935.
    // The six possible options are:
    // 0. paste newlines intact
    // 1. paste up to the first newline (default)
    // 2. replace newlines with spaces
    // 3. strip newlines
    // 4. replace with commas
    // 5. strip newlines and surrounding whitespace
    // So find out what we're expected to do:
    if (IsSingleLineEditor())
    {
        nsAutoString tString(*outString);

        HandleNewLines(tString, mEditor->mNewlineHandling);

        outString->Assign(tString);
    }

    if (IsPasswordEditor())
    {
        // manage the password buffer
        mPasswordText.Insert(*outString, start);

        nsCOMPtr<nsILookAndFeel> lookAndFeel = do_GetService(kLookAndFeelCID);
        if (lookAndFeel->GetEchoPassword() && !DontEchoPassword()) {
            HideLastPWInput();
            mLastStart = start;
            mLastLength = outString->Length();
            if (mTimer)
            {
                mTimer->Cancel();
            }
            else
            {
                mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
                NS_ENSURE_SUCCESS(res, res);
            }
            mTimer->InitWithCallback(this, 600, nsITimer::TYPE_ONE_SHOT);
        }
        else
        {
            res = FillBufWithPWChars(outString, outString->Length());
            NS_ENSURE_SUCCESS(res, res);
        }
    }

    // get the (collapsed) selection location
    nsCOMPtr<nsIDOMNode> selNode;
    PRInt32 selOffset;
    res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
    NS_ENSURE_SUCCESS(res, res);

    // don't put text in places that can't have it
    if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, NS_LITERAL_STRING("#text")))
        return NS_ERROR_FAILURE;

    // we need to get the doc
    nsCOMPtr<nsIDOMDocument>doc;
    res = mEditor->GetDocument(getter_AddRefs(doc));
    NS_ENSURE_SUCCESS(res, res);
    NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);

    if (aAction == kInsertTextIME)
    {
        res = mEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc);
        NS_ENSURE_SUCCESS(res, res);
    }
    else // aAction == kInsertText
    {
        // find where we are
        nsCOMPtr<nsIDOMNode> curNode = selNode;
        PRInt32 curOffset = selOffset;

        // is our text going to be PREformatted?
        // We remember this so that we know how to handle tabs.
        PRBool isPRE;
        res = mEditor->IsPreformatted(selNode, &isPRE);
        NS_ENSURE_SUCCESS(res, res);

        // don't spaz my selection in subtransactions
        nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
        nsString tString(*outString);
        const PRUnichar *unicodeBuf = tString.get();
        nsCOMPtr<nsIDOMNode> unused;
        PRInt32 pos = 0;

        // for efficiency, break out the pre case separately.  This is because
        // it's a lot cheaper to search the input string for only newlines than
        // it is to search for both tabs and newlines.
        if (isPRE)
        {
            while (unicodeBuf && (pos != -1) && ((PRUint32)pos < tString.Length()))
            {
                PRInt32 oldPos = pos;
                PRInt32 subStrLen;
                pos = tString.FindChar(nsCRT::LF, oldPos);

                if (pos != -1)
                {
                    subStrLen = pos - oldPos;
                    // if first char is newline, then use just it
                    if (subStrLen == 0)
                        subStrLen = 1;
                }
                else
                {
                    subStrLen = tString.Length() - oldPos;
                    pos = tString.Length();
                }

                nsDependentSubstring subStr(tString, oldPos, subStrLen);

                // is it a return?
                if (subStr.EqualsLiteral(LFSTR))
                {
                    if (IsSingleLineEditor())
                    {
                        NS_ASSERTION((mEditor->mNewlineHandling == nsIPlaintextEditor::eNewlinesPasteIntact),
                                     "Newline improperly getting into single-line edit field!");
                        res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
                    }
                    else
                    {
                        res = mEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);

                        // If the newline is the last character in the string, and the BR we
                        // just inserted is the last node in the content tree, we need to add
                        // a mozBR so that a blank line is created.

                        if (NS_SUCCEEDED(res) && curNode && pos == (PRInt32)(tString.Length() - 1))
                        {
                            nsCOMPtr<nsIDOMNode> nextChild = mEditor->GetChildAt(curNode, curOffset);

                            if (!nextChild)
                            {
                                // We must be at the end since there isn't a nextChild.
                                //
                                // curNode and curOffset should be set to the position after
                                // the BR we added above, so just create a mozBR at that position.
                                //
                                // Note that we don't update curOffset after we've created/inserted
                                // the mozBR since we never want the selection to be placed after it.

                                res = CreateMozBR(curNode, curOffset, address_of(unused));
                            }
                        }
                    }
                    pos++;
                }
                else
                {
                    res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
                }
                NS_ENSURE_SUCCESS(res, res);
            }
        }
        else
        {
            char specialChars[] = {TAB, nsCRT::LF, 0};
            while (unicodeBuf && (pos != -1) && ((PRUint32)pos < tString.Length()))
            {
                PRInt32 oldPos = pos;
                PRInt32 subStrLen;
                pos = tString.FindCharInSet(specialChars, oldPos);

                if (pos != -1)
                {
                    subStrLen = pos - oldPos;
                    // if first char is newline, then use just it
                    if (subStrLen == 0)
                        subStrLen = 1;
                }
                else
                {
                    subStrLen = tString.Length() - oldPos;
                    pos = tString.Length();
                }

                nsDependentSubstring subStr(tString, oldPos, subStrLen);

                // is it a tab?
                if (subStr.EqualsLiteral("\t"))
                {
                    res = mEditor->InsertTextImpl(NS_LITERAL_STRING("    "), address_of(curNode), &curOffset, doc);
                    pos++;
                }
                // is it a return?
                else if (subStr.EqualsLiteral(LFSTR))
                {
                    res = mEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
                    pos++;
                }
                else
                {
                    res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
                }
                NS_ENSURE_SUCCESS(res, res);
            }
        }
        outString->Assign(tString);

        if (curNode)
        {
            aSelection->Collapse(curNode, curOffset);

            // Make the caret attach to the inserted text, unless this text ends with a LF,
            // in which case make the caret attach to the next line.
            PRBool endsWithLF = !tString.IsEmpty() && tString.get()[tString.Length() - 1] == nsCRT::LF;
            nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection));
            selPrivate->SetInterlinePosition(endsWithLF);
        }
    }
    ASSERT_PASSWORD_LENGTHS_EQUAL()
    return res;
}
nsresult
nsTextStateManager::Init(nsIWidget* aWidget,
                         nsPresContext* aPresContext,
                         nsINode* aNode,
                         PRBool aWantUpdates)
{
  mWidget = aWidget;

  if (!aWantUpdates) {
    mEditableNode = aNode;
    return NS_OK;
  }

  nsIPresShell* presShell = aPresContext->PresShell();

  // get selection and root content
  nsCOMPtr<nsISelectionController> selCon;
  if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
    nsIFrame* frame = static_cast<nsIContent*>(aNode)->GetPrimaryFrame();
    NS_ENSURE_TRUE(frame, NS_ERROR_UNEXPECTED);

    frame->GetSelectionController(aPresContext,
                                  getter_AddRefs(selCon));
  } else {
    // aNode is a document
    selCon = do_QueryInterface(presShell);
  }
  NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);

  nsCOMPtr<nsISelection> sel;
  nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
                                     getter_AddRefs(sel));
  NS_ENSURE_TRUE(sel, NS_ERROR_UNEXPECTED);

  nsCOMPtr<nsIDOMRange> selDomRange;
  rv = sel->GetRangeAt(0, getter_AddRefs(selDomRange));

  if (NS_SUCCEEDED(rv)) {
    nsCOMPtr<nsIRange> selRange(do_QueryInterface(selDomRange));
    NS_ENSURE_TRUE(selRange && selRange->GetStartParent(),
                   NS_ERROR_UNEXPECTED);

    mRootContent = selRange->GetStartParent()->
                     GetSelectionRootContent(presShell);
  } else {
    mRootContent = aNode->GetSelectionRootContent(presShell);
  }
  if (!mRootContent && aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
    // The document node is editable, but there are no contents, this document
    // is not editable.
    return NS_ERROR_NOT_AVAILABLE;
  }
  NS_ENSURE_TRUE(mRootContent, NS_ERROR_UNEXPECTED);

  // add text change observer
  mRootContent->AddMutationObserver(this);

  // add selection change listener
  nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(sel));
  NS_ENSURE_TRUE(selPrivate, NS_ERROR_UNEXPECTED);
  rv = selPrivate->AddSelectionListener(this);
  NS_ENSURE_SUCCESS(rv, rv);
  mSel = sel;

  mEditableNode = aNode;
  return NS_OK;
}