Exemplo n.º 1
0
nsresult
TextEditRules::CollapseSelectionToTrailingBRIfNeeded(Selection* aSelection)
{
  // 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;
  }

  NS_ENSURE_STATE(mTextEditor);

  // If there is no selection ranges, we should set to the end of the editor.
  // This is usually performed in TextEditRules::Init(), however, if the
  // editor is reframed, this may be called by AfterEdit().
  if (!aSelection->RangeCount()) {
    mTextEditor->CollapseSelectionToEnd(aSelection);
  }

  // if we are at the end of the textarea, we need to set the
  // selection to stick to the mozBR at the end of the textarea.
  int32_t selOffset;
  nsCOMPtr<nsINode> selNode;
  nsresult rv =
    EditorBase::GetStartNodeAndOffset(aSelection,
                                      getter_AddRefs(selNode), &selOffset);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  if (!EditorBase::IsTextNode(selNode)) {
    return NS_OK; // Nothing to do if we're not at a text node.
  }

  // nothing to do if we're not at the end of the text node
  if (selOffset != static_cast<int32_t>(selNode->Length())) {
    return NS_OK;
  }

  NS_ENSURE_STATE(mTextEditor);
  nsINode* root = mTextEditor->GetRoot();
  if (NS_WARN_IF(!root)) {
    return NS_ERROR_NULL_POINTER;
  }
  nsINode* parentNode = selNode->GetParentNode();
  if (parentNode != root) {
    return NS_OK;
  }

  nsINode* nextNode = selNode->GetNextSibling();
  if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
    int32_t offsetInParent = EditorBase::GetChildOffset(selNode, parentNode);
    rv = aSelection->Collapse(parentNode, offsetInParent + 1);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
  }
  return NS_OK;
}
nsresult
nsTextEditRules::WillRemoveTextProperty(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled)
{
  if (!aSelection || !aCancel || !aHandled) 
    { return NS_ERROR_NULL_POINTER; }

  // XXX: should probably return a success value other than NS_OK that means "not allowed"
  if (IsPlaintextEditor()) {
    *aCancel = PR_TRUE;
  }
  return NS_OK;
}
nsresult
nsTextEditRules::CollapseSelectionToTrailingBRIfNeeded(nsISelection* aSelection)
{
  // 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 textarea, we need to set the
  // selection to stick to the mozBR at the end of the textarea.
  PRInt32 selOffset;
  nsCOMPtr<nsIDOMNode> selNode;
  nsresult res;
  res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  NS_ENSURE_SUCCESS(res, res);

  nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
  if (!nodeAsText) return NS_OK; // nothing to do if we're not at a text node

  PRUint32 length;
  res = nodeAsText->GetLength(&length);
  NS_ENSURE_SUCCESS(res, res);

  // nothing to do if we're not at the end of the text node
  if (selOffset != PRInt32(length))
    return NS_OK;

  nsCOMPtr<nsIDOMNode> parentNode;
  PRInt32 parentOffset;
  res = nsEditor::GetNodeLocation(selNode, address_of(parentNode),
                                  &parentOffset);
  NS_ENSURE_SUCCESS(res, res);

  nsIDOMElement *rootElem = mEditor->GetRoot();
  nsCOMPtr<nsIDOMNode> root = do_QueryInterface(rootElem);
  NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
  if (parentNode != root) return NS_OK;

  nsCOMPtr<nsIDOMNode> nextNode = mEditor->GetChildAt(parentNode,
                                                      parentOffset + 1);
  if (nextNode && nsTextEditUtils::IsMozBR(nextNode))
  {
    res = aSelection->Collapse(parentNode, parentOffset + 1);
    NS_ENSURE_SUCCESS(res, res);
  }
  return res;
}
Exemplo n.º 4
0
nsresult
TextEditRules::WillSetTextProperty(Selection* aSelection,
                                   bool* aCancel,
                                   bool* aHandled)
{
  if (!aSelection || !aCancel || !aHandled) {
    return NS_ERROR_NULL_POINTER;
  }

  // XXX: should probably return a success value other than NS_OK that means "not allowed"
  if (IsPlaintextEditor()) {
    *aCancel = true;
  }
  return NS_OK;
}
Exemplo n.º 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;
}
NS_IMETHODIMP
nsTextEditRules::Init(nsPlaintextEditor *aEditor)
{
  if (!aEditor) { return NS_ERROR_NULL_POINTER; }

  mEditor = aEditor;  // we hold a non-refcounted reference back to our editor
  nsCOMPtr<nsISelection> selection;
  mEditor->GetSelection(getter_AddRefs(selection));
  NS_ASSERTION(selection, "editor cannot get selection");

  // Put in a magic br if needed. This method handles null selection,
  // which should never happen anyway
  nsresult res = CreateBogusNodeIfNeeded(selection);
  NS_ENSURE_SUCCESS(res, res);

  // If the selection hasn't been set up yet, set it up collapsed to the end of
  // our editable content.
  PRInt32 rangeCount;
  res = selection->GetRangeCount(&rangeCount);
  NS_ENSURE_SUCCESS(res, res);
  if (!rangeCount) {
    res = mEditor->EndOfDocument();
    NS_ENSURE_SUCCESS(res, res);
  }

  if (IsPlaintextEditor())
  {
    // ensure trailing br node
    res = CreateTrailingBRIfNeeded();
    NS_ENSURE_SUCCESS(res, res);
  }

  PRBool deleteBidiImmediately = PR_FALSE;
  nsCOMPtr<nsIPrefBranch> prefBranch =
    do_GetService(NS_PREFSERVICE_CONTRACTID, &res);
  if (NS_SUCCEEDED(res))
    prefBranch->GetBoolPref("bidi.edit.delete_immediately",
                            &deleteBidiImmediately);
  mDeleteBidiImmediately = deleteBidiImmediately;

  return res;
}
Exemplo n.º 7
0
NS_IMETHODIMP
TextEditRules::Init(TextEditor* aTextEditor)
{
  if (!aTextEditor) {
    return NS_ERROR_NULL_POINTER;
  }

  InitFields();

  // We hold a non-refcounted reference back to our editor.
  mTextEditor = aTextEditor;
  RefPtr<Selection> selection = mTextEditor->GetSelection();
  NS_WARNING_ASSERTION(selection, "editor cannot get selection");

  // Put in a magic br if needed. This method handles null selection,
  // which should never happen anyway
  nsresult rv = CreateBogusNodeIfNeeded(selection);
  NS_ENSURE_SUCCESS(rv, rv);

  // If the selection hasn't been set up yet, set it up collapsed to the end of
  // our editable content.
  int32_t rangeCount;
  rv = selection->GetRangeCount(&rangeCount);
  NS_ENSURE_SUCCESS(rv, rv);
  if (!rangeCount) {
    rv = mTextEditor->CollapseSelectionToEnd(selection);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  if (IsPlaintextEditor()) {
    // ensure trailing br node
    rv = CreateTrailingBRIfNeeded();
    NS_ENSURE_SUCCESS(rv, rv);
  }

  mDeleteBidiImmediately =
    Preferences::GetBool("bidi.edit.delete_immediately", false);

  return NS_OK;
}
Exemplo n.º 8
0
nsresult
TextEditRules::WillSetText(Selection& aSelection,
                           bool* aCancel,
                           bool* aHandled,
                           const nsAString* aString,
                           int32_t aMaxLength)
{
  MOZ_ASSERT(aCancel);
  MOZ_ASSERT(aHandled);
  MOZ_ASSERT(aString);

  CANCEL_OPERATION_IF_READONLY_OR_DISABLED

  *aHandled = false;
  *aCancel = false;

  if (NS_WARN_IF(!mTextEditor)) {
    return NS_ERROR_FAILURE;
  }
  RefPtr<TextEditor> textEditor = mTextEditor;

  if (!IsPlaintextEditor() || textEditor->IsIMEComposing() ||
      aMaxLength != -1) {
    // SetTextImpl only supports plain text editor without IME.
    return NS_OK;
  }

  if (IsPasswordEditor() && LookAndFeel::GetEchoPassword() &&
      !DontEchoPassword()) {
    // Echo password timer will implement on InsertText.
    return NS_OK;
  }

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

  RefPtr<Element> rootElement = textEditor->GetRoot();
  uint32_t count = rootElement->GetChildCount();

  // handles only when there is only one node and it's a text node, or empty.

  if (count > 1) {
    return NS_OK;
  }

  nsAutoString tString(*aString);

  if (IsPasswordEditor()) {
    mPasswordText.Assign(tString);
    FillBufWithPWChars(&tString, tString.Length());
  } else if (IsSingleLineEditor()) {
    HandleNewLines(tString, textEditor->mNewlineHandling);
  }

  if (!count) {
    if (tString.IsEmpty()) {
      *aHandled = true;
      return NS_OK;
    }
    RefPtr<nsIDocument> doc = textEditor->GetDocument();
    if (NS_WARN_IF(!doc)) {
      return NS_OK;
    }
    RefPtr<nsTextNode> newNode = EditorBase::CreateTextNode(*doc, tString);
    if (NS_WARN_IF(!newNode)) {
      return NS_OK;
    }
    nsresult rv = textEditor->InsertNode(*newNode, *rootElement, 0);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
    *aHandled = true;

    ASSERT_PASSWORD_LENGTHS_EQUAL();

    return NS_OK;
  }

  nsINode* curNode = rootElement->GetFirstChild();
  if (NS_WARN_IF(!EditorBase::IsTextNode(curNode))) {
    return NS_OK;
  }

  // don't change my selection in subtransactions
  AutoTransactionsConserveSelection dontChangeMySelection(textEditor);

  // Even if empty text, we don't remove text node and set empty text
  // for performance
  nsresult rv = textEditor->SetTextImpl(aSelection, tString,
                                        *curNode->GetAsText());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  *aHandled = true;

  ASSERT_PASSWORD_LENGTHS_EQUAL();

  return NS_OK;
}
Exemplo n.º 9
0
nsresult
TextEditRules::TruncateInsertionIfNeeded(Selection* aSelection,
                                         const nsAString* aInString,
                                         nsAString* aOutString,
                                         int32_t aMaxLength,
                                         bool* aTruncated)
{
  if (!aSelection || !aInString || !aOutString) {
    return NS_ERROR_NULL_POINTER;
  }

  if (!aOutString->Assign(*aInString, mozilla::fallible)) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  if (aTruncated) {
    *aTruncated = false;
  }

  NS_ENSURE_STATE(mTextEditor);
  if (-1 != aMaxLength && IsPlaintextEditor() &&
      !mTextEditor->IsIMEComposing()) {
    // Get the current text length.
    // Get the length of inString.
    // Get the length of the selection.
    //   If selection is collapsed, it is length 0.
    //   Subtract the length of the selection from the len(doc)
    //   since we'll delete the selection on insert.
    //   This is resultingDocLength.
    // Get old length of IME composing string
    //   which will be replaced by new one.
    // If (resultingDocLength) is at or over max, cancel the insert
    // If (resultingDocLength) + (length of input) > max,
    //    set aOutString to subset of inString so length = max
    int32_t docLength;
    nsresult rv = mTextEditor->GetTextLength(&docLength);
    if (NS_FAILED(rv)) {
      return rv;
    }

    uint32_t start, end;
    nsContentUtils::GetSelectionInTextControl(aSelection,
                                              mTextEditor->GetRoot(),
                                              start, end);

    TextComposition* composition = mTextEditor->GetComposition();
    uint32_t oldCompStrLength = composition ? composition->String().Length() : 0;

    const uint32_t selectionLength = end - start;
    const int32_t resultingDocLength = docLength - selectionLength - oldCompStrLength;
    if (resultingDocLength >= aMaxLength) {
      // This call is guaranteed to reduce the capacity of the string, so it
      // cannot cause an OOM.
      aOutString->Truncate();
      if (aTruncated) {
        *aTruncated = true;
      }
    } else {
      int32_t oldLength = aOutString->Length();
      if (oldLength + resultingDocLength > aMaxLength) {
        int32_t newLength = aMaxLength - resultingDocLength;
        MOZ_ASSERT(newLength > 0);
        char16_t newLastChar = aOutString->CharAt(newLength - 1);
        char16_t removingFirstChar = aOutString->CharAt(newLength);
        // Don't separate the string between a surrogate pair.
        if (NS_IS_HIGH_SURROGATE(newLastChar) &&
            NS_IS_LOW_SURROGATE(removingFirstChar)) {
          newLength--;
        }
        // XXX What should we do if we're removing IVS and its preceding
        //     character won't be removed?
        // This call is guaranteed to reduce the capacity of the string, so it
        // cannot cause an OOM.
        aOutString->Truncate(newLength);
        if (aTruncated) {
          *aTruncated = true;
        }
      }
    }
  }
  return NS_OK;
}
Exemplo n.º 10
0
nsresult
TextEditRules::CollapseSelectionToTrailingBRIfNeeded(Selection* aSelection)
{
  // 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;
  }

  NS_ENSURE_STATE(mTextEditor);

  // If there is no selection ranges, we should set to the end of the editor.
  // This is usually performed in TextEditRules::Init(), however, if the
  // editor is reframed, this may be called by AfterEdit().
  if (!aSelection->RangeCount()) {
    mTextEditor->EndOfDocument();
  }

  // if we are at the end of the textarea, we need to set the
  // selection to stick to the mozBR at the end of the textarea.
  int32_t selOffset;
  nsCOMPtr<nsIDOMNode> selNode;
  nsresult rv =
    EditorBase::GetStartNodeAndOffset(aSelection,
                                      getter_AddRefs(selNode), &selOffset);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
  if (!nodeAsText) {
    return NS_OK; // Nothing to do if we're not at a text node.
  }

  uint32_t length;
  rv = nodeAsText->GetLength(&length);
  NS_ENSURE_SUCCESS(rv, rv);

  // nothing to do if we're not at the end of the text node
  if (selOffset != int32_t(length)) {
    return NS_OK;
  }

  int32_t parentOffset;
  nsCOMPtr<nsIDOMNode> parentNode =
    EditorBase::GetNodeLocation(selNode, &parentOffset);

  NS_ENSURE_STATE(mTextEditor);
  nsCOMPtr<nsIDOMNode> root = do_QueryInterface(mTextEditor->GetRoot());
  NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
  if (parentNode != root) {
    return NS_OK;
  }

  nsCOMPtr<nsIDOMNode> nextNode = mTextEditor->GetChildAt(parentNode,
                                                          parentOffset + 1);
  if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
    rv = aSelection->Collapse(parentNode, parentOffset + 1);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  return NS_OK;
}
nsresult
nsTextEditRules::TruncateInsertionIfNeeded(nsISelection *aSelection, 
                                           const nsAString  *aInString,
                                           nsAString  *aOutString,
                                           PRInt32          aMaxLength,
                                           PRBool *aTruncated)
{
  if (!aSelection || !aInString || !aOutString) {return NS_ERROR_NULL_POINTER;}
  
  nsresult res = NS_OK;
  *aOutString = *aInString;
  if (aTruncated) {
    *aTruncated = PR_FALSE;
  }
  
  if ((-1 != aMaxLength) && IsPlaintextEditor() && !mEditor->IsIMEComposing() )
  {
    // Get the current text length.
    // Get the length of inString.
    // Get the length of the selection.
    //   If selection is collapsed, it is length 0.
    //   Subtract the length of the selection from the len(doc) 
    //   since we'll delete the selection on insert.
    //   This is resultingDocLength.
    // Get old length of IME composing string
    //   which will be replaced by new one.
    // If (resultingDocLength) is at or over max, cancel the insert
    // If (resultingDocLength) + (length of input) > max, 
    //    set aOutString to subset of inString so length = max
    PRInt32 docLength;
    res = mEditor->GetTextLength(&docLength);
    if (NS_FAILED(res)) { return res; }

    PRUint32 start, end;
    res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
    if (NS_FAILED(res)) { return res; }

    PRInt32 oldCompStrLength;
    res = mEditor->GetIMEBufferLength(&oldCompStrLength);
    if (NS_FAILED(res)) { return res; }

    const PRInt32 selectionLength = end - start;
    const PRInt32 resultingDocLength = docLength - selectionLength - oldCompStrLength;
    if (resultingDocLength >= aMaxLength)
    {
      aOutString->Truncate();
      if (aTruncated) {
        *aTruncated = PR_TRUE;
      }
    }
    else
    {
      PRInt32 inCount = aOutString->Length();
      if (inCount + resultingDocLength > aMaxLength)
      {
        aOutString->Truncate(aMaxLength - resultingDocLength);
        if (aTruncated) {
          *aTruncated = PR_TRUE;
        }
      }
    }
  }
  return res;
}
Exemplo n.º 12
0
NS_IMETHODIMP
nsTextEditRules::Init(nsPlaintextEditor *aEditor)
{
    if (!aEditor) {
        return NS_ERROR_NULL_POINTER;
    }

    mEditor = aEditor;  // we hold a non-refcounted reference back to our editor
    nsCOMPtr<nsISelection> selection;
    mEditor->GetSelection(getter_AddRefs(selection));
    NS_ASSERTION(selection, "editor cannot get selection");

    // Cache our body node, if available.
    nsIDOMNode *body = mEditor->GetRoot();

    // Put in a magic br if needed. This method handles null selection,
    // which should never happen anyway
    nsresult res = CreateBogusNodeIfNeeded(selection);
    NS_ENSURE_SUCCESS(res, res);

    // If the selection hasn't been set up yet, set it up collapsed to the end of
    // our editable content.
    PRInt32 rangeCount;
    res = selection->GetRangeCount(&rangeCount);
    NS_ENSURE_SUCCESS(res, res);
    if (!rangeCount) {
        res = mEditor->EndOfDocument();
        NS_ENSURE_SUCCESS(res, res);
    }

    if (IsPlaintextEditor())
    {
        // ensure trailing br node
        res = CreateTrailingBRIfNeeded();
        NS_ENSURE_SUCCESS(res, res);
    }

    if (body)
    {
        // create a range that is the entire body contents
        nsCOMPtr<nsIDOMRange> wholeDoc =
            do_CreateInstance("@mozilla.org/content/range;1");
        NS_ENSURE_TRUE(wholeDoc, NS_ERROR_NULL_POINTER);
        wholeDoc->SetStart(body,0);
        nsCOMPtr<nsIDOMNodeList> list;
        res = body->GetChildNodes(getter_AddRefs(list));
        NS_ENSURE_SUCCESS(res, res);
        NS_ENSURE_TRUE(list, NS_ERROR_FAILURE);

        PRUint32 listCount;
        res = list->GetLength(&listCount);
        NS_ENSURE_SUCCESS(res, res);

        res = wholeDoc->SetEnd(body, listCount);
        NS_ENSURE_SUCCESS(res, res);

        // replace newlines in that range with breaks
        res = ReplaceNewlines(wholeDoc);
    }

    PRBool deleteBidiImmediately = PR_FALSE;
    nsCOMPtr<nsIPrefBranch> prefBranch =
        do_GetService(NS_PREFSERVICE_CONTRACTID, &res);
    if (NS_SUCCEEDED(res))
        prefBranch->GetBoolPref("bidi.edit.delete_immediately",
                                &deleteBidiImmediately);
    mDeleteBidiImmediately = deleteBidiImmediately;

    return res;
}