editor/txtsvc/src/nsTextServicesDocument.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/editor/txtsvc/src/nsTextServicesDocument.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3851 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include <stddef.h>                     // for nullptr
    1.10 +
    1.11 +#include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
    1.12 +#include "mozilla/mozalloc.h"           // for operator new, etc
    1.13 +#include "nsAString.h"                  // for nsAString_internal::Length, etc
    1.14 +#include "nsAutoPtr.h"                  // for nsRefPtr
    1.15 +#include "nsContentUtils.h"             // for nsContentUtils
    1.16 +#include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
    1.17 +#include "nsDependentSubstring.h"       // for Substring
    1.18 +#include "nsError.h"                    // for NS_OK, NS_ERROR_FAILURE, etc
    1.19 +#include "nsFilteredContentIterator.h"  // for nsFilteredContentIterator
    1.20 +#include "nsIContent.h"                 // for nsIContent, etc
    1.21 +#include "nsIContentIterator.h"         // for nsIContentIterator
    1.22 +#include "nsID.h"                       // for NS_GET_IID
    1.23 +#include "nsIDOMDocument.h"             // for nsIDOMDocument
    1.24 +#include "nsIDOMElement.h"              // for nsIDOMElement
    1.25 +#include "nsIDOMHTMLDocument.h"         // for nsIDOMHTMLDocument
    1.26 +#include "nsIDOMHTMLElement.h"          // for nsIDOMHTMLElement
    1.27 +#include "nsIDOMNode.h"                 // for nsIDOMNode, etc
    1.28 +#include "nsIDOMRange.h"                // for nsIDOMRange, etc
    1.29 +#include "nsIEditor.h"                  // for nsIEditor, etc
    1.30 +#include "nsINode.h"                    // for nsINode
    1.31 +#include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor
    1.32 +#include "nsISelection.h"               // for nsISelection
    1.33 +#include "nsISelectionController.h"     // for nsISelectionController, etc
    1.34 +#include "nsISupportsBase.h"            // for nsISupports
    1.35 +#include "nsISupportsUtils.h"           // for NS_IF_ADDREF, NS_ADDREF, etc
    1.36 +#include "nsITextServicesFilter.h"      // for nsITextServicesFilter
    1.37 +#include "nsIWordBreaker.h"             // for nsWordRange, nsIWordBreaker
    1.38 +#include "nsRange.h"                    // for nsRange
    1.39 +#include "nsStaticAtom.h"               // for NS_STATIC_ATOM, etc
    1.40 +#include "nsString.h"                   // for nsString, nsAutoString
    1.41 +#include "nsTextServicesDocument.h"
    1.42 +#include "nscore.h"                     // for nsresult, NS_IMETHODIMP, etc
    1.43 +
    1.44 +#define LOCK_DOC(doc)
    1.45 +#define UNLOCK_DOC(doc)
    1.46 +
    1.47 +using namespace mozilla;
    1.48 +
    1.49 +class OffsetEntry
    1.50 +{
    1.51 +public:
    1.52 +  OffsetEntry(nsIDOMNode *aNode, int32_t aOffset, int32_t aLength)
    1.53 +    : mNode(aNode), mNodeOffset(0), mStrOffset(aOffset), mLength(aLength),
    1.54 +      mIsInsertedText(false), mIsValid(true)
    1.55 +  {
    1.56 +    if (mStrOffset < 1)
    1.57 +      mStrOffset = 0;
    1.58 +
    1.59 +    if (mLength < 1)
    1.60 +      mLength = 0;
    1.61 +  }
    1.62 +
    1.63 +  virtual ~OffsetEntry()
    1.64 +  {
    1.65 +    mNode       = 0;
    1.66 +    mNodeOffset = 0;
    1.67 +    mStrOffset  = 0;
    1.68 +    mLength     = 0;
    1.69 +    mIsValid    = false;
    1.70 +  }
    1.71 +
    1.72 +  nsIDOMNode *mNode;
    1.73 +  int32_t mNodeOffset;
    1.74 +  int32_t mStrOffset;
    1.75 +  int32_t mLength;
    1.76 +  bool    mIsInsertedText;
    1.77 +  bool    mIsValid;
    1.78 +};
    1.79 +
    1.80 +#define TS_ATOM(name_, value_) nsIAtom* nsTextServicesDocument::name_ = 0;
    1.81 +#include "nsTSAtomList.h" // IWYU pragma: keep
    1.82 +#undef TS_ATOM
    1.83 +
    1.84 +nsTextServicesDocument::nsTextServicesDocument()
    1.85 +{
    1.86 +  mRefCnt         = 0;
    1.87 +
    1.88 +  mSelStartIndex  = -1;
    1.89 +  mSelStartOffset = -1;
    1.90 +  mSelEndIndex    = -1;
    1.91 +  mSelEndOffset   = -1;
    1.92 +
    1.93 +  mIteratorStatus = eIsDone;
    1.94 +}
    1.95 +
    1.96 +nsTextServicesDocument::~nsTextServicesDocument()
    1.97 +{
    1.98 +  ClearOffsetTable(&mOffsetTable);
    1.99 +}
   1.100 +
   1.101 +#define TS_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
   1.102 +#include "nsTSAtomList.h" // IWYU pragma: keep
   1.103 +#undef TS_ATOM
   1.104 +
   1.105 +/* static */
   1.106 +void
   1.107 +nsTextServicesDocument::RegisterAtoms()
   1.108 +{
   1.109 +  static const nsStaticAtom ts_atoms[] = {
   1.110 +#define TS_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &name_),
   1.111 +#include "nsTSAtomList.h" // IWYU pragma: keep
   1.112 +#undef TS_ATOM
   1.113 +  };
   1.114 +
   1.115 +  NS_RegisterStaticAtoms(ts_atoms);
   1.116 +}
   1.117 +
   1.118 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextServicesDocument)
   1.119 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextServicesDocument)
   1.120 +
   1.121 +NS_INTERFACE_MAP_BEGIN(nsTextServicesDocument)
   1.122 +  NS_INTERFACE_MAP_ENTRY(nsITextServicesDocument)
   1.123 +  NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
   1.124 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITextServicesDocument)
   1.125 +  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsTextServicesDocument)
   1.126 +NS_INTERFACE_MAP_END
   1.127 +
   1.128 +NS_IMPL_CYCLE_COLLECTION(nsTextServicesDocument,
   1.129 +                         mDOMDocument,
   1.130 +                         mSelCon,
   1.131 +                         mIterator,
   1.132 +                         mPrevTextBlock,
   1.133 +                         mNextTextBlock,
   1.134 +                         mExtent,
   1.135 +                         mTxtSvcFilter)
   1.136 +
   1.137 +NS_IMETHODIMP
   1.138 +nsTextServicesDocument::InitWithEditor(nsIEditor *aEditor)
   1.139 +{
   1.140 +  nsresult result = NS_OK;
   1.141 +  nsCOMPtr<nsISelectionController> selCon;
   1.142 +  nsCOMPtr<nsIDOMDocument> doc;
   1.143 +
   1.144 +  NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER);
   1.145 +
   1.146 +  LOCK_DOC(this);
   1.147 +
   1.148 +  // Check to see if we already have an mSelCon. If we do, it
   1.149 +  // better be the same one the editor uses!
   1.150 +
   1.151 +  result = aEditor->GetSelectionController(getter_AddRefs(selCon));
   1.152 +
   1.153 +  if (NS_FAILED(result))
   1.154 +  {
   1.155 +    UNLOCK_DOC(this);
   1.156 +    return result;
   1.157 +  }
   1.158 +
   1.159 +  if (!selCon || (mSelCon && selCon != mSelCon))
   1.160 +  {
   1.161 +    UNLOCK_DOC(this);
   1.162 +    return NS_ERROR_FAILURE;
   1.163 +  }
   1.164 +
   1.165 +  if (!mSelCon)
   1.166 +    mSelCon = selCon;
   1.167 +
   1.168 +  // Check to see if we already have an mDOMDocument. If we do, it
   1.169 +  // better be the same one the editor uses!
   1.170 +
   1.171 +  result = aEditor->GetDocument(getter_AddRefs(doc));
   1.172 +
   1.173 +  if (NS_FAILED(result))
   1.174 +  {
   1.175 +    UNLOCK_DOC(this);
   1.176 +    return result;
   1.177 +  }
   1.178 +
   1.179 +  if (!doc || (mDOMDocument && doc != mDOMDocument))
   1.180 +  {
   1.181 +    UNLOCK_DOC(this);
   1.182 +    return NS_ERROR_FAILURE;
   1.183 +  }
   1.184 +
   1.185 +  if (!mDOMDocument)
   1.186 +  {
   1.187 +    mDOMDocument = doc;
   1.188 +
   1.189 +    result = CreateDocumentContentIterator(getter_AddRefs(mIterator));
   1.190 +
   1.191 +    if (NS_FAILED(result))
   1.192 +    {
   1.193 +      UNLOCK_DOC(this);
   1.194 +      return result;
   1.195 +    }
   1.196 +
   1.197 +    mIteratorStatus = nsTextServicesDocument::eIsDone;
   1.198 +
   1.199 +    result = FirstBlock();
   1.200 +
   1.201 +    if (NS_FAILED(result))
   1.202 +    {
   1.203 +      UNLOCK_DOC(this);
   1.204 +      return result;
   1.205 +    }
   1.206 +  }
   1.207 +
   1.208 +  mEditor = do_GetWeakReference(aEditor);
   1.209 +
   1.210 +  result = aEditor->AddEditActionListener(this);
   1.211 +
   1.212 +  UNLOCK_DOC(this);
   1.213 +
   1.214 +  return result;
   1.215 +}
   1.216 +
   1.217 +NS_IMETHODIMP 
   1.218 +nsTextServicesDocument::GetDocument(nsIDOMDocument **aDoc)
   1.219 +{
   1.220 +  NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
   1.221 +
   1.222 +  *aDoc = nullptr; // init out param
   1.223 +  NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_NOT_INITIALIZED);
   1.224 +
   1.225 +  *aDoc = mDOMDocument;
   1.226 +  NS_ADDREF(*aDoc);
   1.227 +
   1.228 +  return NS_OK;
   1.229 +}
   1.230 +
   1.231 +NS_IMETHODIMP
   1.232 +nsTextServicesDocument::SetExtent(nsIDOMRange* aDOMRange)
   1.233 +{
   1.234 +  NS_ENSURE_ARG_POINTER(aDOMRange);
   1.235 +  NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
   1.236 +
   1.237 +  LOCK_DOC(this);
   1.238 +
   1.239 +  // We need to store a copy of aDOMRange since we don't
   1.240 +  // know where it came from.
   1.241 +
   1.242 +  nsresult result = aDOMRange->CloneRange(getter_AddRefs(mExtent));
   1.243 +
   1.244 +  if (NS_FAILED(result))
   1.245 +  {
   1.246 +    UNLOCK_DOC(this);
   1.247 +    return result;
   1.248 +  }
   1.249 +
   1.250 +  // Create a new iterator based on our new extent range.
   1.251 +
   1.252 +  result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
   1.253 +
   1.254 +  if (NS_FAILED(result))
   1.255 +  {
   1.256 +    UNLOCK_DOC(this);
   1.257 +    return result;
   1.258 +  }
   1.259 +
   1.260 +  // Now position the iterator at the start of the first block
   1.261 +  // in the range.
   1.262 +
   1.263 +  mIteratorStatus = nsTextServicesDocument::eIsDone;
   1.264 +
   1.265 +  result = FirstBlock();
   1.266 +
   1.267 +  UNLOCK_DOC(this);
   1.268 +
   1.269 +  return result;
   1.270 +}
   1.271 +
   1.272 +NS_IMETHODIMP
   1.273 +nsTextServicesDocument::ExpandRangeToWordBoundaries(nsIDOMRange *aRange)
   1.274 +{
   1.275 +  NS_ENSURE_ARG_POINTER(aRange);
   1.276 +
   1.277 +  // Get the end points of the range.
   1.278 +
   1.279 +  nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
   1.280 +  int32_t rngStartOffset, rngEndOffset;
   1.281 +
   1.282 +  nsresult result =  GetRangeEndPoints(aRange,
   1.283 +                                       getter_AddRefs(rngStartNode),
   1.284 +                                       &rngStartOffset,
   1.285 +                                       getter_AddRefs(rngEndNode),
   1.286 +                                       &rngEndOffset);
   1.287 +
   1.288 +  NS_ENSURE_SUCCESS(result, result);
   1.289 +
   1.290 +  // Create a content iterator based on the range.
   1.291 +
   1.292 +  nsCOMPtr<nsIContentIterator> iter;
   1.293 +  result = CreateContentIterator(aRange, getter_AddRefs(iter));
   1.294 +
   1.295 +  NS_ENSURE_SUCCESS(result, result);
   1.296 +
   1.297 +  // Find the first text node in the range.
   1.298 +
   1.299 +  TSDIteratorStatus iterStatus;
   1.300 +
   1.301 +  result = FirstTextNode(iter, &iterStatus);
   1.302 +  NS_ENSURE_SUCCESS(result, result);
   1.303 +
   1.304 +  if (iterStatus == nsTextServicesDocument::eIsDone)
   1.305 +  {
   1.306 +    // No text was found so there's no adjustment necessary!
   1.307 +    return NS_OK;
   1.308 +  }
   1.309 +
   1.310 +  nsINode *firstText = iter->GetCurrentNode();
   1.311 +  NS_ENSURE_TRUE(firstText, NS_ERROR_FAILURE);
   1.312 +
   1.313 +  // Find the last text node in the range.
   1.314 +
   1.315 +  result = LastTextNode(iter, &iterStatus);
   1.316 +  NS_ENSURE_SUCCESS(result, result);
   1.317 +
   1.318 +  if (iterStatus == nsTextServicesDocument::eIsDone)
   1.319 +  {
   1.320 +    // We should never get here because a first text block
   1.321 +    // was found above.
   1.322 +    NS_ASSERTION(false, "Found a first without a last!");
   1.323 +    return NS_ERROR_FAILURE;
   1.324 +  }
   1.325 +
   1.326 +  nsINode *lastText = iter->GetCurrentNode();
   1.327 +  NS_ENSURE_TRUE(lastText, NS_ERROR_FAILURE);
   1.328 +
   1.329 +  // Now make sure our end points are in terms of text nodes in the range!
   1.330 +
   1.331 +  nsCOMPtr<nsIDOMNode> firstTextNode = do_QueryInterface(firstText);
   1.332 +  NS_ENSURE_TRUE(firstTextNode, NS_ERROR_FAILURE);
   1.333 +
   1.334 +  if (rngStartNode != firstTextNode)
   1.335 +  {
   1.336 +    // The range includes the start of the first text node!
   1.337 +    rngStartNode = firstTextNode;
   1.338 +    rngStartOffset = 0;
   1.339 +  }
   1.340 +
   1.341 +  nsCOMPtr<nsIDOMNode> lastTextNode = do_QueryInterface(lastText);
   1.342 +  NS_ENSURE_TRUE(lastTextNode, NS_ERROR_FAILURE);
   1.343 +
   1.344 +  if (rngEndNode != lastTextNode)
   1.345 +  {
   1.346 +    // The range includes the end of the last text node!
   1.347 +    rngEndNode = lastTextNode;
   1.348 +    nsAutoString str;
   1.349 +    result = lastTextNode->GetNodeValue(str);
   1.350 +    rngEndOffset = str.Length();
   1.351 +  }
   1.352 +
   1.353 +  // Create a doc iterator so that we can scan beyond
   1.354 +  // the bounds of the extent range.
   1.355 +
   1.356 +  nsCOMPtr<nsIContentIterator> docIter;
   1.357 +  result = CreateDocumentContentIterator(getter_AddRefs(docIter));
   1.358 +  NS_ENSURE_SUCCESS(result, result);
   1.359 +
   1.360 +  // Grab all the text in the block containing our
   1.361 +  // first text node.
   1.362 +
   1.363 +  result = docIter->PositionAt(firstText);
   1.364 +  NS_ENSURE_SUCCESS(result, result);
   1.365 +
   1.366 +  iterStatus = nsTextServicesDocument::eValid;
   1.367 +
   1.368 +  nsTArray<OffsetEntry*> offsetTable;
   1.369 +  nsAutoString blockStr;
   1.370 +
   1.371 +  result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
   1.372 +                             nullptr, &blockStr);
   1.373 +  if (NS_FAILED(result))
   1.374 +  {
   1.375 +    ClearOffsetTable(&offsetTable);
   1.376 +    return result;
   1.377 +  }
   1.378 +
   1.379 +  nsCOMPtr<nsIDOMNode> wordStartNode, wordEndNode;
   1.380 +  int32_t wordStartOffset, wordEndOffset;
   1.381 +
   1.382 +  result = FindWordBounds(&offsetTable, &blockStr,
   1.383 +                          rngStartNode, rngStartOffset,
   1.384 +                          getter_AddRefs(wordStartNode), &wordStartOffset,
   1.385 +                          getter_AddRefs(wordEndNode), &wordEndOffset);
   1.386 +
   1.387 +  ClearOffsetTable(&offsetTable);
   1.388 +
   1.389 +  NS_ENSURE_SUCCESS(result, result);
   1.390 +
   1.391 +  rngStartNode = wordStartNode;
   1.392 +  rngStartOffset = wordStartOffset;
   1.393 +
   1.394 +  // Grab all the text in the block containing our
   1.395 +  // last text node.
   1.396 +
   1.397 +  result = docIter->PositionAt(lastText);
   1.398 +  NS_ENSURE_SUCCESS(result, result);
   1.399 +
   1.400 +  iterStatus = nsTextServicesDocument::eValid;
   1.401 +
   1.402 +  result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
   1.403 +                             nullptr, &blockStr);
   1.404 +  if (NS_FAILED(result))
   1.405 +  {
   1.406 +    ClearOffsetTable(&offsetTable);
   1.407 +    return result;
   1.408 +  }
   1.409 +
   1.410 +  result = FindWordBounds(&offsetTable, &blockStr,
   1.411 +                          rngEndNode, rngEndOffset,
   1.412 +                          getter_AddRefs(wordStartNode), &wordStartOffset,
   1.413 +                          getter_AddRefs(wordEndNode), &wordEndOffset);
   1.414 +
   1.415 +  ClearOffsetTable(&offsetTable);
   1.416 +
   1.417 +  NS_ENSURE_SUCCESS(result, result);
   1.418 +
   1.419 +  // To prevent expanding the range too much, we only change
   1.420 +  // rngEndNode and rngEndOffset if it isn't already at the start of the
   1.421 +  // word and isn't equivalent to rngStartNode and rngStartOffset.
   1.422 +
   1.423 +  if (rngEndNode != wordStartNode || rngEndOffset != wordStartOffset ||
   1.424 +     (rngEndNode == rngStartNode  && rngEndOffset == rngStartOffset))
   1.425 +  {
   1.426 +    rngEndNode = wordEndNode;
   1.427 +    rngEndOffset = wordEndOffset;
   1.428 +  }
   1.429 +
   1.430 +  // Now adjust the range so that it uses our new
   1.431 +  // end points.
   1.432 +
   1.433 +  result = aRange->SetEnd(rngEndNode, rngEndOffset);
   1.434 +  NS_ENSURE_SUCCESS(result, result);
   1.435 +
   1.436 +  return aRange->SetStart(rngStartNode, rngStartOffset);
   1.437 +}
   1.438 +
   1.439 +NS_IMETHODIMP
   1.440 +nsTextServicesDocument::SetFilter(nsITextServicesFilter *aFilter)
   1.441 +{
   1.442 +  // Hang on to the filter so we can set it into the filtered iterator.
   1.443 +  mTxtSvcFilter = aFilter;
   1.444 +
   1.445 +  return NS_OK;
   1.446 +}
   1.447 +
   1.448 +NS_IMETHODIMP
   1.449 +nsTextServicesDocument::GetCurrentTextBlock(nsString *aStr)
   1.450 +{
   1.451 +  nsresult result;
   1.452 +
   1.453 +  NS_ENSURE_TRUE(aStr, NS_ERROR_NULL_POINTER);
   1.454 +
   1.455 +  aStr->Truncate();
   1.456 +
   1.457 +  NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
   1.458 +
   1.459 +  LOCK_DOC(this);
   1.460 +
   1.461 +  result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   1.462 +                             mExtent, aStr);
   1.463 +
   1.464 +  UNLOCK_DOC(this);
   1.465 +
   1.466 +  return result;
   1.467 +}
   1.468 +
   1.469 +NS_IMETHODIMP
   1.470 +nsTextServicesDocument::FirstBlock()
   1.471 +{
   1.472 +  NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
   1.473 +
   1.474 +  LOCK_DOC(this);
   1.475 +
   1.476 +  nsresult result = FirstTextNode(mIterator, &mIteratorStatus);
   1.477 +
   1.478 +  if (NS_FAILED(result))
   1.479 +  {
   1.480 +    UNLOCK_DOC(this);
   1.481 +    return result;
   1.482 +  }
   1.483 +
   1.484 +  // Keep track of prev and next blocks, just in case
   1.485 +  // the text service blows away the current block.
   1.486 +
   1.487 +  if (mIteratorStatus == nsTextServicesDocument::eValid)
   1.488 +  {
   1.489 +    mPrevTextBlock  = nullptr;
   1.490 +    result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
   1.491 +  }
   1.492 +  else
   1.493 +  {
   1.494 +    // There's no text block in the document!
   1.495 +
   1.496 +    mPrevTextBlock  = nullptr;
   1.497 +    mNextTextBlock  = nullptr;
   1.498 +  }
   1.499 +
   1.500 +  UNLOCK_DOC(this);
   1.501 +
   1.502 +  return result;
   1.503 +}
   1.504 +
   1.505 +NS_IMETHODIMP
   1.506 +nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus,
   1.507 +                                          int32_t *aSelOffset,
   1.508 +                                          int32_t *aSelLength)
   1.509 +{
   1.510 +  nsresult result = NS_OK;
   1.511 +
   1.512 +  NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
   1.513 +
   1.514 +  LOCK_DOC(this);
   1.515 +
   1.516 +  mIteratorStatus = nsTextServicesDocument::eIsDone;
   1.517 +
   1.518 +  *aSelStatus = nsITextServicesDocument::eBlockNotFound;
   1.519 +  *aSelOffset = *aSelLength = -1;
   1.520 +
   1.521 +  if (!mSelCon || !mIterator)
   1.522 +  {
   1.523 +    UNLOCK_DOC(this);
   1.524 +    return NS_ERROR_FAILURE;
   1.525 +  }
   1.526 +
   1.527 +  nsCOMPtr<nsISelection> selection;
   1.528 +  bool isCollapsed = false;
   1.529 +
   1.530 +  result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
   1.531 +
   1.532 +  if (NS_FAILED(result))
   1.533 +  {
   1.534 +    UNLOCK_DOC(this);
   1.535 +    return result;
   1.536 +  }
   1.537 +
   1.538 +  result = selection->GetIsCollapsed(&isCollapsed);
   1.539 +
   1.540 +  if (NS_FAILED(result))
   1.541 +  {
   1.542 +    UNLOCK_DOC(this);
   1.543 +    return result;
   1.544 +  }
   1.545 +
   1.546 +  nsCOMPtr<nsIContentIterator> iter;
   1.547 +  nsCOMPtr<nsIDOMRange>        range;
   1.548 +  nsCOMPtr<nsIDOMNode>         parent;
   1.549 +  int32_t                      i, rangeCount, offset;
   1.550 +
   1.551 +  if (isCollapsed)
   1.552 +  {
   1.553 +    // We have a caret. Check if the caret is in a text node.
   1.554 +    // If it is, make the text node's block the current block.
   1.555 +    // If the caret isn't in a text node, search forwards in
   1.556 +    // the document, till we find a text node.
   1.557 +
   1.558 +    result = selection->GetRangeAt(0, getter_AddRefs(range));
   1.559 +
   1.560 +    if (NS_FAILED(result))
   1.561 +    {
   1.562 +      UNLOCK_DOC(this);
   1.563 +      return result;
   1.564 +    }
   1.565 +
   1.566 +    if (!range)
   1.567 +    {
   1.568 +      UNLOCK_DOC(this);
   1.569 +      return NS_ERROR_FAILURE;
   1.570 +    }
   1.571 +
   1.572 +    result = range->GetStartContainer(getter_AddRefs(parent));
   1.573 +
   1.574 +    if (NS_FAILED(result))
   1.575 +    {
   1.576 +      UNLOCK_DOC(this);
   1.577 +      return result;
   1.578 +    }
   1.579 +
   1.580 +    if (!parent)
   1.581 +    {
   1.582 +      UNLOCK_DOC(this);
   1.583 +      return NS_ERROR_FAILURE;
   1.584 +    }
   1.585 +
   1.586 +    result = range->GetStartOffset(&offset);
   1.587 +
   1.588 +    if (NS_FAILED(result))
   1.589 +    {
   1.590 +      UNLOCK_DOC(this);
   1.591 +      return result;
   1.592 +    }
   1.593 +
   1.594 +    if (IsTextNode(parent))
   1.595 +    {
   1.596 +      // The caret is in a text node. Find the beginning
   1.597 +      // of the text block containing this text node and
   1.598 +      // return.
   1.599 +
   1.600 +      nsCOMPtr<nsIContent> content(do_QueryInterface(parent));
   1.601 +
   1.602 +      if (!content)
   1.603 +      {
   1.604 +        UNLOCK_DOC(this);
   1.605 +        return NS_ERROR_FAILURE;
   1.606 +      }
   1.607 +
   1.608 +      result = mIterator->PositionAt(content);
   1.609 +
   1.610 +      if (NS_FAILED(result))
   1.611 +      {
   1.612 +        UNLOCK_DOC(this);
   1.613 +        return result;
   1.614 +      }
   1.615 +
   1.616 +      result = FirstTextNodeInCurrentBlock(mIterator);
   1.617 +
   1.618 +      if (NS_FAILED(result))
   1.619 +      {
   1.620 +        UNLOCK_DOC(this);
   1.621 +        return result;
   1.622 +      }
   1.623 +
   1.624 +      mIteratorStatus = nsTextServicesDocument::eValid;
   1.625 +
   1.626 +      result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   1.627 +                                 mExtent, nullptr);
   1.628 +
   1.629 +      if (NS_FAILED(result))
   1.630 +      {
   1.631 +        UNLOCK_DOC(this);
   1.632 +        return result;
   1.633 +      }
   1.634 +
   1.635 +      result = GetSelection(aSelStatus, aSelOffset, aSelLength);
   1.636 +
   1.637 +      if (NS_FAILED(result))
   1.638 +      {
   1.639 +        UNLOCK_DOC(this);
   1.640 +        return result;
   1.641 +      }
   1.642 +
   1.643 +      if (*aSelStatus == nsITextServicesDocument::eBlockContains)
   1.644 +        result = SetSelectionInternal(*aSelOffset, *aSelLength, false);
   1.645 +    }
   1.646 +    else
   1.647 +    {
   1.648 +      // The caret isn't in a text node. Create an iterator
   1.649 +      // based on a range that extends from the current caret
   1.650 +      // position to the end of the document, then walk forwards
   1.651 +      // till you find a text node, then find the beginning of it's block.
   1.652 +
   1.653 +      result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, getter_AddRefs(range));
   1.654 +
   1.655 +      if (NS_FAILED(result))
   1.656 +      {
   1.657 +        UNLOCK_DOC(this);
   1.658 +        return result;
   1.659 +      }
   1.660 +
   1.661 +      result = range->GetCollapsed(&isCollapsed);
   1.662 +
   1.663 +      if (NS_FAILED(result))
   1.664 +      {
   1.665 +        UNLOCK_DOC(this);
   1.666 +        return result;
   1.667 +      }
   1.668 +
   1.669 +      if (isCollapsed)
   1.670 +      {
   1.671 +        // If we get here, the range is collapsed because there is nothing after
   1.672 +        // the caret! Just return NS_OK;
   1.673 +
   1.674 +        UNLOCK_DOC(this);
   1.675 +        return NS_OK;
   1.676 +      }
   1.677 +
   1.678 +      result = CreateContentIterator(range, getter_AddRefs(iter));
   1.679 +
   1.680 +      if (NS_FAILED(result))
   1.681 +      {
   1.682 +        UNLOCK_DOC(this);
   1.683 +        return result;
   1.684 +      }
   1.685 +
   1.686 +      iter->First();
   1.687 +
   1.688 +      nsCOMPtr<nsIContent> content;
   1.689 +      while (!iter->IsDone())
   1.690 +      {
   1.691 +        content = do_QueryInterface(iter->GetCurrentNode());
   1.692 +
   1.693 +        if (IsTextNode(content))
   1.694 +          break;
   1.695 +
   1.696 +        content = nullptr;
   1.697 +
   1.698 +        iter->Next();
   1.699 +      }
   1.700 +
   1.701 +      if (!content)
   1.702 +      {
   1.703 +        UNLOCK_DOC(this);
   1.704 +        return NS_OK;
   1.705 +      }
   1.706 +
   1.707 +      result = mIterator->PositionAt(content);
   1.708 +
   1.709 +      if (NS_FAILED(result))
   1.710 +      {
   1.711 +        UNLOCK_DOC(this);
   1.712 +        return result;
   1.713 +      }
   1.714 +
   1.715 +      result = FirstTextNodeInCurrentBlock(mIterator);
   1.716 +
   1.717 +      if (NS_FAILED(result))
   1.718 +      {
   1.719 +        UNLOCK_DOC(this);
   1.720 +        return result;
   1.721 +      }
   1.722 +
   1.723 +      mIteratorStatus = nsTextServicesDocument::eValid;
   1.724 +
   1.725 +      result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   1.726 +                                 mExtent, nullptr);
   1.727 +
   1.728 +      if (NS_FAILED(result))
   1.729 +      {
   1.730 +        UNLOCK_DOC(this);
   1.731 +        return result;
   1.732 +      }
   1.733 +
   1.734 +      result = GetSelection(aSelStatus, aSelOffset, aSelLength);
   1.735 +
   1.736 +      if (NS_FAILED(result))
   1.737 +      {
   1.738 +        UNLOCK_DOC(this);
   1.739 +        return result;
   1.740 +      }
   1.741 +    }
   1.742 +
   1.743 +    UNLOCK_DOC(this);
   1.744 +
   1.745 +    return result;
   1.746 +  }
   1.747 +
   1.748 +  // If we get here, we have an uncollapsed selection!
   1.749 +  // Look backwards through each range in the selection till you
   1.750 +  // find the first text node. If you find one, find the
   1.751 +  // beginning of its text block, and make it the current
   1.752 +  // block.
   1.753 +
   1.754 +  result = selection->GetRangeCount(&rangeCount);
   1.755 +
   1.756 +  if (NS_FAILED(result))
   1.757 +  {
   1.758 +    UNLOCK_DOC(this);
   1.759 +    return result;
   1.760 +  }
   1.761 +
   1.762 +  NS_ASSERTION(rangeCount > 0, "Unexpected range count!");
   1.763 +
   1.764 +  if (rangeCount <= 0)
   1.765 +  {
   1.766 +    UNLOCK_DOC(this);
   1.767 +    return NS_OK;
   1.768 +  }
   1.769 +
   1.770 +  // XXX: We may need to add some code here to make sure
   1.771 +  //      the ranges are sorted in document appearance order!
   1.772 +
   1.773 +  for (i = rangeCount - 1; i >= 0; i--)
   1.774 +  {
   1.775 +    // Get the i'th range from the selection.
   1.776 +
   1.777 +    result = selection->GetRangeAt(i, getter_AddRefs(range));
   1.778 +
   1.779 +    if (NS_FAILED(result))
   1.780 +    {
   1.781 +      UNLOCK_DOC(this);
   1.782 +      return result;
   1.783 +    }
   1.784 +
   1.785 +    // Create an iterator for the range.
   1.786 +
   1.787 +    result = CreateContentIterator(range, getter_AddRefs(iter));
   1.788 +
   1.789 +    if (NS_FAILED(result))
   1.790 +    {
   1.791 +      UNLOCK_DOC(this);
   1.792 +      return result;
   1.793 +    }
   1.794 +
   1.795 +    iter->Last();
   1.796 +
   1.797 +    // Now walk through the range till we find a text node.
   1.798 +
   1.799 +    while (!iter->IsDone())
   1.800 +    {
   1.801 +      if (iter->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
   1.802 +        // We found a text node, so position the document's
   1.803 +        // iterator at the beginning of the block, then get
   1.804 +        // the selection in terms of the string offset.
   1.805 +        nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
   1.806 +
   1.807 +        result = mIterator->PositionAt(content);
   1.808 +
   1.809 +        if (NS_FAILED(result))
   1.810 +        {
   1.811 +          UNLOCK_DOC(this);
   1.812 +          return result;
   1.813 +        }
   1.814 +
   1.815 +        result = FirstTextNodeInCurrentBlock(mIterator);
   1.816 +
   1.817 +        if (NS_FAILED(result))
   1.818 +        {
   1.819 +          UNLOCK_DOC(this);
   1.820 +          return result;
   1.821 +        }
   1.822 +
   1.823 +        mIteratorStatus = nsTextServicesDocument::eValid;
   1.824 +
   1.825 +        result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   1.826 +                                   mExtent, nullptr);
   1.827 +
   1.828 +        if (NS_FAILED(result))
   1.829 +        {
   1.830 +          UNLOCK_DOC(this);
   1.831 +          return result;
   1.832 +        }
   1.833 +
   1.834 +        result = GetSelection(aSelStatus, aSelOffset, aSelLength);
   1.835 +
   1.836 +        UNLOCK_DOC(this);
   1.837 +
   1.838 +        return result;
   1.839 +
   1.840 +      }
   1.841 +
   1.842 +      iter->Prev();
   1.843 +    }
   1.844 +  }
   1.845 +
   1.846 +  // If we get here, we didn't find any text node in the selection!
   1.847 +  // Create a range that extends from the end of the selection,
   1.848 +  // to the end of the document, then iterate forwards through
   1.849 +  // it till you find a text node!
   1.850 +
   1.851 +  result = selection->GetRangeAt(rangeCount - 1, getter_AddRefs(range));
   1.852 +
   1.853 +  if (NS_FAILED(result))
   1.854 +  {
   1.855 +    UNLOCK_DOC(this);
   1.856 +    return result;
   1.857 +  }
   1.858 +
   1.859 +  if (!range)
   1.860 +  {
   1.861 +    UNLOCK_DOC(this);
   1.862 +    return NS_ERROR_FAILURE;
   1.863 +  }
   1.864 +
   1.865 +  result = range->GetEndContainer(getter_AddRefs(parent));
   1.866 +
   1.867 +  if (NS_FAILED(result))
   1.868 +  {
   1.869 +    UNLOCK_DOC(this);
   1.870 +    return result;
   1.871 +  }
   1.872 +
   1.873 +  if (!parent)
   1.874 +  {
   1.875 +    UNLOCK_DOC(this);
   1.876 +    return NS_ERROR_FAILURE;
   1.877 +  }
   1.878 +
   1.879 +  result = range->GetEndOffset(&offset);
   1.880 +
   1.881 +  if (NS_FAILED(result))
   1.882 +  {
   1.883 +    UNLOCK_DOC(this);
   1.884 +    return result;
   1.885 +  }
   1.886 +
   1.887 +  result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, getter_AddRefs(range));
   1.888 +
   1.889 +  if (NS_FAILED(result))
   1.890 +  {
   1.891 +    UNLOCK_DOC(this);
   1.892 +    return result;
   1.893 +  }
   1.894 +
   1.895 +  result = range->GetCollapsed(&isCollapsed);
   1.896 +
   1.897 +  if (NS_FAILED(result))
   1.898 +  {
   1.899 +    UNLOCK_DOC(this);
   1.900 +    return result;
   1.901 +  }
   1.902 +
   1.903 +  if (isCollapsed)
   1.904 +  {
   1.905 +    // If we get here, the range is collapsed because there is nothing after
   1.906 +    // the current selection! Just return NS_OK;
   1.907 +
   1.908 +    UNLOCK_DOC(this);
   1.909 +    return NS_OK;
   1.910 +  }
   1.911 +
   1.912 +  result = CreateContentIterator(range, getter_AddRefs(iter));
   1.913 +
   1.914 +  if (NS_FAILED(result))
   1.915 +  {
   1.916 +    UNLOCK_DOC(this);
   1.917 +    return result;
   1.918 +  }
   1.919 +
   1.920 +  iter->First();
   1.921 +
   1.922 +  while (!iter->IsDone())
   1.923 +  {
   1.924 +    if (iter->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
   1.925 +      // We found a text node! Adjust the document's iterator to point
   1.926 +      // to the beginning of its text block, then get the current selection.
   1.927 +      nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
   1.928 +
   1.929 +      result = mIterator->PositionAt(content);
   1.930 +
   1.931 +      if (NS_FAILED(result))
   1.932 +      {
   1.933 +        UNLOCK_DOC(this);
   1.934 +        return result;
   1.935 +      }
   1.936 +
   1.937 +      result = FirstTextNodeInCurrentBlock(mIterator);
   1.938 +
   1.939 +      if (NS_FAILED(result))
   1.940 +      {
   1.941 +        UNLOCK_DOC(this);
   1.942 +        return result;
   1.943 +      }
   1.944 +
   1.945 +
   1.946 +      mIteratorStatus = nsTextServicesDocument::eValid;
   1.947 +
   1.948 +      result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   1.949 +                                 mExtent, nullptr);
   1.950 +
   1.951 +      if (NS_FAILED(result))
   1.952 +      {
   1.953 +        UNLOCK_DOC(this);
   1.954 +        return result;
   1.955 +      }
   1.956 +
   1.957 +      result = GetSelection(aSelStatus, aSelOffset, aSelLength);
   1.958 +
   1.959 +      UNLOCK_DOC(this);
   1.960 +
   1.961 +      return result;
   1.962 +    }
   1.963 +
   1.964 +    iter->Next();
   1.965 +  }
   1.966 +
   1.967 +  // If we get here, we didn't find any block before or inside
   1.968 +  // the selection! Just return OK.
   1.969 +
   1.970 +  UNLOCK_DOC(this);
   1.971 +
   1.972 +  return NS_OK;
   1.973 +}
   1.974 +
   1.975 +NS_IMETHODIMP
   1.976 +nsTextServicesDocument::PrevBlock()
   1.977 +{
   1.978 +  nsresult result = NS_OK;
   1.979 +
   1.980 +  NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
   1.981 +
   1.982 +  LOCK_DOC(this);
   1.983 +
   1.984 +  if (mIteratorStatus == nsTextServicesDocument::eIsDone)
   1.985 +    return NS_OK;
   1.986 +
   1.987 +  switch (mIteratorStatus)
   1.988 +  {
   1.989 +    case nsTextServicesDocument::eValid:
   1.990 +    case nsTextServicesDocument::eNext:
   1.991 +
   1.992 +      result = FirstTextNodeInPrevBlock(mIterator);
   1.993 +
   1.994 +      if (NS_FAILED(result))
   1.995 +      {
   1.996 +        mIteratorStatus = nsTextServicesDocument::eIsDone;
   1.997 +        UNLOCK_DOC(this);
   1.998 +        return result;
   1.999 +      }
  1.1000 +
  1.1001 +      if (mIterator->IsDone())
  1.1002 +      {
  1.1003 +        mIteratorStatus = nsTextServicesDocument::eIsDone;
  1.1004 +        UNLOCK_DOC(this);
  1.1005 +        return NS_OK;
  1.1006 +      }
  1.1007 +
  1.1008 +      mIteratorStatus = nsTextServicesDocument::eValid;
  1.1009 +      break;
  1.1010 +
  1.1011 +    case nsTextServicesDocument::ePrev:
  1.1012 +
  1.1013 +      // The iterator already points to the previous
  1.1014 +      // block, so don't do anything.
  1.1015 +
  1.1016 +      mIteratorStatus = nsTextServicesDocument::eValid;
  1.1017 +      break;
  1.1018 +
  1.1019 +    default:
  1.1020 +
  1.1021 +      mIteratorStatus = nsTextServicesDocument::eIsDone;
  1.1022 +      break;
  1.1023 +  }
  1.1024 +
  1.1025 +  // Keep track of prev and next blocks, just in case
  1.1026 +  // the text service blows away the current block.
  1.1027 +
  1.1028 +  if (mIteratorStatus == nsTextServicesDocument::eValid)
  1.1029 +  {
  1.1030 +    result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
  1.1031 +    result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
  1.1032 +  }
  1.1033 +  else
  1.1034 +  {
  1.1035 +    // We must be done!
  1.1036 +
  1.1037 +    mPrevTextBlock = nullptr;
  1.1038 +    mNextTextBlock = nullptr;
  1.1039 +  }
  1.1040 +
  1.1041 +  UNLOCK_DOC(this);
  1.1042 +
  1.1043 +  return result;
  1.1044 +}
  1.1045 +
  1.1046 +NS_IMETHODIMP
  1.1047 +nsTextServicesDocument::NextBlock()
  1.1048 +{
  1.1049 +  nsresult result = NS_OK;
  1.1050 +
  1.1051 +  NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
  1.1052 +
  1.1053 +  LOCK_DOC(this);
  1.1054 +
  1.1055 +  if (mIteratorStatus == nsTextServicesDocument::eIsDone)
  1.1056 +    return NS_OK;
  1.1057 +
  1.1058 +  switch (mIteratorStatus)
  1.1059 +  {
  1.1060 +    case nsTextServicesDocument::eValid:
  1.1061 +
  1.1062 +      // Advance the iterator to the next text block.
  1.1063 +
  1.1064 +      result = FirstTextNodeInNextBlock(mIterator);
  1.1065 +
  1.1066 +      if (NS_FAILED(result))
  1.1067 +      {
  1.1068 +        mIteratorStatus = nsTextServicesDocument::eIsDone;
  1.1069 +        UNLOCK_DOC(this);
  1.1070 +        return result;
  1.1071 +      }
  1.1072 +
  1.1073 +      if (mIterator->IsDone())
  1.1074 +      {
  1.1075 +        mIteratorStatus = nsTextServicesDocument::eIsDone;
  1.1076 +        UNLOCK_DOC(this);
  1.1077 +        return NS_OK;
  1.1078 +      }
  1.1079 +
  1.1080 +      mIteratorStatus = nsTextServicesDocument::eValid;
  1.1081 +      break;
  1.1082 +
  1.1083 +    case nsTextServicesDocument::eNext:
  1.1084 +
  1.1085 +      // The iterator already points to the next block,
  1.1086 +      // so don't do anything to it!
  1.1087 +
  1.1088 +      mIteratorStatus = nsTextServicesDocument::eValid;
  1.1089 +      break;
  1.1090 +
  1.1091 +    case nsTextServicesDocument::ePrev:
  1.1092 +
  1.1093 +      // If the iterator is pointing to the previous block,
  1.1094 +      // we know that there is no next text block! Just
  1.1095 +      // fall through to the default case!
  1.1096 +
  1.1097 +    default:
  1.1098 +
  1.1099 +      mIteratorStatus = nsTextServicesDocument::eIsDone;
  1.1100 +      break;
  1.1101 +  }
  1.1102 +
  1.1103 +  // Keep track of prev and next blocks, just in case
  1.1104 +  // the text service blows away the current block.
  1.1105 +
  1.1106 +  if (mIteratorStatus == nsTextServicesDocument::eValid)
  1.1107 +  {
  1.1108 +    result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
  1.1109 +    result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
  1.1110 +  }
  1.1111 +  else
  1.1112 +  {
  1.1113 +    // We must be done.
  1.1114 +
  1.1115 +    mPrevTextBlock = nullptr;
  1.1116 +    mNextTextBlock = nullptr;
  1.1117 +  }
  1.1118 +
  1.1119 +
  1.1120 +  UNLOCK_DOC(this);
  1.1121 +
  1.1122 +  return result;
  1.1123 +}
  1.1124 +
  1.1125 +NS_IMETHODIMP
  1.1126 +nsTextServicesDocument::IsDone(bool *aIsDone)
  1.1127 +{
  1.1128 +  NS_ENSURE_TRUE(aIsDone, NS_ERROR_NULL_POINTER);
  1.1129 +
  1.1130 +  *aIsDone = false;
  1.1131 +
  1.1132 +  NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
  1.1133 +
  1.1134 +  LOCK_DOC(this);
  1.1135 +
  1.1136 +  *aIsDone = (mIteratorStatus == nsTextServicesDocument::eIsDone) ? true : false;
  1.1137 +
  1.1138 +  UNLOCK_DOC(this);
  1.1139 +
  1.1140 +  return NS_OK;
  1.1141 +}
  1.1142 +
  1.1143 +NS_IMETHODIMP
  1.1144 +nsTextServicesDocument::SetSelection(int32_t aOffset, int32_t aLength)
  1.1145 +{
  1.1146 +  nsresult result;
  1.1147 +
  1.1148 +  NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
  1.1149 +
  1.1150 +  LOCK_DOC(this);
  1.1151 +
  1.1152 +  result = SetSelectionInternal(aOffset, aLength, true);
  1.1153 +
  1.1154 +  UNLOCK_DOC(this);
  1.1155 +
  1.1156 +  //**** KDEBUG ****
  1.1157 +  // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1.1158 +  //**** KDEBUG ****
  1.1159 +
  1.1160 +  return result;
  1.1161 +}
  1.1162 +
  1.1163 +NS_IMETHODIMP
  1.1164 +nsTextServicesDocument::ScrollSelectionIntoView()
  1.1165 +{
  1.1166 +  nsresult result;
  1.1167 +
  1.1168 +  NS_ENSURE_TRUE(mSelCon, NS_ERROR_FAILURE);
  1.1169 +
  1.1170 +  LOCK_DOC(this);
  1.1171 +
  1.1172 +  // After ScrollSelectionIntoView(), the pending notifications might be flushed
  1.1173 +  // and PresShell/PresContext/Frames may be dead. See bug 418470.
  1.1174 +  result = mSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION,
  1.1175 +                                            nsISelectionController::SCROLL_SYNCHRONOUS);
  1.1176 +
  1.1177 +  UNLOCK_DOC(this);
  1.1178 +
  1.1179 +  return result;
  1.1180 +}
  1.1181 +
  1.1182 +NS_IMETHODIMP
  1.1183 +nsTextServicesDocument::DeleteSelection()
  1.1184 +{
  1.1185 +  nsresult result = NS_OK;
  1.1186 +
  1.1187 +  // We don't allow deletion during a collapsed selection!
  1.1188 +  nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
  1.1189 +  NS_ASSERTION(editor, "DeleteSelection called without an editor present!"); 
  1.1190 +  NS_ASSERTION(SelectionIsValid(), "DeleteSelection called without a valid selection!"); 
  1.1191 +
  1.1192 +  if (!editor || !SelectionIsValid())
  1.1193 +    return NS_ERROR_FAILURE;
  1.1194 +
  1.1195 +  if (SelectionIsCollapsed())
  1.1196 +    return NS_OK;
  1.1197 +
  1.1198 +  LOCK_DOC(this);
  1.1199 +
  1.1200 +  //**** KDEBUG ****
  1.1201 +  // printf("\n---- Before Delete\n");
  1.1202 +  // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1.1203 +  // PrintOffsetTable();
  1.1204 +  //**** KDEBUG ****
  1.1205 +
  1.1206 +  // If we have an mExtent, save off its current set of
  1.1207 +  // end points so we can compare them against mExtent's
  1.1208 +  // set after the deletion of the content.
  1.1209 +
  1.1210 +  nsCOMPtr<nsIDOMNode> origStartNode, origEndNode;
  1.1211 +  int32_t origStartOffset = 0, origEndOffset = 0;
  1.1212 +
  1.1213 +  if (mExtent)
  1.1214 +  {
  1.1215 +    result = GetRangeEndPoints(mExtent,
  1.1216 +                               getter_AddRefs(origStartNode), &origStartOffset,
  1.1217 +                               getter_AddRefs(origEndNode), &origEndOffset);
  1.1218 +
  1.1219 +    if (NS_FAILED(result))
  1.1220 +    {
  1.1221 +      UNLOCK_DOC(this);
  1.1222 +      return result;
  1.1223 +    }
  1.1224 +  }
  1.1225 +
  1.1226 +  int32_t i, selLength;
  1.1227 +  OffsetEntry *entry, *newEntry;
  1.1228 +
  1.1229 +  for (i = mSelStartIndex; i <= mSelEndIndex; i++)
  1.1230 +  {
  1.1231 +    entry = mOffsetTable[i];
  1.1232 +
  1.1233 +    if (i == mSelStartIndex)
  1.1234 +    {
  1.1235 +      // Calculate the length of the selection. Note that the
  1.1236 +      // selection length can be zero if the start of the selection
  1.1237 +      // is at the very end of a text node entry.
  1.1238 +
  1.1239 +      if (entry->mIsInsertedText)
  1.1240 +      {
  1.1241 +        // Inserted text offset entries have no width when
  1.1242 +        // talking in terms of string offsets! If the beginning
  1.1243 +        // of the selection is in an inserted text offset entry,
  1.1244 +        // the caret is always at the end of the entry!
  1.1245 +
  1.1246 +        selLength = 0;
  1.1247 +      }
  1.1248 +      else
  1.1249 +        selLength = entry->mLength - (mSelStartOffset - entry->mStrOffset);
  1.1250 +
  1.1251 +      if (selLength > 0 && mSelStartOffset > entry->mStrOffset)
  1.1252 +      {
  1.1253 +        // Selection doesn't start at the beginning of the
  1.1254 +        // text node entry. We need to split this entry into
  1.1255 +        // two pieces, the piece before the selection, and
  1.1256 +        // the piece inside the selection.
  1.1257 +
  1.1258 +        result = SplitOffsetEntry(i, selLength);
  1.1259 +
  1.1260 +        if (NS_FAILED(result))
  1.1261 +        {
  1.1262 +          UNLOCK_DOC(this);
  1.1263 +          return result;
  1.1264 +        }
  1.1265 +
  1.1266 +        // Adjust selection indexes to account for new entry:
  1.1267 +
  1.1268 +        ++mSelStartIndex;
  1.1269 +        ++mSelEndIndex;
  1.1270 +        ++i;
  1.1271 +
  1.1272 +        entry = mOffsetTable[i];
  1.1273 +      }
  1.1274 +
  1.1275 +
  1.1276 +      if (selLength > 0 && mSelStartIndex < mSelEndIndex)
  1.1277 +      {
  1.1278 +        // The entire entry is contained in the selection. Mark the
  1.1279 +        // entry invalid.
  1.1280 +
  1.1281 +        entry->mIsValid = false;
  1.1282 +      }
  1.1283 +    }
  1.1284 +
  1.1285 +  //**** KDEBUG ****
  1.1286 +  // printf("\n---- Middle Delete\n");
  1.1287 +  // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1.1288 +  // PrintOffsetTable();
  1.1289 +  //**** KDEBUG ****
  1.1290 +
  1.1291 +    if (i == mSelEndIndex)
  1.1292 +    {
  1.1293 +      if (entry->mIsInsertedText)
  1.1294 +      {
  1.1295 +        // Inserted text offset entries have no width when
  1.1296 +        // talking in terms of string offsets! If the end
  1.1297 +        // of the selection is in an inserted text offset entry,
  1.1298 +        // the selection includes the entire entry!
  1.1299 +
  1.1300 +        entry->mIsValid = false;
  1.1301 +      }
  1.1302 +      else
  1.1303 +      {
  1.1304 +        // Calculate the length of the selection. Note that the
  1.1305 +        // selection length can be zero if the end of the selection
  1.1306 +        // is at the very beginning of a text node entry.
  1.1307 +
  1.1308 +        selLength = mSelEndOffset - entry->mStrOffset;
  1.1309 +
  1.1310 +        if (selLength > 0 && mSelEndOffset < entry->mStrOffset + entry->mLength)
  1.1311 +        {
  1.1312 +          // mStrOffset is guaranteed to be inside the selection, even
  1.1313 +          // when mSelStartIndex == mSelEndIndex.
  1.1314 +
  1.1315 +          result = SplitOffsetEntry(i, entry->mLength - selLength);
  1.1316 +
  1.1317 +          if (NS_FAILED(result))
  1.1318 +          {
  1.1319 +            UNLOCK_DOC(this);
  1.1320 +            return result;
  1.1321 +          }
  1.1322 +
  1.1323 +          // Update the entry fields:
  1.1324 +
  1.1325 +          newEntry = mOffsetTable[i+1];
  1.1326 +          newEntry->mNodeOffset = entry->mNodeOffset;
  1.1327 +        }
  1.1328 +
  1.1329 +
  1.1330 +        if (selLength > 0 && mSelEndOffset == entry->mStrOffset + entry->mLength)
  1.1331 +        {
  1.1332 +          // The entire entry is contained in the selection. Mark the
  1.1333 +          // entry invalid.
  1.1334 +
  1.1335 +          entry->mIsValid = false;
  1.1336 +        }
  1.1337 +      }
  1.1338 +    }
  1.1339 +
  1.1340 +    if (i != mSelStartIndex && i != mSelEndIndex)
  1.1341 +    {
  1.1342 +      // The entire entry is contained in the selection. Mark the
  1.1343 +      // entry invalid.
  1.1344 +
  1.1345 +      entry->mIsValid = false;
  1.1346 +    }
  1.1347 +  }
  1.1348 +
  1.1349 +  // Make sure mIterator always points to something valid!
  1.1350 +
  1.1351 +  AdjustContentIterator();
  1.1352 +
  1.1353 +  // Now delete the actual content!
  1.1354 +
  1.1355 +  result = editor->DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip);
  1.1356 +
  1.1357 +  if (NS_FAILED(result))
  1.1358 +  {
  1.1359 +    UNLOCK_DOC(this);
  1.1360 +    return result;
  1.1361 +  }
  1.1362 +
  1.1363 +  // Now that we've actually deleted the selected content,
  1.1364 +  // check to see if our mExtent has changed, if so, then
  1.1365 +  // we have to create a new content iterator!
  1.1366 +
  1.1367 +  if (origStartNode && origEndNode)
  1.1368 +  {
  1.1369 +    nsCOMPtr<nsIDOMNode> curStartNode, curEndNode;
  1.1370 +    int32_t curStartOffset = 0, curEndOffset = 0;
  1.1371 +
  1.1372 +    result = GetRangeEndPoints(mExtent,
  1.1373 +                               getter_AddRefs(curStartNode), &curStartOffset,
  1.1374 +                               getter_AddRefs(curEndNode), &curEndOffset);
  1.1375 +
  1.1376 +    if (NS_FAILED(result))
  1.1377 +    {
  1.1378 +      UNLOCK_DOC(this);
  1.1379 +      return result;
  1.1380 +    }
  1.1381 +
  1.1382 +    if (origStartNode != curStartNode || origEndNode != curEndNode)
  1.1383 +    {
  1.1384 +      // The range has changed, so we need to create a new content
  1.1385 +      // iterator based on the new range.
  1.1386 +
  1.1387 +      nsCOMPtr<nsIContent> curContent;
  1.1388 +
  1.1389 +      if (mIteratorStatus != nsTextServicesDocument::eIsDone) {
  1.1390 +        // The old iterator is still pointing to something valid,
  1.1391 +        // so get its current node so we can restore it after we
  1.1392 +        // create the new iterator!
  1.1393 +
  1.1394 +        curContent = mIterator->GetCurrentNode()
  1.1395 +                     ? mIterator->GetCurrentNode()->AsContent()
  1.1396 +                     : nullptr;
  1.1397 +      }
  1.1398 +
  1.1399 +      // Create the new iterator.
  1.1400 +
  1.1401 +      result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
  1.1402 +
  1.1403 +      if (NS_FAILED(result))
  1.1404 +      {
  1.1405 +        UNLOCK_DOC(this);
  1.1406 +        return result;
  1.1407 +      }
  1.1408 +
  1.1409 +      // Now make the new iterator point to the content node
  1.1410 +      // the old one was pointing at.
  1.1411 +
  1.1412 +      if (curContent)
  1.1413 +      {
  1.1414 +        result = mIterator->PositionAt(curContent);
  1.1415 +
  1.1416 +        if (NS_FAILED(result))
  1.1417 +          mIteratorStatus = eIsDone;
  1.1418 +        else
  1.1419 +          mIteratorStatus = eValid;
  1.1420 +      }
  1.1421 +    }
  1.1422 +  }
  1.1423 +
  1.1424 +  entry = 0;
  1.1425 +
  1.1426 +  // Move the caret to the end of the first valid entry.
  1.1427 +  // Start with mSelStartIndex since it may still be valid.
  1.1428 +
  1.1429 +  for (i = mSelStartIndex; !entry && i >= 0; i--)
  1.1430 +  {
  1.1431 +    entry = mOffsetTable[i];
  1.1432 +
  1.1433 +    if (!entry->mIsValid)
  1.1434 +      entry = 0;
  1.1435 +    else
  1.1436 +    {
  1.1437 +      mSelStartIndex  = mSelEndIndex  = i;
  1.1438 +      mSelStartOffset = mSelEndOffset = entry->mStrOffset + entry->mLength;
  1.1439 +    }
  1.1440 +  }
  1.1441 +
  1.1442 +  // If we still don't have a valid entry, move the caret
  1.1443 +  // to the next valid entry after the selection:
  1.1444 +
  1.1445 +  for (i = mSelEndIndex; !entry && i < int32_t(mOffsetTable.Length()); i++)
  1.1446 +  {
  1.1447 +    entry = mOffsetTable[i];
  1.1448 +
  1.1449 +    if (!entry->mIsValid)
  1.1450 +      entry = 0;
  1.1451 +    else
  1.1452 +    {
  1.1453 +      mSelStartIndex = mSelEndIndex = i;
  1.1454 +      mSelStartOffset = mSelEndOffset = entry->mStrOffset;
  1.1455 +    }
  1.1456 +  }
  1.1457 +
  1.1458 +  if (entry)
  1.1459 +    result = SetSelection(mSelStartOffset, 0);
  1.1460 +  else
  1.1461 +  {
  1.1462 +    // Uuughh we have no valid offset entry to place our
  1.1463 +    // caret ... just mark the selection invalid.
  1.1464 +
  1.1465 +    mSelStartIndex  = mSelEndIndex  = -1;
  1.1466 +    mSelStartOffset = mSelEndOffset = -1;
  1.1467 +  }
  1.1468 +
  1.1469 +  // Now remove any invalid entries from the offset table.
  1.1470 +
  1.1471 +  result = RemoveInvalidOffsetEntries();
  1.1472 +
  1.1473 +  //**** KDEBUG ****
  1.1474 +  // printf("\n---- After Delete\n");
  1.1475 +  // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1.1476 +  // PrintOffsetTable();
  1.1477 +  //**** KDEBUG ****
  1.1478 +
  1.1479 +  UNLOCK_DOC(this);
  1.1480 +
  1.1481 +  return result;
  1.1482 +}
  1.1483 +
  1.1484 +NS_IMETHODIMP
  1.1485 +nsTextServicesDocument::InsertText(const nsString *aText)
  1.1486 +{
  1.1487 +  nsresult result = NS_OK;
  1.1488 +
  1.1489 +  nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
  1.1490 +  NS_ASSERTION(editor, "InsertText called without an editor present!"); 
  1.1491 +
  1.1492 +  if (!editor || !SelectionIsValid())
  1.1493 +    return NS_ERROR_FAILURE;
  1.1494 +
  1.1495 +  NS_ENSURE_TRUE(aText, NS_ERROR_NULL_POINTER);
  1.1496 +
  1.1497 +  // If the selection is not collapsed, we need to save
  1.1498 +  // off the selection offsets so we can restore the
  1.1499 +  // selection and delete the selected content after we've
  1.1500 +  // inserted the new text. This is necessary to try and
  1.1501 +  // retain as much of the original style of the content
  1.1502 +  // being deleted.
  1.1503 +
  1.1504 +  bool collapsedSelection = SelectionIsCollapsed();
  1.1505 +  int32_t savedSelOffset = mSelStartOffset;
  1.1506 +  int32_t savedSelLength = mSelEndOffset - mSelStartOffset;
  1.1507 +
  1.1508 +  if (!collapsedSelection)
  1.1509 +  {
  1.1510 +    // Collapse to the start of the current selection
  1.1511 +    // for the insert!
  1.1512 +
  1.1513 +    result = SetSelection(mSelStartOffset, 0);
  1.1514 +
  1.1515 +    NS_ENSURE_SUCCESS(result, result);
  1.1516 +  }
  1.1517 +
  1.1518 +
  1.1519 +  LOCK_DOC(this);
  1.1520 +
  1.1521 +  result = editor->BeginTransaction();
  1.1522 +
  1.1523 +  if (NS_FAILED(result))
  1.1524 +  {
  1.1525 +    UNLOCK_DOC(this);
  1.1526 +    return result;
  1.1527 +  }
  1.1528 +
  1.1529 +  nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(editor, &result));
  1.1530 +  if (textEditor)
  1.1531 +    result = textEditor->InsertText(*aText);
  1.1532 +
  1.1533 +  if (NS_FAILED(result))
  1.1534 +  {
  1.1535 +    editor->EndTransaction();
  1.1536 +    UNLOCK_DOC(this);
  1.1537 +    return result;
  1.1538 +  }
  1.1539 +
  1.1540 +  //**** KDEBUG ****
  1.1541 +  // printf("\n---- Before Insert\n");
  1.1542 +  // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1.1543 +  // PrintOffsetTable();
  1.1544 +  //**** KDEBUG ****
  1.1545 +
  1.1546 +  int32_t strLength = aText->Length();
  1.1547 +  uint32_t i;
  1.1548 +
  1.1549 +  nsCOMPtr<nsISelection> selection;
  1.1550 +  OffsetEntry *itEntry;
  1.1551 +  OffsetEntry *entry = mOffsetTable[mSelStartIndex];
  1.1552 +  void *node         = entry->mNode;
  1.1553 +
  1.1554 +  NS_ASSERTION((entry->mIsValid), "Invalid insertion point!");
  1.1555 +
  1.1556 +  if (entry->mStrOffset == mSelStartOffset)
  1.1557 +  {
  1.1558 +    if (entry->mIsInsertedText)
  1.1559 +    {
  1.1560 +      // If the caret is in an inserted text offset entry,
  1.1561 +      // we simply insert the text at the end of the entry.
  1.1562 +
  1.1563 +      entry->mLength += strLength;
  1.1564 +    }
  1.1565 +    else
  1.1566 +    {
  1.1567 +      // Insert an inserted text offset entry before the current
  1.1568 +      // entry!
  1.1569 +
  1.1570 +      itEntry = new OffsetEntry(entry->mNode, entry->mStrOffset, strLength);
  1.1571 +
  1.1572 +      if (!itEntry)
  1.1573 +      {
  1.1574 +        editor->EndTransaction();
  1.1575 +        UNLOCK_DOC(this);
  1.1576 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1577 +      }
  1.1578 +
  1.1579 +      itEntry->mIsInsertedText = true;
  1.1580 +      itEntry->mNodeOffset = entry->mNodeOffset;
  1.1581 +
  1.1582 +      if (!mOffsetTable.InsertElementAt(mSelStartIndex, itEntry))
  1.1583 +      {
  1.1584 +        editor->EndTransaction();
  1.1585 +        UNLOCK_DOC(this);
  1.1586 +        return NS_ERROR_FAILURE;
  1.1587 +      }
  1.1588 +    }
  1.1589 +  }
  1.1590 +  else if ((entry->mStrOffset + entry->mLength) == mSelStartOffset)
  1.1591 +  {
  1.1592 +    // We are inserting text at the end of the current offset entry.
  1.1593 +    // Look at the next valid entry in the table. If it's an inserted
  1.1594 +    // text entry, add to its length and adjust its node offset. If
  1.1595 +    // it isn't, add a new inserted text entry.
  1.1596 +
  1.1597 +    i       = mSelStartIndex + 1;
  1.1598 +    itEntry = 0;
  1.1599 +
  1.1600 +    if (mOffsetTable.Length() > i)
  1.1601 +    {
  1.1602 +      itEntry = mOffsetTable[i];
  1.1603 +
  1.1604 +      if (!itEntry)
  1.1605 +      {
  1.1606 +        editor->EndTransaction();
  1.1607 +        UNLOCK_DOC(this);
  1.1608 +        return NS_ERROR_FAILURE;
  1.1609 +      }
  1.1610 +
  1.1611 +      // Check if the entry is a match. If it isn't, set
  1.1612 +      // iEntry to zero.
  1.1613 +
  1.1614 +      if (!itEntry->mIsInsertedText || itEntry->mStrOffset != mSelStartOffset)
  1.1615 +        itEntry = 0;
  1.1616 +    }
  1.1617 +
  1.1618 +    if (!itEntry)
  1.1619 +    {
  1.1620 +      // We didn't find an inserted text offset entry, so
  1.1621 +      // create one.
  1.1622 +
  1.1623 +      itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, 0);
  1.1624 +
  1.1625 +      if (!itEntry)
  1.1626 +      {
  1.1627 +        editor->EndTransaction();
  1.1628 +        UNLOCK_DOC(this);
  1.1629 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1630 +      }
  1.1631 +
  1.1632 +      itEntry->mNodeOffset = entry->mNodeOffset + entry->mLength;
  1.1633 +      itEntry->mIsInsertedText = true;
  1.1634 +
  1.1635 +      if (!mOffsetTable.InsertElementAt(i, itEntry))
  1.1636 +      {
  1.1637 +        delete itEntry;
  1.1638 +        return NS_ERROR_FAILURE;
  1.1639 +      }
  1.1640 +    }
  1.1641 +
  1.1642 +    // We have a valid inserted text offset entry. Update its
  1.1643 +    // length, adjust the selection indexes, and make sure the
  1.1644 +    // caret is properly placed!
  1.1645 +
  1.1646 +    itEntry->mLength += strLength;
  1.1647 +
  1.1648 +    mSelStartIndex = mSelEndIndex = i;
  1.1649 +          
  1.1650 +    result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  1.1651 +
  1.1652 +    if (NS_FAILED(result))
  1.1653 +    {
  1.1654 +      editor->EndTransaction();
  1.1655 +      UNLOCK_DOC(this);
  1.1656 +      return result;
  1.1657 +    }
  1.1658 +
  1.1659 +    result = selection->Collapse(itEntry->mNode, itEntry->mNodeOffset + itEntry->mLength);
  1.1660 +        
  1.1661 +    if (NS_FAILED(result))
  1.1662 +    {
  1.1663 +      editor->EndTransaction();
  1.1664 +      UNLOCK_DOC(this);
  1.1665 +      return result;
  1.1666 +    }
  1.1667 +  }
  1.1668 +  else if ((entry->mStrOffset + entry->mLength) > mSelStartOffset)
  1.1669 +  {
  1.1670 +    // We are inserting text into the middle of the current offset entry.
  1.1671 +    // split the current entry into two parts, then insert an inserted text
  1.1672 +    // entry between them!
  1.1673 +
  1.1674 +    i = entry->mLength - (mSelStartOffset - entry->mStrOffset);
  1.1675 +
  1.1676 +    result = SplitOffsetEntry(mSelStartIndex, i);
  1.1677 +
  1.1678 +    if (NS_FAILED(result))
  1.1679 +    {
  1.1680 +      editor->EndTransaction();
  1.1681 +      UNLOCK_DOC(this);
  1.1682 +      return result;
  1.1683 +    }
  1.1684 +
  1.1685 +    itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, strLength);
  1.1686 +
  1.1687 +    if (!itEntry)
  1.1688 +    {
  1.1689 +      editor->EndTransaction();
  1.1690 +      UNLOCK_DOC(this);
  1.1691 +      return NS_ERROR_OUT_OF_MEMORY;
  1.1692 +    }
  1.1693 +
  1.1694 +    itEntry->mIsInsertedText = true;
  1.1695 +    itEntry->mNodeOffset     = entry->mNodeOffset + entry->mLength;
  1.1696 +
  1.1697 +    if (!mOffsetTable.InsertElementAt(mSelStartIndex + 1, itEntry))
  1.1698 +    {
  1.1699 +      editor->EndTransaction();
  1.1700 +      UNLOCK_DOC(this);
  1.1701 +      return NS_ERROR_FAILURE;
  1.1702 +    }
  1.1703 +
  1.1704 +    mSelEndIndex = ++mSelStartIndex;
  1.1705 +  }
  1.1706 +
  1.1707 +  // We've just finished inserting an inserted text offset entry.
  1.1708 +  // update all entries with the same mNode pointer that follow
  1.1709 +  // it in the table!
  1.1710 +
  1.1711 +  for (i = mSelStartIndex + 1; i < mOffsetTable.Length(); i++)
  1.1712 +  {
  1.1713 +    entry = mOffsetTable[i];
  1.1714 +
  1.1715 +    if (entry->mNode == node)
  1.1716 +    {
  1.1717 +      if (entry->mIsValid)
  1.1718 +        entry->mNodeOffset += strLength;
  1.1719 +    }
  1.1720 +    else
  1.1721 +      break;
  1.1722 +  }
  1.1723 +
  1.1724 +  //**** KDEBUG ****
  1.1725 +  // printf("\n---- After Insert\n");
  1.1726 +  // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1.1727 +  // PrintOffsetTable();
  1.1728 +  //**** KDEBUG ****
  1.1729 +
  1.1730 +  if (!collapsedSelection)
  1.1731 +  {
  1.1732 +    result = SetSelection(savedSelOffset, savedSelLength);
  1.1733 +
  1.1734 +    if (NS_FAILED(result))
  1.1735 +    {
  1.1736 +      editor->EndTransaction();
  1.1737 +      UNLOCK_DOC(this);
  1.1738 +      return result;
  1.1739 +    }
  1.1740 +
  1.1741 +    result = DeleteSelection();
  1.1742 +  
  1.1743 +    if (NS_FAILED(result))
  1.1744 +    {
  1.1745 +      editor->EndTransaction();
  1.1746 +      UNLOCK_DOC(this);
  1.1747 +      return result;
  1.1748 +    }
  1.1749 +  }
  1.1750 +
  1.1751 +  result = editor->EndTransaction();
  1.1752 +
  1.1753 +  UNLOCK_DOC(this);
  1.1754 +
  1.1755 +  return result;
  1.1756 +}
  1.1757 +
  1.1758 +NS_IMETHODIMP
  1.1759 +nsTextServicesDocument::DidInsertNode(nsIDOMNode *aNode,
  1.1760 +                                      nsIDOMNode *aParent,
  1.1761 +                                      int32_t     aPosition,
  1.1762 +                                      nsresult    aResult)
  1.1763 +{
  1.1764 +  return NS_OK;
  1.1765 +}
  1.1766 +
  1.1767 +NS_IMETHODIMP
  1.1768 +nsTextServicesDocument::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
  1.1769 +{
  1.1770 +  NS_ENSURE_SUCCESS(aResult, NS_OK);
  1.1771 +
  1.1772 +  NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
  1.1773 +
  1.1774 +  //**** KDEBUG ****
  1.1775 +  // printf("** DeleteNode: 0x%.8x\n", aChild);
  1.1776 +  // fflush(stdout);
  1.1777 +  //**** KDEBUG ****
  1.1778 +
  1.1779 +  LOCK_DOC(this);
  1.1780 +
  1.1781 +  int32_t nodeIndex = 0;
  1.1782 +  bool hasEntry = false;
  1.1783 +  OffsetEntry *entry;
  1.1784 +
  1.1785 +  nsresult result = NodeHasOffsetEntry(&mOffsetTable, aChild, &hasEntry, &nodeIndex);
  1.1786 +
  1.1787 +  if (NS_FAILED(result))
  1.1788 +  {
  1.1789 +    UNLOCK_DOC(this);
  1.1790 +    return result;
  1.1791 +  }
  1.1792 +
  1.1793 +  if (!hasEntry)
  1.1794 +  {
  1.1795 +    // It's okay if the node isn't in the offset table, the
  1.1796 +    // editor could be cleaning house.
  1.1797 +
  1.1798 +    UNLOCK_DOC(this);
  1.1799 +    return NS_OK;
  1.1800 +  }
  1.1801 +
  1.1802 +  nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mIterator->GetCurrentNode());
  1.1803 +
  1.1804 +  if (node && node == aChild &&
  1.1805 +      mIteratorStatus != nsTextServicesDocument::eIsDone)
  1.1806 +  {
  1.1807 +    // XXX: This should never really happen because
  1.1808 +    // AdjustContentIterator() should have been called prior
  1.1809 +    // to the delete to try and position the iterator on the
  1.1810 +    // next valid text node in the offset table, and if there
  1.1811 +    // wasn't a next, it would've set mIteratorStatus to eIsDone.
  1.1812 +
  1.1813 +    NS_ERROR("DeleteNode called for current iterator node."); 
  1.1814 +  }
  1.1815 +
  1.1816 +  int32_t tcount = mOffsetTable.Length();
  1.1817 +
  1.1818 +  while (nodeIndex < tcount)
  1.1819 +  {
  1.1820 +    entry = mOffsetTable[nodeIndex];
  1.1821 +
  1.1822 +    if (!entry)
  1.1823 +    {
  1.1824 +      UNLOCK_DOC(this);
  1.1825 +      return NS_ERROR_FAILURE;
  1.1826 +    }
  1.1827 +
  1.1828 +    if (entry->mNode == aChild)
  1.1829 +    {
  1.1830 +      entry->mIsValid = false;
  1.1831 +    }
  1.1832 +
  1.1833 +    nodeIndex++;
  1.1834 +  }
  1.1835 +
  1.1836 +  UNLOCK_DOC(this);
  1.1837 +
  1.1838 +  return NS_OK;
  1.1839 +}
  1.1840 +
  1.1841 +NS_IMETHODIMP
  1.1842 +nsTextServicesDocument::DidSplitNode(nsIDOMNode *aExistingRightNode,
  1.1843 +                                     int32_t     aOffset,
  1.1844 +                                     nsIDOMNode *aNewLeftNode,
  1.1845 +                                     nsresult    aResult)
  1.1846 +{
  1.1847 +  //**** KDEBUG ****
  1.1848 +  // printf("** SplitNode: 0x%.8x  %d  0x%.8x\n", aExistingRightNode, aOffset, aNewLeftNode);
  1.1849 +  // fflush(stdout);
  1.1850 +  //**** KDEBUG ****
  1.1851 +  return NS_OK;
  1.1852 +}
  1.1853 +
  1.1854 +NS_IMETHODIMP
  1.1855 +nsTextServicesDocument::DidJoinNodes(nsIDOMNode  *aLeftNode,
  1.1856 +                                     nsIDOMNode  *aRightNode,
  1.1857 +                                     nsIDOMNode  *aParent,
  1.1858 +                                     nsresult     aResult)
  1.1859 +{
  1.1860 +  NS_ENSURE_SUCCESS(aResult, NS_OK);
  1.1861 +
  1.1862 +  int32_t i;
  1.1863 +  uint16_t type;
  1.1864 +  nsresult result;
  1.1865 +
  1.1866 +  //**** KDEBUG ****
  1.1867 +  // printf("** JoinNodes: 0x%.8x  0x%.8x  0x%.8x\n", aLeftNode, aRightNode, aParent);
  1.1868 +  // fflush(stdout);
  1.1869 +  //**** KDEBUG ****
  1.1870 +
  1.1871 +  // Make sure that both nodes are text nodes -- otherwise we don't care.
  1.1872 +
  1.1873 +  result = aLeftNode->GetNodeType(&type);
  1.1874 +  NS_ENSURE_SUCCESS(result, NS_OK);
  1.1875 +  if (nsIDOMNode::TEXT_NODE != type) {
  1.1876 +    return NS_OK;
  1.1877 +  }
  1.1878 +
  1.1879 +  result = aRightNode->GetNodeType(&type);
  1.1880 +  NS_ENSURE_SUCCESS(result, NS_OK);
  1.1881 +  if (nsIDOMNode::TEXT_NODE != type) {
  1.1882 +    return NS_OK;
  1.1883 +  }
  1.1884 +
  1.1885 +  // Note: The editor merges the contents of the left node into the
  1.1886 +  //       contents of the right.
  1.1887 +
  1.1888 +  int32_t leftIndex = 0;
  1.1889 +  int32_t rightIndex = 0;
  1.1890 +  bool leftHasEntry = false;
  1.1891 +  bool rightHasEntry = false;
  1.1892 +
  1.1893 +  result = NodeHasOffsetEntry(&mOffsetTable, aLeftNode, &leftHasEntry, &leftIndex);
  1.1894 +
  1.1895 +  NS_ENSURE_SUCCESS(result, result);
  1.1896 +
  1.1897 +  if (!leftHasEntry)
  1.1898 +  {
  1.1899 +    // It's okay if the node isn't in the offset table, the
  1.1900 +    // editor could be cleaning house.
  1.1901 +    return NS_OK;
  1.1902 +  }
  1.1903 +
  1.1904 +  result = NodeHasOffsetEntry(&mOffsetTable, aRightNode, &rightHasEntry, &rightIndex);
  1.1905 +
  1.1906 +  NS_ENSURE_SUCCESS(result, result);
  1.1907 +
  1.1908 +  if (!rightHasEntry)
  1.1909 +  {
  1.1910 +    // It's okay if the node isn't in the offset table, the
  1.1911 +    // editor could be cleaning house.
  1.1912 +    return NS_OK;
  1.1913 +  }
  1.1914 +
  1.1915 +  NS_ASSERTION(leftIndex < rightIndex, "Indexes out of order.");
  1.1916 +
  1.1917 +  if (leftIndex > rightIndex)
  1.1918 +  {
  1.1919 +    // Don't know how to handle this situation.
  1.1920 +    return NS_ERROR_FAILURE;
  1.1921 +  }
  1.1922 +
  1.1923 +  LOCK_DOC(this);
  1.1924 +
  1.1925 +  OffsetEntry *entry = mOffsetTable[rightIndex];
  1.1926 +  NS_ASSERTION(entry->mNodeOffset == 0, "Unexpected offset value for rightIndex.");
  1.1927 +
  1.1928 +  // Run through the table and change all entries referring to
  1.1929 +  // the left node so that they now refer to the right node:
  1.1930 +
  1.1931 +  nsAutoString str;
  1.1932 +  result = aLeftNode->GetNodeValue(str);
  1.1933 +  int32_t nodeLength = str.Length();
  1.1934 +
  1.1935 +  for (i = leftIndex; i < rightIndex; i++)
  1.1936 +  {
  1.1937 +    entry = mOffsetTable[i];
  1.1938 +
  1.1939 +    if (entry->mNode == aLeftNode)
  1.1940 +    {
  1.1941 +      if (entry->mIsValid)
  1.1942 +        entry->mNode = aRightNode;
  1.1943 +    }
  1.1944 +    else
  1.1945 +      break;
  1.1946 +  }
  1.1947 +
  1.1948 +  // Run through the table and adjust the node offsets
  1.1949 +  // for all entries referring to the right node.
  1.1950 +
  1.1951 +  for (i = rightIndex; i < int32_t(mOffsetTable.Length()); i++)
  1.1952 +  {
  1.1953 +    entry = mOffsetTable[i];
  1.1954 +
  1.1955 +    if (entry->mNode == aRightNode)
  1.1956 +    {
  1.1957 +      if (entry->mIsValid)
  1.1958 +        entry->mNodeOffset += nodeLength;
  1.1959 +    }
  1.1960 +    else
  1.1961 +      break;
  1.1962 +  }
  1.1963 +
  1.1964 +  // Now check to see if the iterator is pointing to the
  1.1965 +  // left node. If it is, make it point to the right node!
  1.1966 +
  1.1967 +  nsCOMPtr<nsIContent> leftContent = do_QueryInterface(aLeftNode);
  1.1968 +  nsCOMPtr<nsIContent> rightContent = do_QueryInterface(aRightNode);
  1.1969 +
  1.1970 +  if (!leftContent || !rightContent)
  1.1971 +  {
  1.1972 +    UNLOCK_DOC(this);
  1.1973 +    return NS_ERROR_FAILURE;
  1.1974 +  }
  1.1975 +
  1.1976 +  if (mIterator->GetCurrentNode() == leftContent)
  1.1977 +    result = mIterator->PositionAt(rightContent);
  1.1978 +
  1.1979 +  UNLOCK_DOC(this);
  1.1980 +
  1.1981 +  return NS_OK;
  1.1982 +}
  1.1983 +
  1.1984 +nsresult
  1.1985 +nsTextServicesDocument::CreateContentIterator(nsIDOMRange *aRange, nsIContentIterator **aIterator)
  1.1986 +{
  1.1987 +  nsresult result;
  1.1988 +
  1.1989 +  NS_ENSURE_TRUE(aRange && aIterator, NS_ERROR_NULL_POINTER);
  1.1990 +
  1.1991 +  *aIterator = 0;
  1.1992 +
  1.1993 +  // Create a nsFilteredContentIterator
  1.1994 +  // This class wraps the ContentIterator in order to give itself a chance 
  1.1995 +  // to filter out certain content nodes
  1.1996 +  nsFilteredContentIterator* filter = new nsFilteredContentIterator(mTxtSvcFilter);
  1.1997 +  *aIterator = static_cast<nsIContentIterator *>(filter);
  1.1998 +  if (*aIterator) {
  1.1999 +    NS_IF_ADDREF(*aIterator);
  1.2000 +    result = filter ? NS_OK : NS_ERROR_FAILURE;
  1.2001 +  } else {
  1.2002 +    delete filter;
  1.2003 +    result = NS_ERROR_FAILURE;
  1.2004 +  }
  1.2005 +  NS_ENSURE_SUCCESS(result, result);
  1.2006 +
  1.2007 +  NS_ENSURE_TRUE(*aIterator, NS_ERROR_NULL_POINTER);
  1.2008 +
  1.2009 +  result = (*aIterator)->Init(aRange);
  1.2010 +
  1.2011 +  if (NS_FAILED(result))
  1.2012 +  {
  1.2013 +    NS_RELEASE((*aIterator));
  1.2014 +    *aIterator = 0;
  1.2015 +    return result;
  1.2016 +  }
  1.2017 +
  1.2018 +  return NS_OK;
  1.2019 +}
  1.2020 +
  1.2021 +nsresult
  1.2022 +nsTextServicesDocument::GetDocumentContentRootNode(nsIDOMNode **aNode)
  1.2023 +{
  1.2024 +  nsresult result;
  1.2025 +
  1.2026 +  NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  1.2027 +
  1.2028 +  *aNode = 0;
  1.2029 +
  1.2030 +  NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
  1.2031 +
  1.2032 +  nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDOMDocument);
  1.2033 +
  1.2034 +  if (htmlDoc)
  1.2035 +  {
  1.2036 +    // For HTML documents, the content root node is the body.
  1.2037 +
  1.2038 +    nsCOMPtr<nsIDOMHTMLElement> bodyElement;
  1.2039 +
  1.2040 +    result = htmlDoc->GetBody(getter_AddRefs(bodyElement));
  1.2041 +
  1.2042 +    NS_ENSURE_SUCCESS(result, result);
  1.2043 +
  1.2044 +    NS_ENSURE_TRUE(bodyElement, NS_ERROR_FAILURE);
  1.2045 +
  1.2046 +    result = bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
  1.2047 +  }
  1.2048 +  else
  1.2049 +  {
  1.2050 +    // For non-HTML documents, the content root node will be the document element.
  1.2051 +
  1.2052 +    nsCOMPtr<nsIDOMElement> docElement;
  1.2053 +
  1.2054 +    result = mDOMDocument->GetDocumentElement(getter_AddRefs(docElement));
  1.2055 +
  1.2056 +    NS_ENSURE_SUCCESS(result, result);
  1.2057 +
  1.2058 +    NS_ENSURE_TRUE(docElement, NS_ERROR_FAILURE);
  1.2059 +
  1.2060 +    result = docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
  1.2061 +  }
  1.2062 +
  1.2063 +  return result;
  1.2064 +}
  1.2065 +
  1.2066 +nsresult
  1.2067 +nsTextServicesDocument::CreateDocumentContentRange(nsIDOMRange **aRange)
  1.2068 +{
  1.2069 +  *aRange = nullptr;
  1.2070 +
  1.2071 +  nsCOMPtr<nsIDOMNode> node;
  1.2072 +  nsresult rv = GetDocumentContentRootNode(getter_AddRefs(node));
  1.2073 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2074 +  NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
  1.2075 +
  1.2076 +  nsCOMPtr<nsINode> nativeNode = do_QueryInterface(node);
  1.2077 +  NS_ENSURE_STATE(nativeNode);
  1.2078 +
  1.2079 +  nsRefPtr<nsRange> range = new nsRange(nativeNode);
  1.2080 +
  1.2081 +  rv = range->SelectNodeContents(node);
  1.2082 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2083 +
  1.2084 +  range.forget(aRange);
  1.2085 +  return NS_OK;
  1.2086 +}
  1.2087 +
  1.2088 +nsresult
  1.2089 +nsTextServicesDocument::CreateDocumentContentRootToNodeOffsetRange(nsIDOMNode *aParent, int32_t aOffset, bool aToStart, nsIDOMRange **aRange)
  1.2090 +{
  1.2091 +  NS_ENSURE_TRUE(aParent && aRange, NS_ERROR_NULL_POINTER);
  1.2092 +
  1.2093 +  *aRange = 0;
  1.2094 +
  1.2095 +  NS_ASSERTION(aOffset >= 0, "Invalid offset!");
  1.2096 +
  1.2097 +  if (aOffset < 0)
  1.2098 +    return NS_ERROR_FAILURE;
  1.2099 +
  1.2100 +  nsCOMPtr<nsIDOMNode> bodyNode; 
  1.2101 +  nsresult rv = GetDocumentContentRootNode(getter_AddRefs(bodyNode));
  1.2102 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2103 +  NS_ENSURE_TRUE(bodyNode, NS_ERROR_NULL_POINTER);
  1.2104 +
  1.2105 +  nsCOMPtr<nsIDOMNode> startNode;
  1.2106 +  nsCOMPtr<nsIDOMNode> endNode;
  1.2107 +  int32_t startOffset, endOffset;
  1.2108 +
  1.2109 +  if (aToStart) {
  1.2110 +    // The range should begin at the start of the document
  1.2111 +    // and extend up until (aParent, aOffset).
  1.2112 +
  1.2113 +    startNode   = bodyNode;
  1.2114 +    startOffset = 0;
  1.2115 +    endNode     = aParent;
  1.2116 +    endOffset   = aOffset;
  1.2117 +  } else {
  1.2118 +    // The range should begin at (aParent, aOffset) and
  1.2119 +    // extend to the end of the document.
  1.2120 +
  1.2121 +    startNode   = aParent;
  1.2122 +    startOffset = aOffset;
  1.2123 +    endNode     = bodyNode;
  1.2124 +
  1.2125 +    nsCOMPtr<nsINode> body = do_QueryInterface(bodyNode);
  1.2126 +    endOffset = body ? int32_t(body->GetChildCount()) : 0;
  1.2127 +  }
  1.2128 +
  1.2129 +  return nsRange::CreateRange(startNode, startOffset, endNode, endOffset,
  1.2130 +                              aRange);
  1.2131 +}
  1.2132 +
  1.2133 +nsresult
  1.2134 +nsTextServicesDocument::CreateDocumentContentIterator(nsIContentIterator **aIterator)
  1.2135 +{
  1.2136 +  nsresult result;
  1.2137 +
  1.2138 +  NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
  1.2139 +
  1.2140 +  nsCOMPtr<nsIDOMRange> range;
  1.2141 +
  1.2142 +  result = CreateDocumentContentRange(getter_AddRefs(range));
  1.2143 +
  1.2144 +  NS_ENSURE_SUCCESS(result, result);
  1.2145 +
  1.2146 +  result = CreateContentIterator(range, aIterator);
  1.2147 +
  1.2148 +  return result;
  1.2149 +}
  1.2150 +
  1.2151 +nsresult
  1.2152 +nsTextServicesDocument::AdjustContentIterator()
  1.2153 +{
  1.2154 +  nsresult result = NS_OK;
  1.2155 +
  1.2156 +  NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
  1.2157 +
  1.2158 +  nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mIterator->GetCurrentNode()));
  1.2159 +
  1.2160 +  NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  1.2161 +
  1.2162 +  nsIDOMNode *nodePtr = node.get();
  1.2163 +  int32_t tcount      = mOffsetTable.Length();
  1.2164 +
  1.2165 +  nsIDOMNode *prevValidNode = 0;
  1.2166 +  nsIDOMNode *nextValidNode = 0;
  1.2167 +  bool foundEntry = false;
  1.2168 +  OffsetEntry *entry;
  1.2169 +
  1.2170 +  for (int32_t i = 0; i < tcount && !nextValidNode; i++)
  1.2171 +  {
  1.2172 +    entry = mOffsetTable[i];
  1.2173 +
  1.2174 +    NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  1.2175 +
  1.2176 +    if (entry->mNode == nodePtr)
  1.2177 +    {
  1.2178 +      if (entry->mIsValid)
  1.2179 +      {
  1.2180 +        // The iterator is still pointing to something valid!
  1.2181 +        // Do nothing!
  1.2182 +
  1.2183 +        return NS_OK;
  1.2184 +      }
  1.2185 +      else
  1.2186 +      {
  1.2187 +        // We found an invalid entry that points to
  1.2188 +        // the current iterator node. Stop looking for
  1.2189 +        // a previous valid node!
  1.2190 +
  1.2191 +        foundEntry = true;
  1.2192 +      }
  1.2193 +    }
  1.2194 +
  1.2195 +    if (entry->mIsValid)
  1.2196 +    {
  1.2197 +      if (!foundEntry)
  1.2198 +        prevValidNode = entry->mNode;
  1.2199 +      else
  1.2200 +        nextValidNode = entry->mNode;
  1.2201 +    }
  1.2202 +  }
  1.2203 +
  1.2204 +  nsCOMPtr<nsIContent> content;
  1.2205 +
  1.2206 +  if (prevValidNode)
  1.2207 +    content = do_QueryInterface(prevValidNode);
  1.2208 +  else if (nextValidNode)
  1.2209 +    content = do_QueryInterface(nextValidNode);
  1.2210 +
  1.2211 +  if (content)
  1.2212 +  {
  1.2213 +    result = mIterator->PositionAt(content);
  1.2214 +
  1.2215 +    if (NS_FAILED(result))
  1.2216 +      mIteratorStatus = eIsDone;
  1.2217 +    else
  1.2218 +      mIteratorStatus = eValid;
  1.2219 +
  1.2220 +    return result;
  1.2221 +  }
  1.2222 +
  1.2223 +  // If we get here, there aren't any valid entries
  1.2224 +  // in the offset table! Try to position the iterator
  1.2225 +  // on the next text block first, then previous if
  1.2226 +  // one doesn't exist!
  1.2227 +
  1.2228 +  if (mNextTextBlock)
  1.2229 +  {
  1.2230 +    result = mIterator->PositionAt(mNextTextBlock);
  1.2231 +
  1.2232 +    if (NS_FAILED(result))
  1.2233 +    {
  1.2234 +      mIteratorStatus = eIsDone;
  1.2235 +      return result;
  1.2236 +    }
  1.2237 +
  1.2238 +    mIteratorStatus = eNext;
  1.2239 +  }
  1.2240 +  else if (mPrevTextBlock)
  1.2241 +  {
  1.2242 +    result = mIterator->PositionAt(mPrevTextBlock);
  1.2243 +
  1.2244 +    if (NS_FAILED(result))
  1.2245 +    {
  1.2246 +      mIteratorStatus = eIsDone;
  1.2247 +      return result;
  1.2248 +    }
  1.2249 +
  1.2250 +    mIteratorStatus = ePrev;
  1.2251 +  }
  1.2252 +  else
  1.2253 +    mIteratorStatus = eIsDone;
  1.2254 +
  1.2255 +  return NS_OK;
  1.2256 +}
  1.2257 +
  1.2258 +bool
  1.2259 +nsTextServicesDocument::DidSkip(nsIContentIterator* aFilteredIter)
  1.2260 +{
  1.2261 +  // We can assume here that the Iterator is a nsFilteredContentIterator because
  1.2262 +  // all the iterator are created in CreateContentIterator which create a 
  1.2263 +  // nsFilteredContentIterator
  1.2264 +  // So if the iterator bailed on one of the "filtered" content nodes then we 
  1.2265 +  // consider that to be a block and bail with true
  1.2266 +  if (aFilteredIter) {
  1.2267 +    nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
  1.2268 +    if (filter && filter->DidSkip()) {
  1.2269 +      return true;
  1.2270 +    }
  1.2271 +  }
  1.2272 +  return false;
  1.2273 +}
  1.2274 +
  1.2275 +void
  1.2276 +nsTextServicesDocument::ClearDidSkip(nsIContentIterator* aFilteredIter)
  1.2277 +{
  1.2278 +  // Clear filter's skip flag
  1.2279 +  if (aFilteredIter) {
  1.2280 +    nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
  1.2281 +    filter->ClearDidSkip();
  1.2282 +  }
  1.2283 +}
  1.2284 +
  1.2285 +bool
  1.2286 +nsTextServicesDocument::IsBlockNode(nsIContent *aContent)
  1.2287 +{
  1.2288 +  if (!aContent) {
  1.2289 +    NS_ERROR("How did a null pointer get passed to IsBlockNode?");
  1.2290 +    return false;
  1.2291 +  }
  1.2292 +
  1.2293 +  nsIAtom *atom = aContent->Tag();
  1.2294 +
  1.2295 +  return (sAAtom       != atom &&
  1.2296 +          sAddressAtom != atom &&
  1.2297 +          sBigAtom     != atom &&
  1.2298 +          sBAtom       != atom &&
  1.2299 +          sCiteAtom    != atom &&
  1.2300 +          sCodeAtom    != atom &&
  1.2301 +          sDfnAtom     != atom &&
  1.2302 +          sEmAtom      != atom &&
  1.2303 +          sFontAtom    != atom &&
  1.2304 +          sIAtom       != atom &&
  1.2305 +          sKbdAtom     != atom &&
  1.2306 +          sKeygenAtom  != atom &&
  1.2307 +          sNobrAtom    != atom &&
  1.2308 +          sSAtom       != atom &&
  1.2309 +          sSampAtom    != atom &&
  1.2310 +          sSmallAtom   != atom &&
  1.2311 +          sSpacerAtom  != atom &&
  1.2312 +          sSpanAtom    != atom &&
  1.2313 +          sStrikeAtom  != atom &&
  1.2314 +          sStrongAtom  != atom &&
  1.2315 +          sSubAtom     != atom &&
  1.2316 +          sSupAtom     != atom &&
  1.2317 +          sTtAtom      != atom &&
  1.2318 +          sUAtom       != atom &&
  1.2319 +          sVarAtom     != atom &&
  1.2320 +          sWbrAtom     != atom);
  1.2321 +}
  1.2322 +
  1.2323 +bool
  1.2324 +nsTextServicesDocument::HasSameBlockNodeParent(nsIContent *aContent1, nsIContent *aContent2)
  1.2325 +{
  1.2326 +  nsIContent* p1 = aContent1->GetParent();
  1.2327 +  nsIContent* p2 = aContent2->GetParent();
  1.2328 +
  1.2329 +  // Quick test:
  1.2330 +
  1.2331 +  if (p1 == p2)
  1.2332 +    return true;
  1.2333 +
  1.2334 +  // Walk up the parent hierarchy looking for closest block boundary node:
  1.2335 +
  1.2336 +  while (p1 && !IsBlockNode(p1))
  1.2337 +  {
  1.2338 +    p1 = p1->GetParent();
  1.2339 +  }
  1.2340 +
  1.2341 +  while (p2 && !IsBlockNode(p2))
  1.2342 +  {
  1.2343 +    p2 = p2->GetParent();
  1.2344 +  }
  1.2345 +
  1.2346 +  return p1 == p2;
  1.2347 +}
  1.2348 +
  1.2349 +bool
  1.2350 +nsTextServicesDocument::IsTextNode(nsIContent *aContent)
  1.2351 +{
  1.2352 +  NS_ENSURE_TRUE(aContent, false);
  1.2353 +  return nsIDOMNode::TEXT_NODE == aContent->NodeType();
  1.2354 +}
  1.2355 +
  1.2356 +bool
  1.2357 +nsTextServicesDocument::IsTextNode(nsIDOMNode *aNode)
  1.2358 +{
  1.2359 +  NS_ENSURE_TRUE(aNode, false);
  1.2360 +
  1.2361 +  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
  1.2362 +  return IsTextNode(content);
  1.2363 +}
  1.2364 +
  1.2365 +nsresult
  1.2366 +nsTextServicesDocument::SetSelectionInternal(int32_t aOffset, int32_t aLength, bool aDoUpdate)
  1.2367 +{
  1.2368 +  nsresult result = NS_OK;
  1.2369 +
  1.2370 +  NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
  1.2371 +
  1.2372 +  nsIDOMNode *sNode = 0, *eNode = 0;
  1.2373 +  int32_t i, sOffset = 0, eOffset = 0;
  1.2374 +  OffsetEntry *entry;
  1.2375 +
  1.2376 +  // Find start of selection in node offset terms:
  1.2377 +
  1.2378 +  for (i = 0; !sNode && i < int32_t(mOffsetTable.Length()); i++)
  1.2379 +  {
  1.2380 +    entry = mOffsetTable[i];
  1.2381 +    if (entry->mIsValid)
  1.2382 +    {
  1.2383 +      if (entry->mIsInsertedText)
  1.2384 +      {
  1.2385 +        // Caret can only be placed at the end of an
  1.2386 +        // inserted text offset entry, if the offsets
  1.2387 +        // match exactly!
  1.2388 +
  1.2389 +        if (entry->mStrOffset == aOffset)
  1.2390 +        {
  1.2391 +          sNode   = entry->mNode;
  1.2392 +          sOffset = entry->mNodeOffset + entry->mLength;
  1.2393 +        }
  1.2394 +      }
  1.2395 +      else if (aOffset >= entry->mStrOffset)
  1.2396 +      {
  1.2397 +        bool foundEntry = false;
  1.2398 +        int32_t strEndOffset = entry->mStrOffset + entry->mLength;
  1.2399 +
  1.2400 +        if (aOffset < strEndOffset)
  1.2401 +          foundEntry = true;
  1.2402 +        else if (aOffset == strEndOffset)
  1.2403 +        {
  1.2404 +          // Peek after this entry to see if we have any
  1.2405 +          // inserted text entries belonging to the same
  1.2406 +          // entry->mNode. If so, we have to place the selection
  1.2407 +          // after it!
  1.2408 +
  1.2409 +          if ((i+1) < int32_t(mOffsetTable.Length()))
  1.2410 +          {
  1.2411 +            OffsetEntry *nextEntry = mOffsetTable[i+1];
  1.2412 +
  1.2413 +            if (!nextEntry->mIsValid || nextEntry->mStrOffset != aOffset)
  1.2414 +            {
  1.2415 +              // Next offset entry isn't an exact match, so we'll
  1.2416 +              // just use the current entry.
  1.2417 +              foundEntry = true;
  1.2418 +            }
  1.2419 +          }
  1.2420 +        }
  1.2421 +
  1.2422 +        if (foundEntry)
  1.2423 +        {
  1.2424 +          sNode   = entry->mNode;
  1.2425 +          sOffset = entry->mNodeOffset + aOffset - entry->mStrOffset;
  1.2426 +        }
  1.2427 +      }
  1.2428 +
  1.2429 +      if (sNode)
  1.2430 +      {
  1.2431 +        mSelStartIndex  = i;
  1.2432 +        mSelStartOffset = aOffset;
  1.2433 +      }
  1.2434 +    }
  1.2435 +  }
  1.2436 +
  1.2437 +  NS_ENSURE_TRUE(sNode, NS_ERROR_FAILURE);
  1.2438 +
  1.2439 +  // XXX: If we ever get a SetSelection() method in nsIEditor, we should
  1.2440 +  //      use it.
  1.2441 +
  1.2442 +  nsCOMPtr<nsISelection> selection;
  1.2443 +
  1.2444 +  if (aDoUpdate)
  1.2445 +  {
  1.2446 +    result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  1.2447 +
  1.2448 +    NS_ENSURE_SUCCESS(result, result);
  1.2449 +
  1.2450 +    result = selection->Collapse(sNode, sOffset);
  1.2451 +
  1.2452 +    NS_ENSURE_SUCCESS(result, result);
  1.2453 +   }
  1.2454 +
  1.2455 +  if (aLength <= 0)
  1.2456 +  {
  1.2457 +    // We have a collapsed selection. (Caret)
  1.2458 +
  1.2459 +    mSelEndIndex  = mSelStartIndex;
  1.2460 +    mSelEndOffset = mSelStartOffset;
  1.2461 +
  1.2462 +   //**** KDEBUG ****
  1.2463 +   // printf("\n* Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1.2464 +   //**** KDEBUG ****
  1.2465 +
  1.2466 +    return NS_OK;
  1.2467 +  }
  1.2468 +
  1.2469 +  // Find the end of the selection in node offset terms:
  1.2470 +
  1.2471 +  int32_t endOffset = aOffset + aLength;
  1.2472 +
  1.2473 +  for (i = mOffsetTable.Length() - 1; !eNode && i >= 0; i--)
  1.2474 +  {
  1.2475 +    entry = mOffsetTable[i];
  1.2476 +    
  1.2477 +    if (entry->mIsValid)
  1.2478 +    {
  1.2479 +      if (entry->mIsInsertedText)
  1.2480 +      {
  1.2481 +        if (entry->mStrOffset == eOffset)
  1.2482 +        {
  1.2483 +          // If the selection ends on an inserted text offset entry,
  1.2484 +          // the selection includes the entire entry!
  1.2485 +
  1.2486 +          eNode   = entry->mNode;
  1.2487 +          eOffset = entry->mNodeOffset + entry->mLength;
  1.2488 +        }
  1.2489 +      }
  1.2490 +      else if (endOffset >= entry->mStrOffset && endOffset <= entry->mStrOffset + entry->mLength)
  1.2491 +      {
  1.2492 +        eNode   = entry->mNode;
  1.2493 +        eOffset = entry->mNodeOffset + endOffset - entry->mStrOffset;
  1.2494 +      }
  1.2495 +
  1.2496 +      if (eNode)
  1.2497 +      {
  1.2498 +        mSelEndIndex  = i;
  1.2499 +        mSelEndOffset = endOffset;
  1.2500 +      }
  1.2501 +    }
  1.2502 +  }
  1.2503 +
  1.2504 +  if (aDoUpdate && eNode)
  1.2505 +  {
  1.2506 +    result = selection->Extend(eNode, eOffset);
  1.2507 +
  1.2508 +    NS_ENSURE_SUCCESS(result, result);
  1.2509 +  }
  1.2510 +
  1.2511 +  //**** KDEBUG ****
  1.2512 +  // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1.2513 +  //**** KDEBUG ****
  1.2514 +
  1.2515 +  return result;
  1.2516 +}
  1.2517 +
  1.2518 +nsresult
  1.2519 +nsTextServicesDocument::GetSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
  1.2520 +{
  1.2521 +  nsresult result;
  1.2522 +
  1.2523 +  NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
  1.2524 +
  1.2525 +  *aSelStatus = nsITextServicesDocument::eBlockNotFound;
  1.2526 +  *aSelOffset = -1;
  1.2527 +  *aSelLength = -1;
  1.2528 +
  1.2529 +  NS_ENSURE_TRUE(mDOMDocument && mSelCon, NS_ERROR_FAILURE);
  1.2530 +
  1.2531 +  if (mIteratorStatus == nsTextServicesDocument::eIsDone)
  1.2532 +    return NS_OK;
  1.2533 +
  1.2534 +  nsCOMPtr<nsISelection> selection;
  1.2535 +  bool isCollapsed;
  1.2536 +
  1.2537 +  result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  1.2538 +
  1.2539 +  NS_ENSURE_SUCCESS(result, result);
  1.2540 +
  1.2541 +  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  1.2542 +
  1.2543 +  result = selection->GetIsCollapsed(&isCollapsed);
  1.2544 +
  1.2545 +  NS_ENSURE_SUCCESS(result, result);
  1.2546 +
  1.2547 +  // XXX: If we expose this method publicly, we need to
  1.2548 +  //      add LOCK_DOC/UNLOCK_DOC calls!
  1.2549 +
  1.2550 +  // LOCK_DOC(this);
  1.2551 +
  1.2552 +  if (isCollapsed)
  1.2553 +    result = GetCollapsedSelection(aSelStatus, aSelOffset, aSelLength);
  1.2554 +  else
  1.2555 +    result = GetUncollapsedSelection(aSelStatus, aSelOffset, aSelLength);
  1.2556 +
  1.2557 +  // UNLOCK_DOC(this);
  1.2558 +
  1.2559 +  return result;
  1.2560 +}
  1.2561 +
  1.2562 +nsresult
  1.2563 +nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
  1.2564 +{
  1.2565 +  nsCOMPtr<nsISelection> selection;
  1.2566 +  nsresult result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  1.2567 +  NS_ENSURE_SUCCESS(result, result);
  1.2568 +  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  1.2569 +
  1.2570 +  // The calling function should have done the GetIsCollapsed()
  1.2571 +  // check already. Just assume it's collapsed!
  1.2572 +  *aSelStatus = nsITextServicesDocument::eBlockOutside;
  1.2573 +  *aSelOffset = *aSelLength = -1;
  1.2574 +
  1.2575 +  int32_t tableCount = mOffsetTable.Length();
  1.2576 +
  1.2577 +  if (tableCount == 0)
  1.2578 +    return NS_OK;
  1.2579 +
  1.2580 +  // Get pointers to the first and last offset entries
  1.2581 +  // in the table.
  1.2582 +
  1.2583 +  OffsetEntry* eStart = mOffsetTable[0];
  1.2584 +  OffsetEntry* eEnd;
  1.2585 +  if (tableCount > 1)
  1.2586 +    eEnd = mOffsetTable[tableCount - 1];
  1.2587 +  else
  1.2588 +    eEnd = eStart;
  1.2589 +
  1.2590 +  int32_t eStartOffset = eStart->mNodeOffset;
  1.2591 +  int32_t eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
  1.2592 +
  1.2593 +  nsCOMPtr<nsIDOMRange> range;
  1.2594 +  result = selection->GetRangeAt(0, getter_AddRefs(range));
  1.2595 +  NS_ENSURE_SUCCESS(result, result);
  1.2596 +
  1.2597 +  nsCOMPtr<nsIDOMNode> domParent;
  1.2598 +  result = range->GetStartContainer(getter_AddRefs(domParent));
  1.2599 +  NS_ENSURE_SUCCESS(result, result);
  1.2600 +
  1.2601 +  nsCOMPtr<nsINode> parent = do_QueryInterface(domParent);
  1.2602 +  MOZ_ASSERT(parent);
  1.2603 +
  1.2604 +  int32_t offset;
  1.2605 +  result = range->GetStartOffset(&offset);
  1.2606 +  NS_ENSURE_SUCCESS(result, result);
  1.2607 +
  1.2608 +  int32_t e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
  1.2609 +                                               domParent, offset);
  1.2610 +  int32_t e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
  1.2611 +                                               domParent, offset);
  1.2612 +
  1.2613 +  if (e1s1 > 0 || e2s1 < 0) {
  1.2614 +    // We're done if the caret is outside the current text block.
  1.2615 +    return NS_OK;
  1.2616 +  }
  1.2617 +
  1.2618 +  if (parent->NodeType() == nsIDOMNode::TEXT_NODE) {
  1.2619 +    // Good news, the caret is in a text node. Look
  1.2620 +    // through the offset table for the entry that
  1.2621 +    // matches its parent and offset.
  1.2622 +
  1.2623 +    for (int32_t i = 0; i < tableCount; i++) {
  1.2624 +      OffsetEntry* entry = mOffsetTable[i];
  1.2625 +      NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  1.2626 +
  1.2627 +      if (entry->mNode == domParent.get() &&
  1.2628 +          entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
  1.2629 +      {
  1.2630 +        *aSelStatus = nsITextServicesDocument::eBlockContains;
  1.2631 +        *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
  1.2632 +        *aSelLength = 0;
  1.2633 +
  1.2634 +        return NS_OK;
  1.2635 +      }
  1.2636 +    }
  1.2637 +
  1.2638 +    // If we get here, we didn't find a text node entry
  1.2639 +    // in our offset table that matched.
  1.2640 +
  1.2641 +    return NS_ERROR_FAILURE;
  1.2642 +  }
  1.2643 +
  1.2644 +  // The caret is in our text block, but it's positioned in some
  1.2645 +  // non-text node (ex. <b>). Create a range based on the start
  1.2646 +  // and end of the text block, then create an iterator based on
  1.2647 +  // this range, with its initial position set to the closest
  1.2648 +  // child of this non-text node. Then look for the closest text
  1.2649 +  // node.
  1.2650 +
  1.2651 +  result = CreateRange(eStart->mNode, eStartOffset, eEnd->mNode, eEndOffset, getter_AddRefs(range));
  1.2652 +  NS_ENSURE_SUCCESS(result, result);
  1.2653 +
  1.2654 +  nsCOMPtr<nsIContentIterator> iter;
  1.2655 +  result = CreateContentIterator(range, getter_AddRefs(iter));
  1.2656 +  NS_ENSURE_SUCCESS(result, result);
  1.2657 +
  1.2658 +  nsIContent* saveNode;
  1.2659 +  if (parent->HasChildren()) {
  1.2660 +    // XXX: We need to make sure that all of parent's
  1.2661 +    //      children are in the text block.
  1.2662 +
  1.2663 +    // If the parent has children, position the iterator
  1.2664 +    // on the child that is to the left of the offset.
  1.2665 +
  1.2666 +    uint32_t childIndex = (uint32_t)offset;
  1.2667 +
  1.2668 +    if (childIndex > 0) {
  1.2669 +      uint32_t numChildren = parent->GetChildCount();
  1.2670 +      NS_ASSERTION(childIndex <= numChildren, "Invalid selection offset!");
  1.2671 +
  1.2672 +      if (childIndex > numChildren) {
  1.2673 +        childIndex = numChildren;
  1.2674 +      }
  1.2675 +
  1.2676 +      childIndex -= 1;
  1.2677 +    }
  1.2678 +
  1.2679 +    nsIContent* content = parent->GetChildAt(childIndex);
  1.2680 +    NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
  1.2681 +
  1.2682 +    result = iter->PositionAt(content);
  1.2683 +    NS_ENSURE_SUCCESS(result, result);
  1.2684 +
  1.2685 +    saveNode = content;
  1.2686 +  } else {
  1.2687 +    // The parent has no children, so position the iterator
  1.2688 +    // on the parent.
  1.2689 +    NS_ENSURE_TRUE(parent->IsContent(), NS_ERROR_FAILURE);
  1.2690 +    nsCOMPtr<nsIContent> content = parent->AsContent();
  1.2691 +
  1.2692 +    result = iter->PositionAt(content);
  1.2693 +    NS_ENSURE_SUCCESS(result, result);
  1.2694 +
  1.2695 +    saveNode = content;
  1.2696 +  }
  1.2697 +
  1.2698 +  // Now iterate to the left, towards the beginning of
  1.2699 +  // the text block, to find the first text node you
  1.2700 +  // come across.
  1.2701 +
  1.2702 +  nsIContent* node = nullptr;
  1.2703 +  while (!iter->IsDone()) {
  1.2704 +    nsINode* current = iter->GetCurrentNode();
  1.2705 +    if (current->NodeType() == nsIDOMNode::TEXT_NODE) {
  1.2706 +      node = static_cast<nsIContent*>(current);
  1.2707 +      break;
  1.2708 +    }
  1.2709 +
  1.2710 +    iter->Prev();
  1.2711 +  }
  1.2712 +
  1.2713 +  if (node) {
  1.2714 +    // We found a node, now set the offset to the end
  1.2715 +    // of the text node.
  1.2716 +    offset = node->TextLength();
  1.2717 +  } else {
  1.2718 +    // We should never really get here, but I'm paranoid.
  1.2719 +
  1.2720 +    // We didn't find a text node above, so iterate to
  1.2721 +    // the right, towards the end of the text block, looking
  1.2722 +    // for a text node.
  1.2723 +
  1.2724 +    result = iter->PositionAt(saveNode);
  1.2725 +    NS_ENSURE_SUCCESS(result, result);
  1.2726 +
  1.2727 +    node = nullptr;
  1.2728 +    while (!iter->IsDone()) {
  1.2729 +      nsINode* current = iter->GetCurrentNode();
  1.2730 +
  1.2731 +      if (current->NodeType() == nsIDOMNode::TEXT_NODE) {
  1.2732 +        node = static_cast<nsIContent*>(current);
  1.2733 +        break;
  1.2734 +      }
  1.2735 +
  1.2736 +      iter->Next();
  1.2737 +    }
  1.2738 +
  1.2739 +    NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  1.2740 +
  1.2741 +    // We found a text node, so set the offset to
  1.2742 +    // the beginning of the node.
  1.2743 +
  1.2744 +    offset = 0;
  1.2745 +  }
  1.2746 +
  1.2747 +  for (int32_t i = 0; i < tableCount; i++) {
  1.2748 +    OffsetEntry* entry = mOffsetTable[i];
  1.2749 +    NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  1.2750 +
  1.2751 +    if (entry->mNode == node->AsDOMNode() &&
  1.2752 +        entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
  1.2753 +    {
  1.2754 +      *aSelStatus = nsITextServicesDocument::eBlockContains;
  1.2755 +      *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
  1.2756 +      *aSelLength = 0;
  1.2757 +
  1.2758 +      // Now move the caret so that it is actually in the text node.
  1.2759 +      // We do this to keep things in sync.
  1.2760 +      //
  1.2761 +      // In most cases, the user shouldn't see any movement in the caret
  1.2762 +      // on screen.
  1.2763 +
  1.2764 +      result = SetSelectionInternal(*aSelOffset, *aSelLength, true);
  1.2765 +
  1.2766 +      return result;
  1.2767 +    }
  1.2768 +  }
  1.2769 +
  1.2770 +  return NS_ERROR_FAILURE;
  1.2771 +}
  1.2772 +
  1.2773 +nsresult
  1.2774 +nsTextServicesDocument::GetUncollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
  1.2775 +{
  1.2776 +  nsresult result;
  1.2777 +
  1.2778 +  nsCOMPtr<nsISelection> selection;
  1.2779 +  nsCOMPtr<nsIDOMRange> range;
  1.2780 +  OffsetEntry *entry;
  1.2781 +
  1.2782 +  result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  1.2783 +
  1.2784 +  NS_ENSURE_SUCCESS(result, result);
  1.2785 +
  1.2786 +  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  1.2787 +
  1.2788 +  // It is assumed that the calling function has made sure that the
  1.2789 +  // selection is not collapsed, and that the input params to this
  1.2790 +  // method are initialized to some defaults.
  1.2791 +
  1.2792 +  nsCOMPtr<nsIDOMNode> startParent, endParent;
  1.2793 +  int32_t startOffset, endOffset;
  1.2794 +  int32_t rangeCount, tableCount, i;
  1.2795 +  int32_t e1s1, e1s2, e2s1, e2s2;
  1.2796 +
  1.2797 +  OffsetEntry *eStart, *eEnd;
  1.2798 +  int32_t eStartOffset, eEndOffset;
  1.2799 +
  1.2800 +  tableCount = mOffsetTable.Length();
  1.2801 +
  1.2802 +  // Get pointers to the first and last offset entries
  1.2803 +  // in the table.
  1.2804 +
  1.2805 +  eStart = mOffsetTable[0];
  1.2806 +
  1.2807 +  if (tableCount > 1)
  1.2808 +    eEnd = mOffsetTable[tableCount - 1];
  1.2809 +  else
  1.2810 +    eEnd = eStart;
  1.2811 +
  1.2812 +  eStartOffset = eStart->mNodeOffset;
  1.2813 +  eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
  1.2814 +
  1.2815 +  result = selection->GetRangeCount(&rangeCount);
  1.2816 +
  1.2817 +  NS_ENSURE_SUCCESS(result, result);
  1.2818 +
  1.2819 +  // Find the first range in the selection that intersects
  1.2820 +  // the current text block.
  1.2821 +
  1.2822 +  for (i = 0; i < rangeCount; i++)
  1.2823 +  {
  1.2824 +    result = selection->GetRangeAt(i, getter_AddRefs(range));
  1.2825 +
  1.2826 +    NS_ENSURE_SUCCESS(result, result);
  1.2827 +
  1.2828 +    result = GetRangeEndPoints(range,
  1.2829 +                               getter_AddRefs(startParent), &startOffset,
  1.2830 +                               getter_AddRefs(endParent), &endOffset);
  1.2831 +
  1.2832 +    NS_ENSURE_SUCCESS(result, result);
  1.2833 +
  1.2834 +    e1s2 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
  1.2835 +                                         endParent, endOffset);
  1.2836 +    e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
  1.2837 +                                         startParent, startOffset);
  1.2838 +
  1.2839 +    // Break out of the loop if the text block intersects the current range.
  1.2840 +
  1.2841 +    if (e1s2 <= 0 && e2s1 >= 0)
  1.2842 +      break;
  1.2843 +  }
  1.2844 +
  1.2845 +  // We're done if we didn't find an intersecting range.
  1.2846 +
  1.2847 +  if (rangeCount < 1 || e1s2 > 0 || e2s1 < 0)
  1.2848 +  {
  1.2849 +    *aSelStatus = nsITextServicesDocument::eBlockOutside;
  1.2850 +    *aSelOffset = *aSelLength = -1;
  1.2851 +    return NS_OK;
  1.2852 +  }
  1.2853 +
  1.2854 +  // Now that we have an intersecting range, find out more info:
  1.2855 +
  1.2856 +  e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
  1.2857 +                                       startParent, startOffset);
  1.2858 +  e2s2 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
  1.2859 +                                       endParent, endOffset);
  1.2860 +
  1.2861 +  if (rangeCount > 1)
  1.2862 +  {
  1.2863 +    // There are multiple selection ranges, we only deal
  1.2864 +    // with the first one that intersects the current,
  1.2865 +    // text block, so mark this a as a partial.
  1.2866 +
  1.2867 +    *aSelStatus = nsITextServicesDocument::eBlockPartial;
  1.2868 +  }
  1.2869 +  else if (e1s1 > 0 && e2s2 < 0)
  1.2870 +  {
  1.2871 +    // The range extends beyond the start and
  1.2872 +    // end of the current text block.
  1.2873 +
  1.2874 +    *aSelStatus = nsITextServicesDocument::eBlockInside;
  1.2875 +  }
  1.2876 +  else if (e1s1 <= 0 && e2s2 >= 0)
  1.2877 +  {
  1.2878 +    // The current text block contains the entire
  1.2879 +    // range.
  1.2880 +
  1.2881 +    *aSelStatus = nsITextServicesDocument::eBlockContains;
  1.2882 +  }
  1.2883 +  else
  1.2884 +  {
  1.2885 +    // The range partially intersects the block.
  1.2886 +
  1.2887 +    *aSelStatus = nsITextServicesDocument::eBlockPartial;
  1.2888 +  }
  1.2889 +
  1.2890 +  // Now create a range based on the intersection of the
  1.2891 +  // text block and range:
  1.2892 +
  1.2893 +  nsCOMPtr<nsIDOMNode> p1, p2;
  1.2894 +  int32_t     o1,  o2;
  1.2895 +
  1.2896 +  // The start of the range will be the rightmost
  1.2897 +  // start node.
  1.2898 +
  1.2899 +  if (e1s1 >= 0)
  1.2900 +  {
  1.2901 +    p1 = do_QueryInterface(eStart->mNode);
  1.2902 +    o1 = eStartOffset;
  1.2903 +  }
  1.2904 +  else
  1.2905 +  {
  1.2906 +    p1 = startParent;
  1.2907 +    o1 = startOffset;
  1.2908 +  }
  1.2909 +
  1.2910 +  // The end of the range will be the leftmost
  1.2911 +  // end node.
  1.2912 +
  1.2913 +  if (e2s2 <= 0)
  1.2914 +  {
  1.2915 +    p2 = do_QueryInterface(eEnd->mNode);
  1.2916 +    o2 = eEndOffset;
  1.2917 +  }
  1.2918 +  else
  1.2919 +  {
  1.2920 +    p2 = endParent;
  1.2921 +    o2 = endOffset;
  1.2922 +  }
  1.2923 +
  1.2924 +  result = CreateRange(p1, o1, p2, o2, getter_AddRefs(range));
  1.2925 +
  1.2926 +  NS_ENSURE_SUCCESS(result, result);
  1.2927 +
  1.2928 +  // Now iterate over this range to figure out the selection's
  1.2929 +  // block offset and length.
  1.2930 +
  1.2931 +  nsCOMPtr<nsIContentIterator> iter;
  1.2932 +
  1.2933 +  result = CreateContentIterator(range, getter_AddRefs(iter));
  1.2934 +
  1.2935 +  NS_ENSURE_SUCCESS(result, result);
  1.2936 +
  1.2937 +  // Find the first text node in the range.
  1.2938 +  
  1.2939 +  bool found;
  1.2940 +  nsCOMPtr<nsIContent> content;
  1.2941 +
  1.2942 +  iter->First();
  1.2943 +
  1.2944 +  if (!IsTextNode(p1))
  1.2945 +  {
  1.2946 +    found = false;
  1.2947 +
  1.2948 +    while (!iter->IsDone())
  1.2949 +    {
  1.2950 +      content = do_QueryInterface(iter->GetCurrentNode());
  1.2951 +
  1.2952 +      if (IsTextNode(content))
  1.2953 +      {
  1.2954 +        p1 = do_QueryInterface(content);
  1.2955 +
  1.2956 +        NS_ENSURE_TRUE(p1, NS_ERROR_FAILURE);
  1.2957 +
  1.2958 +        o1 = 0;
  1.2959 +        found = true;
  1.2960 +
  1.2961 +        break;
  1.2962 +      }
  1.2963 +
  1.2964 +      iter->Next();
  1.2965 +    }
  1.2966 +
  1.2967 +    NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
  1.2968 +  }
  1.2969 +
  1.2970 +  // Find the last text node in the range.
  1.2971 +
  1.2972 +  iter->Last();
  1.2973 +
  1.2974 +  if (! IsTextNode(p2))
  1.2975 +  {
  1.2976 +    found = false;
  1.2977 +
  1.2978 +    while (!iter->IsDone())
  1.2979 +    {
  1.2980 +      content = do_QueryInterface(iter->GetCurrentNode());
  1.2981 +
  1.2982 +      if (IsTextNode(content))
  1.2983 +      {
  1.2984 +        p2 = do_QueryInterface(content);
  1.2985 +
  1.2986 +        NS_ENSURE_TRUE(p2, NS_ERROR_FAILURE);
  1.2987 +
  1.2988 +        nsString str;
  1.2989 +
  1.2990 +        result = p2->GetNodeValue(str);
  1.2991 +
  1.2992 +        NS_ENSURE_SUCCESS(result, result);
  1.2993 +
  1.2994 +        o2 = str.Length();
  1.2995 +        found = true;
  1.2996 +
  1.2997 +        break;
  1.2998 +      }
  1.2999 +
  1.3000 +      iter->Prev();
  1.3001 +    }
  1.3002 +
  1.3003 +    NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
  1.3004 +  }
  1.3005 +
  1.3006 +  found    = false;
  1.3007 +  *aSelLength = 0;
  1.3008 +
  1.3009 +  for (i = 0; i < tableCount; i++)
  1.3010 +  {
  1.3011 +    entry = mOffsetTable[i];
  1.3012 +
  1.3013 +    NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  1.3014 +
  1.3015 +    if (!found)
  1.3016 +    {
  1.3017 +      if (entry->mNode == p1.get() &&
  1.3018 +          entry->mNodeOffset <= o1 && o1 <= (entry->mNodeOffset + entry->mLength))
  1.3019 +      {
  1.3020 +        *aSelOffset = entry->mStrOffset + (o1 - entry->mNodeOffset);
  1.3021 +
  1.3022 +        if (p1 == p2 &&
  1.3023 +            entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
  1.3024 +        {
  1.3025 +          // The start and end of the range are in the same offset
  1.3026 +          // entry. Calculate the length of the range then we're done.
  1.3027 +
  1.3028 +          *aSelLength = o2 - o1;
  1.3029 +          break;
  1.3030 +        }
  1.3031 +        else
  1.3032 +        {
  1.3033 +          // Add the length of the sub string in this offset entry
  1.3034 +          // that follows the start of the range.
  1.3035 +
  1.3036 +          *aSelLength = entry->mLength - (o1 - entry->mNodeOffset);
  1.3037 +        }
  1.3038 +
  1.3039 +        found = true;
  1.3040 +      }
  1.3041 +    }
  1.3042 +    else // found
  1.3043 +    {
  1.3044 +      if (entry->mNode == p2.get() &&
  1.3045 +          entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
  1.3046 +      {
  1.3047 +        // We found the end of the range. Calculate the length of the
  1.3048 +        // sub string that is before the end of the range, then we're done.
  1.3049 +
  1.3050 +        *aSelLength += o2 - entry->mNodeOffset;
  1.3051 +        break;
  1.3052 +      }
  1.3053 +      else
  1.3054 +      {
  1.3055 +        // The entire entry must be in the range.
  1.3056 +
  1.3057 +        *aSelLength += entry->mLength;
  1.3058 +      }
  1.3059 +    }
  1.3060 +  }
  1.3061 +
  1.3062 +  return result;
  1.3063 +}
  1.3064 +
  1.3065 +bool
  1.3066 +nsTextServicesDocument::SelectionIsCollapsed()
  1.3067 +{
  1.3068 +  return(mSelStartIndex == mSelEndIndex && mSelStartOffset == mSelEndOffset);
  1.3069 +}
  1.3070 +
  1.3071 +bool
  1.3072 +nsTextServicesDocument::SelectionIsValid()
  1.3073 +{
  1.3074 +  return(mSelStartIndex >= 0);
  1.3075 +}
  1.3076 +
  1.3077 +nsresult
  1.3078 +nsTextServicesDocument::GetRangeEndPoints(nsIDOMRange *aRange,
  1.3079 +                                          nsIDOMNode **aStartParent, int32_t *aStartOffset,
  1.3080 +                                          nsIDOMNode **aEndParent, int32_t *aEndOffset)
  1.3081 +{
  1.3082 +  nsresult result;
  1.3083 +
  1.3084 +  NS_ENSURE_TRUE(aRange && aStartParent && aStartOffset && aEndParent && aEndOffset, NS_ERROR_NULL_POINTER);
  1.3085 +
  1.3086 +  result = aRange->GetStartContainer(aStartParent);
  1.3087 +
  1.3088 +  NS_ENSURE_SUCCESS(result, result);
  1.3089 +
  1.3090 +  NS_ENSURE_TRUE(aStartParent, NS_ERROR_FAILURE);
  1.3091 +
  1.3092 +  result = aRange->GetStartOffset(aStartOffset);
  1.3093 +
  1.3094 +  NS_ENSURE_SUCCESS(result, result);
  1.3095 +
  1.3096 +  result = aRange->GetEndContainer(aEndParent);
  1.3097 +
  1.3098 +  NS_ENSURE_SUCCESS(result, result);
  1.3099 +
  1.3100 +  NS_ENSURE_TRUE(aEndParent, NS_ERROR_FAILURE);
  1.3101 +
  1.3102 +  result = aRange->GetEndOffset(aEndOffset);
  1.3103 +
  1.3104 +  return result;
  1.3105 +}
  1.3106 +
  1.3107 +
  1.3108 +nsresult
  1.3109 +nsTextServicesDocument::CreateRange(nsIDOMNode *aStartParent, int32_t aStartOffset,
  1.3110 +                                    nsIDOMNode *aEndParent, int32_t aEndOffset,
  1.3111 +                                    nsIDOMRange **aRange)
  1.3112 +{
  1.3113 +  return nsRange::CreateRange(aStartParent, aStartOffset, aEndParent,
  1.3114 +                              aEndOffset, aRange);
  1.3115 +}
  1.3116 +
  1.3117 +nsresult
  1.3118 +nsTextServicesDocument::FirstTextNode(nsIContentIterator *aIterator,
  1.3119 +                                      TSDIteratorStatus *aIteratorStatus)
  1.3120 +{
  1.3121 +  if (aIteratorStatus)
  1.3122 +    *aIteratorStatus = nsTextServicesDocument::eIsDone;
  1.3123 +
  1.3124 +  aIterator->First();
  1.3125 +
  1.3126 +  while (!aIterator->IsDone()) {
  1.3127 +    if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
  1.3128 +      if (aIteratorStatus)
  1.3129 +        *aIteratorStatus = nsTextServicesDocument::eValid;
  1.3130 +      break;
  1.3131 +    }
  1.3132 +
  1.3133 +    aIterator->Next();
  1.3134 +  }
  1.3135 +
  1.3136 +  return NS_OK;
  1.3137 +}
  1.3138 +
  1.3139 +nsresult
  1.3140 +nsTextServicesDocument::LastTextNode(nsIContentIterator *aIterator,
  1.3141 +                                     TSDIteratorStatus *aIteratorStatus)
  1.3142 +{
  1.3143 +  if (aIteratorStatus)
  1.3144 +    *aIteratorStatus = nsTextServicesDocument::eIsDone;
  1.3145 +
  1.3146 +  aIterator->Last();
  1.3147 +
  1.3148 +  while (!aIterator->IsDone()) {
  1.3149 +    if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
  1.3150 +      if (aIteratorStatus)
  1.3151 +        *aIteratorStatus = nsTextServicesDocument::eValid;
  1.3152 +      break;
  1.3153 +    }
  1.3154 +
  1.3155 +    aIterator->Prev();
  1.3156 +  }
  1.3157 +
  1.3158 +  return NS_OK;
  1.3159 +}
  1.3160 +
  1.3161 +nsresult
  1.3162 +nsTextServicesDocument::FirstTextNodeInCurrentBlock(nsIContentIterator *iter)
  1.3163 +{
  1.3164 +  NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
  1.3165 +
  1.3166 +  ClearDidSkip(iter);
  1.3167 +
  1.3168 +  nsCOMPtr<nsIContent> last;
  1.3169 +
  1.3170 +  // Walk backwards over adjacent text nodes until
  1.3171 +  // we hit a block boundary:
  1.3172 +
  1.3173 +  while (!iter->IsDone())
  1.3174 +  {
  1.3175 +    nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->IsContent()
  1.3176 +                                   ? iter->GetCurrentNode()->AsContent()
  1.3177 +                                   : nullptr;
  1.3178 +
  1.3179 +    if (IsTextNode(content))
  1.3180 +    {
  1.3181 +      if (!last || HasSameBlockNodeParent(content, last))
  1.3182 +        last = content;
  1.3183 +      else
  1.3184 +      {
  1.3185 +        // We're done, the current text node is in a
  1.3186 +        // different block.
  1.3187 +        break;
  1.3188 +      }
  1.3189 +    }
  1.3190 +    else if (last && IsBlockNode(content))
  1.3191 +      break;
  1.3192 +
  1.3193 +    iter->Prev();
  1.3194 +
  1.3195 +    if (DidSkip(iter))
  1.3196 +      break;
  1.3197 +  }
  1.3198 +  
  1.3199 +  if (last)
  1.3200 +    iter->PositionAt(last);
  1.3201 +
  1.3202 +  // XXX: What should we return if last is null?
  1.3203 +
  1.3204 +  return NS_OK;
  1.3205 +}
  1.3206 +
  1.3207 +nsresult
  1.3208 +nsTextServicesDocument::FirstTextNodeInPrevBlock(nsIContentIterator *aIterator)
  1.3209 +{
  1.3210 +  nsCOMPtr<nsIContent> content;
  1.3211 +  nsresult result;
  1.3212 +
  1.3213 +  NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
  1.3214 +
  1.3215 +  // XXX: What if mIterator is not currently on a text node?
  1.3216 +
  1.3217 +  // Make sure mIterator is pointing to the first text node in the
  1.3218 +  // current block:
  1.3219 +
  1.3220 +  result = FirstTextNodeInCurrentBlock(aIterator);
  1.3221 +
  1.3222 +  NS_ENSURE_SUCCESS(result, NS_ERROR_FAILURE);
  1.3223 +
  1.3224 +  // Point mIterator to the first node before the first text node:
  1.3225 +
  1.3226 +  aIterator->Prev();
  1.3227 +
  1.3228 +  if (aIterator->IsDone())
  1.3229 +    return NS_ERROR_FAILURE;
  1.3230 +
  1.3231 +  // Now find the first text node of the next block:
  1.3232 +
  1.3233 +  return FirstTextNodeInCurrentBlock(aIterator);
  1.3234 +}
  1.3235 +
  1.3236 +nsresult
  1.3237 +nsTextServicesDocument::FirstTextNodeInNextBlock(nsIContentIterator *aIterator)
  1.3238 +{
  1.3239 +  nsCOMPtr<nsIContent> prev;
  1.3240 +  bool crossedBlockBoundary = false;
  1.3241 +
  1.3242 +  NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
  1.3243 +
  1.3244 +  ClearDidSkip(aIterator);
  1.3245 +
  1.3246 +  while (!aIterator->IsDone())
  1.3247 +  {
  1.3248 +    nsCOMPtr<nsIContent> content = aIterator->GetCurrentNode()->IsContent()
  1.3249 +                                   ? aIterator->GetCurrentNode()->AsContent()
  1.3250 +                                   : nullptr;
  1.3251 +
  1.3252 +    if (IsTextNode(content))
  1.3253 +    {
  1.3254 +      if (!crossedBlockBoundary && (!prev || HasSameBlockNodeParent(prev, content)))
  1.3255 +        prev = content;
  1.3256 +      else
  1.3257 +        break;
  1.3258 +    }
  1.3259 +    else if (!crossedBlockBoundary && IsBlockNode(content))
  1.3260 +      crossedBlockBoundary = true;
  1.3261 +
  1.3262 +    aIterator->Next();
  1.3263 +
  1.3264 +    if (!crossedBlockBoundary && DidSkip(aIterator))
  1.3265 +      crossedBlockBoundary = true;
  1.3266 +  }
  1.3267 +
  1.3268 +  return NS_OK;
  1.3269 +}
  1.3270 +
  1.3271 +nsresult
  1.3272 +nsTextServicesDocument::GetFirstTextNodeInPrevBlock(nsIContent **aContent)
  1.3273 +{
  1.3274 +  nsresult result;
  1.3275 +
  1.3276 +  NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
  1.3277 +
  1.3278 +  *aContent = 0;
  1.3279 +
  1.3280 +  // Save the iterator's current content node so we can restore
  1.3281 +  // it when we are done:
  1.3282 +
  1.3283 +  nsINode* node = mIterator->GetCurrentNode();
  1.3284 +
  1.3285 +  result = FirstTextNodeInPrevBlock(mIterator);
  1.3286 +
  1.3287 +  if (NS_FAILED(result))
  1.3288 +  {
  1.3289 +    // Try to restore the iterator before returning.
  1.3290 +    mIterator->PositionAt(node);
  1.3291 +    return result;
  1.3292 +  }
  1.3293 +
  1.3294 +  if (!mIterator->IsDone())
  1.3295 +  {
  1.3296 +    nsCOMPtr<nsIContent> current = mIterator->GetCurrentNode()->IsContent()
  1.3297 +                                   ? mIterator->GetCurrentNode()->AsContent()
  1.3298 +                                   : nullptr;
  1.3299 +    current.forget(aContent);
  1.3300 +  }
  1.3301 +
  1.3302 +  // Restore the iterator:
  1.3303 +
  1.3304 +  return mIterator->PositionAt(node);
  1.3305 +}
  1.3306 +
  1.3307 +nsresult
  1.3308 +nsTextServicesDocument::GetFirstTextNodeInNextBlock(nsIContent **aContent)
  1.3309 +{
  1.3310 +  nsresult result;
  1.3311 +
  1.3312 +  NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
  1.3313 +
  1.3314 +  *aContent = 0;
  1.3315 +
  1.3316 +  // Save the iterator's current content node so we can restore
  1.3317 +  // it when we are done:
  1.3318 +
  1.3319 +  nsINode* node = mIterator->GetCurrentNode();
  1.3320 +
  1.3321 +  result = FirstTextNodeInNextBlock(mIterator);
  1.3322 +
  1.3323 +  if (NS_FAILED(result))
  1.3324 +  {
  1.3325 +    // Try to restore the iterator before returning.
  1.3326 +    mIterator->PositionAt(node);
  1.3327 +    return result;
  1.3328 +  }
  1.3329 +
  1.3330 +  if (!mIterator->IsDone())
  1.3331 +  {
  1.3332 +    nsCOMPtr<nsIContent> current = mIterator->GetCurrentNode()->IsContent()
  1.3333 +                                   ? mIterator->GetCurrentNode()->AsContent()
  1.3334 +                                   : nullptr;
  1.3335 +    current.forget(aContent);
  1.3336 +  }
  1.3337 +
  1.3338 +  // Restore the iterator:
  1.3339 +  return mIterator->PositionAt(node);
  1.3340 +}
  1.3341 +
  1.3342 +nsresult
  1.3343 +nsTextServicesDocument::CreateOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable,
  1.3344 +                                          nsIContentIterator *aIterator,
  1.3345 +                                          TSDIteratorStatus *aIteratorStatus,
  1.3346 +                                          nsIDOMRange *aIterRange,
  1.3347 +                                          nsString *aStr)
  1.3348 +{
  1.3349 +  nsresult result = NS_OK;
  1.3350 +
  1.3351 +  nsCOMPtr<nsIContent> first;
  1.3352 +  nsCOMPtr<nsIContent> prev;
  1.3353 +
  1.3354 +  NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
  1.3355 +
  1.3356 +  ClearOffsetTable(aOffsetTable);
  1.3357 +
  1.3358 +  if (aStr)
  1.3359 +    aStr->Truncate();
  1.3360 +
  1.3361 +  if (*aIteratorStatus == nsTextServicesDocument::eIsDone)
  1.3362 +    return NS_OK;
  1.3363 +
  1.3364 +  // If we have an aIterRange, retrieve the endpoints so
  1.3365 +  // they can be used in the while loop below to trim entries
  1.3366 +  // for text nodes that are partially selected by aIterRange.
  1.3367 +  
  1.3368 +  nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
  1.3369 +  int32_t rngStartOffset = 0, rngEndOffset = 0;
  1.3370 +
  1.3371 +  if (aIterRange)
  1.3372 +  {
  1.3373 +    result = GetRangeEndPoints(aIterRange,
  1.3374 +                               getter_AddRefs(rngStartNode), &rngStartOffset,
  1.3375 +                               getter_AddRefs(rngEndNode), &rngEndOffset);
  1.3376 +
  1.3377 +    NS_ENSURE_SUCCESS(result, result);
  1.3378 +  }
  1.3379 +
  1.3380 +  // The text service could have added text nodes to the beginning
  1.3381 +  // of the current block and called this method again. Make sure
  1.3382 +  // we really are at the beginning of the current block:
  1.3383 +
  1.3384 +  result = FirstTextNodeInCurrentBlock(aIterator);
  1.3385 +
  1.3386 +  NS_ENSURE_SUCCESS(result, result);
  1.3387 +
  1.3388 +  int32_t offset = 0;
  1.3389 +
  1.3390 +  ClearDidSkip(aIterator);
  1.3391 +
  1.3392 +  while (!aIterator->IsDone())
  1.3393 +  {
  1.3394 +    nsCOMPtr<nsIContent> content = aIterator->GetCurrentNode()->IsContent()
  1.3395 +                                   ? aIterator->GetCurrentNode()->AsContent()
  1.3396 +                                   : nullptr;
  1.3397 +
  1.3398 +    if (IsTextNode(content))
  1.3399 +    {
  1.3400 +      if (!prev || HasSameBlockNodeParent(prev, content))
  1.3401 +      {
  1.3402 +        nsCOMPtr<nsIDOMNode> node = do_QueryInterface(content);
  1.3403 +
  1.3404 +        if (node)
  1.3405 +        {
  1.3406 +          nsString str;
  1.3407 +
  1.3408 +          result = node->GetNodeValue(str);
  1.3409 +
  1.3410 +          NS_ENSURE_SUCCESS(result, result);
  1.3411 +
  1.3412 +          // Add an entry for this text node into the offset table:
  1.3413 +
  1.3414 +          OffsetEntry *entry = new OffsetEntry(node, offset, str.Length());
  1.3415 +          aOffsetTable->AppendElement(entry);
  1.3416 +
  1.3417 +          // If one or both of the endpoints of the iteration range
  1.3418 +          // are in the text node for this entry, make sure the entry
  1.3419 +          // only accounts for the portion of the text node that is
  1.3420 +          // in the range.
  1.3421 +
  1.3422 +          int32_t startOffset = 0;
  1.3423 +          int32_t endOffset   = str.Length();
  1.3424 +          bool adjustStr    = false;
  1.3425 +
  1.3426 +          if (entry->mNode == rngStartNode)
  1.3427 +          {
  1.3428 +            entry->mNodeOffset = startOffset = rngStartOffset;
  1.3429 +            adjustStr = true;
  1.3430 +          }
  1.3431 +
  1.3432 +          if (entry->mNode == rngEndNode)
  1.3433 +          {
  1.3434 +            endOffset = rngEndOffset;
  1.3435 +            adjustStr = true;
  1.3436 +          }
  1.3437 +
  1.3438 +          if (adjustStr)
  1.3439 +          {
  1.3440 +            entry->mLength = endOffset - startOffset;
  1.3441 +            str = Substring(str, startOffset, entry->mLength);
  1.3442 +          }
  1.3443 +
  1.3444 +          offset += str.Length();
  1.3445 +
  1.3446 +          if (aStr)
  1.3447 +          {
  1.3448 +            // Append the text node's string to the output string:
  1.3449 +
  1.3450 +            if (!first)
  1.3451 +              *aStr = str;
  1.3452 +            else
  1.3453 +              *aStr += str;
  1.3454 +          }
  1.3455 +        }
  1.3456 +
  1.3457 +        prev = content;
  1.3458 +
  1.3459 +        if (!first)
  1.3460 +          first = content;
  1.3461 +      }
  1.3462 +      else
  1.3463 +        break;
  1.3464 +
  1.3465 +    }
  1.3466 +    else if (IsBlockNode(content))
  1.3467 +      break;
  1.3468 +
  1.3469 +    aIterator->Next();
  1.3470 +
  1.3471 +    if (DidSkip(aIterator))
  1.3472 +      break;
  1.3473 +  }
  1.3474 +
  1.3475 +  if (first)
  1.3476 +  {
  1.3477 +    // Always leave the iterator pointing at the first
  1.3478 +    // text node of the current block!
  1.3479 +
  1.3480 +    aIterator->PositionAt(first);
  1.3481 +  }
  1.3482 +  else
  1.3483 +  {
  1.3484 +    // If we never ran across a text node, the iterator
  1.3485 +    // might have been pointing to something invalid to
  1.3486 +    // begin with.
  1.3487 +
  1.3488 +    *aIteratorStatus = nsTextServicesDocument::eIsDone;
  1.3489 +  }
  1.3490 +
  1.3491 +  return result;
  1.3492 +}
  1.3493 +
  1.3494 +nsresult
  1.3495 +nsTextServicesDocument::RemoveInvalidOffsetEntries()
  1.3496 +{
  1.3497 +  OffsetEntry *entry;
  1.3498 +  int32_t i = 0;
  1.3499 +
  1.3500 +  while (uint32_t(i) < mOffsetTable.Length())
  1.3501 +  {
  1.3502 +    entry = mOffsetTable[i];
  1.3503 +
  1.3504 +    if (!entry->mIsValid)
  1.3505 +    {
  1.3506 +      mOffsetTable.RemoveElementAt(i);
  1.3507 +
  1.3508 +      if (mSelStartIndex >= 0 && mSelStartIndex >= i)
  1.3509 +      {
  1.3510 +        // We are deleting an entry that comes before
  1.3511 +        // mSelStartIndex, decrement mSelStartIndex so
  1.3512 +        // that it points to the correct entry!
  1.3513 +
  1.3514 +        NS_ASSERTION(i != mSelStartIndex, "Invalid selection index.");
  1.3515 +
  1.3516 +        --mSelStartIndex;
  1.3517 +        --mSelEndIndex;
  1.3518 +      }
  1.3519 +    }
  1.3520 +    else
  1.3521 +      i++;
  1.3522 +  }
  1.3523 +
  1.3524 +  return NS_OK;
  1.3525 +}
  1.3526 +
  1.3527 +nsresult
  1.3528 +nsTextServicesDocument::ClearOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable)
  1.3529 +{
  1.3530 +  uint32_t i;
  1.3531 +
  1.3532 +  for (i = 0; i < aOffsetTable->Length(); i++)
  1.3533 +  {
  1.3534 +    delete aOffsetTable->ElementAt(i);
  1.3535 +  }
  1.3536 +
  1.3537 +  aOffsetTable->Clear();
  1.3538 +
  1.3539 +  return NS_OK;
  1.3540 +}
  1.3541 +
  1.3542 +nsresult
  1.3543 +nsTextServicesDocument::SplitOffsetEntry(int32_t aTableIndex, int32_t aNewEntryLength)
  1.3544 +{
  1.3545 +  OffsetEntry *entry = mOffsetTable[aTableIndex];
  1.3546 +
  1.3547 +  NS_ASSERTION((aNewEntryLength > 0), "aNewEntryLength <= 0");
  1.3548 +  NS_ASSERTION((aNewEntryLength < entry->mLength), "aNewEntryLength >= mLength");
  1.3549 +
  1.3550 +  if (aNewEntryLength < 1 || aNewEntryLength >= entry->mLength)
  1.3551 +    return NS_ERROR_FAILURE;
  1.3552 +
  1.3553 +  int32_t oldLength = entry->mLength - aNewEntryLength;
  1.3554 +
  1.3555 +  OffsetEntry *newEntry = new OffsetEntry(entry->mNode,
  1.3556 +                                          entry->mStrOffset + oldLength,
  1.3557 +                                          aNewEntryLength);
  1.3558 +
  1.3559 +  if (!mOffsetTable.InsertElementAt(aTableIndex + 1, newEntry))
  1.3560 +  {
  1.3561 +    delete newEntry;
  1.3562 +    return NS_ERROR_FAILURE;
  1.3563 +  }
  1.3564 +
  1.3565 +   // Adjust entry fields:
  1.3566 +
  1.3567 +   entry->mLength        = oldLength;
  1.3568 +   newEntry->mNodeOffset = entry->mNodeOffset + oldLength;
  1.3569 +
  1.3570 +  return NS_OK;
  1.3571 +}
  1.3572 +
  1.3573 +nsresult
  1.3574 +nsTextServicesDocument::NodeHasOffsetEntry(nsTArray<OffsetEntry*> *aOffsetTable, nsIDOMNode *aNode, bool *aHasEntry, int32_t *aEntryIndex)
  1.3575 +{
  1.3576 +  OffsetEntry *entry;
  1.3577 +  uint32_t i;
  1.3578 +
  1.3579 +  NS_ENSURE_TRUE(aNode && aHasEntry && aEntryIndex, NS_ERROR_NULL_POINTER);
  1.3580 +
  1.3581 +  for (i = 0; i < aOffsetTable->Length(); i++)
  1.3582 +  {
  1.3583 +    entry = (*aOffsetTable)[i];
  1.3584 +
  1.3585 +    NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  1.3586 +
  1.3587 +    if (entry->mNode == aNode)
  1.3588 +    {
  1.3589 +      *aHasEntry   = true;
  1.3590 +      *aEntryIndex = i;
  1.3591 +
  1.3592 +      return NS_OK;
  1.3593 +    }
  1.3594 +  }
  1.3595 +
  1.3596 +  *aHasEntry   = false;
  1.3597 +  *aEntryIndex = -1;
  1.3598 +
  1.3599 +  return NS_OK;
  1.3600 +}
  1.3601 +
  1.3602 +// Spellchecker code has this. See bug 211343
  1.3603 +#define IS_NBSP_CHAR(c) (((unsigned char)0xa0)==(c))
  1.3604 +
  1.3605 +nsresult
  1.3606 +nsTextServicesDocument::FindWordBounds(nsTArray<OffsetEntry*> *aOffsetTable,
  1.3607 +                                       nsString *aBlockStr,
  1.3608 +                                       nsIDOMNode *aNode,
  1.3609 +                                       int32_t aNodeOffset,
  1.3610 +                                       nsIDOMNode **aWordStartNode,
  1.3611 +                                       int32_t *aWordStartOffset,
  1.3612 +                                       nsIDOMNode **aWordEndNode,
  1.3613 +                                       int32_t *aWordEndOffset)
  1.3614 +{
  1.3615 +  // Initialize return values.
  1.3616 +
  1.3617 +  if (aWordStartNode)
  1.3618 +    *aWordStartNode = nullptr;
  1.3619 +  if (aWordStartOffset)
  1.3620 +    *aWordStartOffset = 0;
  1.3621 +  if (aWordEndNode)
  1.3622 +    *aWordEndNode = nullptr;
  1.3623 +  if (aWordEndOffset)
  1.3624 +    *aWordEndOffset = 0;
  1.3625 +
  1.3626 +  int32_t entryIndex = 0;
  1.3627 +  bool hasEntry = false;
  1.3628 +
  1.3629 +  // It's assumed that aNode is a text node. The first thing
  1.3630 +  // we do is get its index in the offset table so we can
  1.3631 +  // calculate the dom point's string offset.
  1.3632 +
  1.3633 +  nsresult result = NodeHasOffsetEntry(aOffsetTable, aNode, &hasEntry, &entryIndex);
  1.3634 +  NS_ENSURE_SUCCESS(result, result);
  1.3635 +  NS_ENSURE_TRUE(hasEntry, NS_ERROR_FAILURE);
  1.3636 +
  1.3637 +  // Next we map aNodeOffset into a string offset.
  1.3638 +
  1.3639 +  OffsetEntry *entry = (*aOffsetTable)[entryIndex];
  1.3640 +  uint32_t strOffset = entry->mStrOffset + aNodeOffset - entry->mNodeOffset;
  1.3641 +
  1.3642 +  // Now we use the word breaker to find the beginning and end
  1.3643 +  // of the word from our calculated string offset.
  1.3644 +
  1.3645 +  const char16_t *str = aBlockStr->get();
  1.3646 +  uint32_t strLen = aBlockStr->Length();
  1.3647 +
  1.3648 +  nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
  1.3649 +  nsWordRange res = wordBreaker->FindWord(str, strLen, strOffset);
  1.3650 +  if (res.mBegin > strLen) {
  1.3651 +    return str ? NS_ERROR_ILLEGAL_VALUE : NS_ERROR_NULL_POINTER;
  1.3652 +  }
  1.3653 +
  1.3654 +  // Strip out the NBSPs at the ends
  1.3655 +  while ((res.mBegin <= res.mEnd) && (IS_NBSP_CHAR(str[res.mBegin]))) 
  1.3656 +    res.mBegin++;
  1.3657 +  if (str[res.mEnd] == (unsigned char)0x20)
  1.3658 +  {
  1.3659 +    uint32_t realEndWord = res.mEnd - 1;
  1.3660 +    while ((realEndWord > res.mBegin) && (IS_NBSP_CHAR(str[realEndWord]))) 
  1.3661 +      realEndWord--;
  1.3662 +    if (realEndWord < res.mEnd - 1) 
  1.3663 +      res.mEnd = realEndWord + 1;
  1.3664 +  }
  1.3665 +
  1.3666 +  // Now that we have the string offsets for the beginning
  1.3667 +  // and end of the word, run through the offset table and
  1.3668 +  // convert them back into dom points.
  1.3669 +
  1.3670 +  int32_t i, lastIndex = aOffsetTable->Length() - 1;
  1.3671 +
  1.3672 +  for (i=0; i <= lastIndex; i++)
  1.3673 +  {
  1.3674 +    entry = (*aOffsetTable)[i];
  1.3675 +
  1.3676 +    int32_t strEndOffset = entry->mStrOffset + entry->mLength;
  1.3677 +
  1.3678 +    // Check to see if res.mBegin is within the range covered
  1.3679 +    // by this entry. Note that if res.mBegin is after the last
  1.3680 +    // character covered by this entry, we will use the next
  1.3681 +    // entry if there is one.
  1.3682 +
  1.3683 +    if (uint32_t(entry->mStrOffset) <= res.mBegin &&
  1.3684 +        (res.mBegin < uint32_t(strEndOffset) ||
  1.3685 +        (res.mBegin == uint32_t(strEndOffset) && i == lastIndex)))
  1.3686 +    {
  1.3687 +      if (aWordStartNode)
  1.3688 +      {
  1.3689 +        *aWordStartNode = entry->mNode;
  1.3690 +        NS_IF_ADDREF(*aWordStartNode);
  1.3691 +      }
  1.3692 +
  1.3693 +      if (aWordStartOffset)
  1.3694 +        *aWordStartOffset = entry->mNodeOffset + res.mBegin - entry->mStrOffset;
  1.3695 +
  1.3696 +      if (!aWordEndNode && !aWordEndOffset)
  1.3697 +      {
  1.3698 +        // We've found our start entry, but if we're not looking
  1.3699 +        // for end entries, we're done.
  1.3700 +
  1.3701 +        break;
  1.3702 +      }
  1.3703 +    }
  1.3704 +
  1.3705 +    // Check to see if res.mEnd is within the range covered
  1.3706 +    // by this entry.
  1.3707 +
  1.3708 +    if (uint32_t(entry->mStrOffset) <= res.mEnd && res.mEnd <= uint32_t(strEndOffset))
  1.3709 +    {
  1.3710 +      if (res.mBegin == res.mEnd && res.mEnd == uint32_t(strEndOffset) && i != lastIndex)
  1.3711 +      {
  1.3712 +        // Wait for the next round so that we use the same entry
  1.3713 +        // we did for aWordStartNode.
  1.3714 +
  1.3715 +        continue;
  1.3716 +      }
  1.3717 +
  1.3718 +      if (aWordEndNode)
  1.3719 +      {
  1.3720 +        *aWordEndNode = entry->mNode;
  1.3721 +        NS_IF_ADDREF(*aWordEndNode);
  1.3722 +      }
  1.3723 +
  1.3724 +      if (aWordEndOffset)
  1.3725 +        *aWordEndOffset = entry->mNodeOffset + res.mEnd - entry->mStrOffset;
  1.3726 +
  1.3727 +      break;
  1.3728 +    }
  1.3729 +  }
  1.3730 +
  1.3731 +
  1.3732 +  return NS_OK;
  1.3733 +}
  1.3734 +
  1.3735 +#ifdef DEBUG_kin
  1.3736 +void
  1.3737 +nsTextServicesDocument::PrintOffsetTable()
  1.3738 +{
  1.3739 +  OffsetEntry *entry;
  1.3740 +  uint32_t i;
  1.3741 +
  1.3742 +  for (i = 0; i < mOffsetTable.Length(); i++)
  1.3743 +  {
  1.3744 +    entry = mOffsetTable[i];
  1.3745 +    printf("ENTRY %4d: %p  %c  %c  %4d  %4d  %4d\n",
  1.3746 +           i, entry->mNode,  entry->mIsValid ? 'V' : 'N',
  1.3747 +           entry->mIsInsertedText ? 'I' : 'B',
  1.3748 +           entry->mNodeOffset, entry->mStrOffset, entry->mLength);
  1.3749 +  }
  1.3750 +
  1.3751 +  fflush(stdout);
  1.3752 +}
  1.3753 +
  1.3754 +void
  1.3755 +nsTextServicesDocument::PrintContentNode(nsIContent *aContent)
  1.3756 +{
  1.3757 +  nsString tmpStr, str;
  1.3758 +
  1.3759 +  aContent->Tag()->ToString(tmpStr);
  1.3760 +  printf("%s", NS_LossyConvertUTF16toASCII(tmpStr).get());
  1.3761 +
  1.3762 +  if (nsIDOMNode::TEXT_NODE == aContent->NodeType())
  1.3763 +  {
  1.3764 +    aContent->AppendTextTo(str);
  1.3765 +    printf(":  \"%s\"", NS_LossyConvertUTF16toASCII(str).get());
  1.3766 +  }
  1.3767 +
  1.3768 +  printf("\n");
  1.3769 +  fflush(stdout);
  1.3770 +}
  1.3771 +#endif
  1.3772 +
  1.3773 +NS_IMETHODIMP
  1.3774 +nsTextServicesDocument::WillInsertNode(nsIDOMNode *aNode,
  1.3775 +                              nsIDOMNode *aParent,
  1.3776 +                              int32_t     aPosition)
  1.3777 +{
  1.3778 +  return NS_OK;
  1.3779 +}
  1.3780 +
  1.3781 +NS_IMETHODIMP
  1.3782 +nsTextServicesDocument::WillDeleteNode(nsIDOMNode *aChild)
  1.3783 +{
  1.3784 +  return NS_OK;
  1.3785 +}
  1.3786 +
  1.3787 +NS_IMETHODIMP
  1.3788 +nsTextServicesDocument::WillSplitNode(nsIDOMNode *aExistingRightNode,
  1.3789 +                             int32_t     aOffset)
  1.3790 +{
  1.3791 +  return NS_OK;
  1.3792 +}
  1.3793 +
  1.3794 +NS_IMETHODIMP
  1.3795 +nsTextServicesDocument::WillJoinNodes(nsIDOMNode  *aLeftNode,
  1.3796 +                             nsIDOMNode  *aRightNode,
  1.3797 +                             nsIDOMNode  *aParent)
  1.3798 +{
  1.3799 +  return NS_OK;
  1.3800 +}
  1.3801 +
  1.3802 +
  1.3803 +// -------------------------------
  1.3804 +// stubs for unused listen methods
  1.3805 +// -------------------------------
  1.3806 +
  1.3807 +NS_IMETHODIMP
  1.3808 +nsTextServicesDocument::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition)
  1.3809 +{
  1.3810 +  return NS_OK;
  1.3811 +}
  1.3812 +
  1.3813 +NS_IMETHODIMP
  1.3814 +nsTextServicesDocument::DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition, nsresult aResult)
  1.3815 +{
  1.3816 +  return NS_OK;
  1.3817 +}
  1.3818 +
  1.3819 +NS_IMETHODIMP
  1.3820 +nsTextServicesDocument::WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString)
  1.3821 +{
  1.3822 +  return NS_OK;
  1.3823 +}
  1.3824 +
  1.3825 +NS_IMETHODIMP
  1.3826 +nsTextServicesDocument::DidInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString, nsresult aResult)
  1.3827 +{
  1.3828 +  return NS_OK;
  1.3829 +}
  1.3830 +
  1.3831 +NS_IMETHODIMP
  1.3832 +nsTextServicesDocument::WillDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength)
  1.3833 +{
  1.3834 +  return NS_OK;
  1.3835 +}
  1.3836 +
  1.3837 +NS_IMETHODIMP
  1.3838 +nsTextServicesDocument::DidDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength, nsresult aResult)
  1.3839 +{
  1.3840 +  return NS_OK;
  1.3841 +}
  1.3842 +
  1.3843 +NS_IMETHODIMP
  1.3844 +nsTextServicesDocument::WillDeleteSelection(nsISelection *aSelection)
  1.3845 +{
  1.3846 +  return NS_OK;
  1.3847 +}
  1.3848 +
  1.3849 +NS_IMETHODIMP
  1.3850 +nsTextServicesDocument::DidDeleteSelection(nsISelection *aSelection)
  1.3851 +{
  1.3852 +  return NS_OK;
  1.3853 +}
  1.3854 +

mercurial