Back to index

lightning-sunbird  0.9+nobinonly
nsTextServicesDocument.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Neil Deakin <neil@mozdevgroup.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nscore.h"
00041 #include "nsLayoutCID.h"
00042 #include "nsIAtom.h"
00043 #include "nsStaticAtom.h"
00044 #include "nsString.h"
00045 #include "nsIEnumerator.h"
00046 #include "nsIContent.h"
00047 #include "nsIContentIterator.h"
00048 #include "nsIDOMNodeList.h"
00049 #include "nsIDOMRange.h"
00050 #include "nsIRangeUtils.h"
00051 #include "nsISelection.h"
00052 #include "nsIPlaintextEditor.h"
00053 #include "nsTextServicesDocument.h"
00054 #include "nsFilteredContentIterator.h"
00055 
00056 #include "nsIDOMElement.h"
00057 #include "nsIDOMHTMLElement.h"
00058 #include "nsIDOMHTMLDocument.h"
00059 
00060 #include "nsIWordBreakerFactory.h"
00061 #include "nsLWBrkCIID.h"
00062 #include "nsIServiceManager.h"
00063 
00064 #define LOCK_DOC(doc)
00065 #define UNLOCK_DOC(doc)
00066 
00067 
00068 class OffsetEntry
00069 {
00070 public:
00071   OffsetEntry(nsIDOMNode *aNode, PRInt32 aOffset, PRInt32 aLength)
00072     : mNode(aNode), mNodeOffset(0), mStrOffset(aOffset), mLength(aLength),
00073       mIsInsertedText(PR_FALSE), mIsValid(PR_TRUE)
00074   {
00075     if (mStrOffset < 1)
00076       mStrOffset = 0;
00077 
00078     if (mLength < 1)
00079       mLength = 0;
00080   }
00081 
00082   virtual ~OffsetEntry()
00083   {
00084     mNode       = 0;
00085     mNodeOffset = 0;
00086     mStrOffset  = 0;
00087     mLength     = 0;
00088     mIsValid    = PR_FALSE;
00089   }
00090 
00091   nsIDOMNode *mNode;
00092   PRInt32 mNodeOffset;
00093   PRInt32 mStrOffset;
00094   PRInt32 mLength;
00095   PRBool  mIsInsertedText;
00096   PRBool  mIsValid;
00097 };
00098 
00099 #define TS_ATOM(name_, value_) nsIAtom* nsTextServicesDocument::name_ = 0;
00100 #include "nsTSAtomList.h"
00101 #undef TS_ATOM
00102 
00103 nsIRangeUtils* nsTextServicesDocument::sRangeHelper;
00104 
00105 nsTextServicesDocument::nsTextServicesDocument()
00106 {
00107   mRefCnt         = 0;
00108 
00109   mSelStartIndex  = -1;
00110   mSelStartOffset = -1;
00111   mSelEndIndex    = -1;
00112   mSelEndOffset   = -1;
00113 
00114   mIteratorStatus = eIsDone;
00115 }
00116 
00117 nsTextServicesDocument::~nsTextServicesDocument()
00118 {
00119   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
00120   if (editor && mNotifier)
00121     editor->RemoveEditActionListener(mNotifier);
00122 
00123   ClearOffsetTable(&mOffsetTable);
00124 }
00125 
00126 /* static */
00127 void
00128 nsTextServicesDocument::RegisterAtoms()
00129 {
00130   static const nsStaticAtom ts_atoms[] = {
00131 #define TS_ATOM(name_, value_) { value_, &name_ },
00132 #include "nsTSAtomList.h"
00133 #undef TS_ATOM
00134   };
00135 
00136   NS_RegisterStaticAtoms(ts_atoms, NS_ARRAY_LENGTH(ts_atoms));
00137 }
00138 
00139 /* static */
00140 void
00141 nsTextServicesDocument::Shutdown()
00142 {
00143   NS_IF_RELEASE(sRangeHelper);
00144 }
00145 
00146 #define DEBUG_TEXT_SERVICES__DOCUMENT_REFCNT 1
00147 
00148 #ifdef DEBUG_TEXT_SERVICES__DOCUMENT_REFCNT
00149 
00150 nsrefcnt nsTextServicesDocument::AddRef(void)
00151 {
00152   return ++mRefCnt;
00153 }
00154 
00155 nsrefcnt nsTextServicesDocument::Release(void)
00156 {
00157   NS_PRECONDITION(0 != mRefCnt, "dup release");
00158   if (--mRefCnt == 0) {
00159     NS_DELETEXPCOM(this);
00160     return 0;
00161   }
00162   return mRefCnt;
00163 }
00164 
00165 #else
00166 
00167 NS_IMPL_ADDREF(nsTextServicesDocument)
00168 NS_IMPL_RELEASE(nsTextServicesDocument)
00169 
00170 #endif
00171 
00172 NS_IMPL_QUERY_INTERFACE1(nsTextServicesDocument, nsITextServicesDocument)
00173 
00174 NS_IMETHODIMP
00175 nsTextServicesDocument::InitWithDocument(nsIDOMDocument *aDOMDocument, nsIPresShell *aPresShell)
00176 {
00177   nsresult result = NS_OK;
00178 
00179   if (!aDOMDocument || !aPresShell)
00180     return NS_ERROR_NULL_POINTER;
00181 
00182   NS_ASSERTION(!mSelCon, "mSelCon already initialized!");
00183 
00184   if (mSelCon)
00185     return NS_ERROR_FAILURE;
00186 
00187   NS_ASSERTION(!mDOMDocument, "mDOMDocument already initialized!");
00188 
00189   if (mDOMDocument)
00190     return NS_ERROR_FAILURE;
00191 
00192   LOCK_DOC(this);
00193 
00194   mSelCon   = do_QueryInterface(aPresShell);
00195   mDOMDocument = do_QueryInterface(aDOMDocument);
00196 
00197   result = CreateDocumentContentIterator(getter_AddRefs(mIterator));
00198 
00199   if (NS_FAILED(result))
00200   {
00201     UNLOCK_DOC(this);
00202     return result;
00203   }
00204 
00205   mIteratorStatus = nsTextServicesDocument::eIsDone;
00206 
00207   result = FirstBlock();
00208 
00209   UNLOCK_DOC(this);
00210 
00211   return result;
00212 }
00213 
00214 NS_IMETHODIMP
00215 nsTextServicesDocument::InitWithEditor(nsIEditor *aEditor)
00216 {
00217   nsresult result = NS_OK;
00218   nsCOMPtr<nsISelectionController> selCon;
00219   nsCOMPtr<nsIDOMDocument> doc;
00220 
00221   if (!aEditor)
00222     return NS_ERROR_NULL_POINTER;
00223 
00224   LOCK_DOC(this);
00225 
00226   // Check to see if we already have an mSelCon. If we do, it
00227   // better be the same one the editor uses!
00228 
00229   result = aEditor->GetSelectionController(getter_AddRefs(selCon));
00230 
00231   if (NS_FAILED(result))
00232   {
00233     UNLOCK_DOC(this);
00234     return result;
00235   }
00236 
00237   if (!selCon || (mSelCon && selCon != mSelCon))
00238   {
00239     UNLOCK_DOC(this);
00240     return NS_ERROR_FAILURE;
00241   }
00242 
00243   if (!mSelCon)
00244     mSelCon = selCon;
00245 
00246   // Check to see if we already have an mDOMDocument. If we do, it
00247   // better be the same one the editor uses!
00248 
00249   result = aEditor->GetDocument(getter_AddRefs(doc));
00250 
00251   if (NS_FAILED(result))
00252   {
00253     UNLOCK_DOC(this);
00254     return result;
00255   }
00256 
00257   if (!doc || (mDOMDocument && doc != mDOMDocument))
00258   {
00259     UNLOCK_DOC(this);
00260     return NS_ERROR_FAILURE;
00261   }
00262 
00263   if (!mDOMDocument)
00264   {
00265     mDOMDocument = doc;
00266 
00267     result = CreateDocumentContentIterator(getter_AddRefs(mIterator));
00268 
00269     if (NS_FAILED(result))
00270     {
00271       UNLOCK_DOC(this);
00272       return result;
00273     }
00274 
00275     mIteratorStatus = nsTextServicesDocument::eIsDone;
00276 
00277     result = FirstBlock();
00278 
00279     if (NS_FAILED(result))
00280     {
00281       UNLOCK_DOC(this);
00282       return result;
00283     }
00284   }
00285 
00286   mEditor = do_GetWeakReference(aEditor);
00287   nsTSDNotifier *notifier = new nsTSDNotifier(this);
00288 
00289   if (!notifier)
00290   {
00291     UNLOCK_DOC(this);
00292     return NS_ERROR_OUT_OF_MEMORY;
00293   }
00294 
00295   mNotifier = do_QueryInterface(notifier);
00296 
00297   result = aEditor->AddEditActionListener(mNotifier);
00298 
00299   UNLOCK_DOC(this);
00300 
00301   return result;
00302 }
00303 
00304 NS_IMETHODIMP 
00305 nsTextServicesDocument::GetDocument(nsIDOMDocument **aDoc)
00306 {
00307   if (!aDoc)
00308     return NS_ERROR_NULL_POINTER;
00309 
00310   *aDoc = nsnull; // init out param
00311   if (!mDOMDocument)
00312     return NS_ERROR_NOT_INITIALIZED;
00313 
00314   *aDoc = mDOMDocument;
00315   NS_ADDREF(*aDoc);
00316 
00317   return NS_OK;
00318 }
00319 
00320 NS_IMETHODIMP
00321 nsTextServicesDocument::SetExtent(nsIDOMRange* aDOMRange)
00322 {
00323   NS_ENSURE_ARG_POINTER(aDOMRange);
00324   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
00325 
00326   LOCK_DOC(this);
00327 
00328   // We need to store a copy of aDOMRange since we don't
00329   // know where it came from.
00330 
00331   nsresult result = aDOMRange->CloneRange(getter_AddRefs(mExtent));
00332 
00333   if (NS_FAILED(result))
00334   {
00335     UNLOCK_DOC(this);
00336     return result;
00337   }
00338 
00339   // Create a new iterator based on our new extent range.
00340 
00341   result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
00342 
00343   if (NS_FAILED(result))
00344   {
00345     UNLOCK_DOC(this);
00346     return result;
00347   }
00348 
00349   // Now position the iterator at the start of the first block
00350   // in the range.
00351 
00352   mIteratorStatus = nsTextServicesDocument::eIsDone;
00353 
00354   result = FirstBlock();
00355 
00356   UNLOCK_DOC(this);
00357 
00358   return result;
00359 }
00360 
00361 NS_IMETHODIMP
00362 nsTextServicesDocument::GetExtent(nsIDOMRange** aDOMRange)
00363 {
00364   NS_ENSURE_ARG_POINTER(aDOMRange);
00365 
00366   *aDOMRange = nsnull;
00367 
00368   if (mExtent)
00369   {
00370     // We have an extent range, so just return a
00371     // copy of it.
00372 
00373     return mExtent->CloneRange(aDOMRange);
00374   }
00375 
00376   // We don't have an extent range, so we must be
00377   // iterating over the entire document.
00378 
00379   return CreateDocumentContentRange(aDOMRange);
00380 }
00381 
00382 NS_IMETHODIMP
00383 nsTextServicesDocument::ExpandRangeToWordBoundaries(nsIDOMRange *aRange)
00384 {
00385   NS_ENSURE_ARG_POINTER(aRange);
00386 
00387   // Get the end points of the range.
00388 
00389   nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
00390   PRInt32 rngStartOffset, rngEndOffset;
00391 
00392   nsresult result =  GetRangeEndPoints(aRange,
00393                                        getter_AddRefs(rngStartNode),
00394                                        &rngStartOffset,
00395                                        getter_AddRefs(rngEndNode),
00396                                        &rngEndOffset);
00397 
00398   NS_ENSURE_SUCCESS(result, result);
00399 
00400   // Create a content iterator based on the range.
00401 
00402   nsCOMPtr<nsIContentIterator> iter;
00403   result = CreateContentIterator(aRange, getter_AddRefs(iter));
00404 
00405   NS_ENSURE_SUCCESS(result, result);
00406 
00407   // Find the first text node in the range.
00408 
00409   TSDIteratorStatus iterStatus;
00410 
00411   result = FirstTextNode(iter, &iterStatus);
00412   NS_ENSURE_SUCCESS(result, result);
00413 
00414   if (iterStatus == nsTextServicesDocument::eIsDone)
00415   {
00416     // No text was found so there's no adjustment necessary!
00417     return NS_OK;
00418   }
00419 
00420   nsIContent *firstTextContent = iter->GetCurrentNode();
00421   NS_ENSURE_TRUE(firstTextContent, NS_ERROR_FAILURE);
00422 
00423   // Find the last text node in the range.
00424 
00425   result = LastTextNode(iter, &iterStatus);
00426   NS_ENSURE_SUCCESS(result, result);
00427 
00428   if (iterStatus == nsTextServicesDocument::eIsDone)
00429   {
00430     // We should never get here because a first text block
00431     // was found above.
00432     NS_ASSERTION(PR_FALSE, "Found a first without a last!");
00433     return NS_ERROR_FAILURE;
00434   }
00435 
00436   nsIContent *lastTextContent = iter->GetCurrentNode();
00437   NS_ENSURE_TRUE(lastTextContent, NS_ERROR_FAILURE);
00438 
00439   // Now make sure our end points are in terms of text nodes in the range!
00440 
00441   nsCOMPtr<nsIDOMNode> firstTextNode = do_QueryInterface(firstTextContent);
00442   NS_ENSURE_TRUE(firstTextNode, NS_ERROR_FAILURE);
00443 
00444   if (rngStartNode != firstTextNode)
00445   {
00446     // The range includes the start of the first text node!
00447     rngStartNode = firstTextNode;
00448     rngStartOffset = 0;
00449   }
00450 
00451   nsCOMPtr<nsIDOMNode> lastTextNode = do_QueryInterface(lastTextContent);
00452   NS_ENSURE_TRUE(lastTextNode, NS_ERROR_FAILURE);
00453 
00454   if (rngEndNode != lastTextNode)
00455   {
00456     // The range includes the end of the last text node!
00457     rngEndNode = lastTextNode;
00458     nsAutoString str;
00459     result = lastTextNode->GetNodeValue(str);
00460     rngEndOffset = str.Length();
00461   }
00462 
00463   // Create a doc iterator so that we can scan beyond
00464   // the bounds of the extent range.
00465 
00466   nsCOMPtr<nsIContentIterator> docIter;
00467   result = CreateDocumentContentIterator(getter_AddRefs(docIter));
00468   NS_ENSURE_SUCCESS(result, result);
00469 
00470   // Get a word breaker to use.
00471 
00472   nsCOMPtr<nsIWordBreaker> wordBreaker;
00473   result = GetWordBreaker(getter_AddRefs(wordBreaker));
00474   NS_ENSURE_SUCCESS(result, result);
00475   NS_ENSURE_TRUE(wordBreaker, NS_ERROR_FAILURE);
00476 
00477   // Grab all the text in the block containing our
00478   // first text node.
00479 
00480   result = docIter->PositionAt(firstTextContent);
00481   NS_ENSURE_SUCCESS(result, result);
00482 
00483   iterStatus = nsTextServicesDocument::eValid;
00484 
00485   nsVoidArray offsetTable;
00486   nsAutoString blockStr;
00487 
00488   result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
00489                              nsnull, &blockStr);
00490   if (NS_FAILED(result))
00491   {
00492     ClearOffsetTable(&offsetTable);
00493     return result;
00494   }
00495 
00496   nsCOMPtr<nsIDOMNode> wordStartNode, wordEndNode;
00497   PRInt32 wordStartOffset, wordEndOffset;
00498 
00499   result = FindWordBounds(&offsetTable, &blockStr, wordBreaker,
00500                           rngStartNode, rngStartOffset,
00501                           getter_AddRefs(wordStartNode), &wordStartOffset,
00502                           getter_AddRefs(wordEndNode), &wordEndOffset);
00503 
00504   ClearOffsetTable(&offsetTable);
00505 
00506   NS_ENSURE_SUCCESS(result, result);
00507 
00508   rngStartNode = wordStartNode;
00509   rngStartOffset = wordStartOffset;
00510 
00511   // Grab all the text in the block containing our
00512   // last text node.
00513 
00514   result = docIter->PositionAt(lastTextContent);
00515   NS_ENSURE_SUCCESS(result, result);
00516 
00517   iterStatus = nsTextServicesDocument::eValid;
00518 
00519   result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
00520                              nsnull, &blockStr);
00521   if (NS_FAILED(result))
00522   {
00523     ClearOffsetTable(&offsetTable);
00524     return result;
00525   }
00526 
00527   result = FindWordBounds(&offsetTable, &blockStr, wordBreaker,
00528                           rngEndNode, rngEndOffset,
00529                           getter_AddRefs(wordStartNode), &wordStartOffset,
00530                           getter_AddRefs(wordEndNode), &wordEndOffset);
00531 
00532   ClearOffsetTable(&offsetTable);
00533 
00534   NS_ENSURE_SUCCESS(result, result);
00535 
00536   // To prevent expanding the range too much, we only change
00537   // rngEndNode and rngEndOffset if it isn't already at the start of the
00538   // word and isn't equivalent to rngStartNode and rngStartOffset.
00539 
00540   if (rngEndNode != wordStartNode || rngEndOffset != wordStartOffset ||
00541      (rngEndNode == rngStartNode  && rngEndOffset == rngStartOffset))
00542   {
00543     rngEndNode = wordEndNode;
00544     rngEndOffset = wordEndOffset;
00545   }
00546 
00547   // Now adjust the range so that it uses our new
00548   // end points.
00549 
00550   result = aRange->SetEnd(rngEndNode, rngEndOffset);
00551   NS_ENSURE_SUCCESS(result, result);
00552 
00553   return aRange->SetStart(rngStartNode, rngStartOffset);
00554 }
00555 
00556 NS_IMETHODIMP
00557 nsTextServicesDocument::SetFilter(nsITextServicesFilter *aFilter)
00558 {
00559   // Hang on to the filter so we can set it into the filtered iterator.
00560   mTxtSvcFilter = aFilter;
00561 
00562   return NS_OK;
00563 }
00564 
00565 NS_IMETHODIMP
00566 nsTextServicesDocument::CanEdit(PRBool *aCanEdit)
00567 {
00568   if (!aCanEdit)
00569     return NS_ERROR_NULL_POINTER;
00570 
00571   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
00572 
00573   *aCanEdit = (editor) ? PR_TRUE : PR_FALSE;
00574 
00575   return NS_OK;
00576 }
00577 
00578 NS_IMETHODIMP
00579 nsTextServicesDocument::GetCurrentTextBlock(nsString *aStr)
00580 {
00581   nsresult result;
00582 
00583   if (!aStr)
00584     return NS_ERROR_NULL_POINTER;
00585 
00586   aStr->Truncate();
00587 
00588   if (!mIterator)
00589     return NS_ERROR_FAILURE;
00590 
00591   LOCK_DOC(this);
00592 
00593   result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
00594                              mExtent, aStr);
00595 
00596   UNLOCK_DOC(this);
00597 
00598   return result;
00599 }
00600 
00601 NS_IMETHODIMP
00602 nsTextServicesDocument::FirstBlock()
00603 {
00604   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
00605 
00606   LOCK_DOC(this);
00607 
00608   nsresult result = FirstTextNode(mIterator, &mIteratorStatus);
00609 
00610   if (NS_FAILED(result))
00611   {
00612     UNLOCK_DOC(this);
00613     return result;
00614   }
00615 
00616   // Keep track of prev and next blocks, just in case
00617   // the text service blows away the current block.
00618 
00619   if (mIteratorStatus == nsTextServicesDocument::eValid)
00620   {
00621     mPrevTextBlock  = nsnull;
00622     result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
00623   }
00624   else
00625   {
00626     // There's no text block in the document!
00627 
00628     mPrevTextBlock  = nsnull;
00629     mNextTextBlock  = nsnull;
00630   }
00631 
00632   UNLOCK_DOC(this);
00633 
00634   return result;
00635 }
00636 
00637 NS_IMETHODIMP
00638 nsTextServicesDocument::LastBlock()
00639 {
00640   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
00641 
00642   LOCK_DOC(this);
00643 
00644   // Position the iterator on the last text node
00645   // in the tree, then walk backwards over adjacent
00646   // text nodes until we hit a block boundary:
00647 
00648   nsresult result = LastTextNode(mIterator, &mIteratorStatus);
00649 
00650   if (NS_FAILED(result))
00651   {
00652     UNLOCK_DOC(this);
00653     return result;
00654   }
00655 
00656   result = FirstTextNodeInCurrentBlock(mIterator);
00657 
00658   if (NS_FAILED(result))
00659     mIteratorStatus = nsTextServicesDocument::eIsDone;
00660 
00661   // Keep track of prev and next blocks, just in case
00662   // the text service blows away the current block.
00663 
00664   if (mIteratorStatus == nsTextServicesDocument::eValid)
00665   {
00666     result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
00667     mNextTextBlock = nsnull;
00668   }
00669   else
00670   {
00671     // There's no text block in the document!
00672 
00673     mPrevTextBlock = nsnull;
00674     mNextTextBlock = nsnull;
00675   }
00676 
00677   UNLOCK_DOC(this);
00678 
00679   return result;
00680 }
00681 
00682 // XXX: CODE BLOAT ALERT. FirstSelectedBlock() and LastSelectedBlock()
00683 //      are almost identical! Later, when we have time, we should try
00684 //      and combine them into one method.
00685 
00686 NS_IMETHODIMP
00687 nsTextServicesDocument::FirstSelectedBlock(TSDBlockSelectionStatus *aSelStatus, PRInt32 *aSelOffset, PRInt32 *aSelLength)
00688 {
00689   nsresult result = NS_OK;
00690 
00691   if (!aSelStatus || !aSelOffset || !aSelLength)
00692     return NS_ERROR_NULL_POINTER;
00693 
00694   LOCK_DOC(this);
00695 
00696   mIteratorStatus = nsTextServicesDocument::eIsDone;
00697 
00698   *aSelStatus = nsITextServicesDocument::eBlockNotFound;
00699   *aSelOffset = *aSelLength = -1;
00700 
00701   if (!mSelCon || !mIterator)
00702   {
00703     UNLOCK_DOC(this);
00704     return NS_ERROR_FAILURE;
00705   }
00706 
00707   nsCOMPtr<nsISelection> selection;
00708   PRBool isCollapsed = PR_FALSE;
00709 
00710   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
00711 
00712   if (NS_FAILED(result))
00713   {
00714     UNLOCK_DOC(this);
00715     return result;
00716   }
00717 
00718   result = selection->GetIsCollapsed( &isCollapsed);
00719 
00720   if (NS_FAILED(result))
00721   {
00722     UNLOCK_DOC(this);
00723     return result;
00724   }
00725 
00726   nsCOMPtr<nsIContentIterator> iter;
00727   nsCOMPtr<nsIDOMRange>        range;
00728   nsCOMPtr<nsIDOMNode>         parent;
00729   PRInt32                      i, rangeCount, offset;
00730 
00731   if (isCollapsed)
00732   {
00733     // We have a caret. Check if the caret is in a text node.
00734     // If it is, make the text node's block the current block.
00735     // If the caret isn't in a text node, search backwards in
00736     // the document, till we find a text node.
00737 
00738     result = selection->GetRangeAt(0, getter_AddRefs(range));
00739 
00740     if (NS_FAILED(result))
00741     {
00742       UNLOCK_DOC(this);
00743       return result;
00744     }
00745 
00746     if (!range)
00747     {
00748       UNLOCK_DOC(this);
00749       return NS_ERROR_FAILURE;
00750     }
00751 
00752     result = range->GetStartContainer(getter_AddRefs(parent));
00753 
00754     if (NS_FAILED(result))
00755     {
00756       UNLOCK_DOC(this);
00757       return result;
00758     }
00759 
00760     if (!parent)
00761     {
00762       UNLOCK_DOC(this);
00763       return NS_ERROR_FAILURE;
00764     }
00765 
00766     result = range->GetStartOffset(&offset);
00767 
00768     if (NS_FAILED(result))
00769     {
00770       UNLOCK_DOC(this);
00771       return result;
00772     }
00773 
00774     if (IsTextNode(parent))
00775     {
00776       // The caret is in a text node. Find the beginning
00777       // of the text block containing this text node and
00778       // return.
00779 
00780       nsCOMPtr<nsIContent> content = do_QueryInterface(parent);
00781 
00782       if (!content)
00783       {
00784         UNLOCK_DOC(this);
00785         return NS_ERROR_FAILURE;
00786       }
00787 
00788       result = mIterator->PositionAt(content);
00789 
00790       if (NS_FAILED(result))
00791       {
00792         UNLOCK_DOC(this);
00793         return result;
00794       }
00795 
00796       result = FirstTextNodeInCurrentBlock(mIterator);
00797 
00798       if (NS_FAILED(result))
00799       {
00800         UNLOCK_DOC(this);
00801         return result;
00802       }
00803 
00804       mIteratorStatus = nsTextServicesDocument::eValid;
00805 
00806       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
00807                                  mExtent, nsnull);
00808 
00809       if (NS_FAILED(result))
00810       {
00811         UNLOCK_DOC(this);
00812         return result;
00813       }
00814 
00815       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
00816 
00817       if (NS_FAILED(result))
00818       {
00819         UNLOCK_DOC(this);
00820         return result;
00821       }
00822 
00823       if (*aSelStatus == nsITextServicesDocument::eBlockContains)
00824         result = SetSelectionInternal(*aSelOffset, *aSelLength, PR_FALSE);
00825     }
00826     else
00827     {
00828       // The caret isn't in a text node. Create an iterator
00829       // based on a range that extends from the beginning of
00830       // the document, to the current caret position, then
00831       // walk backwards till you find a text node, then find
00832       // the beginning of the block.
00833 
00834       result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, PR_TRUE, getter_AddRefs(range));
00835 
00836       if (NS_FAILED(result))
00837       {
00838         UNLOCK_DOC(this);
00839         return result;
00840       }
00841 
00842       result = range->GetCollapsed(&isCollapsed);
00843 
00844       if (NS_FAILED(result))
00845       {
00846         UNLOCK_DOC(this);
00847         return result;
00848       }
00849 
00850       if (isCollapsed)
00851       {
00852         // If we get here, the range is collapsed because there is nothing before
00853         // the caret! Just return NS_OK;
00854 
00855         UNLOCK_DOC(this);
00856         return NS_OK;
00857       }
00858 
00859       result = CreateContentIterator(range, getter_AddRefs(iter));
00860 
00861       if (NS_FAILED(result))
00862       {
00863         UNLOCK_DOC(this);
00864         return result;
00865       }
00866 
00867       iter->Last();
00868 
00869       nsIContent *content = nsnull;
00870       while (!iter->IsDone())
00871       {
00872         content = iter->GetCurrentNode();
00873 
00874         if (IsTextNode(content))
00875           break;
00876 
00877         content = nsnull;
00878 
00879         iter->Prev();
00880       }
00881 
00882       if (!content)
00883       {
00884         UNLOCK_DOC(this);
00885         return NS_OK;
00886       }
00887 
00888       result = mIterator->PositionAt(content);
00889 
00890       if (NS_FAILED(result))
00891       {
00892         UNLOCK_DOC(this);
00893         return result;
00894       }
00895 
00896       result = FirstTextNodeInCurrentBlock(mIterator);
00897 
00898       if (NS_FAILED(result))
00899       {
00900         UNLOCK_DOC(this);
00901         return result;
00902       }
00903 
00904       mIteratorStatus = nsTextServicesDocument::eValid;
00905 
00906       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
00907                                  mExtent, nsnull);
00908 
00909       if (NS_FAILED(result))
00910       {
00911         UNLOCK_DOC(this);
00912         return result;
00913       }
00914 
00915       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
00916 
00917       if (NS_FAILED(result))
00918       {
00919         UNLOCK_DOC(this);
00920         return result;
00921       }
00922     }
00923 
00924     UNLOCK_DOC(this);
00925 
00926     return result;
00927   }
00928 
00929   // If we get here, we have an uncollapsed selection!
00930   // Look through each range in the selection till you
00931   // find the first text node. If you find one, find the
00932   // beginning of it's text block, and make it the current
00933   // block.
00934 
00935   result = selection->GetRangeCount(&rangeCount);
00936 
00937   if (NS_FAILED(result))
00938   {
00939     UNLOCK_DOC(this);
00940     return result;
00941   }
00942 
00943   NS_ASSERTION(rangeCount > 0, "Unexpected range count!");
00944 
00945   if (rangeCount <= 0)
00946   {
00947     UNLOCK_DOC(this);
00948     return NS_OK;
00949   }
00950 
00951   // XXX: We may need to add some code here to make sure
00952   //      the ranges are sorted in document appearance order!
00953 
00954   for (i = 0; i < rangeCount; i++)
00955   {
00956     // Get the i'th range from the selection.
00957 
00958     result = selection->GetRangeAt(i, getter_AddRefs(range));
00959 
00960     if (NS_FAILED(result))
00961     {
00962       UNLOCK_DOC(this);
00963       return result;
00964     }
00965 
00966     // Create an iterator for the range.
00967 
00968     result = CreateContentIterator(range, getter_AddRefs(iter));
00969 
00970     if (NS_FAILED(result))
00971     {
00972       UNLOCK_DOC(this);
00973       return result;
00974     }
00975 
00976     iter->First();
00977 
00978     // Now walk through the range till we find a text node.
00979 
00980     while (!iter->IsDone())
00981     {
00982       nsIContent *content = iter->GetCurrentNode();
00983 
00984       if (IsTextNode(content))
00985       {
00986         // We found a text node, so position the document's
00987         // iterator at the beginning of the block, then get
00988         // the selection in terms of the string offset.
00989 
00990         result = mIterator->PositionAt(content);
00991 
00992         if (NS_FAILED(result))
00993         {
00994           UNLOCK_DOC(this);
00995           return result;
00996         }
00997 
00998         result = FirstTextNodeInCurrentBlock(mIterator);
00999 
01000         if (NS_FAILED(result))
01001         {
01002           UNLOCK_DOC(this);
01003           return result;
01004         }
01005 
01006         mIteratorStatus = nsTextServicesDocument::eValid;
01007 
01008         result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
01009                                    mExtent, nsnull);
01010 
01011         if (NS_FAILED(result))
01012         {
01013           UNLOCK_DOC(this);
01014           return result;
01015         }
01016 
01017         result = GetSelection(aSelStatus, aSelOffset, aSelLength);
01018 
01019         UNLOCK_DOC(this);
01020 
01021         return result;
01022 
01023       }
01024 
01025       iter->Next();
01026     }
01027   }
01028 
01029   // If we get here, we didn't find any text node in the selection!
01030   // Create a range that extends from the beginning of the selection,
01031   // to the beginning of the document, then iterate backwards through
01032   // it till you find a text node!
01033 
01034   result = selection->GetRangeAt(0, getter_AddRefs(range));
01035 
01036   if (NS_FAILED(result))
01037   {
01038     UNLOCK_DOC(this);
01039     return result;
01040   }
01041 
01042   if (!range)
01043   {
01044     UNLOCK_DOC(this);
01045     return NS_ERROR_FAILURE;
01046   }
01047 
01048   result = range->GetStartContainer(getter_AddRefs(parent));
01049 
01050   if (NS_FAILED(result))
01051   {
01052     UNLOCK_DOC(this);
01053     return result;
01054   }
01055 
01056   if (!parent)
01057   {
01058     UNLOCK_DOC(this);
01059     return NS_ERROR_FAILURE;
01060   }
01061 
01062   result = range->GetStartOffset(&offset);
01063 
01064   if (NS_FAILED(result))
01065   {
01066     UNLOCK_DOC(this);
01067     return result;
01068   }
01069 
01070   result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, PR_TRUE, getter_AddRefs(range));
01071 
01072   if (NS_FAILED(result))
01073   {
01074     UNLOCK_DOC(this);
01075     return result;
01076   }
01077 
01078   result = range->GetCollapsed(&isCollapsed);
01079 
01080   if (NS_FAILED(result))
01081   {
01082     UNLOCK_DOC(this);
01083     return result;
01084   }
01085 
01086   if (isCollapsed)
01087   {
01088     // If we get here, the range is collapsed because there is nothing before
01089     // the current selection! Just return NS_OK;
01090 
01091     UNLOCK_DOC(this);
01092     return NS_OK;
01093   }
01094 
01095   result = CreateContentIterator(range, getter_AddRefs(iter));
01096 
01097   if (NS_FAILED(result))
01098   {
01099     UNLOCK_DOC(this);
01100     return result;
01101   }
01102 
01103   iter->Last();
01104 
01105   while (!iter->IsDone())
01106   {
01107     nsIContent *content = iter->GetCurrentNode();
01108 
01109     if (IsTextNode(content))
01110     {
01111       // We found a text node! Adjust the document's iterator to point
01112       // to the beginning of it's text block, then get the current selection.
01113 
01114       result = mIterator->PositionAt(content);
01115 
01116       if (NS_FAILED(result))
01117       {
01118         UNLOCK_DOC(this);
01119         return result;
01120       }
01121 
01122       result = FirstTextNodeInCurrentBlock(mIterator);
01123 
01124       if (NS_FAILED(result))
01125       {
01126         UNLOCK_DOC(this);
01127         return result;
01128       }
01129 
01130       mIteratorStatus = nsTextServicesDocument::eValid;
01131 
01132       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
01133                                  mExtent, nsnull);
01134 
01135       if (NS_FAILED(result))
01136       {
01137         UNLOCK_DOC(this);
01138         return result;
01139       }
01140 
01141       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
01142 
01143       UNLOCK_DOC(this);
01144 
01145       return result;
01146     }
01147 
01148     iter->Prev();
01149   }
01150 
01151   // If we get here, we didn't find any block before or inside
01152   // the selection! Just return OK.
01153 
01154   UNLOCK_DOC(this);
01155 
01156   return NS_OK;
01157 }
01158 
01159 // XXX: CODE BLOAT ALERT. FirstSelectedBlock() and LastSelectedBlock()
01160 //      are almost identical! Later, when we have time, we should try
01161 //      and combine them into one method.
01162 
01163 NS_IMETHODIMP
01164 nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus,
01165                                           PRInt32 *aSelOffset,
01166                                           PRInt32 *aSelLength)
01167 {
01168   nsresult result = NS_OK;
01169 
01170   if (!aSelStatus || !aSelOffset || !aSelLength)
01171     return NS_ERROR_NULL_POINTER;
01172 
01173   LOCK_DOC(this);
01174 
01175   mIteratorStatus = nsTextServicesDocument::eIsDone;
01176 
01177   *aSelStatus = nsITextServicesDocument::eBlockNotFound;
01178   *aSelOffset = *aSelLength = -1;
01179 
01180   if (!mSelCon || !mIterator)
01181   {
01182     UNLOCK_DOC(this);
01183     return NS_ERROR_FAILURE;
01184   }
01185 
01186   nsCOMPtr<nsISelection> selection;
01187   PRBool isCollapsed = PR_FALSE;
01188 
01189   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
01190 
01191   if (NS_FAILED(result))
01192   {
01193     UNLOCK_DOC(this);
01194     return result;
01195   }
01196 
01197   result = selection->GetIsCollapsed(&isCollapsed);
01198 
01199   if (NS_FAILED(result))
01200   {
01201     UNLOCK_DOC(this);
01202     return result;
01203   }
01204 
01205   nsCOMPtr<nsIContentIterator> iter;
01206   nsCOMPtr<nsIDOMRange>        range;
01207   nsCOMPtr<nsIDOMNode>         parent;
01208   PRInt32                      i, rangeCount, offset;
01209 
01210   if (isCollapsed)
01211   {
01212     // We have a caret. Check if the caret is in a text node.
01213     // If it is, make the text node's block the current block.
01214     // If the caret isn't in a text node, search forwards in
01215     // the document, till we find a text node.
01216 
01217     result = selection->GetRangeAt(0, getter_AddRefs(range));
01218 
01219     if (NS_FAILED(result))
01220     {
01221       UNLOCK_DOC(this);
01222       return result;
01223     }
01224 
01225     if (!range)
01226     {
01227       UNLOCK_DOC(this);
01228       return NS_ERROR_FAILURE;
01229     }
01230 
01231     result = range->GetStartContainer(getter_AddRefs(parent));
01232 
01233     if (NS_FAILED(result))
01234     {
01235       UNLOCK_DOC(this);
01236       return result;
01237     }
01238 
01239     if (!parent)
01240     {
01241       UNLOCK_DOC(this);
01242       return NS_ERROR_FAILURE;
01243     }
01244 
01245     result = range->GetStartOffset(&offset);
01246 
01247     if (NS_FAILED(result))
01248     {
01249       UNLOCK_DOC(this);
01250       return result;
01251     }
01252 
01253     if (IsTextNode(parent))
01254     {
01255       // The caret is in a text node. Find the beginning
01256       // of the text block containing this text node and
01257       // return.
01258 
01259       nsCOMPtr<nsIContent> content(do_QueryInterface(parent));
01260 
01261       if (!content)
01262       {
01263         UNLOCK_DOC(this);
01264         return NS_ERROR_FAILURE;
01265       }
01266 
01267       result = mIterator->PositionAt(content);
01268 
01269       if (NS_FAILED(result))
01270       {
01271         UNLOCK_DOC(this);
01272         return result;
01273       }
01274 
01275       result = FirstTextNodeInCurrentBlock(mIterator);
01276 
01277       if (NS_FAILED(result))
01278       {
01279         UNLOCK_DOC(this);
01280         return result;
01281       }
01282 
01283       mIteratorStatus = nsTextServicesDocument::eValid;
01284 
01285       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
01286                                  mExtent, nsnull);
01287 
01288       if (NS_FAILED(result))
01289       {
01290         UNLOCK_DOC(this);
01291         return result;
01292       }
01293 
01294       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
01295 
01296       if (NS_FAILED(result))
01297       {
01298         UNLOCK_DOC(this);
01299         return result;
01300       }
01301 
01302       if (*aSelStatus == nsITextServicesDocument::eBlockContains)
01303         result = SetSelectionInternal(*aSelOffset, *aSelLength, PR_FALSE);
01304     }
01305     else
01306     {
01307       // The caret isn't in a text node. Create an iterator
01308       // based on a range that extends from the current caret
01309       // position to the end of the document, then walk forwards
01310       // till you find a text node, then find the beginning of it's block.
01311 
01312       result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, PR_FALSE, getter_AddRefs(range));
01313 
01314       if (NS_FAILED(result))
01315       {
01316         UNLOCK_DOC(this);
01317         return result;
01318       }
01319 
01320       result = range->GetCollapsed(&isCollapsed);
01321 
01322       if (NS_FAILED(result))
01323       {
01324         UNLOCK_DOC(this);
01325         return result;
01326       }
01327 
01328       if (isCollapsed)
01329       {
01330         // If we get here, the range is collapsed because there is nothing after
01331         // the caret! Just return NS_OK;
01332 
01333         UNLOCK_DOC(this);
01334         return NS_OK;
01335       }
01336 
01337       result = CreateContentIterator(range, getter_AddRefs(iter));
01338 
01339       if (NS_FAILED(result))
01340       {
01341         UNLOCK_DOC(this);
01342         return result;
01343       }
01344 
01345       iter->First();
01346 
01347       nsIContent *content = nsnull;
01348       while (!iter->IsDone())
01349       {
01350         content = iter->GetCurrentNode();
01351 
01352         if (IsTextNode(content))
01353           break;
01354 
01355         content = nsnull;
01356 
01357         iter->Next();
01358       }
01359 
01360       if (!content)
01361       {
01362         UNLOCK_DOC(this);
01363         return NS_OK;
01364       }
01365 
01366       result = mIterator->PositionAt(content);
01367 
01368       if (NS_FAILED(result))
01369       {
01370         UNLOCK_DOC(this);
01371         return result;
01372       }
01373 
01374       result = FirstTextNodeInCurrentBlock(mIterator);
01375 
01376       if (NS_FAILED(result))
01377       {
01378         UNLOCK_DOC(this);
01379         return result;
01380       }
01381 
01382       mIteratorStatus = nsTextServicesDocument::eValid;
01383 
01384       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
01385                                  mExtent, nsnull);
01386 
01387       if (NS_FAILED(result))
01388       {
01389         UNLOCK_DOC(this);
01390         return result;
01391       }
01392 
01393       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
01394 
01395       if (NS_FAILED(result))
01396       {
01397         UNLOCK_DOC(this);
01398         return result;
01399       }
01400     }
01401 
01402     UNLOCK_DOC(this);
01403 
01404     return result;
01405   }
01406 
01407   // If we get here, we have an uncollapsed selection!
01408   // Look backwards through each range in the selection till you
01409   // find the first text node. If you find one, find the
01410   // beginning of it's text block, and make it the current
01411   // block.
01412 
01413   result = selection->GetRangeCount(&rangeCount);
01414 
01415   if (NS_FAILED(result))
01416   {
01417     UNLOCK_DOC(this);
01418     return result;
01419   }
01420 
01421   NS_ASSERTION(rangeCount > 0, "Unexpected range count!");
01422 
01423   if (rangeCount <= 0)
01424   {
01425     UNLOCK_DOC(this);
01426     return NS_OK;
01427   }
01428 
01429   // XXX: We may need to add some code here to make sure
01430   //      the ranges are sorted in document appearance order!
01431 
01432   for (i = rangeCount - 1; i >= 0; i--)
01433   {
01434     // Get the i'th range from the selection.
01435 
01436     result = selection->GetRangeAt(i, getter_AddRefs(range));
01437 
01438     if (NS_FAILED(result))
01439     {
01440       UNLOCK_DOC(this);
01441       return result;
01442     }
01443 
01444     // Create an iterator for the range.
01445 
01446     result = CreateContentIterator(range, getter_AddRefs(iter));
01447 
01448     if (NS_FAILED(result))
01449     {
01450       UNLOCK_DOC(this);
01451       return result;
01452     }
01453 
01454     iter->Last();
01455 
01456     // Now walk through the range till we find a text node.
01457 
01458     while (!iter->IsDone())
01459     {
01460       nsIContent *content = iter->GetCurrentNode();
01461 
01462       if (IsTextNode(content))
01463       {
01464         // We found a text node, so position the document's
01465         // iterator at the beginning of the block, then get
01466         // the selection in terms of the string offset.
01467 
01468         result = mIterator->PositionAt(content);
01469 
01470         if (NS_FAILED(result))
01471         {
01472           UNLOCK_DOC(this);
01473           return result;
01474         }
01475 
01476         result = FirstTextNodeInCurrentBlock(mIterator);
01477 
01478         if (NS_FAILED(result))
01479         {
01480           UNLOCK_DOC(this);
01481           return result;
01482         }
01483 
01484         mIteratorStatus = nsTextServicesDocument::eValid;
01485 
01486         result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
01487                                    mExtent, nsnull);
01488 
01489         if (NS_FAILED(result))
01490         {
01491           UNLOCK_DOC(this);
01492           return result;
01493         }
01494 
01495         result = GetSelection(aSelStatus, aSelOffset, aSelLength);
01496 
01497         UNLOCK_DOC(this);
01498 
01499         return result;
01500 
01501       }
01502 
01503       iter->Prev();
01504     }
01505   }
01506 
01507   // If we get here, we didn't find any text node in the selection!
01508   // Create a range that extends from the end of the selection,
01509   // to the end of the document, then iterate forwards through
01510   // it till you find a text node!
01511 
01512   result = selection->GetRangeAt(rangeCount - 1, getter_AddRefs(range));
01513 
01514   if (NS_FAILED(result))
01515   {
01516     UNLOCK_DOC(this);
01517     return result;
01518   }
01519 
01520   if (!range)
01521   {
01522     UNLOCK_DOC(this);
01523     return NS_ERROR_FAILURE;
01524   }
01525 
01526   result = range->GetEndContainer(getter_AddRefs(parent));
01527 
01528   if (NS_FAILED(result))
01529   {
01530     UNLOCK_DOC(this);
01531     return result;
01532   }
01533 
01534   if (!parent)
01535   {
01536     UNLOCK_DOC(this);
01537     return NS_ERROR_FAILURE;
01538   }
01539 
01540   result = range->GetEndOffset(&offset);
01541 
01542   if (NS_FAILED(result))
01543   {
01544     UNLOCK_DOC(this);
01545     return result;
01546   }
01547 
01548   result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, PR_FALSE, getter_AddRefs(range));
01549 
01550   if (NS_FAILED(result))
01551   {
01552     UNLOCK_DOC(this);
01553     return result;
01554   }
01555 
01556   result = range->GetCollapsed(&isCollapsed);
01557 
01558   if (NS_FAILED(result))
01559   {
01560     UNLOCK_DOC(this);
01561     return result;
01562   }
01563 
01564   if (isCollapsed)
01565   {
01566     // If we get here, the range is collapsed because there is nothing after
01567     // the current selection! Just return NS_OK;
01568 
01569     UNLOCK_DOC(this);
01570     return NS_OK;
01571   }
01572 
01573   result = CreateContentIterator(range, getter_AddRefs(iter));
01574 
01575   if (NS_FAILED(result))
01576   {
01577     UNLOCK_DOC(this);
01578     return result;
01579   }
01580 
01581   iter->First();
01582 
01583   while (!iter->IsDone())
01584   {
01585     nsIContent *content = iter->GetCurrentNode();
01586 
01587     if (IsTextNode(content))
01588     {
01589       // We found a text node! Adjust the document's iterator to point
01590       // to the beginning of it's text block, then get the current selection.
01591 
01592       result = mIterator->PositionAt(content);
01593 
01594       if (NS_FAILED(result))
01595       {
01596         UNLOCK_DOC(this);
01597         return result;
01598       }
01599 
01600       result = FirstTextNodeInCurrentBlock(mIterator);
01601 
01602       if (NS_FAILED(result))
01603       {
01604         UNLOCK_DOC(this);
01605         return result;
01606       }
01607 
01608 
01609       mIteratorStatus = nsTextServicesDocument::eValid;
01610 
01611       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
01612                                  mExtent, nsnull);
01613 
01614       if (NS_FAILED(result))
01615       {
01616         UNLOCK_DOC(this);
01617         return result;
01618       }
01619 
01620       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
01621 
01622       UNLOCK_DOC(this);
01623 
01624       return result;
01625     }
01626 
01627     iter->Next();
01628   }
01629 
01630   // If we get here, we didn't find any block before or inside
01631   // the selection! Just return OK.
01632 
01633   UNLOCK_DOC(this);
01634 
01635   return NS_OK;
01636 }
01637 
01638 NS_IMETHODIMP
01639 nsTextServicesDocument::PrevBlock()
01640 {
01641   nsresult result = NS_OK;
01642 
01643   if (!mIterator)
01644     return NS_ERROR_FAILURE;
01645 
01646   LOCK_DOC(this);
01647 
01648   if (mIteratorStatus == nsTextServicesDocument::eIsDone)
01649     return NS_OK;
01650 
01651   switch (mIteratorStatus)
01652   {
01653     case nsTextServicesDocument::eValid:
01654     case nsTextServicesDocument::eNext:
01655 
01656       result = FirstTextNodeInPrevBlock(mIterator);
01657 
01658       if (NS_FAILED(result))
01659       {
01660         mIteratorStatus = nsTextServicesDocument::eIsDone;
01661         UNLOCK_DOC(this);
01662         return result;
01663       }
01664 
01665       if (mIterator->IsDone())
01666       {
01667         mIteratorStatus = nsTextServicesDocument::eIsDone;
01668         UNLOCK_DOC(this);
01669         return NS_OK;
01670       }
01671 
01672       mIteratorStatus = nsTextServicesDocument::eValid;
01673       break;
01674 
01675     case nsTextServicesDocument::ePrev:
01676 
01677       // The iterator already points to the previous
01678       // block, so don't do anything.
01679 
01680       mIteratorStatus = nsTextServicesDocument::eValid;
01681       break;
01682 
01683     default:
01684 
01685       mIteratorStatus = nsTextServicesDocument::eIsDone;
01686       break;
01687   }
01688 
01689   // Keep track of prev and next blocks, just in case
01690   // the text service blows away the current block.
01691 
01692   if (mIteratorStatus == nsTextServicesDocument::eValid)
01693   {
01694     result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
01695     result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
01696   }
01697   else
01698   {
01699     // We must be done!
01700 
01701     mPrevTextBlock = nsnull;
01702     mNextTextBlock = nsnull;
01703   }
01704 
01705   UNLOCK_DOC(this);
01706 
01707   return result;
01708 }
01709 
01710 NS_IMETHODIMP
01711 nsTextServicesDocument::NextBlock()
01712 {
01713   nsresult result = NS_OK;
01714 
01715   if (!mIterator)
01716     return NS_ERROR_FAILURE;
01717 
01718   LOCK_DOC(this);
01719 
01720   if (mIteratorStatus == nsTextServicesDocument::eIsDone)
01721     return NS_OK;
01722 
01723   switch (mIteratorStatus)
01724   {
01725     case nsTextServicesDocument::eValid:
01726 
01727       // Advance the iterator to the next text block.
01728 
01729       result = FirstTextNodeInNextBlock(mIterator);
01730 
01731       if (NS_FAILED(result))
01732       {
01733         mIteratorStatus = nsTextServicesDocument::eIsDone;
01734         UNLOCK_DOC(this);
01735         return result;
01736       }
01737 
01738       if (mIterator->IsDone())
01739       {
01740         mIteratorStatus = nsTextServicesDocument::eIsDone;
01741         UNLOCK_DOC(this);
01742         return NS_OK;
01743       }
01744 
01745       mIteratorStatus = nsTextServicesDocument::eValid;
01746       break;
01747 
01748     case nsTextServicesDocument::eNext:
01749 
01750       // The iterator already points to the next block,
01751       // so don't do anything to it!
01752 
01753       mIteratorStatus = nsTextServicesDocument::eValid;
01754       break;
01755 
01756     case nsTextServicesDocument::ePrev:
01757 
01758       // If the iterator is pointing to the previous block,
01759       // we know that there is no next text block! Just
01760       // fall through to the default case!
01761 
01762     default:
01763 
01764       mIteratorStatus = nsTextServicesDocument::eIsDone;
01765       break;
01766   }
01767 
01768   // Keep track of prev and next blocks, just in case
01769   // the text service blows away the current block.
01770 
01771   if (mIteratorStatus == nsTextServicesDocument::eValid)
01772   {
01773     result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
01774     result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
01775   }
01776   else
01777   {
01778     // We must be done.
01779 
01780     mPrevTextBlock = nsnull;
01781     mNextTextBlock = nsnull;
01782   }
01783 
01784 
01785   UNLOCK_DOC(this);
01786 
01787   return result;
01788 }
01789 
01790 NS_IMETHODIMP
01791 nsTextServicesDocument::IsDone(PRBool *aIsDone)
01792 {
01793   if (!aIsDone)
01794     return NS_ERROR_NULL_POINTER;
01795 
01796   *aIsDone = PR_FALSE;
01797 
01798   if (!mIterator)
01799     return NS_ERROR_FAILURE;
01800 
01801   LOCK_DOC(this);
01802 
01803   *aIsDone = (mIteratorStatus == nsTextServicesDocument::eIsDone) ? PR_TRUE : PR_FALSE;
01804 
01805   UNLOCK_DOC(this);
01806 
01807   return NS_OK;
01808 }
01809 
01810 NS_IMETHODIMP
01811 nsTextServicesDocument::SetSelection(PRInt32 aOffset, PRInt32 aLength)
01812 {
01813   nsresult result;
01814 
01815   if (!mSelCon || aOffset < 0 || aLength < 0)
01816     return NS_ERROR_FAILURE;
01817 
01818   LOCK_DOC(this);
01819 
01820   result = SetSelectionInternal(aOffset, aLength, PR_TRUE);
01821 
01822   UNLOCK_DOC(this);
01823 
01824   //**** KDEBUG ****
01825   // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
01826   //**** KDEBUG ****
01827 
01828   return result;
01829 }
01830 
01831 NS_IMETHODIMP
01832 nsTextServicesDocument::ScrollSelectionIntoView()
01833 {
01834   nsresult result;
01835 
01836   if (!mSelCon)
01837     return NS_ERROR_FAILURE;
01838 
01839   LOCK_DOC(this);
01840 
01841   result = mSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
01842 
01843   UNLOCK_DOC(this);
01844 
01845   return result;
01846 }
01847 
01848 NS_IMETHODIMP
01849 nsTextServicesDocument::DeleteSelection()
01850 {
01851   nsresult result = NS_OK;
01852 
01853   // We don't allow deletion during a collapsed selection!
01854   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
01855   NS_ASSERTION(editor, "DeleteSelection called without an editor present!"); 
01856   NS_ASSERTION(SelectionIsValid(), "DeleteSelection called without a valid selection!"); 
01857 
01858   if (!editor || !SelectionIsValid())
01859     return NS_ERROR_FAILURE;
01860 
01861   if (SelectionIsCollapsed())
01862     return NS_OK;
01863 
01864   LOCK_DOC(this);
01865 
01866   //**** KDEBUG ****
01867   // printf("\n---- Before Delete\n");
01868   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
01869   // PrintOffsetTable();
01870   //**** KDEBUG ****
01871 
01872   // If we have an mExtent, save off it's current set of
01873   // end points so we can compare them against mExtent's
01874   // set after the deletion of the content.
01875 
01876   nsCOMPtr<nsIDOMNode> origStartNode, origEndNode;
01877   PRInt32 origStartOffset = 0, origEndOffset = 0;
01878 
01879   if (mExtent)
01880   {
01881     result = GetRangeEndPoints(mExtent,
01882                                getter_AddRefs(origStartNode), &origStartOffset,
01883                                getter_AddRefs(origEndNode), &origEndOffset);
01884 
01885     if (NS_FAILED(result))
01886     {
01887       UNLOCK_DOC(this);
01888       return result;
01889     }
01890   }
01891 
01892   PRInt32 i, selLength;
01893   OffsetEntry *entry, *newEntry;
01894 
01895   for (i = mSelStartIndex; i <= mSelEndIndex; i++)
01896   {
01897     entry = (OffsetEntry *)mOffsetTable[i];
01898 
01899     if (i == mSelStartIndex)
01900     {
01901       // Calculate the length of the selection. Note that the
01902       // selection length can be zero if the start of the selection
01903       // is at the very end of a text node entry.
01904 
01905       if (entry->mIsInsertedText)
01906       {
01907         // Inserted text offset entries have no width when
01908         // talking in terms of string offsets! If the beginning
01909         // of the selection is in an inserted text offset entry,
01910         // the caret is always at the end of the entry!
01911 
01912         selLength = 0;
01913       }
01914       else
01915         selLength = entry->mLength - (mSelStartOffset - entry->mStrOffset);
01916 
01917       if (selLength > 0 && mSelStartOffset > entry->mStrOffset)
01918       {
01919         // Selection doesn't start at the beginning of the
01920         // text node entry. We need to split this entry into
01921         // two pieces, the piece before the selection, and
01922         // the piece inside the selection.
01923 
01924         result = SplitOffsetEntry(i, selLength);
01925 
01926         if (NS_FAILED(result))
01927         {
01928           UNLOCK_DOC(this);
01929           return result;
01930         }
01931 
01932         // Adjust selection indexes to account for new entry:
01933 
01934         ++mSelStartIndex;
01935         ++mSelEndIndex;
01936         ++i;
01937 
01938         entry = (OffsetEntry *)mOffsetTable[i];
01939       }
01940 
01941 
01942       if (selLength > 0 && mSelStartIndex < mSelEndIndex)
01943       {
01944         // The entire entry is contained in the selection. Mark the
01945         // entry invalid.
01946 
01947         entry->mIsValid = PR_FALSE;
01948       }
01949     }
01950 
01951   //**** KDEBUG ****
01952   // printf("\n---- Middle Delete\n");
01953   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
01954   // PrintOffsetTable();
01955   //**** KDEBUG ****
01956 
01957     if (i == mSelEndIndex)
01958     {
01959       if (entry->mIsInsertedText)
01960       {
01961         // Inserted text offset entries have no width when
01962         // talking in terms of string offsets! If the end
01963         // of the selection is in an inserted text offset entry,
01964         // the selection includes the entire entry!
01965 
01966         entry->mIsValid = PR_FALSE;
01967       }
01968       else
01969       {
01970         // Calculate the length of the selection. Note that the
01971         // selection length can be zero if the end of the selection
01972         // is at the very beginning of a text node entry.
01973 
01974         selLength = mSelEndOffset - entry->mStrOffset;
01975 
01976         if (selLength > 0 && mSelEndOffset < entry->mStrOffset + entry->mLength)
01977         {
01978           // mStrOffset is guaranteed to be inside the selection, even
01979           // when mSelStartIndex == mSelEndIndex.
01980 
01981           result = SplitOffsetEntry(i, entry->mLength - selLength);
01982 
01983           if (NS_FAILED(result))
01984           {
01985             UNLOCK_DOC(this);
01986             return result;
01987           }
01988 
01989           // Update the entry fields:
01990 
01991           newEntry = (OffsetEntry *)mOffsetTable[i+1];
01992           newEntry->mNodeOffset = entry->mNodeOffset;
01993         }
01994 
01995 
01996         if (selLength > 0 && mSelEndOffset == entry->mStrOffset + entry->mLength)
01997         {
01998           // The entire entry is contained in the selection. Mark the
01999           // entry invalid.
02000 
02001           entry->mIsValid = PR_FALSE;
02002         }
02003       }
02004     }
02005 
02006     if (i != mSelStartIndex && i != mSelEndIndex)
02007     {
02008       // The entire entry is contained in the selection. Mark the
02009       // entry invalid.
02010 
02011       entry->mIsValid = PR_FALSE;
02012     }
02013   }
02014 
02015   // Make sure mIterator always points to something valid!
02016 
02017   AdjustContentIterator();
02018 
02019   // Now delete the actual content!
02020 
02021   result = editor->DeleteSelection(nsIEditor::ePrevious);
02022 
02023   if (NS_FAILED(result))
02024   {
02025     UNLOCK_DOC(this);
02026     return result;
02027   }
02028 
02029   // Now that we've actually deleted the selected content,
02030   // check to see if our mExtent has changed, if so, then
02031   // we have to create a new content iterator!
02032 
02033   if (origStartNode && origEndNode)
02034   {
02035     nsCOMPtr<nsIDOMNode> curStartNode, curEndNode;
02036     PRInt32 curStartOffset = 0, curEndOffset = 0;
02037 
02038     result = GetRangeEndPoints(mExtent,
02039                                getter_AddRefs(curStartNode), &curStartOffset,
02040                                getter_AddRefs(curEndNode), &curEndOffset);
02041 
02042     if (NS_FAILED(result))
02043     {
02044       UNLOCK_DOC(this);
02045       return result;
02046     }
02047 
02048     if (origStartNode != curStartNode || origEndNode != curEndNode)
02049     {
02050       // The range has changed, so we need to create a new content
02051       // iterator based on the new range.
02052 
02053       nsIContent *curContent = nsnull;
02054 
02055       if (mIteratorStatus != nsTextServicesDocument::eIsDone)
02056       {
02057         // The old iterator is still pointing to something valid,
02058         // so get it's current node so we can restore it after we
02059         // create the new iterator!
02060 
02061         curContent = mIterator->GetCurrentNode();
02062       }
02063 
02064       // Create the new iterator.
02065 
02066       result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
02067 
02068       if (NS_FAILED(result))
02069       {
02070         UNLOCK_DOC(this);
02071         return result;
02072       }
02073 
02074       // Now make the new iterator point to the content node
02075       // the old one was pointing at.
02076 
02077       if (curContent)
02078       {
02079         result = mIterator->PositionAt(curContent);
02080 
02081         if (NS_FAILED(result))
02082           mIteratorStatus = eIsDone;
02083         else
02084           mIteratorStatus = eValid;
02085       }
02086     }
02087   }
02088 
02089   entry = 0;
02090 
02091   // Move the caret to the end of the first valid entry.
02092   // Start with mSelStartIndex since it may still be valid.
02093 
02094   for (i = mSelStartIndex; !entry && i >= 0; i--)
02095   {
02096     entry = (OffsetEntry *)mOffsetTable[i];
02097 
02098     if (!entry->mIsValid)
02099       entry = 0;
02100     else
02101     {
02102       mSelStartIndex  = mSelEndIndex  = i;
02103       mSelStartOffset = mSelEndOffset = entry->mStrOffset + entry->mLength;
02104     }
02105   }
02106 
02107   // If we still don't have a valid entry, move the caret
02108   // to the next valid entry after the selection:
02109 
02110   for (i = mSelEndIndex; !entry && i < mOffsetTable.Count(); i++)
02111   {
02112     entry = (OffsetEntry *)mOffsetTable[i];
02113 
02114     if (!entry->mIsValid)
02115       entry = 0;
02116     else
02117     {
02118       mSelStartIndex = mSelEndIndex = i;
02119       mSelStartOffset = mSelEndOffset = entry->mStrOffset;
02120     }
02121   }
02122 
02123   if (entry)
02124     result = SetSelection(mSelStartOffset, 0);
02125   else
02126   {
02127     // Uuughh we have no valid offset entry to place our
02128     // caret ... just mark the selection invalid.
02129 
02130     mSelStartIndex  = mSelEndIndex  = -1;
02131     mSelStartOffset = mSelEndOffset = -1;
02132   }
02133 
02134   // Now remove any invalid entries from the offset table.
02135 
02136   result = RemoveInvalidOffsetEntries();
02137 
02138   //**** KDEBUG ****
02139   // printf("\n---- After Delete\n");
02140   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
02141   // PrintOffsetTable();
02142   //**** KDEBUG ****
02143 
02144   UNLOCK_DOC(this);
02145 
02146   return result;
02147 }
02148 
02149 NS_IMETHODIMP
02150 nsTextServicesDocument::InsertText(const nsString *aText)
02151 {
02152   nsresult result = NS_OK;
02153 
02154   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
02155   NS_ASSERTION(editor, "InsertText called without an editor present!"); 
02156 
02157   if (!editor || !SelectionIsValid())
02158     return NS_ERROR_FAILURE;
02159 
02160   if (!aText)
02161     return NS_ERROR_NULL_POINTER;
02162 
02163   // If the selection is not collapsed, we need to save
02164   // off the selection offsets so we can restore the
02165   // selection and delete the selected content after we've
02166   // inserted the new text. This is necessary to try and
02167   // retain as much of the original style of the content
02168   // being deleted.
02169 
02170   PRBool collapsedSelection = SelectionIsCollapsed();
02171   PRInt32 savedSelOffset = mSelStartOffset;
02172   PRInt32 savedSelLength = mSelEndOffset - mSelStartOffset;
02173 
02174   if (!collapsedSelection)
02175   {
02176     // Collapse to the start of the current selection
02177     // for the insert!
02178 
02179     result = SetSelection(mSelStartOffset, 0);
02180 
02181     if (NS_FAILED(result))
02182       return result;
02183   }
02184 
02185 
02186   LOCK_DOC(this);
02187 
02188   result = editor->BeginTransaction();
02189 
02190   if (NS_FAILED(result))
02191   {
02192     UNLOCK_DOC(this);
02193     return result;
02194   }
02195 
02196   nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(editor, &result));
02197   if (textEditor)
02198     result = textEditor->InsertText(*aText);
02199 
02200   if (NS_FAILED(result))
02201   {
02202     editor->EndTransaction();
02203     UNLOCK_DOC(this);
02204     return result;
02205   }
02206 
02207   //**** KDEBUG ****
02208   // printf("\n---- Before Insert\n");
02209   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
02210   // PrintOffsetTable();
02211   //**** KDEBUG ****
02212 
02213   PRInt32 i, strLength = aText->Length();
02214 
02215   nsCOMPtr<nsISelection> selection;
02216   OffsetEntry *itEntry;
02217   OffsetEntry *entry = (OffsetEntry *)mOffsetTable[mSelStartIndex];
02218   void *node         = entry->mNode;
02219 
02220   NS_ASSERTION((entry->mIsValid), "Invalid insertion point!");
02221 
02222   if (entry->mStrOffset == mSelStartOffset)
02223   {
02224     if (entry->mIsInsertedText)
02225     {
02226       // If the caret is in an inserted text offset entry,
02227       // we simply insert the text at the end of the entry.
02228 
02229       entry->mLength += strLength;
02230     }
02231     else
02232     {
02233       // Insert an inserted text offset entry before the current
02234       // entry!
02235 
02236       itEntry = new OffsetEntry(entry->mNode, entry->mStrOffset, strLength);
02237 
02238       if (!itEntry)
02239       {
02240         editor->EndTransaction();
02241         UNLOCK_DOC(this);
02242         return NS_ERROR_OUT_OF_MEMORY;
02243       }
02244 
02245       itEntry->mIsInsertedText = PR_TRUE;
02246       itEntry->mNodeOffset = entry->mNodeOffset;
02247 
02248       if (!mOffsetTable.InsertElementAt(itEntry, mSelStartIndex))
02249       {
02250         editor->EndTransaction();
02251         UNLOCK_DOC(this);
02252         return NS_ERROR_FAILURE;
02253       }
02254     }
02255   }
02256   else if ((entry->mStrOffset + entry->mLength) == mSelStartOffset)
02257   {
02258     // We are inserting text at the end of the current offset entry.
02259     // Look at the next valid entry in the table. If it's an inserted
02260     // text entry, add to it's length and adjust it's node offset. If
02261     // it isn't, add a new inserted text entry.
02262 
02263     i       = mSelStartIndex + 1;
02264     itEntry = 0;
02265 
02266     if (mOffsetTable.Count() > i)
02267     {
02268       itEntry = (OffsetEntry *)mOffsetTable[i];
02269 
02270       if (!itEntry)
02271       {
02272         editor->EndTransaction();
02273         UNLOCK_DOC(this);
02274         return NS_ERROR_FAILURE;
02275       }
02276 
02277       // Check if the entry is a match. If it isn't, set
02278       // iEntry to zero.
02279 
02280       if (!itEntry->mIsInsertedText || itEntry->mStrOffset != mSelStartOffset)
02281         itEntry = 0;
02282     }
02283 
02284     if (!itEntry)
02285     {
02286       // We didn't find an inserted text offset entry, so
02287       // create one.
02288 
02289       itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, 0);
02290 
02291       if (!itEntry)
02292       {
02293         editor->EndTransaction();
02294         UNLOCK_DOC(this);
02295         return NS_ERROR_OUT_OF_MEMORY;
02296       }
02297 
02298       itEntry->mNodeOffset = entry->mNodeOffset + entry->mLength;
02299       itEntry->mIsInsertedText = PR_TRUE;
02300 
02301       if (!mOffsetTable.InsertElementAt(itEntry, i))
02302       {
02303         delete itEntry;
02304         return NS_ERROR_FAILURE;
02305       }
02306     }
02307 
02308     // We have a valid inserted text offset entry. Update it's
02309     // length, adjust the selection indexes, and make sure the
02310     // caret is properly placed!
02311 
02312     itEntry->mLength += strLength;
02313 
02314     mSelStartIndex = mSelEndIndex = i;
02315           
02316     result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
02317 
02318     if (NS_FAILED(result))
02319     {
02320       editor->EndTransaction();
02321       UNLOCK_DOC(this);
02322       return result;
02323     }
02324 
02325     result = selection->Collapse(itEntry->mNode, itEntry->mNodeOffset + itEntry->mLength);
02326         
02327     if (NS_FAILED(result))
02328     {
02329       editor->EndTransaction();
02330       UNLOCK_DOC(this);
02331       return result;
02332     }
02333   }
02334   else if ((entry->mStrOffset + entry->mLength) > mSelStartOffset)
02335   {
02336     // We are inserting text into the middle of the current offset entry.
02337     // split the current entry into two parts, then insert an inserted text
02338     // entry between them!
02339 
02340     i = entry->mLength - (mSelStartOffset - entry->mStrOffset);
02341 
02342     result = SplitOffsetEntry(mSelStartIndex, i);
02343 
02344     if (NS_FAILED(result))
02345     {
02346       editor->EndTransaction();
02347       UNLOCK_DOC(this);
02348       return result;
02349     }
02350 
02351     itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, strLength);
02352 
02353     if (!itEntry)
02354     {
02355       editor->EndTransaction();
02356       UNLOCK_DOC(this);
02357       return NS_ERROR_OUT_OF_MEMORY;
02358     }
02359 
02360     itEntry->mIsInsertedText = PR_TRUE;
02361     itEntry->mNodeOffset     = entry->mNodeOffset + entry->mLength;
02362 
02363     if (!mOffsetTable.InsertElementAt(itEntry, mSelStartIndex + 1))
02364     {
02365       editor->EndTransaction();
02366       UNLOCK_DOC(this);
02367       return NS_ERROR_FAILURE;
02368     }
02369 
02370     mSelEndIndex = ++mSelStartIndex;
02371   }
02372 
02373   // We've just finished inserting an inserted text offset entry.
02374   // update all entries with the same mNode pointer that follow
02375   // it in the table!
02376 
02377   for (i = mSelStartIndex + 1; i < mOffsetTable.Count(); i++)
02378   {
02379     entry = (OffsetEntry *)mOffsetTable[i];
02380 
02381     if (entry->mNode == node)
02382     {
02383       if (entry->mIsValid)
02384         entry->mNodeOffset += strLength;
02385     }
02386     else
02387       break;
02388   }
02389 
02390   //**** KDEBUG ****
02391   // printf("\n---- After Insert\n");
02392   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
02393   // PrintOffsetTable();
02394   //**** KDEBUG ****
02395 
02396   if (!collapsedSelection)
02397   {
02398     result = SetSelection(savedSelOffset, savedSelLength);
02399 
02400     if (NS_FAILED(result))
02401     {
02402       editor->EndTransaction();
02403       UNLOCK_DOC(this);
02404       return result;
02405     }
02406 
02407     result = DeleteSelection();
02408   
02409     if (NS_FAILED(result))
02410     {
02411       editor->EndTransaction();
02412       UNLOCK_DOC(this);
02413       return result;
02414     }
02415   }
02416 
02417   result = editor->EndTransaction();
02418 
02419   UNLOCK_DOC(this);
02420 
02421   return result;
02422 }
02423 
02424 NS_IMETHODIMP
02425 nsTextServicesDocument::SetDisplayStyle(TSDDisplayStyle aStyle)
02426 {
02427   return NS_ERROR_NOT_IMPLEMENTED;
02428 }
02429 
02430 NS_IMETHODIMP
02431 nsTextServicesDocument::GetDOMRangeFor(PRInt32 aOffset, PRInt32 aLength, nsIDOMRange** aRange)
02432 {
02433   if (!mSelCon || aOffset < 0 || aLength < 0)
02434     return NS_ERROR_FAILURE;
02435 
02436   nsIDOMNode *sNode = 0, *eNode = 0;
02437   PRInt32 i, sOffset = 0, eOffset = 0;
02438   OffsetEntry *entry;
02439 
02440   // Find the start
02441   for (i = 0; !sNode && i < mOffsetTable.Count(); i++)
02442   {
02443     entry = (OffsetEntry *)mOffsetTable[i];
02444     if (entry->mIsValid)
02445     {
02446       if (entry->mIsInsertedText)
02447       {
02448         if (entry->mStrOffset == aOffset)
02449         {
02450           sNode   = entry->mNode;
02451           sOffset = entry->mNodeOffset + entry->mLength;
02452         }
02453       }
02454       else if (aOffset >= entry->mStrOffset && aOffset <= entry->mStrOffset + entry->mLength)
02455       {
02456         sNode   = entry->mNode;
02457         sOffset = entry->mNodeOffset + aOffset - entry->mStrOffset;
02458       }
02459     }
02460   }
02461 
02462   if (!sNode)
02463     return NS_ERROR_FAILURE;
02464 
02465   // Now find the end
02466   PRInt32 endOffset = aOffset + aLength;
02467 
02468   for (i = mOffsetTable.Count() - 1; !eNode && i >= 0; i--)
02469   {
02470     entry = (OffsetEntry *)mOffsetTable[i];
02471     
02472     if (entry->mIsValid)
02473     {
02474       if (entry->mIsInsertedText)
02475       {
02476         if (entry->mStrOffset == eOffset)
02477         {
02478           eNode   = entry->mNode;
02479           eOffset = entry->mNodeOffset + entry->mLength;
02480         }
02481       }
02482       else if (endOffset >= entry->mStrOffset && endOffset <= entry->mStrOffset + entry->mLength)
02483       {
02484         eNode   = entry->mNode;
02485         eOffset = entry->mNodeOffset + endOffset - entry->mStrOffset;
02486       }
02487     }
02488   }
02489 
02490   return CreateRange(sNode, sOffset, eNode, eOffset, aRange);
02491 }
02492 
02493 nsresult
02494 nsTextServicesDocument::InsertNode(nsIDOMNode *aNode,
02495                                    nsIDOMNode *aParent,
02496                                    PRInt32 aPosition)
02497 {
02498   return NS_OK;
02499 }
02500 
02501 nsresult
02502 nsTextServicesDocument::DeleteNode(nsIDOMNode *aChild)
02503 {
02504   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
02505 
02506   //**** KDEBUG ****
02507   // printf("** DeleteNode: 0x%.8x\n", aChild);
02508   // fflush(stdout);
02509   //**** KDEBUG ****
02510 
02511   LOCK_DOC(this);
02512 
02513   PRInt32 nodeIndex, tcount;
02514   PRBool hasEntry;
02515   OffsetEntry *entry;
02516 
02517   nsresult result = NodeHasOffsetEntry(&mOffsetTable, aChild, &hasEntry, &nodeIndex);
02518 
02519   if (NS_FAILED(result))
02520   {
02521     UNLOCK_DOC(this);
02522     return result;
02523   }
02524 
02525   if (!hasEntry)
02526   {
02527     // It's okay if the node isn't in the offset table, the
02528     // editor could be cleaning house.
02529 
02530     UNLOCK_DOC(this);
02531     return NS_OK;
02532   }
02533 
02534   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mIterator->GetCurrentNode());
02535 
02536   if (node && node == aChild &&
02537       mIteratorStatus != nsTextServicesDocument::eIsDone)
02538   {
02539     // XXX: This should never really happen because
02540     // AdjustContentIterator() should have been called prior
02541     // to the delete to try and position the iterator on the
02542     // next valid text node in the offset table, and if there
02543     // wasn't a next, it would've set mIteratorStatus to eIsDone.
02544 
02545     NS_ASSERTION(0, "DeleteNode called for current iterator node."); 
02546   }
02547 
02548   tcount = mOffsetTable.Count();
02549 
02550   while (nodeIndex < tcount)
02551   {
02552     entry = (OffsetEntry *)mOffsetTable[nodeIndex];
02553 
02554     if (!entry)
02555     {
02556       UNLOCK_DOC(this);
02557       return NS_ERROR_FAILURE;
02558     }
02559 
02560     if (entry->mNode == aChild)
02561     {
02562       entry->mIsValid = PR_FALSE;
02563     }
02564 
02565     nodeIndex++;
02566   }
02567 
02568   UNLOCK_DOC(this);
02569 
02570   return NS_OK;
02571 }
02572 
02573 nsresult
02574 nsTextServicesDocument::SplitNode(nsIDOMNode *aExistingRightNode,
02575                                   PRInt32 aOffset,
02576                                   nsIDOMNode *aNewLeftNode)
02577 {
02578   //**** KDEBUG ****
02579   // printf("** SplitNode: 0x%.8x  %d  0x%.8x\n", aExistingRightNode, aOffset, aNewLeftNode);
02580   // fflush(stdout);
02581   //**** KDEBUG ****
02582   return NS_OK;
02583 }
02584 
02585 nsresult
02586 nsTextServicesDocument::JoinNodes(nsIDOMNode  *aLeftNode,
02587                                   nsIDOMNode  *aRightNode,
02588                                   nsIDOMNode  *aParent)
02589 {
02590   PRInt32 i;
02591   PRUint16 type;
02592   nsresult result;
02593 
02594   //**** KDEBUG ****
02595   // printf("** JoinNodes: 0x%.8x  0x%.8x  0x%.8x\n", aLeftNode, aRightNode, aParent);
02596   // fflush(stdout);
02597   //**** KDEBUG ****
02598 
02599   // Make sure that both nodes are text nodes!
02600 
02601   result = aLeftNode->GetNodeType(&type);
02602 
02603   if (NS_FAILED(result))
02604     return PR_FALSE;
02605 
02606   if (nsIDOMNode::TEXT_NODE != type)
02607   {
02608     NS_ASSERTION(0, "JoinNode called with a non-text left node!");
02609     return NS_ERROR_FAILURE;
02610   }
02611 
02612   result = aRightNode->GetNodeType(&type);
02613 
02614   if (NS_FAILED(result))
02615     return PR_FALSE;
02616 
02617   if (nsIDOMNode::TEXT_NODE != type)
02618   {
02619     NS_ASSERTION(0, "JoinNode called with a non-text right node!");
02620     return NS_ERROR_FAILURE;
02621   }
02622 
02623   // Note: The editor merges the contents of the left node into the
02624   //       contents of the right.
02625 
02626   PRInt32 leftIndex, rightIndex;
02627   PRBool leftHasEntry, rightHasEntry;
02628 
02629   result = NodeHasOffsetEntry(&mOffsetTable, aLeftNode, &leftHasEntry, &leftIndex);
02630 
02631   if (NS_FAILED(result))
02632     return result;
02633 
02634   if (!leftHasEntry)
02635   {
02636     // XXX: Not sure if we should be throwing an error here!
02637     NS_ASSERTION(0, "JoinNode called with node not listed in offset table.");
02638     return NS_ERROR_FAILURE;
02639   }
02640 
02641   result = NodeHasOffsetEntry(&mOffsetTable, aRightNode, &rightHasEntry, &rightIndex);
02642 
02643   if (NS_FAILED(result))
02644     return result;
02645 
02646   if (!rightHasEntry)
02647   {
02648     return NS_ERROR_FAILURE;
02649   }
02650 
02651   NS_ASSERTION(leftIndex < rightIndex, "Indexes out of order.");
02652 
02653   if (leftIndex > rightIndex)
02654   {
02655     // Don't know how to handle this situation.
02656     return NS_ERROR_FAILURE;
02657   }
02658 
02659   LOCK_DOC(this);
02660 
02661   OffsetEntry *entry = (OffsetEntry *)mOffsetTable[rightIndex];
02662   NS_ASSERTION(entry->mNodeOffset == 0, "Unexpected offset value for rightIndex.");
02663 
02664   // Run through the table and change all entries referring to
02665   // the left node so that they now refer to the right node:
02666 
02667   nsAutoString str;
02668   result = aLeftNode->GetNodeValue(str);
02669   PRInt32 nodeLength = str.Length();
02670 
02671   for (i = leftIndex; i < rightIndex; i++)
02672   {
02673     entry = (OffsetEntry *)mOffsetTable[i];
02674 
02675     if (entry->mNode == aLeftNode)
02676     {
02677       if (entry->mIsValid)
02678         entry->mNode = aRightNode;
02679     }
02680     else
02681       break;
02682   }
02683 
02684   // Run through the table and adjust the node offsets
02685   // for all entries referring to the right node.
02686 
02687   for (i = rightIndex; i < mOffsetTable.Count(); i++)
02688   {
02689     entry = (OffsetEntry *)mOffsetTable[i];
02690 
02691     if (entry->mNode == aRightNode)
02692     {
02693       if (entry->mIsValid)
02694         entry->mNodeOffset += nodeLength;
02695     }
02696     else
02697       break;
02698   }
02699 
02700   // Now check to see if the iterator is pointing to the
02701   // left node. If it is, make it point to the right node!
02702 
02703   nsCOMPtr<nsIContent> leftContent = do_QueryInterface(aLeftNode);
02704   nsCOMPtr<nsIContent> rightContent = do_QueryInterface(aRightNode);
02705 
02706   if (!leftContent || !rightContent)
02707   {
02708     UNLOCK_DOC(this);
02709     return NS_ERROR_FAILURE;
02710   }
02711 
02712   if (mIterator->GetCurrentNode() == leftContent)
02713     result = mIterator->PositionAt(rightContent);
02714 
02715   UNLOCK_DOC(this);
02716 
02717   return NS_OK;
02718 }
02719 
02720 nsresult
02721 nsTextServicesDocument::CreateContentIterator(nsIDOMRange *aRange, nsIContentIterator **aIterator)
02722 {
02723   nsresult result;
02724 
02725   if (!aRange || !aIterator)
02726     return NS_ERROR_NULL_POINTER;
02727 
02728   *aIterator = 0;
02729 
02730   // Create a nsFilteredContentIterator
02731   // This class wraps the ContentIterator in order to give itself a chance 
02732   // to filter out certain content nodes
02733   nsFilteredContentIterator* filter = new nsFilteredContentIterator(mTxtSvcFilter);
02734   *aIterator = NS_STATIC_CAST(nsIContentIterator *, filter);
02735   if (*aIterator) {
02736     NS_IF_ADDREF(*aIterator);
02737     result = filter ? NS_OK : NS_ERROR_FAILURE;
02738   } else {
02739     delete filter;
02740     result = NS_ERROR_FAILURE;
02741   }
02742   NS_ENSURE_SUCCESS(result, result);
02743 
02744   if (!*aIterator)
02745     return NS_ERROR_NULL_POINTER;
02746 
02747   result = (*aIterator)->Init(aRange);
02748 
02749   if (NS_FAILED(result))
02750   {
02751     NS_RELEASE((*aIterator));
02752     *aIterator = 0;
02753     return result;
02754   }
02755 
02756   return NS_OK;
02757 }
02758 
02759 nsresult
02760 nsTextServicesDocument::GetDocumentContentRootNode(nsIDOMNode **aNode)
02761 {
02762   nsresult result;
02763 
02764   if (!aNode)
02765     return NS_ERROR_NULL_POINTER;
02766 
02767   *aNode = 0;
02768 
02769   if (!mDOMDocument)
02770     return NS_ERROR_FAILURE;
02771 
02772   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDOMDocument);
02773 
02774   if (htmlDoc)
02775   {
02776     // For HTML documents, the content root node is the body.
02777 
02778     nsCOMPtr<nsIDOMHTMLElement> bodyElement;
02779 
02780     result = htmlDoc->GetBody(getter_AddRefs(bodyElement));
02781 
02782     if (NS_FAILED(result))
02783       return result;
02784 
02785     if (!bodyElement)
02786       return NS_ERROR_FAILURE;
02787 
02788     result = bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
02789   }
02790   else
02791   {
02792     // For non-HTML documents, the content root node will be the document element.
02793 
02794     nsCOMPtr<nsIDOMElement> docElement;
02795 
02796     result = mDOMDocument->GetDocumentElement(getter_AddRefs(docElement));
02797 
02798     if (NS_FAILED(result))
02799       return result;
02800 
02801     if (!docElement)
02802       return NS_ERROR_FAILURE;
02803 
02804     result = docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
02805   }
02806 
02807   return result;
02808 }
02809 
02810 nsresult
02811 nsTextServicesDocument::CreateDocumentContentRange(nsIDOMRange **aRange)
02812 {
02813   nsresult result;
02814 
02815   if (!aRange)
02816     return NS_ERROR_NULL_POINTER;
02817 
02818   *aRange = 0;
02819 
02820   nsCOMPtr<nsIDOMNode>node;
02821 
02822   result = GetDocumentContentRootNode(getter_AddRefs(node));
02823 
02824   if (NS_FAILED(result))
02825     return result;
02826 
02827   if (!node)
02828     return NS_ERROR_NULL_POINTER;
02829 
02830   result = CallCreateInstance("@mozilla.org/content/range;1", aRange);
02831   if (NS_FAILED(result))
02832     return result;
02833 
02834   if (!*aRange)
02835     return NS_ERROR_NULL_POINTER;
02836 
02837   result = (*aRange)->SelectNodeContents(node);
02838 
02839   if (NS_FAILED(result))
02840   {
02841     NS_RELEASE((*aRange));
02842     *aRange = 0;
02843     return result;
02844   }
02845 
02846   return NS_OK;
02847 }
02848 
02849 nsresult
02850 nsTextServicesDocument::CreateDocumentContentRootToNodeOffsetRange(nsIDOMNode *aParent, PRInt32 aOffset, PRBool aToStart, nsIDOMRange **aRange)
02851 {
02852   nsresult result;
02853 
02854   if (!aParent || !aRange)
02855     return NS_ERROR_NULL_POINTER;
02856 
02857   *aRange = 0;
02858 
02859   NS_ASSERTION(aOffset >= 0, "Invalid offset!");
02860 
02861   if (aOffset < 0)
02862     return NS_ERROR_FAILURE;
02863 
02864   nsCOMPtr<nsIDOMNode> bodyNode; 
02865 
02866   result = GetDocumentContentRootNode(getter_AddRefs(bodyNode));
02867 
02868   if (NS_FAILED(result))
02869     return result;
02870 
02871   if (!bodyNode)
02872     return NS_ERROR_NULL_POINTER;
02873 
02874   nsCOMPtr<nsIDOMNode> startNode;
02875   nsCOMPtr<nsIDOMNode> endNode;
02876   PRInt32 startOffset, endOffset;
02877 
02878   if (aToStart)
02879   {
02880     // The range should begin at the start of the document
02881     // and extend up until (aParent, aOffset).
02882 
02883     startNode   = bodyNode;
02884     startOffset = 0;
02885     endNode     = do_QueryInterface(aParent);
02886     endOffset   = aOffset;
02887   }
02888   else
02889   {
02890     // The range should begin at (aParent, aOffset) and
02891     // extend to the end of the document.
02892 
02893     nsCOMPtr<nsIDOMNodeList> nodeList;
02894     PRUint32 nodeListLength;
02895 
02896     startNode   = do_QueryInterface(aParent);
02897     startOffset = aOffset;
02898     endNode     = bodyNode;
02899     endOffset   = 0;
02900 
02901     result = bodyNode->GetChildNodes(getter_AddRefs(nodeList));
02902 
02903     if (NS_FAILED(result))
02904       return NS_ERROR_FAILURE;
02905 
02906     if (nodeList)
02907     {
02908       result = nodeList->GetLength(&nodeListLength);
02909 
02910       if (NS_FAILED(result))
02911         return NS_ERROR_FAILURE;
02912 
02913       endOffset = (PRInt32)nodeListLength;
02914     }
02915   }
02916 
02917   result = CallCreateInstance("@mozilla.org/content/range;1", aRange);
02918   if (NS_FAILED(result))
02919     return result;
02920 
02921   if (!*aRange)
02922     return NS_ERROR_NULL_POINTER;
02923 
02924   result = (*aRange)->SetStart(startNode, startOffset);
02925 
02926   if (NS_SUCCEEDED(result))
02927     result = (*aRange)->SetEnd(endNode, endOffset);
02928 
02929   if (NS_FAILED(result))
02930   {
02931     NS_RELEASE((*aRange));
02932     *aRange = 0;
02933   }
02934 
02935   return result;
02936 }
02937 
02938 nsresult
02939 nsTextServicesDocument::CreateDocumentContentIterator(nsIContentIterator **aIterator)
02940 {
02941   nsresult result;
02942 
02943   if (!aIterator)
02944     return NS_ERROR_NULL_POINTER;
02945 
02946   nsCOMPtr<nsIDOMRange> range;
02947 
02948   result = CreateDocumentContentRange(getter_AddRefs(range));
02949 
02950   if (NS_FAILED(result))
02951     return result;
02952 
02953   result = CreateContentIterator(range, aIterator);
02954 
02955   return result;
02956 }
02957 
02958 nsresult
02959 nsTextServicesDocument::AdjustContentIterator()
02960 {
02961   nsresult result = NS_OK;
02962 
02963   if (!mIterator)
02964     return NS_ERROR_FAILURE;
02965 
02966   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mIterator->GetCurrentNode()));
02967 
02968   if (!node)
02969     return NS_ERROR_FAILURE;
02970 
02971   nsIDOMNode *nodePtr = node.get();
02972   PRInt32 tcount      = mOffsetTable.Count();
02973 
02974   nsIDOMNode *prevValidNode = 0;
02975   nsIDOMNode *nextValidNode = 0;
02976   PRBool foundEntry = PR_FALSE;
02977   OffsetEntry *entry;
02978 
02979   for (PRInt32 i = 0; i < tcount && !nextValidNode; i++)
02980   {
02981     entry = (OffsetEntry *)mOffsetTable[i];
02982 
02983     if (!entry)
02984       return NS_ERROR_FAILURE;
02985 
02986     if (entry->mNode == nodePtr)
02987     {
02988       if (entry->mIsValid)
02989       {
02990         // The iterator is still pointing to something valid!
02991         // Do nothing!
02992 
02993         return NS_OK;
02994       }
02995       else
02996       {
02997         // We found an invalid entry that points to
02998         // the current iterator node. Stop looking for
02999         // a previous valid node!
03000 
03001         foundEntry = PR_TRUE;
03002       }
03003     }
03004 
03005     if (entry->mIsValid)
03006     {
03007       if (!foundEntry)
03008         prevValidNode = entry->mNode;
03009       else
03010         nextValidNode = entry->mNode;
03011     }
03012   }
03013 
03014   nsCOMPtr<nsIContent> content;
03015 
03016   if (prevValidNode)
03017     content = do_QueryInterface(prevValidNode);
03018   else if (nextValidNode)
03019     content = do_QueryInterface(nextValidNode);
03020 
03021   if (content)
03022   {
03023     result = mIterator->PositionAt(content);
03024 
03025     if (NS_FAILED(result))
03026       mIteratorStatus = eIsDone;
03027     else
03028       mIteratorStatus = eValid;
03029 
03030     return result;
03031   }
03032 
03033   // If we get here, there aren't any valid entries
03034   // in the offset table! Try to position the iterator
03035   // on the next text block first, then previous if
03036   // one doesn't exist!
03037 
03038   if (mNextTextBlock)
03039   {
03040     result = mIterator->PositionAt(mNextTextBlock);
03041 
03042     if (NS_FAILED(result))
03043     {
03044       mIteratorStatus = eIsDone;
03045       return result;
03046     }
03047 
03048     mIteratorStatus = eNext;
03049   }
03050   else if (mPrevTextBlock)
03051   {
03052     result = mIterator->PositionAt(mPrevTextBlock);
03053 
03054     if (NS_FAILED(result))
03055     {
03056       mIteratorStatus = eIsDone;
03057       return result;
03058     }
03059 
03060     mIteratorStatus = ePrev;
03061   }
03062   else
03063     mIteratorStatus = eIsDone;
03064 
03065   return NS_OK;
03066 }
03067 
03068 PRBool
03069 nsTextServicesDocument::DidSkip(nsIContentIterator* aFilteredIter)
03070 {
03071   // We can assume here that the Iterator is a nsFilteredContentIterator because
03072   // all the iterator are created in CreateContentIterator which create a 
03073   // nsFilteredContentIterator
03074   // So if the iterator bailed on one of the "filtered" content nodes then we 
03075   // consider that to be a block and bail with PR_TRUE
03076   if (aFilteredIter) {
03077     nsFilteredContentIterator* filter = NS_STATIC_CAST(nsFilteredContentIterator *, aFilteredIter);
03078     if (filter && filter->DidSkip()) {
03079       return PR_TRUE;
03080     }
03081   }
03082   return PR_FALSE;
03083 }
03084 
03085 void
03086 nsTextServicesDocument::ClearDidSkip(nsIContentIterator* aFilteredIter)
03087 {
03088   // Clear filter's skip flag
03089   if (aFilteredIter) {
03090     nsFilteredContentIterator* filter = NS_STATIC_CAST(nsFilteredContentIterator *, aFilteredIter);
03091     filter->ClearDidSkip();
03092   }
03093 }
03094 
03095 PRBool
03096 nsTextServicesDocument::IsBlockNode(nsIContent *aContent)
03097 {
03098   nsIAtom *atom = aContent->Tag();
03099 
03100   return (sAAtom       != atom &&
03101           sAddressAtom != atom &&
03102           sBigAtom     != atom &&
03103           sBlinkAtom   != atom &&
03104           sBAtom       != atom &&
03105           sCiteAtom    != atom &&
03106           sCodeAtom    != atom &&
03107           sDfnAtom     != atom &&
03108           sEmAtom      != atom &&
03109           sFontAtom    != atom &&
03110           sIAtom       != atom &&
03111           sKbdAtom     != atom &&
03112           sKeygenAtom  != atom &&
03113           sNobrAtom    != atom &&
03114           sSAtom       != atom &&
03115           sSampAtom    != atom &&
03116           sSmallAtom   != atom &&
03117           sSpacerAtom  != atom &&
03118           sSpanAtom    != atom &&
03119           sStrikeAtom  != atom &&
03120           sStrongAtom  != atom &&
03121           sSubAtom     != atom &&
03122           sSupAtom     != atom &&
03123           sTtAtom      != atom &&
03124           sUAtom       != atom &&
03125           sVarAtom     != atom &&
03126           sWbrAtom     != atom);
03127 }
03128 
03129 PRBool
03130 nsTextServicesDocument::HasSameBlockNodeParent(nsIContent *aContent1, nsIContent *aContent2)
03131 {
03132   nsIContent* p1 = aContent1->GetParent();
03133   nsIContent* p2 = aContent2->GetParent();
03134 
03135   // Quick test:
03136 
03137   if (p1 == p2)
03138     return PR_TRUE;
03139 
03140   // Walk up the parent hierarchy looking for closest block boundary node:
03141 
03142   while (p1 && !IsBlockNode(p1))
03143   {
03144     p1 = p1->GetParent();
03145   }
03146 
03147   while (p2 && !IsBlockNode(p2))
03148   {
03149     p2 = p2->GetParent();
03150   }
03151 
03152   return p1 == p2;
03153 }
03154 
03155 PRBool
03156 nsTextServicesDocument::IsTextNode(nsIContent *aContent)
03157 {
03158   if (!aContent)
03159     return PR_FALSE;
03160 
03161   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aContent);
03162 
03163   return IsTextNode(node);
03164 }
03165 
03166 PRBool
03167 nsTextServicesDocument::IsTextNode(nsIDOMNode *aNode)
03168 {
03169   if (!aNode)
03170     return PR_FALSE;
03171 
03172   PRUint16 type;
03173 
03174   nsresult result = aNode->GetNodeType(&type);
03175 
03176   if (NS_FAILED(result))
03177     return PR_FALSE;
03178 
03179   return nsIDOMNode::TEXT_NODE == type;
03180 }
03181 
03182 nsresult
03183 nsTextServicesDocument::SetSelectionInternal(PRInt32 aOffset, PRInt32 aLength, PRBool aDoUpdate)
03184 {
03185   nsresult result = NS_OK;
03186 
03187   if (!mSelCon || aOffset < 0 || aLength < 0)
03188     return NS_ERROR_FAILURE;
03189 
03190   nsIDOMNode *sNode = 0, *eNode = 0;
03191   PRInt32 i, sOffset = 0, eOffset = 0;
03192   OffsetEntry *entry;
03193 
03194   // Find start of selection in node offset terms:
03195 
03196   for (i = 0; !sNode && i < mOffsetTable.Count(); i++)
03197   {
03198     entry = (OffsetEntry *)mOffsetTable[i];
03199     if (entry->mIsValid)
03200     {
03201       if (entry->mIsInsertedText)
03202       {
03203         // Caret can only be placed at the end of an
03204         // inserted text offset entry, if the offsets
03205         // match exactly!
03206 
03207         if (entry->mStrOffset == aOffset)
03208         {
03209           sNode   = entry->mNode;
03210           sOffset = entry->mNodeOffset + entry->mLength;
03211         }
03212       }
03213       else if (aOffset >= entry->mStrOffset)
03214       {
03215         PRBool foundEntry = PR_FALSE;
03216         PRInt32 strEndOffset = entry->mStrOffset + entry->mLength;
03217 
03218         if (aOffset < strEndOffset)
03219           foundEntry = PR_TRUE;
03220         else if (aOffset == strEndOffset)
03221         {
03222           // Peek after this entry to see if we have any
03223           // inserted text entries belonging to the same
03224           // entry->mNode. If so, we have to place the selection
03225           // after it!
03226 
03227           if ((i+1) < mOffsetTable.Count())
03228           {
03229             OffsetEntry *nextEntry = (OffsetEntry *)mOffsetTable[i+1];
03230 
03231             if (!nextEntry->mIsValid || nextEntry->mStrOffset != aOffset)
03232             {
03233               // Next offset entry isn't an exact match, so we'll
03234               // just use the current entry.
03235               foundEntry = PR_TRUE;
03236             }
03237           }
03238         }
03239 
03240         if (foundEntry)
03241         {
03242           sNode   = entry->mNode;
03243           sOffset = entry->mNodeOffset + aOffset - entry->mStrOffset;
03244         }
03245       }
03246 
03247       if (sNode)
03248       {
03249         mSelStartIndex  = i;
03250         mSelStartOffset = aOffset;
03251       }
03252     }
03253   }
03254 
03255   if (!sNode)
03256     return NS_ERROR_FAILURE;
03257 
03258   // XXX: If we ever get a SetSelection() method in nsIEditor, we should
03259   //      use it.
03260 
03261   nsCOMPtr<nsISelection> selection;
03262 
03263   if (aDoUpdate)
03264   {
03265     result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
03266 
03267     if (NS_FAILED(result))
03268       return result;
03269 
03270     result = selection->Collapse(sNode, sOffset);
03271 
03272     if (NS_FAILED(result))
03273       return result;
03274    }
03275 
03276   if (aLength <= 0)
03277   {
03278     // We have a collapsed selection. (Caret)
03279 
03280     mSelEndIndex  = mSelStartIndex;
03281     mSelEndOffset = mSelStartOffset;
03282 
03283    //**** KDEBUG ****
03284    // printf("\n* Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
03285    //**** KDEBUG ****
03286 
03287     return NS_OK;
03288   }
03289 
03290   // Find the end of the selection in node offset terms:
03291 
03292   PRInt32 endOffset = aOffset + aLength;
03293 
03294   for (i = mOffsetTable.Count() - 1; !eNode && i >= 0; i--)
03295   {
03296     entry = (OffsetEntry *)mOffsetTable[i];
03297     
03298     if (entry->mIsValid)
03299     {
03300       if (entry->mIsInsertedText)
03301       {
03302         if (entry->mStrOffset == eOffset)
03303         {
03304           // If the selection ends on an inserted text offset entry,
03305           // the selection includes the entire entry!
03306 
03307           eNode   = entry->mNode;
03308           eOffset = entry->mNodeOffset + entry->mLength;
03309         }
03310       }
03311       else if (endOffset >= entry->mStrOffset && endOffset <= entry->mStrOffset + entry->mLength)
03312       {
03313         eNode   = entry->mNode;
03314         eOffset = entry->mNodeOffset + endOffset - entry->mStrOffset;
03315       }
03316 
03317       if (eNode)
03318       {
03319         mSelEndIndex  = i;
03320         mSelEndOffset = endOffset;
03321       }
03322     }
03323   }
03324 
03325   if (aDoUpdate && eNode)
03326   {
03327     result = selection->Extend(eNode, eOffset);
03328 
03329     if (NS_FAILED(result))
03330       return result;
03331   }
03332 
03333   //**** KDEBUG ****
03334   // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
03335   //**** KDEBUG ****
03336 
03337   return result;
03338 }
03339 
03340 nsresult
03341 nsTextServicesDocument::GetSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, PRInt32 *aSelOffset, PRInt32 *aSelLength)
03342 {
03343   nsresult result;
03344 
03345   if (!aSelStatus || !aSelOffset || !aSelLength)
03346     return NS_ERROR_NULL_POINTER;
03347 
03348   *aSelStatus = nsITextServicesDocument::eBlockNotFound;
03349   *aSelOffset = -1;
03350   *aSelLength = -1;
03351 
03352   if (!mDOMDocument || !mSelCon)
03353     return NS_ERROR_FAILURE;
03354 
03355   if (mIteratorStatus == nsTextServicesDocument::eIsDone)
03356     return NS_OK;
03357 
03358   nsCOMPtr<nsISelection> selection;
03359   PRBool isCollapsed;
03360 
03361   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
03362 
03363   if (NS_FAILED(result))
03364     return result;
03365 
03366   if (!selection)
03367     return NS_ERROR_FAILURE;
03368 
03369   result = selection->GetIsCollapsed(&isCollapsed);
03370 
03371   if (NS_FAILED(result))
03372     return result;
03373 
03374   // XXX: If we expose this method publicly, we need to
03375   //      add LOCK_DOC/UNLOCK_DOC calls!
03376 
03377   // LOCK_DOC(this);
03378 
03379   if (isCollapsed)
03380     result = GetCollapsedSelection(aSelStatus, aSelOffset, aSelLength);
03381   else
03382     result = GetUncollapsedSelection(aSelStatus, aSelOffset, aSelLength);
03383 
03384   // UNLOCK_DOC(this);
03385 
03386   return result;
03387 }
03388 
03389 nsresult
03390 nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, PRInt32 *aSelOffset, PRInt32 *aSelLength)
03391 {
03392   nsresult result;
03393   nsCOMPtr<nsISelection> selection;
03394 
03395   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
03396 
03397   if (NS_FAILED(result))
03398     return result;
03399 
03400   if (!selection)
03401     return NS_ERROR_FAILURE;
03402 
03403   // The calling function should have done the GetIsCollapsed()
03404   // check already. Just assume it's collapsed!
03405 
03406   nsCOMPtr<nsIDOMRange> range;
03407   OffsetEntry *entry;
03408   nsCOMPtr<nsIDOMNode> parent;
03409   PRInt32 offset, tableCount, i;
03410   PRInt32 e1s1, e2s1;
03411 
03412   OffsetEntry *eStart, *eEnd;
03413   PRInt32 eStartOffset, eEndOffset;
03414 
03415 
03416   *aSelStatus = nsITextServicesDocument::eBlockOutside;
03417   *aSelOffset = *aSelLength = -1;
03418 
03419   tableCount = mOffsetTable.Count();
03420 
03421   if (tableCount == 0)
03422     return NS_OK;
03423 
03424   // Get pointers to the first and last offset entries
03425   // in the table.
03426 
03427   eStart = (OffsetEntry *)mOffsetTable[0];
03428 
03429   if (tableCount > 1)
03430     eEnd = (OffsetEntry *)mOffsetTable[tableCount - 1];
03431   else
03432     eEnd = eStart;
03433 
03434   eStartOffset = eStart->mNodeOffset;
03435   eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
03436 
03437   result = selection->GetRangeAt(0, getter_AddRefs(range));
03438 
03439   if (NS_FAILED(result))
03440     return result;
03441 
03442   result = range->GetStartContainer(getter_AddRefs(parent));
03443 
03444   if (NS_FAILED(result))
03445     return result;
03446 
03447   result = range->GetStartOffset(&offset);
03448 
03449   if (NS_FAILED(result))
03450     return result;
03451 
03452   result = ComparePoints(eStart->mNode, eStartOffset, parent, offset, &e1s1);
03453 
03454   if (NS_FAILED(result))
03455     return result;
03456 
03457   result = ComparePoints(eEnd->mNode, eEndOffset, parent, offset, &e2s1);
03458 
03459   if (NS_FAILED(result))
03460     return result;
03461 
03462   if (e1s1 > 0 || e2s1 < 0)
03463   {
03464     // We're done if the caret is outside the
03465     // current text block.
03466 
03467     return NS_OK;
03468   }
03469 
03470   if (IsTextNode(parent))
03471   {
03472     // Good news, the caret is in a text node. Look
03473     // through the offset table for the entry that
03474     // matches it's parent and offset.
03475 
03476     for (i = 0; i < tableCount; i++)
03477     {
03478       entry = (OffsetEntry *)mOffsetTable[i];
03479 
03480       if (!entry)
03481         return NS_ERROR_FAILURE;
03482 
03483       if (entry->mNode == parent.get() &&
03484           entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
03485       {
03486         *aSelStatus = nsITextServicesDocument::eBlockContains;
03487         *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
03488         *aSelLength = 0;
03489 
03490         return NS_OK;
03491       }
03492     }
03493 
03494     // If we get here, we didn't find a text node entry
03495     // in our offset table that matched.
03496 
03497     return NS_ERROR_FAILURE;
03498   }
03499 
03500   // The caret is in our text block, but it's positioned in some
03501   // non-text node (ex. <b>). Create a range based on the start
03502   // and end of the text block, then create an iterator based on
03503   // this range, with it's initial position set to the closest
03504   // child of this non-text node. Then look for the closest text
03505   // node.
03506 
03507   nsCOMPtr<nsIDOMNode> node, saveNode;
03508   nsCOMPtr<nsIDOMNodeList> children;
03509   nsCOMPtr<nsIContentIterator> iter;
03510   PRBool hasChildren;
03511 
03512   result = CreateRange(eStart->mNode, eStartOffset, eEnd->mNode, eEndOffset, getter_AddRefs(range));
03513 
03514   if (NS_FAILED(result))
03515     return result;
03516 
03517   result = CreateContentIterator(range, getter_AddRefs(iter));
03518 
03519   if (NS_FAILED(result))
03520     return result;
03521 
03522   result = parent->HasChildNodes(&hasChildren);
03523 
03524   if (NS_FAILED(result))
03525     return result;
03526 
03527   if (hasChildren)
03528   {
03529     // XXX: We need to make sure that all of parent's
03530     //      children are in the text block.
03531 
03532     // If the parent has children, position the iterator
03533     // on the child that is to the left of the offset.
03534 
03535     PRUint32 childIndex = (PRUint32)offset;
03536 
03537     result = parent->GetChildNodes(getter_AddRefs(children));
03538 
03539     if (NS_FAILED(result))
03540       return result;
03541 
03542     if (!children)
03543       return NS_ERROR_FAILURE;
03544 
03545     if (childIndex > 0)
03546     {
03547       PRUint32 numChildren;
03548 
03549       result = children->GetLength(&numChildren);
03550 
03551       if (NS_FAILED(result))
03552         return result;
03553 
03554       NS_ASSERTION(childIndex <= numChildren, "Invalid selection offset!");
03555 
03556       if (childIndex > numChildren)
03557         childIndex = numChildren;
03558 
03559       childIndex -= 1;
03560     }
03561 
03562     result = children->Item(childIndex, getter_AddRefs(saveNode));
03563 
03564     if (NS_FAILED(result))
03565       return result;
03566 
03567     nsCOMPtr<nsIContent> content(do_QueryInterface(saveNode));
03568 
03569     if (!content)
03570       return NS_ERROR_FAILURE;
03571 
03572     result = iter->PositionAt(content);
03573 
03574     if (NS_FAILED(result))
03575       return result;
03576   }
03577   else
03578   {
03579     // The parent has no children, so position the iterator
03580     // on the parent.
03581 
03582     nsCOMPtr<nsIContent> content(do_QueryInterface(parent));
03583 
03584     if (!content)
03585       return NS_ERROR_FAILURE;
03586 
03587     result = iter->PositionAt(content);
03588 
03589     if (NS_FAILED(result))
03590       return result;
03591 
03592     saveNode = parent;
03593   }
03594 
03595   // Now iterate to the left, towards the beginning of
03596   // the text block, to find the first text node you
03597   // come across.
03598 
03599   while (!iter->IsDone())
03600   {
03601     nsIContent *content = iter->GetCurrentNode();
03602 
03603     if (IsTextNode(content))
03604     {
03605       node = do_QueryInterface(content);
03606 
03607       if (!node)
03608         return NS_ERROR_FAILURE;
03609 
03610       break;
03611     }
03612 
03613     node = nsnull;
03614 
03615     iter->Prev();
03616   }
03617 
03618   if (node)
03619   {
03620     // We found a node, now set the offset to the end
03621     // of the text node.
03622 
03623     nsAutoString str;
03624     result = node->GetNodeValue(str);
03625 
03626     if (NS_FAILED(result))
03627       return result;
03628 
03629     offset = str.Length();
03630   }
03631   else
03632   {
03633     // We should never really get here, but I'm paranoid.
03634 
03635     // We didn't find a text node above, so iterate to
03636     // the right, towards the end of the text block, looking
03637     // for a text node.
03638 
03639     {
03640       nsCOMPtr<nsIContent> content(do_QueryInterface(saveNode));
03641 
03642       result = iter->PositionAt(content);
03643 
03644       if (NS_FAILED(result))
03645         return result;
03646     }
03647 
03648     while (!iter->IsDone())
03649     {
03650       nsIContent *content = iter->GetCurrentNode();
03651 
03652       if (IsTextNode(content))
03653       {
03654         node = do_QueryInterface(content);
03655 
03656         if (!node)
03657           return NS_ERROR_FAILURE;
03658 
03659         break;
03660       }
03661 
03662       node = nsnull;
03663 
03664       iter->Next();
03665     }
03666 
03667     if (!node)
03668       return NS_ERROR_FAILURE;
03669 
03670     // We found a text node, so set the offset to
03671     // the begining of the node.
03672 
03673     offset = 0;
03674   }
03675 
03676   for (i = 0; i < tableCount; i++)
03677   {
03678     entry = (OffsetEntry *)mOffsetTable[i];
03679 
03680     if (!entry)
03681       return NS_ERROR_FAILURE;
03682 
03683     if (entry->mNode == node.get() &&
03684         entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
03685     {
03686       *aSelStatus = nsITextServicesDocument::eBlockContains;
03687       *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
03688       *aSelLength = 0;
03689 
03690       // Now move the caret so that it is actually in the text node.
03691       // We do this to keep things in sync.
03692       //
03693       // In most cases, the user shouldn't see any movement in the caret
03694       // on screen.
03695 
03696       result = SetSelectionInternal(*aSelOffset, *aSelLength, PR_TRUE);
03697 
03698       return result;
03699     }
03700   }
03701 
03702   return NS_ERROR_FAILURE;
03703 }
03704 
03705 nsresult
03706 nsTextServicesDocument::GetUncollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, PRInt32 *aSelOffset, PRInt32 *aSelLength)
03707 {
03708   nsresult result;
03709 
03710   nsCOMPtr<nsISelection> selection;
03711   nsCOMPtr<nsIDOMRange> range;
03712   OffsetEntry *entry;
03713 
03714   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
03715 
03716   if (NS_FAILED(result))
03717     return result;
03718 
03719   if (!selection)
03720     return NS_ERROR_FAILURE;
03721 
03722   // It is assumed that the calling function has made sure that the
03723   // selection is not collapsed, and that the input params to this
03724   // method are initialized to some defaults.
03725 
03726   nsCOMPtr<nsIDOMNode> startParent, endParent;
03727   PRInt32 startOffset, endOffset;
03728   PRInt32 rangeCount, tableCount, i;
03729   PRInt32 e1s1, e1s2, e2s1, e2s2;
03730 
03731   OffsetEntry *eStart, *eEnd;
03732   PRInt32 eStartOffset, eEndOffset;
03733 
03734   tableCount = mOffsetTable.Count();
03735 
03736   // Get pointers to the first and last offset entries
03737   // in the table.
03738 
03739   eStart = (OffsetEntry *)mOffsetTable[0];
03740 
03741   if (tableCount > 1)
03742     eEnd = (OffsetEntry *)mOffsetTable[tableCount - 1];
03743   else
03744     eEnd = eStart;
03745 
03746   eStartOffset = eStart->mNodeOffset;
03747   eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
03748 
03749   result = selection->GetRangeCount(&rangeCount);
03750 
03751   if (NS_FAILED(result))
03752     return result;
03753 
03754   // Find the first range in the selection that intersects
03755   // the current text block.
03756 
03757   for (i = 0; i < rangeCount; i++)
03758   {
03759     result = selection->GetRangeAt(i, getter_AddRefs(range));
03760 
03761     if (NS_FAILED(result))
03762       return result;
03763 
03764     result = GetRangeEndPoints(range,
03765                                getter_AddRefs(startParent), &startOffset,
03766                                getter_AddRefs(endParent), &endOffset);
03767 
03768     if (NS_FAILED(result))
03769       return result;
03770 
03771     result = ComparePoints(eStart->mNode, eStartOffset, endParent, endOffset, &e1s2);
03772 
03773     if (NS_FAILED(result))
03774       return result;
03775 
03776     result = ComparePoints(eEnd->mNode, eEndOffset, startParent, startOffset, &e2s1);
03777 
03778     if (NS_FAILED(result))
03779       return result;
03780 
03781     // Break out of the loop if the text block intersects the current range.
03782 
03783     if (e1s2 <= 0 && e2s1 >= 0)
03784       break;
03785   }
03786 
03787   // We're done if we didn't find an intersecting range.
03788 
03789   if (rangeCount < 1 || e1s2 > 0 || e2s1 < 0)
03790   {
03791     *aSelStatus = nsITextServicesDocument::eBlockOutside;
03792     *aSelOffset = *aSelLength = -1;
03793     return NS_OK;
03794   }
03795 
03796   // Now that we have an intersecting range, find out more info:
03797 
03798   result = ComparePoints(eStart->mNode, eStartOffset, startParent, startOffset, &e1s1);
03799 
03800   if (NS_FAILED(result))
03801     return result;
03802 
03803   result = ComparePoints(eEnd->mNode, eEndOffset, endParent, endOffset, &e2s2);
03804 
03805   if (NS_FAILED(result))
03806     return result;
03807 
03808   if (rangeCount > 1)
03809   {
03810     // There are multiple selection ranges, we only deal
03811     // with the first one that intersects the current,
03812     // text block, so mark this a as a partial.
03813 
03814     *aSelStatus = nsITextServicesDocument::eBlockPartial;
03815   }
03816   else if (e1s1 > 0 && e2s2 < 0)
03817   {
03818     // The range extends beyond the start and
03819     // end of the current text block.
03820 
03821     *aSelStatus = nsITextServicesDocument::eBlockInside;
03822   }
03823   else if (e1s1 <= 0 && e2s2 >= 0)
03824   {
03825     // The current text block contains the entire
03826     // range.
03827 
03828     *aSelStatus = nsITextServicesDocument::eBlockContains;
03829   }
03830   else
03831   {
03832     // The range partially intersects the block.
03833 
03834     *aSelStatus = nsITextServicesDocument::eBlockPartial;
03835   }
03836 
03837   // Now create a range based on the intersection of the
03838   // text block and range:
03839 
03840   nsCOMPtr<nsIDOMNode> p1, p2;
03841   PRInt32     o1,  o2;
03842 
03843   // The start of the range will be the rightmost
03844   // start node.
03845 
03846   if (e1s1 >= 0)
03847   {
03848     p1 = do_QueryInterface(eStart->mNode);
03849     o1 = eStartOffset;
03850   }
03851   else
03852   {
03853     p1 = startParent;
03854     o1 = startOffset;
03855   }
03856 
03857   // The end of the range will be the leftmost
03858   // end node.
03859 
03860   if (e2s2 <= 0)
03861   {
03862     p2 = do_QueryInterface(eEnd->mNode);
03863     o2 = eEndOffset;
03864   }
03865   else
03866   {
03867     p2 = endParent;
03868     o2 = endOffset;
03869   }
03870 
03871   result = CreateRange(p1, o1, p2, o2, getter_AddRefs(range));
03872 
03873   if (NS_FAILED(result))
03874     return result;
03875 
03876   // Now iterate over this range to figure out the selection's
03877   // block offset and length.
03878 
03879   nsCOMPtr<nsIContentIterator> iter;
03880 
03881   result = CreateContentIterator(range, getter_AddRefs(iter));
03882 
03883   if (NS_FAILED(result))
03884     return result;
03885 
03886   // Find the first text node in the range.
03887   
03888   PRBool found;
03889   nsIContent *content;
03890 
03891   iter->First();
03892 
03893   if (!IsTextNode(p1))
03894   {
03895     found = PR_FALSE;
03896 
03897     while (!iter->IsDone())
03898     {
03899       content = iter->GetCurrentNode();
03900 
03901       if (IsTextNode(content))
03902       {
03903         p1 = do_QueryInterface(content);
03904 
03905         if (!p1)
03906           return NS_ERROR_FAILURE;
03907 
03908         o1 = 0;
03909         found = PR_TRUE;
03910 
03911         break;
03912       }
03913 
03914       iter->Next();
03915     }
03916 
03917     if (!found)
03918       return NS_ERROR_FAILURE;
03919   }
03920 
03921   // Find the last text node in the range.
03922 
03923   iter->Last();
03924 
03925   if (! IsTextNode(p2))
03926   {
03927     found = PR_FALSE;
03928 
03929     while (!iter->IsDone())
03930     {
03931       content = iter->GetCurrentNode();
03932 
03933       if (IsTextNode(content))
03934       {
03935         p2 = do_QueryInterface(content);
03936 
03937         if (!p2)
03938           return NS_ERROR_FAILURE;
03939 
03940         nsString str;
03941 
03942         result = p2->GetNodeValue(str);
03943 
03944         if (NS_FAILED(result))
03945           return result;
03946 
03947         o2 = str.Length();
03948         found = PR_TRUE;
03949 
03950         break;
03951       }
03952 
03953       iter->Prev();
03954     }
03955 
03956     if (!found)
03957       return NS_ERROR_FAILURE;
03958   }
03959 
03960   found    = PR_FALSE;
03961   *aSelLength = 0;
03962 
03963   for (i = 0; i < tableCount; i++)
03964   {
03965     entry = (OffsetEntry *)mOffsetTable[i];
03966 
03967     if (!entry)
03968       return NS_ERROR_FAILURE;
03969 
03970     if (!found)
03971     {
03972       if (entry->mNode == p1.get() &&
03973           entry->mNodeOffset <= o1 && o1 <= (entry->mNodeOffset + entry->mLength))
03974       {
03975         *aSelOffset = entry->mStrOffset + (o1 - entry->mNodeOffset);
03976 
03977         if (p1 == p2 &&
03978             entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
03979         {
03980           // The start and end of the range are in the same offset
03981           // entry. Calculate the length of the range then we're done.
03982 
03983           *aSelLength = o2 - o1;
03984           break;
03985         }
03986         else
03987         {
03988           // Add the length of the sub string in this offset entry
03989           // that follows the start of the range.
03990 
03991           *aSelLength = entry->mLength - (o1 - entry->mNodeOffset);
03992         }
03993 
03994         found = PR_TRUE;
03995       }
03996     }
03997     else // found
03998     {
03999       if (entry->mNode == p2.get() &&
04000           entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
04001       {
04002         // We found the end of the range. Calculate the length of the
04003         // sub string that is before the end of the range, then we're done.
04004 
04005         *aSelLength += o2 - entry->mNodeOffset;
04006         break;
04007       }
04008       else
04009       {
04010         // The entire entry must be in the range.
04011 
04012         *aSelLength += entry->mLength;
04013       }
04014     }
04015   }
04016 
04017   return result;
04018 }
04019 
04020 PRBool
04021 nsTextServicesDocument::SelectionIsCollapsed()
04022 {
04023   return(mSelStartIndex == mSelEndIndex && mSelStartOffset == mSelEndOffset);
04024 }
04025 
04026 PRBool
04027 nsTextServicesDocument::SelectionIsValid()
04028 {
04029   return(mSelStartIndex >= 0);
04030 }
04031 
04032 nsresult
04033 nsTextServicesDocument::ComparePoints(nsIDOMNode* aParent1, PRInt32 aOffset1,
04034                                       nsIDOMNode* aParent2, PRInt32 aOffset2,
04035                                       PRInt32 *aResult)
04036 {
04037   nsresult result;
04038   
04039   if (!sRangeHelper) {
04040     result = CallGetService("@mozilla.org/content/range-utils;1",
04041                             &sRangeHelper);
04042     if (!sRangeHelper)
04043       return result;
04044   }
04045 
04046   *aResult = sRangeHelper->ComparePoints(aParent1, aOffset1,
04047                                          aParent2, aOffset2);
04048   return NS_OK;
04049 }
04050 
04051 nsresult
04052 nsTextServicesDocument::GetRangeEndPoints(nsIDOMRange *aRange,
04053                                           nsIDOMNode **aStartParent, PRInt32 *aStartOffset,
04054                                           nsIDOMNode **aEndParent, PRInt32 *aEndOffset)
04055 {
04056   nsresult result;
04057 
04058   if (!aRange || !aStartParent || !aStartOffset || !aEndParent || !aEndOffset)
04059     return NS_ERROR_NULL_POINTER;
04060 
04061   result = aRange->GetStartContainer(aStartParent);
04062 
04063   if (NS_FAILED(result))
04064     return result;
04065 
04066   if (!aStartParent)
04067     return NS_ERROR_FAILURE;
04068 
04069   result = aRange->GetStartOffset(aStartOffset);
04070 
04071   if (NS_FAILED(result))
04072     return result;
04073 
04074   result = aRange->GetEndContainer(aEndParent);
04075 
04076   if (NS_FAILED(result))
04077     return result;
04078 
04079   if (!aEndParent)
04080     return NS_ERROR_FAILURE;
04081 
04082   result = aRange->GetEndOffset(aEndOffset);
04083 
04084   return result;
04085 }
04086 
04087 
04088 nsresult
04089 nsTextServicesDocument::CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset,
04090                                     nsIDOMNode *aEndParent, PRInt32 aEndOffset,
04091                                     nsIDOMRange **aRange)
04092 {
04093   nsresult result;
04094 
04095   result = CallCreateInstance("@mozilla.org/content/range;1", aRange);
04096   if (NS_FAILED(result))
04097     return result;
04098 
04099   if (!*aRange)
04100     return NS_ERROR_NULL_POINTER;
04101 
04102   result = (*aRange)->SetStart(aStartParent, aStartOffset);
04103 
04104   if (NS_SUCCEEDED(result))
04105     result = (*aRange)->SetEnd(aEndParent, aEndOffset);
04106 
04107   if (NS_FAILED(result))
04108   {
04109     NS_RELEASE((*aRange));
04110     *aRange = 0;
04111   }
04112 
04113   return result;
04114 }
04115 
04116 nsresult
04117 nsTextServicesDocument::FirstTextNode(nsIContentIterator *aIterator,
04118                                       TSDIteratorStatus *aIteratorStatus)
04119 {
04120   if (aIteratorStatus)
04121     *aIteratorStatus = nsTextServicesDocument::eIsDone;
04122 
04123   aIterator->First();
04124 
04125   while (!aIterator->IsDone())
04126   {
04127     nsIContent *content = aIterator->GetCurrentNode();
04128 
04129     if (IsTextNode(content))
04130     {
04131       if (aIteratorStatus)
04132         *aIteratorStatus = nsTextServicesDocument::eValid;
04133       break;
04134     }
04135 
04136     aIterator->Next();
04137   }
04138 
04139   return NS_OK;
04140 }
04141 
04142 nsresult
04143 nsTextServicesDocument::LastTextNode(nsIContentIterator *aIterator,
04144                                      TSDIteratorStatus *aIteratorStatus)
04145 {
04146   if (aIteratorStatus)
04147     *aIteratorStatus = nsTextServicesDocument::eIsDone;
04148 
04149   aIterator->Last();
04150 
04151   while (!aIterator->IsDone())
04152   {
04153     nsIContent *content = aIterator->GetCurrentNode();
04154 
04155     if (IsTextNode(content))
04156     {
04157       if (aIteratorStatus)
04158         *aIteratorStatus = nsTextServicesDocument::eValid;
04159       break;
04160     }
04161 
04162     aIterator->Prev();
04163   }
04164 
04165   return NS_OK;
04166 }
04167 
04168 nsresult
04169 nsTextServicesDocument::FirstTextNodeInCurrentBlock(nsIContentIterator *iter)
04170 {
04171   if (!iter)
04172     return NS_ERROR_NULL_POINTER;
04173 
04174   ClearDidSkip(iter);
04175 
04176   nsCOMPtr<nsIContent> last;
04177 
04178   // Walk backwards over adjacent text nodes until
04179   // we hit a block boundary:
04180 
04181   while (!iter->IsDone())
04182   {
04183     nsIContent *content = iter->GetCurrentNode();
04184 
04185     if (IsTextNode(content))
04186     {
04187       if (!last || HasSameBlockNodeParent(content, last))
04188         last = content;
04189       else
04190       {
04191         // We're done, the current text node is in a
04192         // different block.
04193         break;
04194       }
04195     }
04196     else if (last && IsBlockNode(content))
04197       break;
04198 
04199     iter->Prev();
04200 
04201     if (DidSkip(iter))
04202       break;
04203   }
04204   
04205   if (last)
04206     iter->PositionAt(last);
04207 
04208   // XXX: What should we return if last is null?
04209 
04210   return NS_OK;
04211 }
04212 
04213 nsresult
04214 nsTextServicesDocument::FirstTextNodeInPrevBlock(nsIContentIterator *aIterator)
04215 {
04216   nsCOMPtr<nsIContent> content;
04217   nsresult result;
04218 
04219   if (!aIterator)
04220     return NS_ERROR_NULL_POINTER;
04221 
04222   // XXX: What if mIterator is not currently on a text node?
04223 
04224   // Make sure mIterator is pointing to the first text node in the
04225   // current block:
04226 
04227   result = FirstTextNodeInCurrentBlock(aIterator);
04228 
04229   if (NS_FAILED(result))
04230     return NS_ERROR_FAILURE;
04231 
04232   // Point mIterator to the first node before the first text node:
04233 
04234   aIterator->Prev();
04235 
04236   if (aIterator->IsDone())
04237     return NS_ERROR_FAILURE;
04238 
04239   // Now find the first text node of the next block:
04240 
04241   return FirstTextNodeInCurrentBlock(aIterator);
04242 }
04243 
04244 nsresult
04245 nsTextServicesDocument::FirstTextNodeInNextBlock(nsIContentIterator *aIterator)
04246 {
04247   nsCOMPtr<nsIContent> prev;
04248   PRBool crossedBlockBoundary = PR_FALSE;
04249 
04250   if (!aIterator)
04251     return NS_ERROR_NULL_POINTER;
04252 
04253   ClearDidSkip(aIterator);
04254 
04255   while (!aIterator->IsDone())
04256   {
04257     nsIContent *content = aIterator->GetCurrentNode();
04258 
04259     if (IsTextNode(content))
04260     {
04261       if (!crossedBlockBoundary && (!prev || HasSameBlockNodeParent(prev, content)))
04262         prev = content;
04263       else
04264         break;
04265     }
04266     else if (!crossedBlockBoundary && IsBlockNode(content))
04267       crossedBlockBoundary = PR_TRUE;
04268 
04269     aIterator->Next();
04270 
04271     if (!crossedBlockBoundary && DidSkip(aIterator))
04272       crossedBlockBoundary = PR_TRUE;
04273   }
04274 
04275   return NS_OK;
04276 }
04277 
04278 nsresult
04279 nsTextServicesDocument::GetFirstTextNodeInPrevBlock(nsIContent **aContent)
04280 {
04281   nsresult result;
04282 
04283   if (!aContent)
04284     return NS_ERROR_NULL_POINTER;
04285 
04286   *aContent = 0;
04287 
04288   // Save the iterator's current content node so we can restore
04289   // it when we are done:
04290 
04291   nsIContent *content = mIterator->GetCurrentNode();
04292 
04293   result = FirstTextNodeInPrevBlock(mIterator);
04294 
04295   if (NS_FAILED(result))
04296   {
04297     // Try to restore the iterator before returning.
04298     mIterator->PositionAt(content);
04299     return result;
04300   }
04301 
04302   if (!mIterator->IsDone())
04303   {
04304     NS_ADDREF(*aContent = mIterator->GetCurrentNode());
04305   }
04306 
04307   // Restore the iterator:
04308 
04309   return mIterator->PositionAt(content);
04310 }
04311 
04312 nsresult
04313 nsTextServicesDocument::GetFirstTextNodeInNextBlock(nsIContent **aContent)
04314 {
04315   nsresult result;
04316 
04317   if (!aContent)
04318     return NS_ERROR_NULL_POINTER;
04319 
04320   *aContent = 0;
04321 
04322   // Save the iterator's current content node so we can restore
04323   // it when we are done:
04324 
04325   nsIContent *content = mIterator->GetCurrentNode();
04326 
04327   result = FirstTextNodeInNextBlock(mIterator);
04328 
04329   if (NS_FAILED(result))
04330   {
04331     // Try to restore the iterator before returning.
04332     mIterator->PositionAt(content);
04333     return result;
04334   }
04335 
04336   if (!mIterator->IsDone())
04337   {
04338     NS_ADDREF(*aContent = mIterator->GetCurrentNode());
04339   }
04340 
04341   // Restore the iterator:
04342   return mIterator->PositionAt(content);
04343 }
04344 
04345 nsresult
04346 nsTextServicesDocument::CreateOffsetTable(nsVoidArray *aOffsetTable,
04347                                           nsIContentIterator *aIterator,
04348                                           TSDIteratorStatus *aIteratorStatus,
04349                                           nsIDOMRange *aIterRange,
04350                                           nsString *aStr)
04351 {
04352   nsresult result = NS_OK;
04353 
04354   nsCOMPtr<nsIContent> first;
04355   nsCOMPtr<nsIContent> prev;
04356 
04357   if (!aIterator)
04358     return NS_ERROR_NULL_POINTER;
04359 
04360   ClearOffsetTable(aOffsetTable);
04361 
04362   if (aStr)
04363     aStr->Truncate();
04364 
04365   if (*aIteratorStatus == nsTextServicesDocument::eIsDone)
04366     return NS_OK;
04367 
04368   // If we have an aIterRange, retrieve the endpoints so
04369   // they can be used in the while loop below to trim entries
04370   // for text nodes that are partially selected by aIterRange.
04371   
04372   nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
04373   PRInt32 rngStartOffset = 0, rngEndOffset = 0;
04374 
04375   if (aIterRange)
04376   {
04377     result = GetRangeEndPoints(aIterRange,
04378                                getter_AddRefs(rngStartNode), &rngStartOffset,
04379                                getter_AddRefs(rngEndNode), &rngEndOffset);
04380 
04381     NS_ENSURE_SUCCESS(result, result);
04382   }
04383 
04384   // The text service could have added text nodes to the beginning
04385   // of the current block and called this method again. Make sure
04386   // we really are at the beginning of the current block:
04387 
04388   result = FirstTextNodeInCurrentBlock(aIterator);
04389 
04390   if (NS_FAILED(result))
04391     return result;
04392 
04393   PRInt32 offset = 0;
04394 
04395   ClearDidSkip(aIterator);
04396 
04397   while (!aIterator->IsDone())
04398   {
04399     nsIContent *content = aIterator->GetCurrentNode();
04400 
04401     if (IsTextNode(content))
04402     {
04403       if (!prev || HasSameBlockNodeParent(prev, content))
04404       {
04405         nsCOMPtr<nsIDOMNode> node = do_QueryInterface(content);
04406 
04407         if (node)
04408         {
04409           nsString str;
04410 
04411           result = node->GetNodeValue(str);
04412 
04413           if (NS_FAILED(result))
04414             return result;
04415 
04416           // Add an entry for this text node into the offset table:
04417 
04418           OffsetEntry *entry = new OffsetEntry(node, offset, str.Length());
04419 
04420           if (!entry)
04421             return NS_ERROR_OUT_OF_MEMORY;
04422 
04423           aOffsetTable->AppendElement((void *)entry);
04424 
04425           // If one or both of the endpoints of the iteration range
04426           // are in the text node for this entry, make sure the entry
04427           // only accounts for the portion of the text node that is
04428           // in the range.
04429 
04430           PRInt32 startOffset = 0;
04431           PRInt32 endOffset   = str.Length();
04432           PRBool adjustStr    = PR_FALSE;
04433 
04434           if (entry->mNode == rngStartNode)
04435           {
04436             entry->mNodeOffset = startOffset = rngStartOffset;
04437             adjustStr = PR_TRUE;
04438           }
04439 
04440           if (entry->mNode == rngEndNode)
04441           {
04442             endOffset = rngEndOffset;
04443             adjustStr = PR_TRUE;
04444           }
04445 
04446           if (adjustStr)
04447           {
04448             entry->mLength = endOffset - startOffset;
04449             str = Substring(str, startOffset, entry->mLength);
04450           }
04451 
04452           offset += str.Length();
04453 
04454           if (aStr)
04455           {
04456             // Append the text node's string to the output string:
04457 
04458             if (!first)
04459               *aStr = str;
04460             else
04461               *aStr += str;
04462           }
04463         }
04464 
04465         prev = content;
04466 
04467         if (!first)
04468           first = content;
04469       }
04470       else
04471         break;
04472 
04473     }
04474     else if (IsBlockNode(content))
04475       break;
04476 
04477     aIterator->Next();
04478 
04479     if (DidSkip(aIterator))
04480       break;
04481   }
04482 
04483   if (first)
04484   {
04485     // Always leave the iterator pointing at the first
04486     // text node of the current block!
04487 
04488     aIterator->PositionAt(first);
04489   }
04490   else
04491   {
04492     // If we never ran across a text node, the iterator
04493     // might have been pointing to something invalid to
04494     // begin with.
04495 
04496     *aIteratorStatus = nsTextServicesDocument::eIsDone;
04497   }
04498 
04499   return result;
04500 }
04501 
04502 nsresult
04503 nsTextServicesDocument::RemoveInvalidOffsetEntries()
04504 {
04505   OffsetEntry *entry;
04506   PRInt32 i = 0;
04507 
04508   while (i < mOffsetTable.Count())
04509   {
04510     entry = (OffsetEntry *)mOffsetTable[i];
04511 
04512     if (!entry->mIsValid)
04513     {
04514       if (!mOffsetTable.RemoveElementAt(i))
04515         return NS_ERROR_FAILURE;
04516 
04517       if (mSelStartIndex >= 0 && mSelStartIndex >= i)
04518       {
04519         // We are deleting an entry that comes before
04520         // mSelStartIndex, decrement mSelStartIndex so
04521         // that it points to the correct entry!
04522 
04523         NS_ASSERTION(i != mSelStartIndex, "Invalid selection index.");
04524 
04525         --mSelStartIndex;
04526         --mSelEndIndex;
04527       }
04528     }
04529     else
04530       i++;
04531   }
04532 
04533   return NS_OK;
04534 }
04535 
04536 nsresult
04537 nsTextServicesDocument::ClearOffsetTable(nsVoidArray *aOffsetTable)
04538 {
04539   PRInt32 i;
04540 
04541   for (i = 0; i < aOffsetTable->Count(); i++)
04542   {
04543     OffsetEntry *entry = (OffsetEntry *)(*aOffsetTable)[i];
04544     if (entry)
04545       delete entry;
04546   }
04547 
04548   aOffsetTable->Clear();
04549 
04550   return NS_OK;
04551 }
04552 
04553 nsresult
04554 nsTextServicesDocument::SplitOffsetEntry(PRInt32 aTableIndex, PRInt32 aNewEntryLength)
04555 {
04556   OffsetEntry *entry = (OffsetEntry *)mOffsetTable[aTableIndex];
04557 
04558   NS_ASSERTION((aNewEntryLength > 0), "aNewEntryLength <= 0");
04559   NS_ASSERTION((aNewEntryLength < entry->mLength), "aNewEntryLength >= mLength");
04560 
04561   if (aNewEntryLength < 1 || aNewEntryLength >= entry->mLength)
04562     return NS_ERROR_FAILURE;
04563 
04564   PRInt32 oldLength = entry->mLength - aNewEntryLength;
04565 
04566   OffsetEntry *newEntry = new OffsetEntry(entry->mNode,
04567                                           entry->mStrOffset + oldLength,
04568                                           aNewEntryLength);
04569 
04570   if (!newEntry)
04571     return NS_ERROR_OUT_OF_MEMORY;
04572 
04573   if (!mOffsetTable.InsertElementAt(newEntry, aTableIndex + 1))
04574   {
04575     delete newEntry;
04576     return NS_ERROR_FAILURE;
04577   }
04578 
04579    // Adjust entry fields:
04580 
04581    entry->mLength        = oldLength;
04582    newEntry->mNodeOffset = entry->mNodeOffset + oldLength;
04583 
04584   return NS_OK;
04585 }
04586 
04587 nsresult
04588 nsTextServicesDocument::NodeHasOffsetEntry(nsVoidArray *aOffsetTable, nsIDOMNode *aNode, PRBool *aHasEntry, PRInt32 *aEntryIndex)
04589 {
04590   OffsetEntry *entry;
04591   PRInt32 i;
04592 
04593   if (!aNode || !aHasEntry || !aEntryIndex)
04594     return NS_ERROR_NULL_POINTER;
04595 
04596   for (i = 0; i < aOffsetTable->Count(); i++)
04597   {
04598     entry = (OffsetEntry *)(*aOffsetTable)[i];
04599 
04600     if (!entry)
04601       return NS_ERROR_FAILURE;
04602 
04603     if (entry->mNode == aNode)
04604     {
04605       *aHasEntry   = PR_TRUE;
04606       *aEntryIndex = i;
04607 
04608       return NS_OK;
04609     }
04610   }
04611 
04612   *aHasEntry   = PR_FALSE;
04613   *aEntryIndex = -1;
04614 
04615   return NS_OK;
04616 }
04617 
04618 nsresult
04619 nsTextServicesDocument::GetWordBreaker(nsIWordBreaker** aResult)
04620 {
04621   NS_ENSURE_ARG_POINTER(aResult);
04622 
04623   *aResult = nsnull;
04624 
04625   nsresult result;
04626   nsCOMPtr<nsIWordBreakerFactory> wbf(do_GetService(NS_LWBRK_CONTRACTID, &result));
04627   if (NS_SUCCEEDED(result) && wbf)
04628   {
04629     nsString wbarg;
04630     result = wbf->GetBreaker(wbarg, aResult);
04631   }
04632 
04633   return result;
04634 }
04635 
04636 // Spellchecker code has this. See bug 211343
04637 #ifdef XP_MAC
04638 #define IS_NBSP_CHAR(c) (((unsigned char)0xca)==(c))
04639 #else
04640 #define IS_NBSP_CHAR(c) (((unsigned char)0xa0)==(c))
04641 #endif
04642 
04643 nsresult
04644 nsTextServicesDocument::FindWordBounds(nsVoidArray *aOffsetTable,
04645                                        nsString *aBlockStr,
04646                                        nsIWordBreaker *aWordBreaker,
04647                                        nsIDOMNode *aNode,
04648                                        PRInt32 aNodeOffset,
04649                                        nsIDOMNode **aWordStartNode,
04650                                        PRInt32 *aWordStartOffset,
04651                                        nsIDOMNode **aWordEndNode,
04652                                        PRInt32 *aWordEndOffset)
04653 {
04654   // Initialize return values.
04655 
04656   if (aWordStartNode)
04657     *aWordStartNode = nsnull;
04658   if (aWordStartOffset)
04659     *aWordStartOffset = 0;
04660   if (aWordEndNode)
04661     *aWordEndNode = nsnull;
04662   if (aWordEndOffset)
04663     *aWordEndOffset = 0;
04664 
04665   PRInt32 entryIndex;
04666   PRBool hasEntry;
04667 
04668   // It's assumed that aNode is a text node. The first thing
04669   // we do is get it's index in the offset table so we can
04670   // calculate the dom point's string offset.
04671 
04672   nsresult result = NodeHasOffsetEntry(aOffsetTable, aNode, &hasEntry, &entryIndex);
04673   NS_ENSURE_SUCCESS(result, result);
04674   NS_ENSURE_TRUE(hasEntry, NS_ERROR_FAILURE);
04675 
04676   // Next we map aNodeOffset into a string offset.
04677 
04678   OffsetEntry *entry = (OffsetEntry *)(*aOffsetTable)[entryIndex];
04679   PRUint32 strOffset = entry->mStrOffset + aNodeOffset - entry->mNodeOffset;
04680 
04681   // Now we use the word breaker to find the beginning and end
04682   // of the word from our calculated string offset.
04683 
04684   const PRUnichar *str = aBlockStr->get();
04685   PRUint32 strLen = aBlockStr->Length();
04686   PRUint32 beginWord=0, endWord=0;
04687 
04688   result = aWordBreaker->FindWord(str, strLen, strOffset,
04689                                  &beginWord, &endWord);
04690   NS_ENSURE_SUCCESS(result, result);
04691 
04692   // Strip out the NBSPs at the ends
04693   while ((beginWord <= endWord) && (IS_NBSP_CHAR(str[beginWord]))) 
04694     beginWord++;
04695   if (str[endWord] == (unsigned char)0x20)
04696   {
04697     PRUint32 realEndWord = endWord - 1;
04698     while ((realEndWord > beginWord) && (IS_NBSP_CHAR(str[realEndWord]))) 
04699       realEndWord--;
04700     if (realEndWord < endWord - 1) 
04701       endWord = realEndWord + 1;
04702   }
04703 
04704   // Now that we have the string offsets for the beginning
04705   // and end of the word, run through the offset table and
04706   // convert them back into dom points.
04707 
04708   PRInt32 i, lastIndex = aOffsetTable->Count() - 1;
04709 
04710   for (i=0; i <= lastIndex; i++)
04711   {
04712     entry = (OffsetEntry *)(*aOffsetTable)[i];
04713 
04714     PRInt32 strEndOffset = entry->mStrOffset + entry->mLength;
04715 
04716     // Check to see if beginWord is within the range covered
04717     // by this entry. Note that if beginWord is after the last
04718     // character covered by this entry, we will use the next
04719     // entry if there is one.
04720 
04721     if (entry->mStrOffset <= beginWord &&
04722        (beginWord < strEndOffset || (beginWord == strEndOffset && i == lastIndex)))
04723     {
04724       if (aWordStartNode)
04725       {
04726         *aWordStartNode = entry->mNode;
04727         NS_IF_ADDREF(*aWordStartNode);
04728       }
04729 
04730       if (aWordStartOffset)
04731         *aWordStartOffset = entry->mNodeOffset + beginWord - entry->mStrOffset;
04732 
04733       if (!aWordEndNode && !aWordEndOffset)
04734       {
04735         // We've found our start entry, but if we're not looking
04736         // for end entries, we're done.
04737 
04738         break;
04739       }
04740     }
04741 
04742     // Check to see if endWord is within the range covered
04743     // by this entry.
04744 
04745     if (entry->mStrOffset <= endWord && endWord <= strEndOffset)
04746     {
04747       if (beginWord == endWord && endWord == strEndOffset && i != lastIndex)
04748       {
04749         // Wait for the next round so that we use the same entry
04750         // we did for aWordStartNode.
04751 
04752         continue;
04753       }
04754 
04755       if (aWordEndNode)
04756       {
04757         *aWordEndNode = entry->mNode;
04758         NS_IF_ADDREF(*aWordEndNode);
04759       }
04760 
04761       if (aWordEndOffset)
04762         *aWordEndOffset = entry->mNodeOffset + endWord - entry->mStrOffset;
04763 
04764       break;
04765     }
04766   }
04767 
04768 
04769   return NS_OK;
04770 }
04771 
04772 #ifdef DEBUG_kin
04773 void
04774 nsTextServicesDocument::PrintOffsetTable()
04775 {
04776   OffsetEntry *entry;
04777   PRInt32 i;
04778 
04779   for (i = 0; i < mOffsetTable.Count(); i++)
04780   {
04781     entry = (OffsetEntry *)mOffsetTable[i];
04782     printf("ENTRY %4d: %p  %c  %c  %4d  %4d  %4d\n",
04783            i, entry->mNode,  entry->mIsValid ? 'V' : 'N',
04784            entry->mIsInsertedText ? 'I' : 'B',
04785            entry->mNodeOffset, entry->mStrOffset, entry->mLength);
04786   }
04787 
04788   fflush(stdout);
04789 }
04790 
04791 void
04792 nsTextServicesDocument::PrintContentNode(nsIContent *aContent)
04793 {
04794   nsString tmpStr, str;
04795   nsresult result;
04796 
04797   aContent->Tag()->ToString(tmpStr);
04798   printf("%s", NS_LossyConvertUCS2toASCII(tmpStr).get());
04799 
04800   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aContent);
04801 
04802   if (node)
04803   {
04804     PRUint16 type;
04805 
04806     result = node->GetNodeType(&type);
04807 
04808     if (NS_FAILED(result))
04809       return;
04810 
04811     if (nsIDOMNode::TEXT_NODE == type)
04812     {
04813       result = node->GetNodeValue(str);
04814 
04815       if (NS_FAILED(result))
04816         return;
04817 
04818       printf(":  \"%s\"", NS_LossyConvertUCS2toASCII(str).get());
04819     }
04820   }
04821 
04822   printf("\n");
04823   fflush(stdout);
04824 }
04825 #endif