NS_IMETHODIMP 
nsTextEditRules::DidDoAction(nsISelection *aSelection,
                             nsRulesInfo *aInfo, nsresult aResult)
{
  // don't let any txns in here move the selection around behind our back.
  // Note that this won't prevent explicit selection setting from working.
  nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);

  NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
    
  // my kingdom for dynamic cast
  nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);

  switch (info->action)
  {
   case kInsertBreak:
     return DidInsertBreak(aSelection, aResult);
    case kInsertText:
    case kInsertTextIME:
      return DidInsertText(aSelection, aResult);
    case kDeleteSelection:
      return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
    case kUndo:
      return DidUndo(aSelection, aResult);
    case kRedo:
      return DidRedo(aSelection, aResult);
    case kSetTextProperty:
      return DidSetTextProperty(aSelection, aResult);
    case kRemoveTextProperty:
      return DidRemoveTextProperty(aSelection, aResult);
    case kOutputText:
      return DidOutputText(aSelection, aResult);
  }
  // Don't fail on transactions we don't handle here!
  return NS_OK;
}
Ejemplo n.º 2
0
nsresult
TextEditRules::WillInsertText(EditAction aAction,
                              Selection* aSelection,
                              bool* aCancel,
                              bool* aHandled,
                              const nsAString* inString,
                              nsAString* outString,
                              int32_t aMaxLength)
{
  if (!aSelection || !aCancel || !aHandled) {
    return NS_ERROR_NULL_POINTER;
  }

  if (inString->IsEmpty() && aAction != EditAction::insertIMEText) {
    // 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 = true;
    *aHandled = false;
    return NS_OK;
  }

  // initialize out param
  *aCancel = false;
  *aHandled = true;

  // handle docs with a max length
  // NOTE, this function copies inString into outString for us.
  bool truncated = false;
  nsresult rv = TruncateInsertionIfNeeded(aSelection, inString, outString,
                                          aMaxLength, &truncated);
  NS_ENSURE_SUCCESS(rv, rv);
  // If we're exceeding the maxlength when composing IME, we need to clean up
  // the composing text, so we shouldn't return early.
  if (truncated && outString->IsEmpty() &&
      aAction != EditAction::insertIMEText) {
    *aCancel = true;
    return NS_OK;
  }

  int32_t start = 0;
  int32_t end = 0;

  // handle password field docs
  if (IsPasswordEditor()) {
    NS_ENSURE_STATE(mTextEditor);
    nsContentUtils::GetSelectionInTextControl(aSelection,
                                              mTextEditor->GetRoot(),
                                              start, end);
  }

  // if the selection isn't collapsed, delete it.
  bool bCollapsed;
  rv = aSelection->GetIsCollapsed(&bCollapsed);
  NS_ENSURE_SUCCESS(rv, rv);
  if (!bCollapsed) {
    NS_ENSURE_STATE(mTextEditor);
    rv = mTextEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
    NS_ENSURE_SUCCESS(rv, rv);
  }

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

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

  // 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);

    NS_ENSURE_STATE(mTextEditor);
    HandleNewLines(tString, mTextEditor->mNewlineHandling);

    outString->Assign(tString);
  }

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

    if (LookAndFeel::GetEchoPassword() && !DontEchoPassword()) {
      HideLastPWInput();
      mLastStart = start;
      mLastLength = outString->Length();
      if (mTimer) {
        mTimer->Cancel();
      } else {
        mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
        NS_ENSURE_SUCCESS(rv, rv);
      }
      mTimer->InitWithCallback(this, LookAndFeel::GetPasswordMaskDelay(),
                               nsITimer::TYPE_ONE_SHOT);
    } else {
      FillBufWithPWChars(outString, outString->Length());
    }
  }

  // get the (collapsed) selection location
  NS_ENSURE_STATE(aSelection->GetRangeAt(0));
  nsCOMPtr<nsINode> selNode = aSelection->GetRangeAt(0)->GetStartParent();
  int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset();
  NS_ENSURE_STATE(selNode);

  // don't put text in places that can't have it
  NS_ENSURE_STATE(mTextEditor);
  if (!EditorBase::IsTextNode(selNode) &&
      !mTextEditor->CanContainTag(*selNode, *nsGkAtoms::textTagName)) {
    return NS_ERROR_FAILURE;
  }

  // we need to get the doc
  NS_ENSURE_STATE(mTextEditor);
  nsCOMPtr<nsIDocument> doc = mTextEditor->GetDocument();
  NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);

  if (aAction == EditAction::insertIMEText) {
    NS_ENSURE_STATE(mTextEditor);
    // Find better insertion point to insert text.
    mTextEditor->FindBetterInsertionPoint(selNode, selOffset);
    // If there is one or more IME selections, its minimum offset should be
    // the insertion point.
    int32_t IMESelectionOffset =
      mTextEditor->GetIMESelectionStartOffsetIn(selNode);
    if (IMESelectionOffset >= 0) {
      selOffset = IMESelectionOffset;
    }
    rv = mTextEditor->InsertTextImpl(*outString, address_of(selNode),
                                     &selOffset, doc);
    NS_ENSURE_SUCCESS(rv, rv);
  } else {
    // aAction == EditAction::insertText; find where we are
    nsCOMPtr<nsINode> curNode = selNode;
    int32_t curOffset = selOffset;

    // don't spaz my selection in subtransactions
    NS_ENSURE_STATE(mTextEditor);
    AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);

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

    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.
      bool endsWithLF =
        !outString->IsEmpty() && outString->Last() == nsCRT::LF;
      aSelection->SetInterlinePosition(endsWithLF);

      aSelection->Collapse(curNode, curOffset);
    }
  }
  ASSERT_PASSWORD_LENGTHS_EQUAL()
  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;
}
Ejemplo n.º 4
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;
}