nsresult
nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
                                                nsINode* aNode,
                                                PRInt32 aNodeOffset,
                                                PRUint32* aNativeOffset)
{
  NS_ASSERTION(aNativeOffset, "param is invalid");

  nsCOMPtr<nsIRange> prev = new nsRange();
  NS_ENSURE_TRUE(prev, NS_ERROR_OUT_OF_MEMORY);
  nsCOMPtr<nsIDOMRange> domPrev(do_QueryInterface(prev));
  NS_ASSERTION(domPrev, "nsRange doesn't have nsIDOMRange??");
  nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(aRootContent));
  domPrev->SetStart(rootDOMNode, 0);

  nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(aNode));
  NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode");
  domPrev->SetEnd(startDOMNode, aNodeOffset);

  nsCOMPtr<nsIContentIterator> iter;
  nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
  NS_ENSURE_SUCCESS(rv, rv);
  NS_ASSERTION(iter, "NS_NewContentIterator succeeded, but the result is null");
  iter->Init(domPrev);

  nsCOMPtr<nsINode> startNode = do_QueryInterface(startDOMNode);
  nsINode* endNode = aNode;

  *aNativeOffset = 0;
  for (; !iter->IsDone(); iter->Next()) {
    nsINode* node = iter->GetCurrentNode();
    if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
      continue;
    nsIContent* content = static_cast<nsIContent*>(node);

    if (node->IsNodeOfType(nsINode::eTEXT)) {
      // Note: our range always starts from offset 0
      if (node == endNode)
        *aNativeOffset += GetNativeTextLength(content, aNodeOffset);
      else
        *aNativeOffset += GetNativeTextLength(content);
    } else if (IsContentBR(content)) {
#if defined(XP_WIN)
      // On Windows, the length of the newline is 2.
      *aNativeOffset += 2;
#else
      // On other platforms, the length of the newline is 1.
      *aNativeOffset += 1;
#endif
    }
  }
  return NS_OK;
}
nsresult
nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
                                                nsINode* aNode,
                                                PRInt32 aNodeOffset,
                                                PRUint32* aNativeOffset)
{
  NS_ASSERTION(aNativeOffset, "param is invalid");

  nsRefPtr<nsRange> prev = new nsRange();
  nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(aRootContent));
  prev->SetStart(rootDOMNode, 0);

  nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(aNode));
  NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode");
  prev->SetEnd(startDOMNode, aNodeOffset);

  nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
  iter->Init(prev);

  nsCOMPtr<nsINode> startNode = do_QueryInterface(startDOMNode);
  nsINode* endNode = aNode;

  *aNativeOffset = 0;
  for (; !iter->IsDone(); iter->Next()) {
    nsINode* node = iter->GetCurrentNode();
    if (!node)
      break;
    if (!node->IsNodeOfType(nsINode::eCONTENT))
      continue;
    nsIContent* content = static_cast<nsIContent*>(node);

    if (node->IsNodeOfType(nsINode::eTEXT)) {
      // Note: our range always starts from offset 0
      if (node == endNode)
        *aNativeOffset += GetNativeTextLength(content, aNodeOffset);
      else
        *aNativeOffset += GetNativeTextLength(content);
    } else if (IsContentBR(content)) {
#if defined(XP_WIN)
      // On Windows, the length of the newline is 2.
      *aNativeOffset += 2;
#else
      // On other platforms, the length of the newline is 1.
      *aNativeOffset += 1;
#endif
    }
  }
  return NS_OK;
}
nsresult
nsContentEventHandler::SetRangeFromFlatTextOffset(
                              nsIRange* aRange,
                              PRUint32 aNativeOffset,
                              PRUint32 aNativeLength,
                              PRBool aExpandToClusterBoundaries)
{
  nsCOMPtr<nsIContentIterator> iter;
  nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
  NS_ENSURE_SUCCESS(rv, rv);
  NS_ASSERTION(iter, "NS_NewContentIterator succeeded, but the result is null");
  rv = iter->Init(mRootContent);
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIDOMRange> domRange(do_QueryInterface(aRange));
  NS_ASSERTION(domRange, "aRange doesn't have nsIDOMRange!");

  PRUint32 nativeOffset = 0;
  PRUint32 nativeEndOffset = aNativeOffset + aNativeLength;
  nsCOMPtr<nsIContent> content;
  for (; !iter->IsDone(); iter->Next()) {
    nsINode* node = iter->GetCurrentNode();
    if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
      continue;
    nsIContent* content = static_cast<nsIContent*>(node);

    PRUint32 nativeTextLength;
    nativeTextLength = GetNativeTextLength(content);
    if (nativeTextLength == 0)
      continue;

    if (nativeOffset <= aNativeOffset &&
        aNativeOffset < nativeOffset + nativeTextLength) {
      nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
      NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");

      PRUint32 xpOffset =
        content->IsNodeOfType(nsINode::eTEXT) ?
          ConvertToXPOffset(content, aNativeOffset - nativeOffset) : 0;

      if (aExpandToClusterBoundaries) {
        rv = ExpandToClusterBoundary(content, PR_FALSE, &xpOffset);
        NS_ENSURE_SUCCESS(rv, rv);
      }

      rv = domRange->SetStart(domNode, PRInt32(xpOffset));
      NS_ENSURE_SUCCESS(rv, rv);
      if (aNativeLength == 0) {
        // Ensure that the end offset and the start offset are same.
        rv = domRange->SetEnd(domNode, PRInt32(xpOffset));
        NS_ENSURE_SUCCESS(rv, rv);
        return NS_OK;
      }
    }
    if (nativeEndOffset <= nativeOffset + nativeTextLength) {
      nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
      NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");

      PRUint32 xpOffset;
      if (content->IsNodeOfType(nsINode::eTEXT)) {
        xpOffset = ConvertToXPOffset(content, nativeEndOffset - nativeOffset);
        if (aExpandToClusterBoundaries) {
          rv = ExpandToClusterBoundary(content, PR_TRUE, &xpOffset);
          NS_ENSURE_SUCCESS(rv, rv);
        }
      } else {
        // Use first position of next node, because the end node is ignored
        // by ContentIterator when the offset is zero.
        xpOffset = 0;
        iter->Next();
        if (iter->IsDone())
          break;
        domNode = do_QueryInterface(iter->GetCurrentNode());
      }

      rv = domRange->SetEnd(domNode, PRInt32(xpOffset));
      NS_ENSURE_SUCCESS(rv, rv);
      return NS_OK;
    }

    nativeOffset += nativeTextLength;
  }

  if (nativeOffset < aNativeOffset)
    return NS_ERROR_FAILURE;

  nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mRootContent));
  NS_ASSERTION(domNode, "lastContent doesn't have nsIDOMNode!");
  if (!content) {
    rv = domRange->SetStart(domNode, 0);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  rv = domRange->SetEnd(domNode, PRInt32(mRootContent->GetChildCount()));
  NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed");
  return rv;
}