LCOV - code coverage report
Current view: directory - editor/txtsvc/src - nsTextServicesDocument.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1399 5 0.4 %
Date: 2012-06-02 Functions: 79 3 3.8 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Pierre Phaneuf <pp@ludusdesign.com>
      24                 :  *   Neil Deakin <neil@mozdevgroup.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "mozilla/Util.h"
      41                 : 
      42                 : #include "nscore.h"
      43                 : #include "nsLayoutCID.h"
      44                 : #include "nsIAtom.h"
      45                 : #include "nsStaticAtom.h"
      46                 : #include "nsString.h"
      47                 : #include "nsIEnumerator.h"
      48                 : #include "nsIContent.h"
      49                 : #include "nsIContentIterator.h"
      50                 : #include "nsIDOMNodeList.h"
      51                 : #include "nsIDOMRange.h"
      52                 : #include "nsContentUtils.h"
      53                 : #include "nsISelection.h"
      54                 : #include "nsIPlaintextEditor.h"
      55                 : #include "nsTextServicesDocument.h"
      56                 : #include "nsFilteredContentIterator.h"
      57                 : 
      58                 : #include "nsIDOMElement.h"
      59                 : #include "nsIDOMHTMLElement.h"
      60                 : #include "nsIDOMHTMLDocument.h"
      61                 : 
      62                 : #include "nsLWBrkCIID.h"
      63                 : #include "nsIWordBreaker.h"
      64                 : #include "nsIServiceManager.h"
      65                 : 
      66                 : #define LOCK_DOC(doc)
      67                 : #define UNLOCK_DOC(doc)
      68                 : 
      69                 : using namespace mozilla;
      70                 : 
      71                 : class OffsetEntry
      72                 : {
      73                 : public:
      74               0 :   OffsetEntry(nsIDOMNode *aNode, PRInt32 aOffset, PRInt32 aLength)
      75                 :     : mNode(aNode), mNodeOffset(0), mStrOffset(aOffset), mLength(aLength),
      76               0 :       mIsInsertedText(false), mIsValid(true)
      77                 :   {
      78               0 :     if (mStrOffset < 1)
      79               0 :       mStrOffset = 0;
      80                 : 
      81               0 :     if (mLength < 1)
      82               0 :       mLength = 0;
      83               0 :   }
      84                 : 
      85               0 :   virtual ~OffsetEntry()
      86               0 :   {
      87               0 :     mNode       = 0;
      88               0 :     mNodeOffset = 0;
      89               0 :     mStrOffset  = 0;
      90               0 :     mLength     = 0;
      91               0 :     mIsValid    = false;
      92               0 :   }
      93                 : 
      94                 :   nsIDOMNode *mNode;
      95                 :   PRInt32 mNodeOffset;
      96                 :   PRInt32 mStrOffset;
      97                 :   PRInt32 mLength;
      98                 :   bool    mIsInsertedText;
      99                 :   bool    mIsValid;
     100                 : };
     101                 : 
     102                 : #define TS_ATOM(name_, value_) nsIAtom* nsTextServicesDocument::name_ = 0;
     103                 : #include "nsTSAtomList.h"
     104                 : #undef TS_ATOM
     105                 : 
     106               0 : nsTextServicesDocument::nsTextServicesDocument()
     107                 : {
     108               0 :   mRefCnt         = 0;
     109                 : 
     110               0 :   mSelStartIndex  = -1;
     111               0 :   mSelStartOffset = -1;
     112               0 :   mSelEndIndex    = -1;
     113               0 :   mSelEndOffset   = -1;
     114                 : 
     115               0 :   mIteratorStatus = eIsDone;
     116               0 : }
     117                 : 
     118               0 : nsTextServicesDocument::~nsTextServicesDocument()
     119                 : {
     120               0 :   ClearOffsetTable(&mOffsetTable);
     121               0 : }
     122                 : 
     123                 : #define TS_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
     124                 : #include "nsTSAtomList.h"
     125                 : #undef TS_ATOM
     126                 : 
     127                 : /* static */
     128                 : void
     129            1404 : nsTextServicesDocument::RegisterAtoms()
     130                 : {
     131                 :   static const nsStaticAtom ts_atoms[] = {
     132                 : #define TS_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &name_),
     133                 : #include "nsTSAtomList.h"
     134                 : #undef TS_ATOM
     135                 :   };
     136                 : 
     137            1404 :   NS_RegisterStaticAtoms(ts_atoms);
     138            1404 : }
     139                 : 
     140               0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextServicesDocument)
     141               0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextServicesDocument)
     142                 : 
     143               0 : NS_INTERFACE_MAP_BEGIN(nsTextServicesDocument)
     144               0 :   NS_INTERFACE_MAP_ENTRY(nsITextServicesDocument)
     145               0 :   NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
     146               0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITextServicesDocument)
     147               0 :   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsTextServicesDocument)
     148               0 : NS_INTERFACE_MAP_END
     149                 : 
     150            1464 : NS_IMPL_CYCLE_COLLECTION_7(nsTextServicesDocument,
     151                 :                            mDOMDocument,
     152                 :                            mSelCon,
     153                 :                            mIterator,
     154                 :                            mPrevTextBlock,
     155                 :                            mNextTextBlock,
     156                 :                            mExtent,
     157                 :                            mTxtSvcFilter)
     158                 : 
     159                 : NS_IMETHODIMP
     160               0 : nsTextServicesDocument::InitWithEditor(nsIEditor *aEditor)
     161                 : {
     162               0 :   nsresult result = NS_OK;
     163               0 :   nsCOMPtr<nsISelectionController> selCon;
     164               0 :   nsCOMPtr<nsIDOMDocument> doc;
     165                 : 
     166               0 :   NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER);
     167                 : 
     168                 :   LOCK_DOC(this);
     169                 : 
     170                 :   // Check to see if we already have an mSelCon. If we do, it
     171                 :   // better be the same one the editor uses!
     172                 : 
     173               0 :   result = aEditor->GetSelectionController(getter_AddRefs(selCon));
     174                 : 
     175               0 :   if (NS_FAILED(result))
     176                 :   {
     177                 :     UNLOCK_DOC(this);
     178               0 :     return result;
     179                 :   }
     180                 : 
     181               0 :   if (!selCon || (mSelCon && selCon != mSelCon))
     182                 :   {
     183                 :     UNLOCK_DOC(this);
     184               0 :     return NS_ERROR_FAILURE;
     185                 :   }
     186                 : 
     187               0 :   if (!mSelCon)
     188               0 :     mSelCon = selCon;
     189                 : 
     190                 :   // Check to see if we already have an mDOMDocument. If we do, it
     191                 :   // better be the same one the editor uses!
     192                 : 
     193               0 :   result = aEditor->GetDocument(getter_AddRefs(doc));
     194                 : 
     195               0 :   if (NS_FAILED(result))
     196                 :   {
     197                 :     UNLOCK_DOC(this);
     198               0 :     return result;
     199                 :   }
     200                 : 
     201               0 :   if (!doc || (mDOMDocument && doc != mDOMDocument))
     202                 :   {
     203                 :     UNLOCK_DOC(this);
     204               0 :     return NS_ERROR_FAILURE;
     205                 :   }
     206                 : 
     207               0 :   if (!mDOMDocument)
     208                 :   {
     209               0 :     mDOMDocument = doc;
     210                 : 
     211               0 :     result = CreateDocumentContentIterator(getter_AddRefs(mIterator));
     212                 : 
     213               0 :     if (NS_FAILED(result))
     214                 :     {
     215                 :       UNLOCK_DOC(this);
     216               0 :       return result;
     217                 :     }
     218                 : 
     219               0 :     mIteratorStatus = nsTextServicesDocument::eIsDone;
     220                 : 
     221               0 :     result = FirstBlock();
     222                 : 
     223               0 :     if (NS_FAILED(result))
     224                 :     {
     225                 :       UNLOCK_DOC(this);
     226               0 :       return result;
     227                 :     }
     228                 :   }
     229                 : 
     230               0 :   mEditor = do_GetWeakReference(aEditor);
     231                 : 
     232               0 :   result = aEditor->AddEditActionListener(this);
     233                 : 
     234                 :   UNLOCK_DOC(this);
     235                 : 
     236               0 :   return result;
     237                 : }
     238                 : 
     239                 : NS_IMETHODIMP 
     240               0 : nsTextServicesDocument::GetDocument(nsIDOMDocument **aDoc)
     241                 : {
     242               0 :   NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
     243                 : 
     244               0 :   *aDoc = nsnull; // init out param
     245               0 :   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_NOT_INITIALIZED);
     246                 : 
     247               0 :   *aDoc = mDOMDocument;
     248               0 :   NS_ADDREF(*aDoc);
     249                 : 
     250               0 :   return NS_OK;
     251                 : }
     252                 : 
     253                 : NS_IMETHODIMP
     254               0 : nsTextServicesDocument::SetExtent(nsIDOMRange* aDOMRange)
     255                 : {
     256               0 :   NS_ENSURE_ARG_POINTER(aDOMRange);
     257               0 :   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
     258                 : 
     259                 :   LOCK_DOC(this);
     260                 : 
     261                 :   // We need to store a copy of aDOMRange since we don't
     262                 :   // know where it came from.
     263                 : 
     264               0 :   nsresult result = aDOMRange->CloneRange(getter_AddRefs(mExtent));
     265                 : 
     266               0 :   if (NS_FAILED(result))
     267                 :   {
     268                 :     UNLOCK_DOC(this);
     269               0 :     return result;
     270                 :   }
     271                 : 
     272                 :   // Create a new iterator based on our new extent range.
     273                 : 
     274               0 :   result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
     275                 : 
     276               0 :   if (NS_FAILED(result))
     277                 :   {
     278                 :     UNLOCK_DOC(this);
     279               0 :     return result;
     280                 :   }
     281                 : 
     282                 :   // Now position the iterator at the start of the first block
     283                 :   // in the range.
     284                 : 
     285               0 :   mIteratorStatus = nsTextServicesDocument::eIsDone;
     286                 : 
     287               0 :   result = FirstBlock();
     288                 : 
     289                 :   UNLOCK_DOC(this);
     290                 : 
     291               0 :   return result;
     292                 : }
     293                 : 
     294                 : NS_IMETHODIMP
     295               0 : nsTextServicesDocument::ExpandRangeToWordBoundaries(nsIDOMRange *aRange)
     296                 : {
     297               0 :   NS_ENSURE_ARG_POINTER(aRange);
     298                 : 
     299                 :   // Get the end points of the range.
     300                 : 
     301               0 :   nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
     302                 :   PRInt32 rngStartOffset, rngEndOffset;
     303                 : 
     304                 :   nsresult result =  GetRangeEndPoints(aRange,
     305               0 :                                        getter_AddRefs(rngStartNode),
     306                 :                                        &rngStartOffset,
     307               0 :                                        getter_AddRefs(rngEndNode),
     308               0 :                                        &rngEndOffset);
     309                 : 
     310               0 :   NS_ENSURE_SUCCESS(result, result);
     311                 : 
     312                 :   // Create a content iterator based on the range.
     313                 : 
     314               0 :   nsCOMPtr<nsIContentIterator> iter;
     315               0 :   result = CreateContentIterator(aRange, getter_AddRefs(iter));
     316                 : 
     317               0 :   NS_ENSURE_SUCCESS(result, result);
     318                 : 
     319                 :   // Find the first text node in the range.
     320                 : 
     321                 :   TSDIteratorStatus iterStatus;
     322                 : 
     323               0 :   result = FirstTextNode(iter, &iterStatus);
     324               0 :   NS_ENSURE_SUCCESS(result, result);
     325                 : 
     326               0 :   if (iterStatus == nsTextServicesDocument::eIsDone)
     327                 :   {
     328                 :     // No text was found so there's no adjustment necessary!
     329               0 :     return NS_OK;
     330                 :   }
     331                 : 
     332               0 :   nsINode *firstText = iter->GetCurrentNode();
     333               0 :   NS_ENSURE_TRUE(firstText, NS_ERROR_FAILURE);
     334                 : 
     335                 :   // Find the last text node in the range.
     336                 : 
     337               0 :   result = LastTextNode(iter, &iterStatus);
     338               0 :   NS_ENSURE_SUCCESS(result, result);
     339                 : 
     340               0 :   if (iterStatus == nsTextServicesDocument::eIsDone)
     341                 :   {
     342                 :     // We should never get here because a first text block
     343                 :     // was found above.
     344               0 :     NS_ASSERTION(false, "Found a first without a last!");
     345               0 :     return NS_ERROR_FAILURE;
     346                 :   }
     347                 : 
     348               0 :   nsINode *lastText = iter->GetCurrentNode();
     349               0 :   NS_ENSURE_TRUE(lastText, NS_ERROR_FAILURE);
     350                 : 
     351                 :   // Now make sure our end points are in terms of text nodes in the range!
     352                 : 
     353               0 :   nsCOMPtr<nsIDOMNode> firstTextNode = do_QueryInterface(firstText);
     354               0 :   NS_ENSURE_TRUE(firstTextNode, NS_ERROR_FAILURE);
     355                 : 
     356               0 :   if (rngStartNode != firstTextNode)
     357                 :   {
     358                 :     // The range includes the start of the first text node!
     359               0 :     rngStartNode = firstTextNode;
     360               0 :     rngStartOffset = 0;
     361                 :   }
     362                 : 
     363               0 :   nsCOMPtr<nsIDOMNode> lastTextNode = do_QueryInterface(lastText);
     364               0 :   NS_ENSURE_TRUE(lastTextNode, NS_ERROR_FAILURE);
     365                 : 
     366               0 :   if (rngEndNode != lastTextNode)
     367                 :   {
     368                 :     // The range includes the end of the last text node!
     369               0 :     rngEndNode = lastTextNode;
     370               0 :     nsAutoString str;
     371               0 :     result = lastTextNode->GetNodeValue(str);
     372               0 :     rngEndOffset = str.Length();
     373                 :   }
     374                 : 
     375                 :   // Create a doc iterator so that we can scan beyond
     376                 :   // the bounds of the extent range.
     377                 : 
     378               0 :   nsCOMPtr<nsIContentIterator> docIter;
     379               0 :   result = CreateDocumentContentIterator(getter_AddRefs(docIter));
     380               0 :   NS_ENSURE_SUCCESS(result, result);
     381                 : 
     382                 :   // Grab all the text in the block containing our
     383                 :   // first text node.
     384                 : 
     385               0 :   result = docIter->PositionAt(firstText);
     386               0 :   NS_ENSURE_SUCCESS(result, result);
     387                 : 
     388               0 :   iterStatus = nsTextServicesDocument::eValid;
     389                 : 
     390               0 :   nsTArray<OffsetEntry*> offsetTable;
     391               0 :   nsAutoString blockStr;
     392                 : 
     393                 :   result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
     394               0 :                              nsnull, &blockStr);
     395               0 :   if (NS_FAILED(result))
     396                 :   {
     397               0 :     ClearOffsetTable(&offsetTable);
     398               0 :     return result;
     399                 :   }
     400                 : 
     401               0 :   nsCOMPtr<nsIDOMNode> wordStartNode, wordEndNode;
     402                 :   PRInt32 wordStartOffset, wordEndOffset;
     403                 : 
     404                 :   result = FindWordBounds(&offsetTable, &blockStr,
     405                 :                           rngStartNode, rngStartOffset,
     406               0 :                           getter_AddRefs(wordStartNode), &wordStartOffset,
     407               0 :                           getter_AddRefs(wordEndNode), &wordEndOffset);
     408                 : 
     409               0 :   ClearOffsetTable(&offsetTable);
     410                 : 
     411               0 :   NS_ENSURE_SUCCESS(result, result);
     412                 : 
     413               0 :   rngStartNode = wordStartNode;
     414               0 :   rngStartOffset = wordStartOffset;
     415                 : 
     416                 :   // Grab all the text in the block containing our
     417                 :   // last text node.
     418                 : 
     419               0 :   result = docIter->PositionAt(lastText);
     420               0 :   NS_ENSURE_SUCCESS(result, result);
     421                 : 
     422               0 :   iterStatus = nsTextServicesDocument::eValid;
     423                 : 
     424                 :   result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
     425               0 :                              nsnull, &blockStr);
     426               0 :   if (NS_FAILED(result))
     427                 :   {
     428               0 :     ClearOffsetTable(&offsetTable);
     429               0 :     return result;
     430                 :   }
     431                 : 
     432                 :   result = FindWordBounds(&offsetTable, &blockStr,
     433                 :                           rngEndNode, rngEndOffset,
     434               0 :                           getter_AddRefs(wordStartNode), &wordStartOffset,
     435               0 :                           getter_AddRefs(wordEndNode), &wordEndOffset);
     436                 : 
     437               0 :   ClearOffsetTable(&offsetTable);
     438                 : 
     439               0 :   NS_ENSURE_SUCCESS(result, result);
     440                 : 
     441                 :   // To prevent expanding the range too much, we only change
     442                 :   // rngEndNode and rngEndOffset if it isn't already at the start of the
     443                 :   // word and isn't equivalent to rngStartNode and rngStartOffset.
     444                 : 
     445               0 :   if (rngEndNode != wordStartNode || rngEndOffset != wordStartOffset ||
     446               0 :      (rngEndNode == rngStartNode  && rngEndOffset == rngStartOffset))
     447                 :   {
     448               0 :     rngEndNode = wordEndNode;
     449               0 :     rngEndOffset = wordEndOffset;
     450                 :   }
     451                 : 
     452                 :   // Now adjust the range so that it uses our new
     453                 :   // end points.
     454                 : 
     455               0 :   result = aRange->SetEnd(rngEndNode, rngEndOffset);
     456               0 :   NS_ENSURE_SUCCESS(result, result);
     457                 : 
     458               0 :   return aRange->SetStart(rngStartNode, rngStartOffset);
     459                 : }
     460                 : 
     461                 : NS_IMETHODIMP
     462               0 : nsTextServicesDocument::SetFilter(nsITextServicesFilter *aFilter)
     463                 : {
     464                 :   // Hang on to the filter so we can set it into the filtered iterator.
     465               0 :   mTxtSvcFilter = aFilter;
     466                 : 
     467               0 :   return NS_OK;
     468                 : }
     469                 : 
     470                 : NS_IMETHODIMP
     471               0 : nsTextServicesDocument::GetCurrentTextBlock(nsString *aStr)
     472                 : {
     473                 :   nsresult result;
     474                 : 
     475               0 :   NS_ENSURE_TRUE(aStr, NS_ERROR_NULL_POINTER);
     476                 : 
     477               0 :   aStr->Truncate();
     478                 : 
     479               0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
     480                 : 
     481                 :   LOCK_DOC(this);
     482                 : 
     483                 :   result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     484               0 :                              mExtent, aStr);
     485                 : 
     486                 :   UNLOCK_DOC(this);
     487                 : 
     488               0 :   return result;
     489                 : }
     490                 : 
     491                 : NS_IMETHODIMP
     492               0 : nsTextServicesDocument::FirstBlock()
     493                 : {
     494               0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
     495                 : 
     496                 :   LOCK_DOC(this);
     497                 : 
     498               0 :   nsresult result = FirstTextNode(mIterator, &mIteratorStatus);
     499                 : 
     500               0 :   if (NS_FAILED(result))
     501                 :   {
     502                 :     UNLOCK_DOC(this);
     503               0 :     return result;
     504                 :   }
     505                 : 
     506                 :   // Keep track of prev and next blocks, just in case
     507                 :   // the text service blows away the current block.
     508                 : 
     509               0 :   if (mIteratorStatus == nsTextServicesDocument::eValid)
     510                 :   {
     511               0 :     mPrevTextBlock  = nsnull;
     512               0 :     result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
     513                 :   }
     514                 :   else
     515                 :   {
     516                 :     // There's no text block in the document!
     517                 : 
     518               0 :     mPrevTextBlock  = nsnull;
     519               0 :     mNextTextBlock  = nsnull;
     520                 :   }
     521                 : 
     522                 :   UNLOCK_DOC(this);
     523                 : 
     524               0 :   return result;
     525                 : }
     526                 : 
     527                 : NS_IMETHODIMP
     528               0 : nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus,
     529                 :                                           PRInt32 *aSelOffset,
     530                 :                                           PRInt32 *aSelLength)
     531                 : {
     532               0 :   nsresult result = NS_OK;
     533                 : 
     534               0 :   NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
     535                 : 
     536                 :   LOCK_DOC(this);
     537                 : 
     538               0 :   mIteratorStatus = nsTextServicesDocument::eIsDone;
     539                 : 
     540               0 :   *aSelStatus = nsITextServicesDocument::eBlockNotFound;
     541               0 :   *aSelOffset = *aSelLength = -1;
     542                 : 
     543               0 :   if (!mSelCon || !mIterator)
     544                 :   {
     545                 :     UNLOCK_DOC(this);
     546               0 :     return NS_ERROR_FAILURE;
     547                 :   }
     548                 : 
     549               0 :   nsCOMPtr<nsISelection> selection;
     550               0 :   bool isCollapsed = false;
     551                 : 
     552               0 :   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
     553                 : 
     554               0 :   if (NS_FAILED(result))
     555                 :   {
     556                 :     UNLOCK_DOC(this);
     557               0 :     return result;
     558                 :   }
     559                 : 
     560               0 :   result = selection->GetIsCollapsed(&isCollapsed);
     561                 : 
     562               0 :   if (NS_FAILED(result))
     563                 :   {
     564                 :     UNLOCK_DOC(this);
     565               0 :     return result;
     566                 :   }
     567                 : 
     568               0 :   nsCOMPtr<nsIContentIterator> iter;
     569               0 :   nsCOMPtr<nsIDOMRange>        range;
     570               0 :   nsCOMPtr<nsIDOMNode>         parent;
     571                 :   PRInt32                      i, rangeCount, offset;
     572                 : 
     573               0 :   if (isCollapsed)
     574                 :   {
     575                 :     // We have a caret. Check if the caret is in a text node.
     576                 :     // If it is, make the text node's block the current block.
     577                 :     // If the caret isn't in a text node, search forwards in
     578                 :     // the document, till we find a text node.
     579                 : 
     580               0 :     result = selection->GetRangeAt(0, getter_AddRefs(range));
     581                 : 
     582               0 :     if (NS_FAILED(result))
     583                 :     {
     584                 :       UNLOCK_DOC(this);
     585               0 :       return result;
     586                 :     }
     587                 : 
     588               0 :     if (!range)
     589                 :     {
     590                 :       UNLOCK_DOC(this);
     591               0 :       return NS_ERROR_FAILURE;
     592                 :     }
     593                 : 
     594               0 :     result = range->GetStartContainer(getter_AddRefs(parent));
     595                 : 
     596               0 :     if (NS_FAILED(result))
     597                 :     {
     598                 :       UNLOCK_DOC(this);
     599               0 :       return result;
     600                 :     }
     601                 : 
     602               0 :     if (!parent)
     603                 :     {
     604                 :       UNLOCK_DOC(this);
     605               0 :       return NS_ERROR_FAILURE;
     606                 :     }
     607                 : 
     608               0 :     result = range->GetStartOffset(&offset);
     609                 : 
     610               0 :     if (NS_FAILED(result))
     611                 :     {
     612                 :       UNLOCK_DOC(this);
     613               0 :       return result;
     614                 :     }
     615                 : 
     616               0 :     if (IsTextNode(parent))
     617                 :     {
     618                 :       // The caret is in a text node. Find the beginning
     619                 :       // of the text block containing this text node and
     620                 :       // return.
     621                 : 
     622               0 :       nsCOMPtr<nsIContent> content(do_QueryInterface(parent));
     623                 : 
     624               0 :       if (!content)
     625                 :       {
     626                 :         UNLOCK_DOC(this);
     627               0 :         return NS_ERROR_FAILURE;
     628                 :       }
     629                 : 
     630               0 :       result = mIterator->PositionAt(content);
     631                 : 
     632               0 :       if (NS_FAILED(result))
     633                 :       {
     634                 :         UNLOCK_DOC(this);
     635               0 :         return result;
     636                 :       }
     637                 : 
     638               0 :       result = FirstTextNodeInCurrentBlock(mIterator);
     639                 : 
     640               0 :       if (NS_FAILED(result))
     641                 :       {
     642                 :         UNLOCK_DOC(this);
     643               0 :         return result;
     644                 :       }
     645                 : 
     646               0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     647                 : 
     648                 :       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     649               0 :                                  mExtent, nsnull);
     650                 : 
     651               0 :       if (NS_FAILED(result))
     652                 :       {
     653                 :         UNLOCK_DOC(this);
     654               0 :         return result;
     655                 :       }
     656                 : 
     657               0 :       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
     658                 : 
     659               0 :       if (NS_FAILED(result))
     660                 :       {
     661                 :         UNLOCK_DOC(this);
     662               0 :         return result;
     663                 :       }
     664                 : 
     665               0 :       if (*aSelStatus == nsITextServicesDocument::eBlockContains)
     666               0 :         result = SetSelectionInternal(*aSelOffset, *aSelLength, false);
     667                 :     }
     668                 :     else
     669                 :     {
     670                 :       // The caret isn't in a text node. Create an iterator
     671                 :       // based on a range that extends from the current caret
     672                 :       // position to the end of the document, then walk forwards
     673                 :       // till you find a text node, then find the beginning of it's block.
     674                 : 
     675               0 :       result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, getter_AddRefs(range));
     676                 : 
     677               0 :       if (NS_FAILED(result))
     678                 :       {
     679                 :         UNLOCK_DOC(this);
     680               0 :         return result;
     681                 :       }
     682                 : 
     683               0 :       result = range->GetCollapsed(&isCollapsed);
     684                 : 
     685               0 :       if (NS_FAILED(result))
     686                 :       {
     687                 :         UNLOCK_DOC(this);
     688               0 :         return result;
     689                 :       }
     690                 : 
     691               0 :       if (isCollapsed)
     692                 :       {
     693                 :         // If we get here, the range is collapsed because there is nothing after
     694                 :         // the caret! Just return NS_OK;
     695                 : 
     696                 :         UNLOCK_DOC(this);
     697               0 :         return NS_OK;
     698                 :       }
     699                 : 
     700               0 :       result = CreateContentIterator(range, getter_AddRefs(iter));
     701                 : 
     702               0 :       if (NS_FAILED(result))
     703                 :       {
     704                 :         UNLOCK_DOC(this);
     705               0 :         return result;
     706                 :       }
     707                 : 
     708               0 :       iter->First();
     709                 : 
     710               0 :       nsCOMPtr<nsIContent> content;
     711               0 :       while (!iter->IsDone())
     712                 :       {
     713               0 :         content = do_QueryInterface(iter->GetCurrentNode());
     714                 : 
     715               0 :         if (IsTextNode(content))
     716               0 :           break;
     717                 : 
     718               0 :         content = nsnull;
     719                 : 
     720               0 :         iter->Next();
     721                 :       }
     722                 : 
     723               0 :       if (!content)
     724                 :       {
     725                 :         UNLOCK_DOC(this);
     726               0 :         return NS_OK;
     727                 :       }
     728                 : 
     729               0 :       result = mIterator->PositionAt(content);
     730                 : 
     731               0 :       if (NS_FAILED(result))
     732                 :       {
     733                 :         UNLOCK_DOC(this);
     734               0 :         return result;
     735                 :       }
     736                 : 
     737               0 :       result = FirstTextNodeInCurrentBlock(mIterator);
     738                 : 
     739               0 :       if (NS_FAILED(result))
     740                 :       {
     741                 :         UNLOCK_DOC(this);
     742               0 :         return result;
     743                 :       }
     744                 : 
     745               0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     746                 : 
     747                 :       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     748               0 :                                  mExtent, nsnull);
     749                 : 
     750               0 :       if (NS_FAILED(result))
     751                 :       {
     752                 :         UNLOCK_DOC(this);
     753               0 :         return result;
     754                 :       }
     755                 : 
     756               0 :       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
     757                 : 
     758               0 :       if (NS_FAILED(result))
     759                 :       {
     760                 :         UNLOCK_DOC(this);
     761               0 :         return result;
     762                 :       }
     763                 :     }
     764                 : 
     765                 :     UNLOCK_DOC(this);
     766                 : 
     767               0 :     return result;
     768                 :   }
     769                 : 
     770                 :   // If we get here, we have an uncollapsed selection!
     771                 :   // Look backwards through each range in the selection till you
     772                 :   // find the first text node. If you find one, find the
     773                 :   // beginning of its text block, and make it the current
     774                 :   // block.
     775                 : 
     776               0 :   result = selection->GetRangeCount(&rangeCount);
     777                 : 
     778               0 :   if (NS_FAILED(result))
     779                 :   {
     780                 :     UNLOCK_DOC(this);
     781               0 :     return result;
     782                 :   }
     783                 : 
     784               0 :   NS_ASSERTION(rangeCount > 0, "Unexpected range count!");
     785                 : 
     786               0 :   if (rangeCount <= 0)
     787                 :   {
     788                 :     UNLOCK_DOC(this);
     789               0 :     return NS_OK;
     790                 :   }
     791                 : 
     792                 :   // XXX: We may need to add some code here to make sure
     793                 :   //      the ranges are sorted in document appearance order!
     794                 : 
     795               0 :   for (i = rangeCount - 1; i >= 0; i--)
     796                 :   {
     797                 :     // Get the i'th range from the selection.
     798                 : 
     799               0 :     result = selection->GetRangeAt(i, getter_AddRefs(range));
     800                 : 
     801               0 :     if (NS_FAILED(result))
     802                 :     {
     803                 :       UNLOCK_DOC(this);
     804               0 :       return result;
     805                 :     }
     806                 : 
     807                 :     // Create an iterator for the range.
     808                 : 
     809               0 :     result = CreateContentIterator(range, getter_AddRefs(iter));
     810                 : 
     811               0 :     if (NS_FAILED(result))
     812                 :     {
     813                 :       UNLOCK_DOC(this);
     814               0 :       return result;
     815                 :     }
     816                 : 
     817               0 :     iter->Last();
     818                 : 
     819                 :     // Now walk through the range till we find a text node.
     820                 : 
     821               0 :     while (!iter->IsDone())
     822                 :     {
     823               0 :       nsCOMPtr<nsIContent> content = do_QueryInterface(iter->GetCurrentNode());
     824                 : 
     825               0 :       if (IsTextNode(content))
     826                 :       {
     827                 :         // We found a text node, so position the document's
     828                 :         // iterator at the beginning of the block, then get
     829                 :         // the selection in terms of the string offset.
     830                 : 
     831               0 :         result = mIterator->PositionAt(content);
     832                 : 
     833               0 :         if (NS_FAILED(result))
     834                 :         {
     835                 :           UNLOCK_DOC(this);
     836               0 :           return result;
     837                 :         }
     838                 : 
     839               0 :         result = FirstTextNodeInCurrentBlock(mIterator);
     840                 : 
     841               0 :         if (NS_FAILED(result))
     842                 :         {
     843                 :           UNLOCK_DOC(this);
     844               0 :           return result;
     845                 :         }
     846                 : 
     847               0 :         mIteratorStatus = nsTextServicesDocument::eValid;
     848                 : 
     849                 :         result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     850               0 :                                    mExtent, nsnull);
     851                 : 
     852               0 :         if (NS_FAILED(result))
     853                 :         {
     854                 :           UNLOCK_DOC(this);
     855               0 :           return result;
     856                 :         }
     857                 : 
     858               0 :         result = GetSelection(aSelStatus, aSelOffset, aSelLength);
     859                 : 
     860                 :         UNLOCK_DOC(this);
     861                 : 
     862               0 :         return result;
     863                 : 
     864                 :       }
     865                 : 
     866               0 :       iter->Prev();
     867                 :     }
     868                 :   }
     869                 : 
     870                 :   // If we get here, we didn't find any text node in the selection!
     871                 :   // Create a range that extends from the end of the selection,
     872                 :   // to the end of the document, then iterate forwards through
     873                 :   // it till you find a text node!
     874                 : 
     875               0 :   result = selection->GetRangeAt(rangeCount - 1, getter_AddRefs(range));
     876                 : 
     877               0 :   if (NS_FAILED(result))
     878                 :   {
     879                 :     UNLOCK_DOC(this);
     880               0 :     return result;
     881                 :   }
     882                 : 
     883               0 :   if (!range)
     884                 :   {
     885                 :     UNLOCK_DOC(this);
     886               0 :     return NS_ERROR_FAILURE;
     887                 :   }
     888                 : 
     889               0 :   result = range->GetEndContainer(getter_AddRefs(parent));
     890                 : 
     891               0 :   if (NS_FAILED(result))
     892                 :   {
     893                 :     UNLOCK_DOC(this);
     894               0 :     return result;
     895                 :   }
     896                 : 
     897               0 :   if (!parent)
     898                 :   {
     899                 :     UNLOCK_DOC(this);
     900               0 :     return NS_ERROR_FAILURE;
     901                 :   }
     902                 : 
     903               0 :   result = range->GetEndOffset(&offset);
     904                 : 
     905               0 :   if (NS_FAILED(result))
     906                 :   {
     907                 :     UNLOCK_DOC(this);
     908               0 :     return result;
     909                 :   }
     910                 : 
     911               0 :   result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, getter_AddRefs(range));
     912                 : 
     913               0 :   if (NS_FAILED(result))
     914                 :   {
     915                 :     UNLOCK_DOC(this);
     916               0 :     return result;
     917                 :   }
     918                 : 
     919               0 :   result = range->GetCollapsed(&isCollapsed);
     920                 : 
     921               0 :   if (NS_FAILED(result))
     922                 :   {
     923                 :     UNLOCK_DOC(this);
     924               0 :     return result;
     925                 :   }
     926                 : 
     927               0 :   if (isCollapsed)
     928                 :   {
     929                 :     // If we get here, the range is collapsed because there is nothing after
     930                 :     // the current selection! Just return NS_OK;
     931                 : 
     932                 :     UNLOCK_DOC(this);
     933               0 :     return NS_OK;
     934                 :   }
     935                 : 
     936               0 :   result = CreateContentIterator(range, getter_AddRefs(iter));
     937                 : 
     938               0 :   if (NS_FAILED(result))
     939                 :   {
     940                 :     UNLOCK_DOC(this);
     941               0 :     return result;
     942                 :   }
     943                 : 
     944               0 :   iter->First();
     945                 : 
     946               0 :   while (!iter->IsDone())
     947                 :   {
     948               0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(iter->GetCurrentNode());
     949                 : 
     950               0 :     if (IsTextNode(content))
     951                 :     {
     952                 :       // We found a text node! Adjust the document's iterator to point
     953                 :       // to the beginning of its text block, then get the current selection.
     954                 : 
     955               0 :       result = mIterator->PositionAt(content);
     956                 : 
     957               0 :       if (NS_FAILED(result))
     958                 :       {
     959                 :         UNLOCK_DOC(this);
     960               0 :         return result;
     961                 :       }
     962                 : 
     963               0 :       result = FirstTextNodeInCurrentBlock(mIterator);
     964                 : 
     965               0 :       if (NS_FAILED(result))
     966                 :       {
     967                 :         UNLOCK_DOC(this);
     968               0 :         return result;
     969                 :       }
     970                 : 
     971                 : 
     972               0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     973                 : 
     974                 :       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     975               0 :                                  mExtent, nsnull);
     976                 : 
     977               0 :       if (NS_FAILED(result))
     978                 :       {
     979                 :         UNLOCK_DOC(this);
     980               0 :         return result;
     981                 :       }
     982                 : 
     983               0 :       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
     984                 : 
     985                 :       UNLOCK_DOC(this);
     986                 : 
     987               0 :       return result;
     988                 :     }
     989                 : 
     990               0 :     iter->Next();
     991                 :   }
     992                 : 
     993                 :   // If we get here, we didn't find any block before or inside
     994                 :   // the selection! Just return OK.
     995                 : 
     996                 :   UNLOCK_DOC(this);
     997                 : 
     998               0 :   return NS_OK;
     999                 : }
    1000                 : 
    1001                 : NS_IMETHODIMP
    1002               0 : nsTextServicesDocument::PrevBlock()
    1003                 : {
    1004               0 :   nsresult result = NS_OK;
    1005                 : 
    1006               0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
    1007                 : 
    1008                 :   LOCK_DOC(this);
    1009                 : 
    1010               0 :   if (mIteratorStatus == nsTextServicesDocument::eIsDone)
    1011               0 :     return NS_OK;
    1012                 : 
    1013               0 :   switch (mIteratorStatus)
    1014                 :   {
    1015                 :     case nsTextServicesDocument::eValid:
    1016                 :     case nsTextServicesDocument::eNext:
    1017                 : 
    1018               0 :       result = FirstTextNodeInPrevBlock(mIterator);
    1019                 : 
    1020               0 :       if (NS_FAILED(result))
    1021                 :       {
    1022               0 :         mIteratorStatus = nsTextServicesDocument::eIsDone;
    1023                 :         UNLOCK_DOC(this);
    1024               0 :         return result;
    1025                 :       }
    1026                 : 
    1027               0 :       if (mIterator->IsDone())
    1028                 :       {
    1029               0 :         mIteratorStatus = nsTextServicesDocument::eIsDone;
    1030                 :         UNLOCK_DOC(this);
    1031               0 :         return NS_OK;
    1032                 :       }
    1033                 : 
    1034               0 :       mIteratorStatus = nsTextServicesDocument::eValid;
    1035               0 :       break;
    1036                 : 
    1037                 :     case nsTextServicesDocument::ePrev:
    1038                 : 
    1039                 :       // The iterator already points to the previous
    1040                 :       // block, so don't do anything.
    1041                 : 
    1042               0 :       mIteratorStatus = nsTextServicesDocument::eValid;
    1043               0 :       break;
    1044                 : 
    1045                 :     default:
    1046                 : 
    1047               0 :       mIteratorStatus = nsTextServicesDocument::eIsDone;
    1048               0 :       break;
    1049                 :   }
    1050                 : 
    1051                 :   // Keep track of prev and next blocks, just in case
    1052                 :   // the text service blows away the current block.
    1053                 : 
    1054               0 :   if (mIteratorStatus == nsTextServicesDocument::eValid)
    1055                 :   {
    1056               0 :     result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
    1057               0 :     result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
    1058                 :   }
    1059                 :   else
    1060                 :   {
    1061                 :     // We must be done!
    1062                 : 
    1063               0 :     mPrevTextBlock = nsnull;
    1064               0 :     mNextTextBlock = nsnull;
    1065                 :   }
    1066                 : 
    1067                 :   UNLOCK_DOC(this);
    1068                 : 
    1069               0 :   return result;
    1070                 : }
    1071                 : 
    1072                 : NS_IMETHODIMP
    1073               0 : nsTextServicesDocument::NextBlock()
    1074                 : {
    1075               0 :   nsresult result = NS_OK;
    1076                 : 
    1077               0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
    1078                 : 
    1079                 :   LOCK_DOC(this);
    1080                 : 
    1081               0 :   if (mIteratorStatus == nsTextServicesDocument::eIsDone)
    1082               0 :     return NS_OK;
    1083                 : 
    1084               0 :   switch (mIteratorStatus)
    1085                 :   {
    1086                 :     case nsTextServicesDocument::eValid:
    1087                 : 
    1088                 :       // Advance the iterator to the next text block.
    1089                 : 
    1090               0 :       result = FirstTextNodeInNextBlock(mIterator);
    1091                 : 
    1092               0 :       if (NS_FAILED(result))
    1093                 :       {
    1094               0 :         mIteratorStatus = nsTextServicesDocument::eIsDone;
    1095                 :         UNLOCK_DOC(this);
    1096               0 :         return result;
    1097                 :       }
    1098                 : 
    1099               0 :       if (mIterator->IsDone())
    1100                 :       {
    1101               0 :         mIteratorStatus = nsTextServicesDocument::eIsDone;
    1102                 :         UNLOCK_DOC(this);
    1103               0 :         return NS_OK;
    1104                 :       }
    1105                 : 
    1106               0 :       mIteratorStatus = nsTextServicesDocument::eValid;
    1107               0 :       break;
    1108                 : 
    1109                 :     case nsTextServicesDocument::eNext:
    1110                 : 
    1111                 :       // The iterator already points to the next block,
    1112                 :       // so don't do anything to it!
    1113                 : 
    1114               0 :       mIteratorStatus = nsTextServicesDocument::eValid;
    1115               0 :       break;
    1116                 : 
    1117                 :     case nsTextServicesDocument::ePrev:
    1118                 : 
    1119                 :       // If the iterator is pointing to the previous block,
    1120                 :       // we know that there is no next text block! Just
    1121                 :       // fall through to the default case!
    1122                 : 
    1123                 :     default:
    1124                 : 
    1125               0 :       mIteratorStatus = nsTextServicesDocument::eIsDone;
    1126               0 :       break;
    1127                 :   }
    1128                 : 
    1129                 :   // Keep track of prev and next blocks, just in case
    1130                 :   // the text service blows away the current block.
    1131                 : 
    1132               0 :   if (mIteratorStatus == nsTextServicesDocument::eValid)
    1133                 :   {
    1134               0 :     result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
    1135               0 :     result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
    1136                 :   }
    1137                 :   else
    1138                 :   {
    1139                 :     // We must be done.
    1140                 : 
    1141               0 :     mPrevTextBlock = nsnull;
    1142               0 :     mNextTextBlock = nsnull;
    1143                 :   }
    1144                 : 
    1145                 : 
    1146                 :   UNLOCK_DOC(this);
    1147                 : 
    1148               0 :   return result;
    1149                 : }
    1150                 : 
    1151                 : NS_IMETHODIMP
    1152               0 : nsTextServicesDocument::IsDone(bool *aIsDone)
    1153                 : {
    1154               0 :   NS_ENSURE_TRUE(aIsDone, NS_ERROR_NULL_POINTER);
    1155                 : 
    1156               0 :   *aIsDone = false;
    1157                 : 
    1158               0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
    1159                 : 
    1160                 :   LOCK_DOC(this);
    1161                 : 
    1162               0 :   *aIsDone = (mIteratorStatus == nsTextServicesDocument::eIsDone) ? true : false;
    1163                 : 
    1164                 :   UNLOCK_DOC(this);
    1165                 : 
    1166               0 :   return NS_OK;
    1167                 : }
    1168                 : 
    1169                 : NS_IMETHODIMP
    1170               0 : nsTextServicesDocument::SetSelection(PRInt32 aOffset, PRInt32 aLength)
    1171                 : {
    1172                 :   nsresult result;
    1173                 : 
    1174               0 :   NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
    1175                 : 
    1176                 :   LOCK_DOC(this);
    1177                 : 
    1178               0 :   result = SetSelectionInternal(aOffset, aLength, true);
    1179                 : 
    1180                 :   UNLOCK_DOC(this);
    1181                 : 
    1182                 :   //**** KDEBUG ****
    1183                 :   // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1184                 :   //**** KDEBUG ****
    1185                 : 
    1186               0 :   return result;
    1187                 : }
    1188                 : 
    1189                 : NS_IMETHODIMP
    1190               0 : nsTextServicesDocument::ScrollSelectionIntoView()
    1191                 : {
    1192                 :   nsresult result;
    1193                 : 
    1194               0 :   NS_ENSURE_TRUE(mSelCon, NS_ERROR_FAILURE);
    1195                 : 
    1196                 :   LOCK_DOC(this);
    1197                 : 
    1198                 :   // After ScrollSelectionIntoView(), the pending notifications might be flushed
    1199                 :   // and PresShell/PresContext/Frames may be dead. See bug 418470.
    1200               0 :   result = mSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION,
    1201               0 :                                             nsISelectionController::SCROLL_SYNCHRONOUS);
    1202                 : 
    1203                 :   UNLOCK_DOC(this);
    1204                 : 
    1205               0 :   return result;
    1206                 : }
    1207                 : 
    1208                 : NS_IMETHODIMP
    1209               0 : nsTextServicesDocument::DeleteSelection()
    1210                 : {
    1211               0 :   nsresult result = NS_OK;
    1212                 : 
    1213                 :   // We don't allow deletion during a collapsed selection!
    1214               0 :   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
    1215               0 :   NS_ASSERTION(editor, "DeleteSelection called without an editor present!"); 
    1216               0 :   NS_ASSERTION(SelectionIsValid(), "DeleteSelection called without a valid selection!"); 
    1217                 : 
    1218               0 :   if (!editor || !SelectionIsValid())
    1219               0 :     return NS_ERROR_FAILURE;
    1220                 : 
    1221               0 :   if (SelectionIsCollapsed())
    1222               0 :     return NS_OK;
    1223                 : 
    1224                 :   LOCK_DOC(this);
    1225                 : 
    1226                 :   //**** KDEBUG ****
    1227                 :   // printf("\n---- Before Delete\n");
    1228                 :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1229                 :   // PrintOffsetTable();
    1230                 :   //**** KDEBUG ****
    1231                 : 
    1232                 :   // If we have an mExtent, save off its current set of
    1233                 :   // end points so we can compare them against mExtent's
    1234                 :   // set after the deletion of the content.
    1235                 : 
    1236               0 :   nsCOMPtr<nsIDOMNode> origStartNode, origEndNode;
    1237               0 :   PRInt32 origStartOffset = 0, origEndOffset = 0;
    1238                 : 
    1239               0 :   if (mExtent)
    1240                 :   {
    1241                 :     result = GetRangeEndPoints(mExtent,
    1242               0 :                                getter_AddRefs(origStartNode), &origStartOffset,
    1243               0 :                                getter_AddRefs(origEndNode), &origEndOffset);
    1244                 : 
    1245               0 :     if (NS_FAILED(result))
    1246                 :     {
    1247                 :       UNLOCK_DOC(this);
    1248               0 :       return result;
    1249                 :     }
    1250                 :   }
    1251                 : 
    1252                 :   PRInt32 i, selLength;
    1253                 :   OffsetEntry *entry, *newEntry;
    1254                 : 
    1255               0 :   for (i = mSelStartIndex; i <= mSelEndIndex; i++)
    1256                 :   {
    1257               0 :     entry = mOffsetTable[i];
    1258                 : 
    1259               0 :     if (i == mSelStartIndex)
    1260                 :     {
    1261                 :       // Calculate the length of the selection. Note that the
    1262                 :       // selection length can be zero if the start of the selection
    1263                 :       // is at the very end of a text node entry.
    1264                 : 
    1265               0 :       if (entry->mIsInsertedText)
    1266                 :       {
    1267                 :         // Inserted text offset entries have no width when
    1268                 :         // talking in terms of string offsets! If the beginning
    1269                 :         // of the selection is in an inserted text offset entry,
    1270                 :         // the caret is always at the end of the entry!
    1271                 : 
    1272               0 :         selLength = 0;
    1273                 :       }
    1274                 :       else
    1275               0 :         selLength = entry->mLength - (mSelStartOffset - entry->mStrOffset);
    1276                 : 
    1277               0 :       if (selLength > 0 && mSelStartOffset > entry->mStrOffset)
    1278                 :       {
    1279                 :         // Selection doesn't start at the beginning of the
    1280                 :         // text node entry. We need to split this entry into
    1281                 :         // two pieces, the piece before the selection, and
    1282                 :         // the piece inside the selection.
    1283                 : 
    1284               0 :         result = SplitOffsetEntry(i, selLength);
    1285                 : 
    1286               0 :         if (NS_FAILED(result))
    1287                 :         {
    1288                 :           UNLOCK_DOC(this);
    1289               0 :           return result;
    1290                 :         }
    1291                 : 
    1292                 :         // Adjust selection indexes to account for new entry:
    1293                 : 
    1294               0 :         ++mSelStartIndex;
    1295               0 :         ++mSelEndIndex;
    1296               0 :         ++i;
    1297                 : 
    1298               0 :         entry = mOffsetTable[i];
    1299                 :       }
    1300                 : 
    1301                 : 
    1302               0 :       if (selLength > 0 && mSelStartIndex < mSelEndIndex)
    1303                 :       {
    1304                 :         // The entire entry is contained in the selection. Mark the
    1305                 :         // entry invalid.
    1306                 : 
    1307               0 :         entry->mIsValid = false;
    1308                 :       }
    1309                 :     }
    1310                 : 
    1311                 :   //**** KDEBUG ****
    1312                 :   // printf("\n---- Middle Delete\n");
    1313                 :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1314                 :   // PrintOffsetTable();
    1315                 :   //**** KDEBUG ****
    1316                 : 
    1317               0 :     if (i == mSelEndIndex)
    1318                 :     {
    1319               0 :       if (entry->mIsInsertedText)
    1320                 :       {
    1321                 :         // Inserted text offset entries have no width when
    1322                 :         // talking in terms of string offsets! If the end
    1323                 :         // of the selection is in an inserted text offset entry,
    1324                 :         // the selection includes the entire entry!
    1325                 : 
    1326               0 :         entry->mIsValid = false;
    1327                 :       }
    1328                 :       else
    1329                 :       {
    1330                 :         // Calculate the length of the selection. Note that the
    1331                 :         // selection length can be zero if the end of the selection
    1332                 :         // is at the very beginning of a text node entry.
    1333                 : 
    1334               0 :         selLength = mSelEndOffset - entry->mStrOffset;
    1335                 : 
    1336               0 :         if (selLength > 0 && mSelEndOffset < entry->mStrOffset + entry->mLength)
    1337                 :         {
    1338                 :           // mStrOffset is guaranteed to be inside the selection, even
    1339                 :           // when mSelStartIndex == mSelEndIndex.
    1340                 : 
    1341               0 :           result = SplitOffsetEntry(i, entry->mLength - selLength);
    1342                 : 
    1343               0 :           if (NS_FAILED(result))
    1344                 :           {
    1345                 :             UNLOCK_DOC(this);
    1346               0 :             return result;
    1347                 :           }
    1348                 : 
    1349                 :           // Update the entry fields:
    1350                 : 
    1351               0 :           newEntry = mOffsetTable[i+1];
    1352               0 :           newEntry->mNodeOffset = entry->mNodeOffset;
    1353                 :         }
    1354                 : 
    1355                 : 
    1356               0 :         if (selLength > 0 && mSelEndOffset == entry->mStrOffset + entry->mLength)
    1357                 :         {
    1358                 :           // The entire entry is contained in the selection. Mark the
    1359                 :           // entry invalid.
    1360                 : 
    1361               0 :           entry->mIsValid = false;
    1362                 :         }
    1363                 :       }
    1364                 :     }
    1365                 : 
    1366               0 :     if (i != mSelStartIndex && i != mSelEndIndex)
    1367                 :     {
    1368                 :       // The entire entry is contained in the selection. Mark the
    1369                 :       // entry invalid.
    1370                 : 
    1371               0 :       entry->mIsValid = false;
    1372                 :     }
    1373                 :   }
    1374                 : 
    1375                 :   // Make sure mIterator always points to something valid!
    1376                 : 
    1377               0 :   AdjustContentIterator();
    1378                 : 
    1379                 :   // Now delete the actual content!
    1380                 : 
    1381               0 :   result = editor->DeleteSelection(nsIEditor::ePrevious);
    1382                 : 
    1383               0 :   if (NS_FAILED(result))
    1384                 :   {
    1385                 :     UNLOCK_DOC(this);
    1386               0 :     return result;
    1387                 :   }
    1388                 : 
    1389                 :   // Now that we've actually deleted the selected content,
    1390                 :   // check to see if our mExtent has changed, if so, then
    1391                 :   // we have to create a new content iterator!
    1392                 : 
    1393               0 :   if (origStartNode && origEndNode)
    1394                 :   {
    1395               0 :     nsCOMPtr<nsIDOMNode> curStartNode, curEndNode;
    1396               0 :     PRInt32 curStartOffset = 0, curEndOffset = 0;
    1397                 : 
    1398                 :     result = GetRangeEndPoints(mExtent,
    1399               0 :                                getter_AddRefs(curStartNode), &curStartOffset,
    1400               0 :                                getter_AddRefs(curEndNode), &curEndOffset);
    1401                 : 
    1402               0 :     if (NS_FAILED(result))
    1403                 :     {
    1404                 :       UNLOCK_DOC(this);
    1405               0 :       return result;
    1406                 :     }
    1407                 : 
    1408               0 :     if (origStartNode != curStartNode || origEndNode != curEndNode)
    1409                 :     {
    1410                 :       // The range has changed, so we need to create a new content
    1411                 :       // iterator based on the new range.
    1412                 : 
    1413               0 :       nsCOMPtr<nsIContent> curContent;
    1414                 : 
    1415               0 :       if (mIteratorStatus != nsTextServicesDocument::eIsDone)
    1416                 :       {
    1417                 :         // The old iterator is still pointing to something valid,
    1418                 :         // so get its current node so we can restore it after we
    1419                 :         // create the new iterator!
    1420                 : 
    1421               0 :         curContent = do_QueryInterface(mIterator->GetCurrentNode());
    1422                 :       }
    1423                 : 
    1424                 :       // Create the new iterator.
    1425                 : 
    1426               0 :       result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
    1427                 : 
    1428               0 :       if (NS_FAILED(result))
    1429                 :       {
    1430                 :         UNLOCK_DOC(this);
    1431               0 :         return result;
    1432                 :       }
    1433                 : 
    1434                 :       // Now make the new iterator point to the content node
    1435                 :       // the old one was pointing at.
    1436                 : 
    1437               0 :       if (curContent)
    1438                 :       {
    1439               0 :         result = mIterator->PositionAt(curContent);
    1440                 : 
    1441               0 :         if (NS_FAILED(result))
    1442               0 :           mIteratorStatus = eIsDone;
    1443                 :         else
    1444               0 :           mIteratorStatus = eValid;
    1445                 :       }
    1446                 :     }
    1447                 :   }
    1448                 : 
    1449               0 :   entry = 0;
    1450                 : 
    1451                 :   // Move the caret to the end of the first valid entry.
    1452                 :   // Start with mSelStartIndex since it may still be valid.
    1453                 : 
    1454               0 :   for (i = mSelStartIndex; !entry && i >= 0; i--)
    1455                 :   {
    1456               0 :     entry = mOffsetTable[i];
    1457                 : 
    1458               0 :     if (!entry->mIsValid)
    1459               0 :       entry = 0;
    1460                 :     else
    1461                 :     {
    1462               0 :       mSelStartIndex  = mSelEndIndex  = i;
    1463               0 :       mSelStartOffset = mSelEndOffset = entry->mStrOffset + entry->mLength;
    1464                 :     }
    1465                 :   }
    1466                 : 
    1467                 :   // If we still don't have a valid entry, move the caret
    1468                 :   // to the next valid entry after the selection:
    1469                 : 
    1470               0 :   for (i = mSelEndIndex; !entry && i < PRInt32(mOffsetTable.Length()); i++)
    1471                 :   {
    1472               0 :     entry = mOffsetTable[i];
    1473                 : 
    1474               0 :     if (!entry->mIsValid)
    1475               0 :       entry = 0;
    1476                 :     else
    1477                 :     {
    1478               0 :       mSelStartIndex = mSelEndIndex = i;
    1479               0 :       mSelStartOffset = mSelEndOffset = entry->mStrOffset;
    1480                 :     }
    1481                 :   }
    1482                 : 
    1483               0 :   if (entry)
    1484               0 :     result = SetSelection(mSelStartOffset, 0);
    1485                 :   else
    1486                 :   {
    1487                 :     // Uuughh we have no valid offset entry to place our
    1488                 :     // caret ... just mark the selection invalid.
    1489                 : 
    1490               0 :     mSelStartIndex  = mSelEndIndex  = -1;
    1491               0 :     mSelStartOffset = mSelEndOffset = -1;
    1492                 :   }
    1493                 : 
    1494                 :   // Now remove any invalid entries from the offset table.
    1495                 : 
    1496               0 :   result = RemoveInvalidOffsetEntries();
    1497                 : 
    1498                 :   //**** KDEBUG ****
    1499                 :   // printf("\n---- After Delete\n");
    1500                 :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1501                 :   // PrintOffsetTable();
    1502                 :   //**** KDEBUG ****
    1503                 : 
    1504                 :   UNLOCK_DOC(this);
    1505                 : 
    1506               0 :   return result;
    1507                 : }
    1508                 : 
    1509                 : NS_IMETHODIMP
    1510               0 : nsTextServicesDocument::InsertText(const nsString *aText)
    1511                 : {
    1512               0 :   nsresult result = NS_OK;
    1513                 : 
    1514               0 :   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
    1515               0 :   NS_ASSERTION(editor, "InsertText called without an editor present!"); 
    1516                 : 
    1517               0 :   if (!editor || !SelectionIsValid())
    1518               0 :     return NS_ERROR_FAILURE;
    1519                 : 
    1520               0 :   NS_ENSURE_TRUE(aText, NS_ERROR_NULL_POINTER);
    1521                 : 
    1522                 :   // If the selection is not collapsed, we need to save
    1523                 :   // off the selection offsets so we can restore the
    1524                 :   // selection and delete the selected content after we've
    1525                 :   // inserted the new text. This is necessary to try and
    1526                 :   // retain as much of the original style of the content
    1527                 :   // being deleted.
    1528                 : 
    1529               0 :   bool collapsedSelection = SelectionIsCollapsed();
    1530               0 :   PRInt32 savedSelOffset = mSelStartOffset;
    1531               0 :   PRInt32 savedSelLength = mSelEndOffset - mSelStartOffset;
    1532                 : 
    1533               0 :   if (!collapsedSelection)
    1534                 :   {
    1535                 :     // Collapse to the start of the current selection
    1536                 :     // for the insert!
    1537                 : 
    1538               0 :     result = SetSelection(mSelStartOffset, 0);
    1539                 : 
    1540               0 :     NS_ENSURE_SUCCESS(result, result);
    1541                 :   }
    1542                 : 
    1543                 : 
    1544                 :   LOCK_DOC(this);
    1545                 : 
    1546               0 :   result = editor->BeginTransaction();
    1547                 : 
    1548               0 :   if (NS_FAILED(result))
    1549                 :   {
    1550                 :     UNLOCK_DOC(this);
    1551               0 :     return result;
    1552                 :   }
    1553                 : 
    1554               0 :   nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(editor, &result));
    1555               0 :   if (textEditor)
    1556               0 :     result = textEditor->InsertText(*aText);
    1557                 : 
    1558               0 :   if (NS_FAILED(result))
    1559                 :   {
    1560               0 :     editor->EndTransaction();
    1561                 :     UNLOCK_DOC(this);
    1562               0 :     return result;
    1563                 :   }
    1564                 : 
    1565                 :   //**** KDEBUG ****
    1566                 :   // printf("\n---- Before Insert\n");
    1567                 :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1568                 :   // PrintOffsetTable();
    1569                 :   //**** KDEBUG ****
    1570                 : 
    1571               0 :   PRInt32 strLength = aText->Length();
    1572                 :   PRUint32 i;
    1573                 : 
    1574               0 :   nsCOMPtr<nsISelection> selection;
    1575                 :   OffsetEntry *itEntry;
    1576               0 :   OffsetEntry *entry = mOffsetTable[mSelStartIndex];
    1577               0 :   void *node         = entry->mNode;
    1578                 : 
    1579               0 :   NS_ASSERTION((entry->mIsValid), "Invalid insertion point!");
    1580                 : 
    1581               0 :   if (entry->mStrOffset == mSelStartOffset)
    1582                 :   {
    1583               0 :     if (entry->mIsInsertedText)
    1584                 :     {
    1585                 :       // If the caret is in an inserted text offset entry,
    1586                 :       // we simply insert the text at the end of the entry.
    1587                 : 
    1588               0 :       entry->mLength += strLength;
    1589                 :     }
    1590                 :     else
    1591                 :     {
    1592                 :       // Insert an inserted text offset entry before the current
    1593                 :       // entry!
    1594                 : 
    1595               0 :       itEntry = new OffsetEntry(entry->mNode, entry->mStrOffset, strLength);
    1596                 : 
    1597               0 :       if (!itEntry)
    1598                 :       {
    1599               0 :         editor->EndTransaction();
    1600                 :         UNLOCK_DOC(this);
    1601               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1602                 :       }
    1603                 : 
    1604               0 :       itEntry->mIsInsertedText = true;
    1605               0 :       itEntry->mNodeOffset = entry->mNodeOffset;
    1606                 : 
    1607               0 :       if (!mOffsetTable.InsertElementAt(mSelStartIndex, itEntry))
    1608                 :       {
    1609               0 :         editor->EndTransaction();
    1610                 :         UNLOCK_DOC(this);
    1611               0 :         return NS_ERROR_FAILURE;
    1612                 :       }
    1613                 :     }
    1614                 :   }
    1615               0 :   else if ((entry->mStrOffset + entry->mLength) == mSelStartOffset)
    1616                 :   {
    1617                 :     // We are inserting text at the end of the current offset entry.
    1618                 :     // Look at the next valid entry in the table. If it's an inserted
    1619                 :     // text entry, add to its length and adjust its node offset. If
    1620                 :     // it isn't, add a new inserted text entry.
    1621                 : 
    1622               0 :     i       = mSelStartIndex + 1;
    1623               0 :     itEntry = 0;
    1624                 : 
    1625               0 :     if (mOffsetTable.Length() > i)
    1626                 :     {
    1627               0 :       itEntry = mOffsetTable[i];
    1628                 : 
    1629               0 :       if (!itEntry)
    1630                 :       {
    1631               0 :         editor->EndTransaction();
    1632                 :         UNLOCK_DOC(this);
    1633               0 :         return NS_ERROR_FAILURE;
    1634                 :       }
    1635                 : 
    1636                 :       // Check if the entry is a match. If it isn't, set
    1637                 :       // iEntry to zero.
    1638                 : 
    1639               0 :       if (!itEntry->mIsInsertedText || itEntry->mStrOffset != mSelStartOffset)
    1640               0 :         itEntry = 0;
    1641                 :     }
    1642                 : 
    1643               0 :     if (!itEntry)
    1644                 :     {
    1645                 :       // We didn't find an inserted text offset entry, so
    1646                 :       // create one.
    1647                 : 
    1648               0 :       itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, 0);
    1649                 : 
    1650               0 :       if (!itEntry)
    1651                 :       {
    1652               0 :         editor->EndTransaction();
    1653                 :         UNLOCK_DOC(this);
    1654               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1655                 :       }
    1656                 : 
    1657               0 :       itEntry->mNodeOffset = entry->mNodeOffset + entry->mLength;
    1658               0 :       itEntry->mIsInsertedText = true;
    1659                 : 
    1660               0 :       if (!mOffsetTable.InsertElementAt(i, itEntry))
    1661                 :       {
    1662               0 :         delete itEntry;
    1663               0 :         return NS_ERROR_FAILURE;
    1664                 :       }
    1665                 :     }
    1666                 : 
    1667                 :     // We have a valid inserted text offset entry. Update its
    1668                 :     // length, adjust the selection indexes, and make sure the
    1669                 :     // caret is properly placed!
    1670                 : 
    1671               0 :     itEntry->mLength += strLength;
    1672                 : 
    1673               0 :     mSelStartIndex = mSelEndIndex = i;
    1674                 :           
    1675               0 :     result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
    1676                 : 
    1677               0 :     if (NS_FAILED(result))
    1678                 :     {
    1679               0 :       editor->EndTransaction();
    1680                 :       UNLOCK_DOC(this);
    1681               0 :       return result;
    1682                 :     }
    1683                 : 
    1684               0 :     result = selection->Collapse(itEntry->mNode, itEntry->mNodeOffset + itEntry->mLength);
    1685                 :         
    1686               0 :     if (NS_FAILED(result))
    1687                 :     {
    1688               0 :       editor->EndTransaction();
    1689                 :       UNLOCK_DOC(this);
    1690               0 :       return result;
    1691                 :     }
    1692                 :   }
    1693               0 :   else if ((entry->mStrOffset + entry->mLength) > mSelStartOffset)
    1694                 :   {
    1695                 :     // We are inserting text into the middle of the current offset entry.
    1696                 :     // split the current entry into two parts, then insert an inserted text
    1697                 :     // entry between them!
    1698                 : 
    1699               0 :     i = entry->mLength - (mSelStartOffset - entry->mStrOffset);
    1700                 : 
    1701               0 :     result = SplitOffsetEntry(mSelStartIndex, i);
    1702                 : 
    1703               0 :     if (NS_FAILED(result))
    1704                 :     {
    1705               0 :       editor->EndTransaction();
    1706                 :       UNLOCK_DOC(this);
    1707               0 :       return result;
    1708                 :     }
    1709                 : 
    1710               0 :     itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, strLength);
    1711                 : 
    1712               0 :     if (!itEntry)
    1713                 :     {
    1714               0 :       editor->EndTransaction();
    1715                 :       UNLOCK_DOC(this);
    1716               0 :       return NS_ERROR_OUT_OF_MEMORY;
    1717                 :     }
    1718                 : 
    1719               0 :     itEntry->mIsInsertedText = true;
    1720               0 :     itEntry->mNodeOffset     = entry->mNodeOffset + entry->mLength;
    1721                 : 
    1722               0 :     if (!mOffsetTable.InsertElementAt(mSelStartIndex + 1, itEntry))
    1723                 :     {
    1724               0 :       editor->EndTransaction();
    1725                 :       UNLOCK_DOC(this);
    1726               0 :       return NS_ERROR_FAILURE;
    1727                 :     }
    1728                 : 
    1729               0 :     mSelEndIndex = ++mSelStartIndex;
    1730                 :   }
    1731                 : 
    1732                 :   // We've just finished inserting an inserted text offset entry.
    1733                 :   // update all entries with the same mNode pointer that follow
    1734                 :   // it in the table!
    1735                 : 
    1736               0 :   for (i = mSelStartIndex + 1; i < mOffsetTable.Length(); i++)
    1737                 :   {
    1738               0 :     entry = mOffsetTable[i];
    1739                 : 
    1740               0 :     if (entry->mNode == node)
    1741                 :     {
    1742               0 :       if (entry->mIsValid)
    1743               0 :         entry->mNodeOffset += strLength;
    1744                 :     }
    1745                 :     else
    1746               0 :       break;
    1747                 :   }
    1748                 : 
    1749                 :   //**** KDEBUG ****
    1750                 :   // printf("\n---- After Insert\n");
    1751                 :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1752                 :   // PrintOffsetTable();
    1753                 :   //**** KDEBUG ****
    1754                 : 
    1755               0 :   if (!collapsedSelection)
    1756                 :   {
    1757               0 :     result = SetSelection(savedSelOffset, savedSelLength);
    1758                 : 
    1759               0 :     if (NS_FAILED(result))
    1760                 :     {
    1761               0 :       editor->EndTransaction();
    1762                 :       UNLOCK_DOC(this);
    1763               0 :       return result;
    1764                 :     }
    1765                 : 
    1766               0 :     result = DeleteSelection();
    1767                 :   
    1768               0 :     if (NS_FAILED(result))
    1769                 :     {
    1770               0 :       editor->EndTransaction();
    1771                 :       UNLOCK_DOC(this);
    1772               0 :       return result;
    1773                 :     }
    1774                 :   }
    1775                 : 
    1776               0 :   result = editor->EndTransaction();
    1777                 : 
    1778                 :   UNLOCK_DOC(this);
    1779                 : 
    1780               0 :   return result;
    1781                 : }
    1782                 : 
    1783                 : NS_IMETHODIMP
    1784               0 : nsTextServicesDocument::DidInsertNode(nsIDOMNode *aNode,
    1785                 :                                       nsIDOMNode *aParent,
    1786                 :                                       PRInt32     aPosition,
    1787                 :                                       nsresult    aResult)
    1788                 : {
    1789               0 :   return NS_OK;
    1790                 : }
    1791                 : 
    1792                 : NS_IMETHODIMP
    1793               0 : nsTextServicesDocument::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
    1794                 : {
    1795               0 :   NS_ENSURE_SUCCESS(aResult, NS_OK);
    1796                 : 
    1797               0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
    1798                 : 
    1799                 :   //**** KDEBUG ****
    1800                 :   // printf("** DeleteNode: 0x%.8x\n", aChild);
    1801                 :   // fflush(stdout);
    1802                 :   //**** KDEBUG ****
    1803                 : 
    1804                 :   LOCK_DOC(this);
    1805                 : 
    1806               0 :   PRInt32 nodeIndex = 0;
    1807               0 :   bool hasEntry = false;
    1808                 :   OffsetEntry *entry;
    1809                 : 
    1810               0 :   nsresult result = NodeHasOffsetEntry(&mOffsetTable, aChild, &hasEntry, &nodeIndex);
    1811                 : 
    1812               0 :   if (NS_FAILED(result))
    1813                 :   {
    1814                 :     UNLOCK_DOC(this);
    1815               0 :     return result;
    1816                 :   }
    1817                 : 
    1818               0 :   if (!hasEntry)
    1819                 :   {
    1820                 :     // It's okay if the node isn't in the offset table, the
    1821                 :     // editor could be cleaning house.
    1822                 : 
    1823                 :     UNLOCK_DOC(this);
    1824               0 :     return NS_OK;
    1825                 :   }
    1826                 : 
    1827               0 :   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mIterator->GetCurrentNode());
    1828                 : 
    1829               0 :   if (node && node == aChild &&
    1830                 :       mIteratorStatus != nsTextServicesDocument::eIsDone)
    1831                 :   {
    1832                 :     // XXX: This should never really happen because
    1833                 :     // AdjustContentIterator() should have been called prior
    1834                 :     // to the delete to try and position the iterator on the
    1835                 :     // next valid text node in the offset table, and if there
    1836                 :     // wasn't a next, it would've set mIteratorStatus to eIsDone.
    1837                 : 
    1838               0 :     NS_ERROR("DeleteNode called for current iterator node."); 
    1839                 :   }
    1840                 : 
    1841               0 :   PRInt32 tcount = mOffsetTable.Length();
    1842                 : 
    1843               0 :   while (nodeIndex < tcount)
    1844                 :   {
    1845               0 :     entry = mOffsetTable[nodeIndex];
    1846                 : 
    1847               0 :     if (!entry)
    1848                 :     {
    1849                 :       UNLOCK_DOC(this);
    1850               0 :       return NS_ERROR_FAILURE;
    1851                 :     }
    1852                 : 
    1853               0 :     if (entry->mNode == aChild)
    1854                 :     {
    1855               0 :       entry->mIsValid = false;
    1856                 :     }
    1857                 : 
    1858               0 :     nodeIndex++;
    1859                 :   }
    1860                 : 
    1861                 :   UNLOCK_DOC(this);
    1862                 : 
    1863               0 :   return NS_OK;
    1864                 : }
    1865                 : 
    1866                 : NS_IMETHODIMP
    1867               0 : nsTextServicesDocument::DidSplitNode(nsIDOMNode *aExistingRightNode,
    1868                 :                                      PRInt32     aOffset,
    1869                 :                                      nsIDOMNode *aNewLeftNode,
    1870                 :                                      nsresult    aResult)
    1871                 : {
    1872                 :   //**** KDEBUG ****
    1873                 :   // printf("** SplitNode: 0x%.8x  %d  0x%.8x\n", aExistingRightNode, aOffset, aNewLeftNode);
    1874                 :   // fflush(stdout);
    1875                 :   //**** KDEBUG ****
    1876               0 :   return NS_OK;
    1877                 : }
    1878                 : 
    1879                 : NS_IMETHODIMP
    1880               0 : nsTextServicesDocument::DidJoinNodes(nsIDOMNode  *aLeftNode,
    1881                 :                                      nsIDOMNode  *aRightNode,
    1882                 :                                      nsIDOMNode  *aParent,
    1883                 :                                      nsresult     aResult)
    1884                 : {
    1885               0 :   NS_ENSURE_SUCCESS(aResult, NS_OK);
    1886                 : 
    1887                 :   PRInt32 i;
    1888                 :   PRUint16 type;
    1889                 :   nsresult result;
    1890                 : 
    1891                 :   //**** KDEBUG ****
    1892                 :   // printf("** JoinNodes: 0x%.8x  0x%.8x  0x%.8x\n", aLeftNode, aRightNode, aParent);
    1893                 :   // fflush(stdout);
    1894                 :   //**** KDEBUG ****
    1895                 : 
    1896                 :   // Make sure that both nodes are text nodes!
    1897                 : 
    1898               0 :   result = aLeftNode->GetNodeType(&type);
    1899                 : 
    1900               0 :   NS_ENSURE_SUCCESS(result, false);
    1901                 : 
    1902               0 :   if (nsIDOMNode::TEXT_NODE != type)
    1903                 :   {
    1904               0 :     NS_ERROR("JoinNode called with a non-text left node!");
    1905               0 :     return NS_ERROR_FAILURE;
    1906                 :   }
    1907                 : 
    1908               0 :   result = aRightNode->GetNodeType(&type);
    1909                 : 
    1910               0 :   NS_ENSURE_SUCCESS(result, false);
    1911                 : 
    1912               0 :   if (nsIDOMNode::TEXT_NODE != type)
    1913                 :   {
    1914               0 :     NS_ERROR("JoinNode called with a non-text right node!");
    1915               0 :     return NS_ERROR_FAILURE;
    1916                 :   }
    1917                 : 
    1918                 :   // Note: The editor merges the contents of the left node into the
    1919                 :   //       contents of the right.
    1920                 : 
    1921               0 :   PRInt32 leftIndex = 0;
    1922               0 :   PRInt32 rightIndex = 0;
    1923               0 :   bool leftHasEntry = false;
    1924               0 :   bool rightHasEntry = false;
    1925                 : 
    1926               0 :   result = NodeHasOffsetEntry(&mOffsetTable, aLeftNode, &leftHasEntry, &leftIndex);
    1927                 : 
    1928               0 :   NS_ENSURE_SUCCESS(result, result);
    1929                 : 
    1930               0 :   if (!leftHasEntry)
    1931                 :   {
    1932                 :     // It's okay if the node isn't in the offset table, the
    1933                 :     // editor could be cleaning house.
    1934               0 :     return NS_OK;
    1935                 :   }
    1936                 : 
    1937               0 :   result = NodeHasOffsetEntry(&mOffsetTable, aRightNode, &rightHasEntry, &rightIndex);
    1938                 : 
    1939               0 :   NS_ENSURE_SUCCESS(result, result);
    1940                 : 
    1941               0 :   if (!rightHasEntry)
    1942                 :   {
    1943                 :     // It's okay if the node isn't in the offset table, the
    1944                 :     // editor could be cleaning house.
    1945               0 :     return NS_OK;
    1946                 :   }
    1947                 : 
    1948               0 :   NS_ASSERTION(leftIndex < rightIndex, "Indexes out of order.");
    1949                 : 
    1950               0 :   if (leftIndex > rightIndex)
    1951                 :   {
    1952                 :     // Don't know how to handle this situation.
    1953               0 :     return NS_ERROR_FAILURE;
    1954                 :   }
    1955                 : 
    1956                 :   LOCK_DOC(this);
    1957                 : 
    1958               0 :   OffsetEntry *entry = mOffsetTable[rightIndex];
    1959               0 :   NS_ASSERTION(entry->mNodeOffset == 0, "Unexpected offset value for rightIndex.");
    1960                 : 
    1961                 :   // Run through the table and change all entries referring to
    1962                 :   // the left node so that they now refer to the right node:
    1963                 : 
    1964               0 :   nsAutoString str;
    1965               0 :   result = aLeftNode->GetNodeValue(str);
    1966               0 :   PRInt32 nodeLength = str.Length();
    1967                 : 
    1968               0 :   for (i = leftIndex; i < rightIndex; i++)
    1969                 :   {
    1970               0 :     entry = mOffsetTable[i];
    1971                 : 
    1972               0 :     if (entry->mNode == aLeftNode)
    1973                 :     {
    1974               0 :       if (entry->mIsValid)
    1975               0 :         entry->mNode = aRightNode;
    1976                 :     }
    1977                 :     else
    1978               0 :       break;
    1979                 :   }
    1980                 : 
    1981                 :   // Run through the table and adjust the node offsets
    1982                 :   // for all entries referring to the right node.
    1983                 : 
    1984               0 :   for (i = rightIndex; i < PRInt32(mOffsetTable.Length()); i++)
    1985                 :   {
    1986               0 :     entry = mOffsetTable[i];
    1987                 : 
    1988               0 :     if (entry->mNode == aRightNode)
    1989                 :     {
    1990               0 :       if (entry->mIsValid)
    1991               0 :         entry->mNodeOffset += nodeLength;
    1992                 :     }
    1993                 :     else
    1994               0 :       break;
    1995                 :   }
    1996                 : 
    1997                 :   // Now check to see if the iterator is pointing to the
    1998                 :   // left node. If it is, make it point to the right node!
    1999                 : 
    2000               0 :   nsCOMPtr<nsIContent> leftContent = do_QueryInterface(aLeftNode);
    2001               0 :   nsCOMPtr<nsIContent> rightContent = do_QueryInterface(aRightNode);
    2002                 : 
    2003               0 :   if (!leftContent || !rightContent)
    2004                 :   {
    2005                 :     UNLOCK_DOC(this);
    2006               0 :     return NS_ERROR_FAILURE;
    2007                 :   }
    2008                 : 
    2009               0 :   if (mIterator->GetCurrentNode() == leftContent)
    2010               0 :     result = mIterator->PositionAt(rightContent);
    2011                 : 
    2012                 :   UNLOCK_DOC(this);
    2013                 : 
    2014               0 :   return NS_OK;
    2015                 : }
    2016                 : 
    2017                 : nsresult
    2018               0 : nsTextServicesDocument::CreateContentIterator(nsIDOMRange *aRange, nsIContentIterator **aIterator)
    2019                 : {
    2020                 :   nsresult result;
    2021                 : 
    2022               0 :   NS_ENSURE_TRUE(aRange && aIterator, NS_ERROR_NULL_POINTER);
    2023                 : 
    2024               0 :   *aIterator = 0;
    2025                 : 
    2026                 :   // Create a nsFilteredContentIterator
    2027                 :   // This class wraps the ContentIterator in order to give itself a chance 
    2028                 :   // to filter out certain content nodes
    2029               0 :   nsFilteredContentIterator* filter = new nsFilteredContentIterator(mTxtSvcFilter);
    2030               0 :   *aIterator = static_cast<nsIContentIterator *>(filter);
    2031               0 :   if (*aIterator) {
    2032               0 :     NS_IF_ADDREF(*aIterator);
    2033               0 :     result = filter ? NS_OK : NS_ERROR_FAILURE;
    2034                 :   } else {
    2035               0 :     delete filter;
    2036               0 :     result = NS_ERROR_FAILURE;
    2037                 :   }
    2038               0 :   NS_ENSURE_SUCCESS(result, result);
    2039                 : 
    2040               0 :   NS_ENSURE_TRUE(*aIterator, NS_ERROR_NULL_POINTER);
    2041                 : 
    2042               0 :   result = (*aIterator)->Init(aRange);
    2043                 : 
    2044               0 :   if (NS_FAILED(result))
    2045                 :   {
    2046               0 :     NS_RELEASE((*aIterator));
    2047               0 :     *aIterator = 0;
    2048               0 :     return result;
    2049                 :   }
    2050                 : 
    2051               0 :   return NS_OK;
    2052                 : }
    2053                 : 
    2054                 : nsresult
    2055               0 : nsTextServicesDocument::GetDocumentContentRootNode(nsIDOMNode **aNode)
    2056                 : {
    2057                 :   nsresult result;
    2058                 : 
    2059               0 :   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
    2060                 : 
    2061               0 :   *aNode = 0;
    2062                 : 
    2063               0 :   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
    2064                 : 
    2065               0 :   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDOMDocument);
    2066                 : 
    2067               0 :   if (htmlDoc)
    2068                 :   {
    2069                 :     // For HTML documents, the content root node is the body.
    2070                 : 
    2071               0 :     nsCOMPtr<nsIDOMHTMLElement> bodyElement;
    2072                 : 
    2073               0 :     result = htmlDoc->GetBody(getter_AddRefs(bodyElement));
    2074                 : 
    2075               0 :     NS_ENSURE_SUCCESS(result, result);
    2076                 : 
    2077               0 :     NS_ENSURE_TRUE(bodyElement, NS_ERROR_FAILURE);
    2078                 : 
    2079               0 :     result = bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
    2080                 :   }
    2081                 :   else
    2082                 :   {
    2083                 :     // For non-HTML documents, the content root node will be the document element.
    2084                 : 
    2085               0 :     nsCOMPtr<nsIDOMElement> docElement;
    2086                 : 
    2087               0 :     result = mDOMDocument->GetDocumentElement(getter_AddRefs(docElement));
    2088                 : 
    2089               0 :     NS_ENSURE_SUCCESS(result, result);
    2090                 : 
    2091               0 :     NS_ENSURE_TRUE(docElement, NS_ERROR_FAILURE);
    2092                 : 
    2093               0 :     result = docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
    2094                 :   }
    2095                 : 
    2096               0 :   return result;
    2097                 : }
    2098                 : 
    2099                 : nsresult
    2100               0 : nsTextServicesDocument::CreateDocumentContentRange(nsIDOMRange **aRange)
    2101                 : {
    2102               0 :   *aRange = NULL;
    2103                 : 
    2104               0 :   nsCOMPtr<nsIDOMNode> node;
    2105               0 :   nsresult rv = GetDocumentContentRootNode(getter_AddRefs(node));
    2106               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2107               0 :   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
    2108                 : 
    2109               0 :   nsRefPtr<nsRange> range = new nsRange();
    2110                 : 
    2111               0 :   rv = range->SelectNodeContents(node);
    2112               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2113                 : 
    2114               0 :   range.forget(aRange);
    2115               0 :   return NS_OK;
    2116                 : }
    2117                 : 
    2118                 : nsresult
    2119               0 : nsTextServicesDocument::CreateDocumentContentRootToNodeOffsetRange(nsIDOMNode *aParent, PRInt32 aOffset, bool aToStart, nsIDOMRange **aRange)
    2120                 : {
    2121               0 :   NS_ENSURE_TRUE(aParent && aRange, NS_ERROR_NULL_POINTER);
    2122                 : 
    2123               0 :   *aRange = 0;
    2124                 : 
    2125               0 :   NS_ASSERTION(aOffset >= 0, "Invalid offset!");
    2126                 : 
    2127               0 :   if (aOffset < 0)
    2128               0 :     return NS_ERROR_FAILURE;
    2129                 : 
    2130               0 :   nsCOMPtr<nsIDOMNode> bodyNode; 
    2131               0 :   nsresult rv = GetDocumentContentRootNode(getter_AddRefs(bodyNode));
    2132               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2133               0 :   NS_ENSURE_TRUE(bodyNode, NS_ERROR_NULL_POINTER);
    2134                 : 
    2135               0 :   nsCOMPtr<nsIDOMNode> startNode;
    2136               0 :   nsCOMPtr<nsIDOMNode> endNode;
    2137                 :   PRInt32 startOffset, endOffset;
    2138                 : 
    2139               0 :   if (aToStart) {
    2140                 :     // The range should begin at the start of the document
    2141                 :     // and extend up until (aParent, aOffset).
    2142                 : 
    2143               0 :     startNode   = bodyNode;
    2144               0 :     startOffset = 0;
    2145               0 :     endNode     = aParent;
    2146               0 :     endOffset   = aOffset;
    2147                 :   } else {
    2148                 :     // The range should begin at (aParent, aOffset) and
    2149                 :     // extend to the end of the document.
    2150                 : 
    2151               0 :     nsCOMPtr<nsIDOMNodeList> nodeList;
    2152                 :     PRUint32 nodeListLength;
    2153                 : 
    2154               0 :     startNode   = aParent;
    2155               0 :     startOffset = aOffset;
    2156               0 :     endNode     = bodyNode;
    2157               0 :     endOffset   = 0;
    2158                 : 
    2159               0 :     rv = bodyNode->GetChildNodes(getter_AddRefs(nodeList));
    2160               0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    2161                 : 
    2162               0 :     if (nodeList) {
    2163               0 :       rv = nodeList->GetLength(&nodeListLength);
    2164               0 :       NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    2165                 : 
    2166               0 :       endOffset = (PRInt32)nodeListLength;
    2167                 :     }
    2168                 :   }
    2169                 : 
    2170                 :   return nsRange::CreateRange(startNode, startOffset, endNode, endOffset,
    2171               0 :                               aRange);
    2172                 : }
    2173                 : 
    2174                 : nsresult
    2175               0 : nsTextServicesDocument::CreateDocumentContentIterator(nsIContentIterator **aIterator)
    2176                 : {
    2177                 :   nsresult result;
    2178                 : 
    2179               0 :   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
    2180                 : 
    2181               0 :   nsCOMPtr<nsIDOMRange> range;
    2182                 : 
    2183               0 :   result = CreateDocumentContentRange(getter_AddRefs(range));
    2184                 : 
    2185               0 :   NS_ENSURE_SUCCESS(result, result);
    2186                 : 
    2187               0 :   result = CreateContentIterator(range, aIterator);
    2188                 : 
    2189               0 :   return result;
    2190                 : }
    2191                 : 
    2192                 : nsresult
    2193               0 : nsTextServicesDocument::AdjustContentIterator()
    2194                 : {
    2195               0 :   nsresult result = NS_OK;
    2196                 : 
    2197               0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
    2198                 : 
    2199               0 :   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mIterator->GetCurrentNode()));
    2200                 : 
    2201               0 :   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
    2202                 : 
    2203               0 :   nsIDOMNode *nodePtr = node.get();
    2204               0 :   PRInt32 tcount      = mOffsetTable.Length();
    2205                 : 
    2206               0 :   nsIDOMNode *prevValidNode = 0;
    2207               0 :   nsIDOMNode *nextValidNode = 0;
    2208               0 :   bool foundEntry = false;
    2209                 :   OffsetEntry *entry;
    2210                 : 
    2211               0 :   for (PRInt32 i = 0; i < tcount && !nextValidNode; i++)
    2212                 :   {
    2213               0 :     entry = mOffsetTable[i];
    2214                 : 
    2215               0 :     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    2216                 : 
    2217               0 :     if (entry->mNode == nodePtr)
    2218                 :     {
    2219               0 :       if (entry->mIsValid)
    2220                 :       {
    2221                 :         // The iterator is still pointing to something valid!
    2222                 :         // Do nothing!
    2223                 : 
    2224               0 :         return NS_OK;
    2225                 :       }
    2226                 :       else
    2227                 :       {
    2228                 :         // We found an invalid entry that points to
    2229                 :         // the current iterator node. Stop looking for
    2230                 :         // a previous valid node!
    2231                 : 
    2232               0 :         foundEntry = true;
    2233                 :       }
    2234                 :     }
    2235                 : 
    2236               0 :     if (entry->mIsValid)
    2237                 :     {
    2238               0 :       if (!foundEntry)
    2239               0 :         prevValidNode = entry->mNode;
    2240                 :       else
    2241               0 :         nextValidNode = entry->mNode;
    2242                 :     }
    2243                 :   }
    2244                 : 
    2245               0 :   nsCOMPtr<nsIContent> content;
    2246                 : 
    2247               0 :   if (prevValidNode)
    2248               0 :     content = do_QueryInterface(prevValidNode);
    2249               0 :   else if (nextValidNode)
    2250               0 :     content = do_QueryInterface(nextValidNode);
    2251                 : 
    2252               0 :   if (content)
    2253                 :   {
    2254               0 :     result = mIterator->PositionAt(content);
    2255                 : 
    2256               0 :     if (NS_FAILED(result))
    2257               0 :       mIteratorStatus = eIsDone;
    2258                 :     else
    2259               0 :       mIteratorStatus = eValid;
    2260                 : 
    2261               0 :     return result;
    2262                 :   }
    2263                 : 
    2264                 :   // If we get here, there aren't any valid entries
    2265                 :   // in the offset table! Try to position the iterator
    2266                 :   // on the next text block first, then previous if
    2267                 :   // one doesn't exist!
    2268                 : 
    2269               0 :   if (mNextTextBlock)
    2270                 :   {
    2271               0 :     result = mIterator->PositionAt(mNextTextBlock);
    2272                 : 
    2273               0 :     if (NS_FAILED(result))
    2274                 :     {
    2275               0 :       mIteratorStatus = eIsDone;
    2276               0 :       return result;
    2277                 :     }
    2278                 : 
    2279               0 :     mIteratorStatus = eNext;
    2280                 :   }
    2281               0 :   else if (mPrevTextBlock)
    2282                 :   {
    2283               0 :     result = mIterator->PositionAt(mPrevTextBlock);
    2284                 : 
    2285               0 :     if (NS_FAILED(result))
    2286                 :     {
    2287               0 :       mIteratorStatus = eIsDone;
    2288               0 :       return result;
    2289                 :     }
    2290                 : 
    2291               0 :     mIteratorStatus = ePrev;
    2292                 :   }
    2293                 :   else
    2294               0 :     mIteratorStatus = eIsDone;
    2295                 : 
    2296               0 :   return NS_OK;
    2297                 : }
    2298                 : 
    2299                 : bool
    2300               0 : nsTextServicesDocument::DidSkip(nsIContentIterator* aFilteredIter)
    2301                 : {
    2302                 :   // We can assume here that the Iterator is a nsFilteredContentIterator because
    2303                 :   // all the iterator are created in CreateContentIterator which create a 
    2304                 :   // nsFilteredContentIterator
    2305                 :   // So if the iterator bailed on one of the "filtered" content nodes then we 
    2306                 :   // consider that to be a block and bail with true
    2307               0 :   if (aFilteredIter) {
    2308               0 :     nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
    2309               0 :     if (filter && filter->DidSkip()) {
    2310               0 :       return true;
    2311                 :     }
    2312                 :   }
    2313               0 :   return false;
    2314                 : }
    2315                 : 
    2316                 : void
    2317               0 : nsTextServicesDocument::ClearDidSkip(nsIContentIterator* aFilteredIter)
    2318                 : {
    2319                 :   // Clear filter's skip flag
    2320               0 :   if (aFilteredIter) {
    2321               0 :     nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
    2322               0 :     filter->ClearDidSkip();
    2323                 :   }
    2324               0 : }
    2325                 : 
    2326                 : bool
    2327               0 : nsTextServicesDocument::IsBlockNode(nsIContent *aContent)
    2328                 : {
    2329               0 :   if (!aContent) {
    2330               0 :     NS_ERROR("How did a null pointer get passed to IsBlockNode?");
    2331               0 :     return false;
    2332                 :   }
    2333                 : 
    2334               0 :   nsIAtom *atom = aContent->Tag();
    2335                 : 
    2336                 :   return (sAAtom       != atom &&
    2337                 :           sAddressAtom != atom &&
    2338                 :           sBigAtom     != atom &&
    2339                 :           sBlinkAtom   != atom &&
    2340                 :           sBAtom       != atom &&
    2341                 :           sCiteAtom    != atom &&
    2342                 :           sCodeAtom    != atom &&
    2343                 :           sDfnAtom     != atom &&
    2344                 :           sEmAtom      != atom &&
    2345                 :           sFontAtom    != atom &&
    2346                 :           sIAtom       != atom &&
    2347                 :           sKbdAtom     != atom &&
    2348                 :           sKeygenAtom  != atom &&
    2349                 :           sNobrAtom    != atom &&
    2350                 :           sSAtom       != atom &&
    2351                 :           sSampAtom    != atom &&
    2352                 :           sSmallAtom   != atom &&
    2353                 :           sSpacerAtom  != atom &&
    2354                 :           sSpanAtom    != atom &&
    2355                 :           sStrikeAtom  != atom &&
    2356                 :           sStrongAtom  != atom &&
    2357                 :           sSubAtom     != atom &&
    2358                 :           sSupAtom     != atom &&
    2359                 :           sTtAtom      != atom &&
    2360                 :           sUAtom       != atom &&
    2361                 :           sVarAtom     != atom &&
    2362               0 :           sWbrAtom     != atom);
    2363                 : }
    2364                 : 
    2365                 : bool
    2366               0 : nsTextServicesDocument::HasSameBlockNodeParent(nsIContent *aContent1, nsIContent *aContent2)
    2367                 : {
    2368               0 :   nsIContent* p1 = aContent1->GetParent();
    2369               0 :   nsIContent* p2 = aContent2->GetParent();
    2370                 : 
    2371                 :   // Quick test:
    2372                 : 
    2373               0 :   if (p1 == p2)
    2374               0 :     return true;
    2375                 : 
    2376                 :   // Walk up the parent hierarchy looking for closest block boundary node:
    2377                 : 
    2378               0 :   while (p1 && !IsBlockNode(p1))
    2379                 :   {
    2380               0 :     p1 = p1->GetParent();
    2381                 :   }
    2382                 : 
    2383               0 :   while (p2 && !IsBlockNode(p2))
    2384                 :   {
    2385               0 :     p2 = p2->GetParent();
    2386                 :   }
    2387                 : 
    2388               0 :   return p1 == p2;
    2389                 : }
    2390                 : 
    2391                 : bool
    2392               0 : nsTextServicesDocument::IsTextNode(nsIContent *aContent)
    2393                 : {
    2394               0 :   NS_ENSURE_TRUE(aContent, false);
    2395               0 :   return nsIDOMNode::TEXT_NODE == aContent->NodeType();
    2396                 : }
    2397                 : 
    2398                 : bool
    2399               0 : nsTextServicesDocument::IsTextNode(nsIDOMNode *aNode)
    2400                 : {
    2401               0 :   NS_ENSURE_TRUE(aNode, false);
    2402                 : 
    2403               0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
    2404               0 :   return IsTextNode(content);
    2405                 : }
    2406                 : 
    2407                 : nsresult
    2408               0 : nsTextServicesDocument::SetSelectionInternal(PRInt32 aOffset, PRInt32 aLength, bool aDoUpdate)
    2409                 : {
    2410               0 :   nsresult result = NS_OK;
    2411                 : 
    2412               0 :   NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
    2413                 : 
    2414               0 :   nsIDOMNode *sNode = 0, *eNode = 0;
    2415               0 :   PRInt32 i, sOffset = 0, eOffset = 0;
    2416                 :   OffsetEntry *entry;
    2417                 : 
    2418                 :   // Find start of selection in node offset terms:
    2419                 : 
    2420               0 :   for (i = 0; !sNode && i < PRInt32(mOffsetTable.Length()); i++)
    2421                 :   {
    2422               0 :     entry = mOffsetTable[i];
    2423               0 :     if (entry->mIsValid)
    2424                 :     {
    2425               0 :       if (entry->mIsInsertedText)
    2426                 :       {
    2427                 :         // Caret can only be placed at the end of an
    2428                 :         // inserted text offset entry, if the offsets
    2429                 :         // match exactly!
    2430                 : 
    2431               0 :         if (entry->mStrOffset == aOffset)
    2432                 :         {
    2433               0 :           sNode   = entry->mNode;
    2434               0 :           sOffset = entry->mNodeOffset + entry->mLength;
    2435                 :         }
    2436                 :       }
    2437               0 :       else if (aOffset >= entry->mStrOffset)
    2438                 :       {
    2439               0 :         bool foundEntry = false;
    2440               0 :         PRInt32 strEndOffset = entry->mStrOffset + entry->mLength;
    2441                 : 
    2442               0 :         if (aOffset < strEndOffset)
    2443               0 :           foundEntry = true;
    2444               0 :         else if (aOffset == strEndOffset)
    2445                 :         {
    2446                 :           // Peek after this entry to see if we have any
    2447                 :           // inserted text entries belonging to the same
    2448                 :           // entry->mNode. If so, we have to place the selection
    2449                 :           // after it!
    2450                 : 
    2451               0 :           if ((i+1) < PRInt32(mOffsetTable.Length()))
    2452                 :           {
    2453               0 :             OffsetEntry *nextEntry = mOffsetTable[i+1];
    2454                 : 
    2455               0 :             if (!nextEntry->mIsValid || nextEntry->mStrOffset != aOffset)
    2456                 :             {
    2457                 :               // Next offset entry isn't an exact match, so we'll
    2458                 :               // just use the current entry.
    2459               0 :               foundEntry = true;
    2460                 :             }
    2461                 :           }
    2462                 :         }
    2463                 : 
    2464               0 :         if (foundEntry)
    2465                 :         {
    2466               0 :           sNode   = entry->mNode;
    2467               0 :           sOffset = entry->mNodeOffset + aOffset - entry->mStrOffset;
    2468                 :         }
    2469                 :       }
    2470                 : 
    2471               0 :       if (sNode)
    2472                 :       {
    2473               0 :         mSelStartIndex  = i;
    2474               0 :         mSelStartOffset = aOffset;
    2475                 :       }
    2476                 :     }
    2477                 :   }
    2478                 : 
    2479               0 :   NS_ENSURE_TRUE(sNode, NS_ERROR_FAILURE);
    2480                 : 
    2481                 :   // XXX: If we ever get a SetSelection() method in nsIEditor, we should
    2482                 :   //      use it.
    2483                 : 
    2484               0 :   nsCOMPtr<nsISelection> selection;
    2485                 : 
    2486               0 :   if (aDoUpdate)
    2487                 :   {
    2488               0 :     result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
    2489                 : 
    2490               0 :     NS_ENSURE_SUCCESS(result, result);
    2491                 : 
    2492               0 :     result = selection->Collapse(sNode, sOffset);
    2493                 : 
    2494               0 :     NS_ENSURE_SUCCESS(result, result);
    2495                 :    }
    2496                 : 
    2497               0 :   if (aLength <= 0)
    2498                 :   {
    2499                 :     // We have a collapsed selection. (Caret)
    2500                 : 
    2501               0 :     mSelEndIndex  = mSelStartIndex;
    2502               0 :     mSelEndOffset = mSelStartOffset;
    2503                 : 
    2504                 :    //**** KDEBUG ****
    2505                 :    // printf("\n* Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    2506                 :    //**** KDEBUG ****
    2507                 : 
    2508               0 :     return NS_OK;
    2509                 :   }
    2510                 : 
    2511                 :   // Find the end of the selection in node offset terms:
    2512                 : 
    2513               0 :   PRInt32 endOffset = aOffset + aLength;
    2514                 : 
    2515               0 :   for (i = mOffsetTable.Length() - 1; !eNode && i >= 0; i--)
    2516                 :   {
    2517               0 :     entry = mOffsetTable[i];
    2518                 :     
    2519               0 :     if (entry->mIsValid)
    2520                 :     {
    2521               0 :       if (entry->mIsInsertedText)
    2522                 :       {
    2523               0 :         if (entry->mStrOffset == eOffset)
    2524                 :         {
    2525                 :           // If the selection ends on an inserted text offset entry,
    2526                 :           // the selection includes the entire entry!
    2527                 : 
    2528               0 :           eNode   = entry->mNode;
    2529               0 :           eOffset = entry->mNodeOffset + entry->mLength;
    2530                 :         }
    2531                 :       }
    2532               0 :       else if (endOffset >= entry->mStrOffset && endOffset <= entry->mStrOffset + entry->mLength)
    2533                 :       {
    2534               0 :         eNode   = entry->mNode;
    2535               0 :         eOffset = entry->mNodeOffset + endOffset - entry->mStrOffset;
    2536                 :       }
    2537                 : 
    2538               0 :       if (eNode)
    2539                 :       {
    2540               0 :         mSelEndIndex  = i;
    2541               0 :         mSelEndOffset = endOffset;
    2542                 :       }
    2543                 :     }
    2544                 :   }
    2545                 : 
    2546               0 :   if (aDoUpdate && eNode)
    2547                 :   {
    2548               0 :     result = selection->Extend(eNode, eOffset);
    2549                 : 
    2550               0 :     NS_ENSURE_SUCCESS(result, result);
    2551                 :   }
    2552                 : 
    2553                 :   //**** KDEBUG ****
    2554                 :   // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    2555                 :   //**** KDEBUG ****
    2556                 : 
    2557               0 :   return result;
    2558                 : }
    2559                 : 
    2560                 : nsresult
    2561               0 : nsTextServicesDocument::GetSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, PRInt32 *aSelOffset, PRInt32 *aSelLength)
    2562                 : {
    2563                 :   nsresult result;
    2564                 : 
    2565               0 :   NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
    2566                 : 
    2567               0 :   *aSelStatus = nsITextServicesDocument::eBlockNotFound;
    2568               0 :   *aSelOffset = -1;
    2569               0 :   *aSelLength = -1;
    2570                 : 
    2571               0 :   NS_ENSURE_TRUE(mDOMDocument && mSelCon, NS_ERROR_FAILURE);
    2572                 : 
    2573               0 :   if (mIteratorStatus == nsTextServicesDocument::eIsDone)
    2574               0 :     return NS_OK;
    2575                 : 
    2576               0 :   nsCOMPtr<nsISelection> selection;
    2577                 :   bool isCollapsed;
    2578                 : 
    2579               0 :   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
    2580                 : 
    2581               0 :   NS_ENSURE_SUCCESS(result, result);
    2582                 : 
    2583               0 :   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
    2584                 : 
    2585               0 :   result = selection->GetIsCollapsed(&isCollapsed);
    2586                 : 
    2587               0 :   NS_ENSURE_SUCCESS(result, result);
    2588                 : 
    2589                 :   // XXX: If we expose this method publicly, we need to
    2590                 :   //      add LOCK_DOC/UNLOCK_DOC calls!
    2591                 : 
    2592                 :   // LOCK_DOC(this);
    2593                 : 
    2594               0 :   if (isCollapsed)
    2595               0 :     result = GetCollapsedSelection(aSelStatus, aSelOffset, aSelLength);
    2596                 :   else
    2597               0 :     result = GetUncollapsedSelection(aSelStatus, aSelOffset, aSelLength);
    2598                 : 
    2599                 :   // UNLOCK_DOC(this);
    2600                 : 
    2601               0 :   return result;
    2602                 : }
    2603                 : 
    2604                 : nsresult
    2605               0 : nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, PRInt32 *aSelOffset, PRInt32 *aSelLength)
    2606                 : {
    2607                 :   nsresult result;
    2608               0 :   nsCOMPtr<nsISelection> selection;
    2609                 : 
    2610               0 :   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
    2611                 : 
    2612               0 :   NS_ENSURE_SUCCESS(result, result);
    2613                 : 
    2614               0 :   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
    2615                 : 
    2616                 :   // The calling function should have done the GetIsCollapsed()
    2617                 :   // check already. Just assume it's collapsed!
    2618                 : 
    2619               0 :   nsCOMPtr<nsIDOMRange> range;
    2620                 :   OffsetEntry *entry;
    2621               0 :   nsCOMPtr<nsIDOMNode> parent;
    2622                 :   PRInt32 offset, tableCount, i;
    2623                 :   PRInt32 e1s1, e2s1;
    2624                 : 
    2625                 :   OffsetEntry *eStart, *eEnd;
    2626                 :   PRInt32 eStartOffset, eEndOffset;
    2627                 : 
    2628                 : 
    2629               0 :   *aSelStatus = nsITextServicesDocument::eBlockOutside;
    2630               0 :   *aSelOffset = *aSelLength = -1;
    2631                 : 
    2632               0 :   tableCount = mOffsetTable.Length();
    2633                 : 
    2634               0 :   if (tableCount == 0)
    2635               0 :     return NS_OK;
    2636                 : 
    2637                 :   // Get pointers to the first and last offset entries
    2638                 :   // in the table.
    2639                 : 
    2640               0 :   eStart = mOffsetTable[0];
    2641                 : 
    2642               0 :   if (tableCount > 1)
    2643               0 :     eEnd = mOffsetTable[tableCount - 1];
    2644                 :   else
    2645               0 :     eEnd = eStart;
    2646                 : 
    2647               0 :   eStartOffset = eStart->mNodeOffset;
    2648               0 :   eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
    2649                 : 
    2650               0 :   result = selection->GetRangeAt(0, getter_AddRefs(range));
    2651                 : 
    2652               0 :   NS_ENSURE_SUCCESS(result, result);
    2653                 : 
    2654               0 :   result = range->GetStartContainer(getter_AddRefs(parent));
    2655                 : 
    2656               0 :   NS_ENSURE_SUCCESS(result, result);
    2657                 : 
    2658               0 :   result = range->GetStartOffset(&offset);
    2659                 : 
    2660               0 :   NS_ENSURE_SUCCESS(result, result);
    2661                 : 
    2662                 :   e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
    2663               0 :                                        parent, offset);
    2664                 :   e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
    2665               0 :                                        parent, offset);
    2666                 : 
    2667               0 :   if (e1s1 > 0 || e2s1 < 0)
    2668                 :   {
    2669                 :     // We're done if the caret is outside the
    2670                 :     // current text block.
    2671                 : 
    2672               0 :     return NS_OK;
    2673                 :   }
    2674                 : 
    2675               0 :   if (IsTextNode(parent))
    2676                 :   {
    2677                 :     // Good news, the caret is in a text node. Look
    2678                 :     // through the offset table for the entry that
    2679                 :     // matches its parent and offset.
    2680                 : 
    2681               0 :     for (i = 0; i < tableCount; i++)
    2682                 :     {
    2683               0 :       entry = mOffsetTable[i];
    2684                 : 
    2685               0 :       NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    2686                 : 
    2687               0 :       if (entry->mNode == parent.get() &&
    2688                 :           entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
    2689                 :       {
    2690               0 :         *aSelStatus = nsITextServicesDocument::eBlockContains;
    2691               0 :         *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
    2692               0 :         *aSelLength = 0;
    2693                 : 
    2694               0 :         return NS_OK;
    2695                 :       }
    2696                 :     }
    2697                 : 
    2698                 :     // If we get here, we didn't find a text node entry
    2699                 :     // in our offset table that matched.
    2700                 : 
    2701               0 :     return NS_ERROR_FAILURE;
    2702                 :   }
    2703                 : 
    2704                 :   // The caret is in our text block, but it's positioned in some
    2705                 :   // non-text node (ex. <b>). Create a range based on the start
    2706                 :   // and end of the text block, then create an iterator based on
    2707                 :   // this range, with its initial position set to the closest
    2708                 :   // child of this non-text node. Then look for the closest text
    2709                 :   // node.
    2710                 : 
    2711               0 :   nsCOMPtr<nsIDOMNode> node, saveNode;
    2712               0 :   nsCOMPtr<nsIDOMNodeList> children;
    2713               0 :   nsCOMPtr<nsIContentIterator> iter;
    2714                 :   bool hasChildren;
    2715                 : 
    2716               0 :   result = CreateRange(eStart->mNode, eStartOffset, eEnd->mNode, eEndOffset, getter_AddRefs(range));
    2717                 : 
    2718               0 :   NS_ENSURE_SUCCESS(result, result);
    2719                 : 
    2720               0 :   result = CreateContentIterator(range, getter_AddRefs(iter));
    2721                 : 
    2722               0 :   NS_ENSURE_SUCCESS(result, result);
    2723                 : 
    2724               0 :   result = parent->HasChildNodes(&hasChildren);
    2725                 : 
    2726               0 :   NS_ENSURE_SUCCESS(result, result);
    2727                 : 
    2728               0 :   if (hasChildren)
    2729                 :   {
    2730                 :     // XXX: We need to make sure that all of parent's
    2731                 :     //      children are in the text block.
    2732                 : 
    2733                 :     // If the parent has children, position the iterator
    2734                 :     // on the child that is to the left of the offset.
    2735                 : 
    2736               0 :     PRUint32 childIndex = (PRUint32)offset;
    2737                 : 
    2738               0 :     result = parent->GetChildNodes(getter_AddRefs(children));
    2739                 : 
    2740               0 :     NS_ENSURE_SUCCESS(result, result);
    2741                 : 
    2742               0 :     NS_ENSURE_TRUE(children, NS_ERROR_FAILURE);
    2743                 : 
    2744               0 :     if (childIndex > 0)
    2745                 :     {
    2746                 :       PRUint32 numChildren;
    2747                 : 
    2748               0 :       result = children->GetLength(&numChildren);
    2749                 : 
    2750               0 :       NS_ENSURE_SUCCESS(result, result);
    2751                 : 
    2752               0 :       NS_ASSERTION(childIndex <= numChildren, "Invalid selection offset!");
    2753                 : 
    2754               0 :       if (childIndex > numChildren)
    2755               0 :         childIndex = numChildren;
    2756                 : 
    2757               0 :       childIndex -= 1;
    2758                 :     }
    2759                 : 
    2760               0 :     result = children->Item(childIndex, getter_AddRefs(saveNode));
    2761                 : 
    2762               0 :     NS_ENSURE_SUCCESS(result, result);
    2763                 : 
    2764               0 :     nsCOMPtr<nsIContent> content(do_QueryInterface(saveNode));
    2765                 : 
    2766               0 :     NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
    2767                 : 
    2768               0 :     result = iter->PositionAt(content);
    2769                 : 
    2770               0 :     NS_ENSURE_SUCCESS(result, result);
    2771                 :   }
    2772                 :   else
    2773                 :   {
    2774                 :     // The parent has no children, so position the iterator
    2775                 :     // on the parent.
    2776                 : 
    2777               0 :     nsCOMPtr<nsIContent> content(do_QueryInterface(parent));
    2778                 : 
    2779               0 :     NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
    2780                 : 
    2781               0 :     result = iter->PositionAt(content);
    2782                 : 
    2783               0 :     NS_ENSURE_SUCCESS(result, result);
    2784                 : 
    2785               0 :     saveNode = parent;
    2786                 :   }
    2787                 : 
    2788                 :   // Now iterate to the left, towards the beginning of
    2789                 :   // the text block, to find the first text node you
    2790                 :   // come across.
    2791                 : 
    2792               0 :   while (!iter->IsDone())
    2793                 :   {
    2794               0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(iter->GetCurrentNode());
    2795                 : 
    2796               0 :     if (IsTextNode(content))
    2797                 :     {
    2798               0 :       node = do_QueryInterface(content);
    2799                 : 
    2800               0 :       NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
    2801                 : 
    2802                 :       break;
    2803                 :     }
    2804                 : 
    2805               0 :     node = nsnull;
    2806                 : 
    2807               0 :     iter->Prev();
    2808                 :   }
    2809                 : 
    2810               0 :   if (node)
    2811                 :   {
    2812                 :     // We found a node, now set the offset to the end
    2813                 :     // of the text node.
    2814                 : 
    2815               0 :     nsAutoString str;
    2816               0 :     result = node->GetNodeValue(str);
    2817                 : 
    2818               0 :     NS_ENSURE_SUCCESS(result, result);
    2819                 : 
    2820               0 :     offset = str.Length();
    2821                 :   }
    2822                 :   else
    2823                 :   {
    2824                 :     // We should never really get here, but I'm paranoid.
    2825                 : 
    2826                 :     // We didn't find a text node above, so iterate to
    2827                 :     // the right, towards the end of the text block, looking
    2828                 :     // for a text node.
    2829                 : 
    2830                 :     {
    2831               0 :       nsCOMPtr<nsIContent> content(do_QueryInterface(saveNode));
    2832                 : 
    2833               0 :       result = iter->PositionAt(content);
    2834                 : 
    2835               0 :       NS_ENSURE_SUCCESS(result, result);
    2836                 :     }
    2837                 : 
    2838               0 :     while (!iter->IsDone())
    2839                 :     {
    2840               0 :       nsCOMPtr<nsIContent> content = do_QueryInterface(iter->GetCurrentNode());
    2841                 : 
    2842               0 :       if (IsTextNode(content))
    2843                 :       {
    2844               0 :         node = do_QueryInterface(content);
    2845                 : 
    2846               0 :         NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
    2847                 : 
    2848                 :         break;
    2849                 :       }
    2850                 : 
    2851               0 :       node = nsnull;
    2852                 : 
    2853               0 :       iter->Next();
    2854                 :     }
    2855                 : 
    2856               0 :     NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
    2857                 : 
    2858                 :     // We found a text node, so set the offset to
    2859                 :     // the begining of the node.
    2860                 : 
    2861               0 :     offset = 0;
    2862                 :   }
    2863                 : 
    2864               0 :   for (i = 0; i < tableCount; i++)
    2865                 :   {
    2866               0 :     entry = mOffsetTable[i];
    2867                 : 
    2868               0 :     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    2869                 : 
    2870               0 :     if (entry->mNode == node.get() &&
    2871                 :         entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
    2872                 :     {
    2873               0 :       *aSelStatus = nsITextServicesDocument::eBlockContains;
    2874               0 :       *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
    2875               0 :       *aSelLength = 0;
    2876                 : 
    2877                 :       // Now move the caret so that it is actually in the text node.
    2878                 :       // We do this to keep things in sync.
    2879                 :       //
    2880                 :       // In most cases, the user shouldn't see any movement in the caret
    2881                 :       // on screen.
    2882                 : 
    2883               0 :       result = SetSelectionInternal(*aSelOffset, *aSelLength, true);
    2884                 : 
    2885               0 :       return result;
    2886                 :     }
    2887                 :   }
    2888                 : 
    2889               0 :   return NS_ERROR_FAILURE;
    2890                 : }
    2891                 : 
    2892                 : nsresult
    2893               0 : nsTextServicesDocument::GetUncollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, PRInt32 *aSelOffset, PRInt32 *aSelLength)
    2894                 : {
    2895                 :   nsresult result;
    2896                 : 
    2897               0 :   nsCOMPtr<nsISelection> selection;
    2898               0 :   nsCOMPtr<nsIDOMRange> range;
    2899                 :   OffsetEntry *entry;
    2900                 : 
    2901               0 :   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
    2902                 : 
    2903               0 :   NS_ENSURE_SUCCESS(result, result);
    2904                 : 
    2905               0 :   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
    2906                 : 
    2907                 :   // It is assumed that the calling function has made sure that the
    2908                 :   // selection is not collapsed, and that the input params to this
    2909                 :   // method are initialized to some defaults.
    2910                 : 
    2911               0 :   nsCOMPtr<nsIDOMNode> startParent, endParent;
    2912                 :   PRInt32 startOffset, endOffset;
    2913                 :   PRInt32 rangeCount, tableCount, i;
    2914                 :   PRInt32 e1s1, e1s2, e2s1, e2s2;
    2915                 : 
    2916                 :   OffsetEntry *eStart, *eEnd;
    2917                 :   PRInt32 eStartOffset, eEndOffset;
    2918                 : 
    2919               0 :   tableCount = mOffsetTable.Length();
    2920                 : 
    2921                 :   // Get pointers to the first and last offset entries
    2922                 :   // in the table.
    2923                 : 
    2924               0 :   eStart = mOffsetTable[0];
    2925                 : 
    2926               0 :   if (tableCount > 1)
    2927               0 :     eEnd = mOffsetTable[tableCount - 1];
    2928                 :   else
    2929               0 :     eEnd = eStart;
    2930                 : 
    2931               0 :   eStartOffset = eStart->mNodeOffset;
    2932               0 :   eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
    2933                 : 
    2934               0 :   result = selection->GetRangeCount(&rangeCount);
    2935                 : 
    2936               0 :   NS_ENSURE_SUCCESS(result, result);
    2937                 : 
    2938                 :   // Find the first range in the selection that intersects
    2939                 :   // the current text block.
    2940                 : 
    2941               0 :   for (i = 0; i < rangeCount; i++)
    2942                 :   {
    2943               0 :     result = selection->GetRangeAt(i, getter_AddRefs(range));
    2944                 : 
    2945               0 :     NS_ENSURE_SUCCESS(result, result);
    2946                 : 
    2947                 :     result = GetRangeEndPoints(range,
    2948               0 :                                getter_AddRefs(startParent), &startOffset,
    2949               0 :                                getter_AddRefs(endParent), &endOffset);
    2950                 : 
    2951               0 :     NS_ENSURE_SUCCESS(result, result);
    2952                 : 
    2953                 :     e1s2 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
    2954               0 :                                          endParent, endOffset);
    2955                 :     e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
    2956               0 :                                          startParent, startOffset);
    2957                 : 
    2958                 :     // Break out of the loop if the text block intersects the current range.
    2959                 : 
    2960               0 :     if (e1s2 <= 0 && e2s1 >= 0)
    2961               0 :       break;
    2962                 :   }
    2963                 : 
    2964                 :   // We're done if we didn't find an intersecting range.
    2965                 : 
    2966               0 :   if (rangeCount < 1 || e1s2 > 0 || e2s1 < 0)
    2967                 :   {
    2968               0 :     *aSelStatus = nsITextServicesDocument::eBlockOutside;
    2969               0 :     *aSelOffset = *aSelLength = -1;
    2970               0 :     return NS_OK;
    2971                 :   }
    2972                 : 
    2973                 :   // Now that we have an intersecting range, find out more info:
    2974                 : 
    2975                 :   e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
    2976               0 :                                        startParent, startOffset);
    2977                 :   e2s2 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
    2978               0 :                                        endParent, endOffset);
    2979                 : 
    2980               0 :   if (rangeCount > 1)
    2981                 :   {
    2982                 :     // There are multiple selection ranges, we only deal
    2983                 :     // with the first one that intersects the current,
    2984                 :     // text block, so mark this a as a partial.
    2985                 : 
    2986               0 :     *aSelStatus = nsITextServicesDocument::eBlockPartial;
    2987                 :   }
    2988               0 :   else if (e1s1 > 0 && e2s2 < 0)
    2989                 :   {
    2990                 :     // The range extends beyond the start and
    2991                 :     // end of the current text block.
    2992                 : 
    2993               0 :     *aSelStatus = nsITextServicesDocument::eBlockInside;
    2994                 :   }
    2995               0 :   else if (e1s1 <= 0 && e2s2 >= 0)
    2996                 :   {
    2997                 :     // The current text block contains the entire
    2998                 :     // range.
    2999                 : 
    3000               0 :     *aSelStatus = nsITextServicesDocument::eBlockContains;
    3001                 :   }
    3002                 :   else
    3003                 :   {
    3004                 :     // The range partially intersects the block.
    3005                 : 
    3006               0 :     *aSelStatus = nsITextServicesDocument::eBlockPartial;
    3007                 :   }
    3008                 : 
    3009                 :   // Now create a range based on the intersection of the
    3010                 :   // text block and range:
    3011                 : 
    3012               0 :   nsCOMPtr<nsIDOMNode> p1, p2;
    3013                 :   PRInt32     o1,  o2;
    3014                 : 
    3015                 :   // The start of the range will be the rightmost
    3016                 :   // start node.
    3017                 : 
    3018               0 :   if (e1s1 >= 0)
    3019                 :   {
    3020               0 :     p1 = do_QueryInterface(eStart->mNode);
    3021               0 :     o1 = eStartOffset;
    3022                 :   }
    3023                 :   else
    3024                 :   {
    3025               0 :     p1 = startParent;
    3026               0 :     o1 = startOffset;
    3027                 :   }
    3028                 : 
    3029                 :   // The end of the range will be the leftmost
    3030                 :   // end node.
    3031                 : 
    3032               0 :   if (e2s2 <= 0)
    3033                 :   {
    3034               0 :     p2 = do_QueryInterface(eEnd->mNode);
    3035               0 :     o2 = eEndOffset;
    3036                 :   }
    3037                 :   else
    3038                 :   {
    3039               0 :     p2 = endParent;
    3040               0 :     o2 = endOffset;
    3041                 :   }
    3042                 : 
    3043               0 :   result = CreateRange(p1, o1, p2, o2, getter_AddRefs(range));
    3044                 : 
    3045               0 :   NS_ENSURE_SUCCESS(result, result);
    3046                 : 
    3047                 :   // Now iterate over this range to figure out the selection's
    3048                 :   // block offset and length.
    3049                 : 
    3050               0 :   nsCOMPtr<nsIContentIterator> iter;
    3051                 : 
    3052               0 :   result = CreateContentIterator(range, getter_AddRefs(iter));
    3053                 : 
    3054               0 :   NS_ENSURE_SUCCESS(result, result);
    3055                 : 
    3056                 :   // Find the first text node in the range.
    3057                 :   
    3058                 :   bool found;
    3059               0 :   nsCOMPtr<nsIContent> content;
    3060                 : 
    3061               0 :   iter->First();
    3062                 : 
    3063               0 :   if (!IsTextNode(p1))
    3064                 :   {
    3065               0 :     found = false;
    3066                 : 
    3067               0 :     while (!iter->IsDone())
    3068                 :     {
    3069               0 :       content = do_QueryInterface(iter->GetCurrentNode());
    3070                 : 
    3071               0 :       if (IsTextNode(content))
    3072                 :       {
    3073               0 :         p1 = do_QueryInterface(content);
    3074                 : 
    3075               0 :         NS_ENSURE_TRUE(p1, NS_ERROR_FAILURE);
    3076                 : 
    3077               0 :         o1 = 0;
    3078               0 :         found = true;
    3079                 : 
    3080               0 :         break;
    3081                 :       }
    3082                 : 
    3083               0 :       iter->Next();
    3084                 :     }
    3085                 : 
    3086               0 :     NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
    3087                 :   }
    3088                 : 
    3089                 :   // Find the last text node in the range.
    3090                 : 
    3091               0 :   iter->Last();
    3092                 : 
    3093               0 :   if (! IsTextNode(p2))
    3094                 :   {
    3095               0 :     found = false;
    3096                 : 
    3097               0 :     while (!iter->IsDone())
    3098                 :     {
    3099               0 :       content = do_QueryInterface(iter->GetCurrentNode());
    3100                 : 
    3101               0 :       if (IsTextNode(content))
    3102                 :       {
    3103               0 :         p2 = do_QueryInterface(content);
    3104                 : 
    3105               0 :         NS_ENSURE_TRUE(p2, NS_ERROR_FAILURE);
    3106                 : 
    3107               0 :         nsString str;
    3108                 : 
    3109               0 :         result = p2->GetNodeValue(str);
    3110                 : 
    3111               0 :         NS_ENSURE_SUCCESS(result, result);
    3112                 : 
    3113               0 :         o2 = str.Length();
    3114               0 :         found = true;
    3115                 : 
    3116               0 :         break;
    3117                 :       }
    3118                 : 
    3119               0 :       iter->Prev();
    3120                 :     }
    3121                 : 
    3122               0 :     NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
    3123                 :   }
    3124                 : 
    3125               0 :   found    = false;
    3126               0 :   *aSelLength = 0;
    3127                 : 
    3128               0 :   for (i = 0; i < tableCount; i++)
    3129                 :   {
    3130               0 :     entry = mOffsetTable[i];
    3131                 : 
    3132               0 :     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    3133                 : 
    3134               0 :     if (!found)
    3135                 :     {
    3136               0 :       if (entry->mNode == p1.get() &&
    3137                 :           entry->mNodeOffset <= o1 && o1 <= (entry->mNodeOffset + entry->mLength))
    3138                 :       {
    3139               0 :         *aSelOffset = entry->mStrOffset + (o1 - entry->mNodeOffset);
    3140                 : 
    3141               0 :         if (p1 == p2 &&
    3142                 :             entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
    3143                 :         {
    3144                 :           // The start and end of the range are in the same offset
    3145                 :           // entry. Calculate the length of the range then we're done.
    3146                 : 
    3147               0 :           *aSelLength = o2 - o1;
    3148               0 :           break;
    3149                 :         }
    3150                 :         else
    3151                 :         {
    3152                 :           // Add the length of the sub string in this offset entry
    3153                 :           // that follows the start of the range.
    3154                 : 
    3155               0 :           *aSelLength = entry->mLength - (o1 - entry->mNodeOffset);
    3156                 :         }
    3157                 : 
    3158               0 :         found = true;
    3159                 :       }
    3160                 :     }
    3161                 :     else // found
    3162                 :     {
    3163               0 :       if (entry->mNode == p2.get() &&
    3164                 :           entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
    3165                 :       {
    3166                 :         // We found the end of the range. Calculate the length of the
    3167                 :         // sub string that is before the end of the range, then we're done.
    3168                 : 
    3169               0 :         *aSelLength += o2 - entry->mNodeOffset;
    3170               0 :         break;
    3171                 :       }
    3172                 :       else
    3173                 :       {
    3174                 :         // The entire entry must be in the range.
    3175                 : 
    3176               0 :         *aSelLength += entry->mLength;
    3177                 :       }
    3178                 :     }
    3179                 :   }
    3180                 : 
    3181               0 :   return result;
    3182                 : }
    3183                 : 
    3184                 : bool
    3185               0 : nsTextServicesDocument::SelectionIsCollapsed()
    3186                 : {
    3187               0 :   return(mSelStartIndex == mSelEndIndex && mSelStartOffset == mSelEndOffset);
    3188                 : }
    3189                 : 
    3190                 : bool
    3191               0 : nsTextServicesDocument::SelectionIsValid()
    3192                 : {
    3193               0 :   return(mSelStartIndex >= 0);
    3194                 : }
    3195                 : 
    3196                 : nsresult
    3197               0 : nsTextServicesDocument::GetRangeEndPoints(nsIDOMRange *aRange,
    3198                 :                                           nsIDOMNode **aStartParent, PRInt32 *aStartOffset,
    3199                 :                                           nsIDOMNode **aEndParent, PRInt32 *aEndOffset)
    3200                 : {
    3201                 :   nsresult result;
    3202                 : 
    3203               0 :   NS_ENSURE_TRUE(aRange && aStartParent && aStartOffset && aEndParent && aEndOffset, NS_ERROR_NULL_POINTER);
    3204                 : 
    3205               0 :   result = aRange->GetStartContainer(aStartParent);
    3206                 : 
    3207               0 :   NS_ENSURE_SUCCESS(result, result);
    3208                 : 
    3209               0 :   NS_ENSURE_TRUE(aStartParent, NS_ERROR_FAILURE);
    3210                 : 
    3211               0 :   result = aRange->GetStartOffset(aStartOffset);
    3212                 : 
    3213               0 :   NS_ENSURE_SUCCESS(result, result);
    3214                 : 
    3215               0 :   result = aRange->GetEndContainer(aEndParent);
    3216                 : 
    3217               0 :   NS_ENSURE_SUCCESS(result, result);
    3218                 : 
    3219               0 :   NS_ENSURE_TRUE(aEndParent, NS_ERROR_FAILURE);
    3220                 : 
    3221               0 :   result = aRange->GetEndOffset(aEndOffset);
    3222                 : 
    3223               0 :   return result;
    3224                 : }
    3225                 : 
    3226                 : 
    3227                 : nsresult
    3228               0 : nsTextServicesDocument::CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset,
    3229                 :                                     nsIDOMNode *aEndParent, PRInt32 aEndOffset,
    3230                 :                                     nsIDOMRange **aRange)
    3231                 : {
    3232                 :   return nsRange::CreateRange(aStartParent, aStartOffset, aEndParent,
    3233               0 :                               aEndOffset, aRange);
    3234                 : }
    3235                 : 
    3236                 : nsresult
    3237               0 : nsTextServicesDocument::FirstTextNode(nsIContentIterator *aIterator,
    3238                 :                                       TSDIteratorStatus *aIteratorStatus)
    3239                 : {
    3240               0 :   if (aIteratorStatus)
    3241               0 :     *aIteratorStatus = nsTextServicesDocument::eIsDone;
    3242                 : 
    3243               0 :   aIterator->First();
    3244                 : 
    3245               0 :   while (!aIterator->IsDone()) {
    3246               0 :     if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
    3247               0 :       if (aIteratorStatus)
    3248               0 :         *aIteratorStatus = nsTextServicesDocument::eValid;
    3249               0 :       break;
    3250                 :     }
    3251                 : 
    3252               0 :     aIterator->Next();
    3253                 :   }
    3254                 : 
    3255               0 :   return NS_OK;
    3256                 : }
    3257                 : 
    3258                 : nsresult
    3259               0 : nsTextServicesDocument::LastTextNode(nsIContentIterator *aIterator,
    3260                 :                                      TSDIteratorStatus *aIteratorStatus)
    3261                 : {
    3262               0 :   if (aIteratorStatus)
    3263               0 :     *aIteratorStatus = nsTextServicesDocument::eIsDone;
    3264                 : 
    3265               0 :   aIterator->Last();
    3266                 : 
    3267               0 :   while (!aIterator->IsDone()) {
    3268               0 :     if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
    3269               0 :       if (aIteratorStatus)
    3270               0 :         *aIteratorStatus = nsTextServicesDocument::eValid;
    3271               0 :       break;
    3272                 :     }
    3273                 : 
    3274               0 :     aIterator->Prev();
    3275                 :   }
    3276                 : 
    3277               0 :   return NS_OK;
    3278                 : }
    3279                 : 
    3280                 : nsresult
    3281               0 : nsTextServicesDocument::FirstTextNodeInCurrentBlock(nsIContentIterator *iter)
    3282                 : {
    3283               0 :   NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
    3284                 : 
    3285               0 :   ClearDidSkip(iter);
    3286                 : 
    3287               0 :   nsCOMPtr<nsIContent> last;
    3288                 : 
    3289                 :   // Walk backwards over adjacent text nodes until
    3290                 :   // we hit a block boundary:
    3291                 : 
    3292               0 :   while (!iter->IsDone())
    3293                 :   {
    3294               0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(iter->GetCurrentNode());
    3295                 : 
    3296               0 :     if (IsTextNode(content))
    3297                 :     {
    3298               0 :       if (!last || HasSameBlockNodeParent(content, last))
    3299               0 :         last = content;
    3300                 :       else
    3301                 :       {
    3302                 :         // We're done, the current text node is in a
    3303                 :         // different block.
    3304                 :         break;
    3305                 :       }
    3306                 :     }
    3307               0 :     else if (last && IsBlockNode(content))
    3308                 :       break;
    3309                 : 
    3310               0 :     iter->Prev();
    3311                 : 
    3312               0 :     if (DidSkip(iter))
    3313                 :       break;
    3314                 :   }
    3315                 :   
    3316               0 :   if (last)
    3317               0 :     iter->PositionAt(last);
    3318                 : 
    3319                 :   // XXX: What should we return if last is null?
    3320                 : 
    3321               0 :   return NS_OK;
    3322                 : }
    3323                 : 
    3324                 : nsresult
    3325               0 : nsTextServicesDocument::FirstTextNodeInPrevBlock(nsIContentIterator *aIterator)
    3326                 : {
    3327               0 :   nsCOMPtr<nsIContent> content;
    3328                 :   nsresult result;
    3329                 : 
    3330               0 :   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
    3331                 : 
    3332                 :   // XXX: What if mIterator is not currently on a text node?
    3333                 : 
    3334                 :   // Make sure mIterator is pointing to the first text node in the
    3335                 :   // current block:
    3336                 : 
    3337               0 :   result = FirstTextNodeInCurrentBlock(aIterator);
    3338                 : 
    3339               0 :   NS_ENSURE_SUCCESS(result, NS_ERROR_FAILURE);
    3340                 : 
    3341                 :   // Point mIterator to the first node before the first text node:
    3342                 : 
    3343               0 :   aIterator->Prev();
    3344                 : 
    3345               0 :   if (aIterator->IsDone())
    3346               0 :     return NS_ERROR_FAILURE;
    3347                 : 
    3348                 :   // Now find the first text node of the next block:
    3349                 : 
    3350               0 :   return FirstTextNodeInCurrentBlock(aIterator);
    3351                 : }
    3352                 : 
    3353                 : nsresult
    3354               0 : nsTextServicesDocument::FirstTextNodeInNextBlock(nsIContentIterator *aIterator)
    3355                 : {
    3356               0 :   nsCOMPtr<nsIContent> prev;
    3357               0 :   bool crossedBlockBoundary = false;
    3358                 : 
    3359               0 :   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
    3360                 : 
    3361               0 :   ClearDidSkip(aIterator);
    3362                 : 
    3363               0 :   while (!aIterator->IsDone())
    3364                 :   {
    3365               0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(aIterator->GetCurrentNode());
    3366                 : 
    3367               0 :     if (IsTextNode(content))
    3368                 :     {
    3369               0 :       if (!crossedBlockBoundary && (!prev || HasSameBlockNodeParent(prev, content)))
    3370               0 :         prev = content;
    3371                 :       else
    3372                 :         break;
    3373                 :     }
    3374               0 :     else if (!crossedBlockBoundary && IsBlockNode(content))
    3375               0 :       crossedBlockBoundary = true;
    3376                 : 
    3377               0 :     aIterator->Next();
    3378                 : 
    3379               0 :     if (!crossedBlockBoundary && DidSkip(aIterator))
    3380               0 :       crossedBlockBoundary = true;
    3381                 :   }
    3382                 : 
    3383               0 :   return NS_OK;
    3384                 : }
    3385                 : 
    3386                 : nsresult
    3387               0 : nsTextServicesDocument::GetFirstTextNodeInPrevBlock(nsIContent **aContent)
    3388                 : {
    3389                 :   nsresult result;
    3390                 : 
    3391               0 :   NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
    3392                 : 
    3393               0 :   *aContent = 0;
    3394                 : 
    3395                 :   // Save the iterator's current content node so we can restore
    3396                 :   // it when we are done:
    3397                 : 
    3398               0 :   nsINode* node = mIterator->GetCurrentNode();
    3399                 : 
    3400               0 :   result = FirstTextNodeInPrevBlock(mIterator);
    3401                 : 
    3402               0 :   if (NS_FAILED(result))
    3403                 :   {
    3404                 :     // Try to restore the iterator before returning.
    3405               0 :     mIterator->PositionAt(node);
    3406               0 :     return result;
    3407                 :   }
    3408                 : 
    3409               0 :   if (!mIterator->IsDone())
    3410                 :   {
    3411               0 :     nsCOMPtr<nsIContent> current = do_QueryInterface(mIterator->GetCurrentNode());
    3412               0 :     current.forget(aContent);
    3413                 :   }
    3414                 : 
    3415                 :   // Restore the iterator:
    3416                 : 
    3417               0 :   return mIterator->PositionAt(node);
    3418                 : }
    3419                 : 
    3420                 : nsresult
    3421               0 : nsTextServicesDocument::GetFirstTextNodeInNextBlock(nsIContent **aContent)
    3422                 : {
    3423                 :   nsresult result;
    3424                 : 
    3425               0 :   NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
    3426                 : 
    3427               0 :   *aContent = 0;
    3428                 : 
    3429                 :   // Save the iterator's current content node so we can restore
    3430                 :   // it when we are done:
    3431                 : 
    3432               0 :   nsINode* node = mIterator->GetCurrentNode();
    3433                 : 
    3434               0 :   result = FirstTextNodeInNextBlock(mIterator);
    3435                 : 
    3436               0 :   if (NS_FAILED(result))
    3437                 :   {
    3438                 :     // Try to restore the iterator before returning.
    3439               0 :     mIterator->PositionAt(node);
    3440               0 :     return result;
    3441                 :   }
    3442                 : 
    3443               0 :   if (!mIterator->IsDone())
    3444                 :   {
    3445               0 :     nsCOMPtr<nsIContent> current = do_QueryInterface(mIterator->GetCurrentNode());
    3446               0 :     current.forget(aContent);
    3447                 :   }
    3448                 : 
    3449                 :   // Restore the iterator:
    3450               0 :   return mIterator->PositionAt(node);
    3451                 : }
    3452                 : 
    3453                 : nsresult
    3454               0 : nsTextServicesDocument::CreateOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable,
    3455                 :                                           nsIContentIterator *aIterator,
    3456                 :                                           TSDIteratorStatus *aIteratorStatus,
    3457                 :                                           nsIDOMRange *aIterRange,
    3458                 :                                           nsString *aStr)
    3459                 : {
    3460               0 :   nsresult result = NS_OK;
    3461                 : 
    3462               0 :   nsCOMPtr<nsIContent> first;
    3463               0 :   nsCOMPtr<nsIContent> prev;
    3464                 : 
    3465               0 :   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
    3466                 : 
    3467               0 :   ClearOffsetTable(aOffsetTable);
    3468                 : 
    3469               0 :   if (aStr)
    3470               0 :     aStr->Truncate();
    3471                 : 
    3472               0 :   if (*aIteratorStatus == nsTextServicesDocument::eIsDone)
    3473               0 :     return NS_OK;
    3474                 : 
    3475                 :   // If we have an aIterRange, retrieve the endpoints so
    3476                 :   // they can be used in the while loop below to trim entries
    3477                 :   // for text nodes that are partially selected by aIterRange.
    3478                 :   
    3479               0 :   nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
    3480               0 :   PRInt32 rngStartOffset = 0, rngEndOffset = 0;
    3481                 : 
    3482               0 :   if (aIterRange)
    3483                 :   {
    3484                 :     result = GetRangeEndPoints(aIterRange,
    3485               0 :                                getter_AddRefs(rngStartNode), &rngStartOffset,
    3486               0 :                                getter_AddRefs(rngEndNode), &rngEndOffset);
    3487                 : 
    3488               0 :     NS_ENSURE_SUCCESS(result, result);
    3489                 :   }
    3490                 : 
    3491                 :   // The text service could have added text nodes to the beginning
    3492                 :   // of the current block and called this method again. Make sure
    3493                 :   // we really are at the beginning of the current block:
    3494                 : 
    3495               0 :   result = FirstTextNodeInCurrentBlock(aIterator);
    3496                 : 
    3497               0 :   NS_ENSURE_SUCCESS(result, result);
    3498                 : 
    3499               0 :   PRInt32 offset = 0;
    3500                 : 
    3501               0 :   ClearDidSkip(aIterator);
    3502                 : 
    3503               0 :   while (!aIterator->IsDone())
    3504                 :   {
    3505               0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(aIterator->GetCurrentNode());
    3506                 : 
    3507               0 :     if (IsTextNode(content))
    3508                 :     {
    3509               0 :       if (!prev || HasSameBlockNodeParent(prev, content))
    3510                 :       {
    3511               0 :         nsCOMPtr<nsIDOMNode> node = do_QueryInterface(content);
    3512                 : 
    3513               0 :         if (node)
    3514                 :         {
    3515               0 :           nsString str;
    3516                 : 
    3517               0 :           result = node->GetNodeValue(str);
    3518                 : 
    3519               0 :           NS_ENSURE_SUCCESS(result, result);
    3520                 : 
    3521                 :           // Add an entry for this text node into the offset table:
    3522                 : 
    3523               0 :           OffsetEntry *entry = new OffsetEntry(node, offset, str.Length());
    3524               0 :           aOffsetTable->AppendElement(entry);
    3525                 : 
    3526                 :           // If one or both of the endpoints of the iteration range
    3527                 :           // are in the text node for this entry, make sure the entry
    3528                 :           // only accounts for the portion of the text node that is
    3529                 :           // in the range.
    3530                 : 
    3531               0 :           PRInt32 startOffset = 0;
    3532               0 :           PRInt32 endOffset   = str.Length();
    3533               0 :           bool adjustStr    = false;
    3534                 : 
    3535               0 :           if (entry->mNode == rngStartNode)
    3536                 :           {
    3537               0 :             entry->mNodeOffset = startOffset = rngStartOffset;
    3538               0 :             adjustStr = true;
    3539                 :           }
    3540                 : 
    3541               0 :           if (entry->mNode == rngEndNode)
    3542                 :           {
    3543               0 :             endOffset = rngEndOffset;
    3544               0 :             adjustStr = true;
    3545                 :           }
    3546                 : 
    3547               0 :           if (adjustStr)
    3548                 :           {
    3549               0 :             entry->mLength = endOffset - startOffset;
    3550               0 :             str = Substring(str, startOffset, entry->mLength);
    3551                 :           }
    3552                 : 
    3553               0 :           offset += str.Length();
    3554                 : 
    3555               0 :           if (aStr)
    3556                 :           {
    3557                 :             // Append the text node's string to the output string:
    3558                 : 
    3559               0 :             if (!first)
    3560               0 :               *aStr = str;
    3561                 :             else
    3562               0 :               *aStr += str;
    3563                 :           }
    3564                 :         }
    3565                 : 
    3566               0 :         prev = content;
    3567                 : 
    3568               0 :         if (!first)
    3569               0 :           first = content;
    3570                 :       }
    3571                 :       else
    3572                 :         break;
    3573                 : 
    3574                 :     }
    3575               0 :     else if (IsBlockNode(content))
    3576                 :       break;
    3577                 : 
    3578               0 :     aIterator->Next();
    3579                 : 
    3580               0 :     if (DidSkip(aIterator))
    3581                 :       break;
    3582                 :   }
    3583                 : 
    3584               0 :   if (first)
    3585                 :   {
    3586                 :     // Always leave the iterator pointing at the first
    3587                 :     // text node of the current block!
    3588                 : 
    3589               0 :     aIterator->PositionAt(first);
    3590                 :   }
    3591                 :   else
    3592                 :   {
    3593                 :     // If we never ran across a text node, the iterator
    3594                 :     // might have been pointing to something invalid to
    3595                 :     // begin with.
    3596                 : 
    3597               0 :     *aIteratorStatus = nsTextServicesDocument::eIsDone;
    3598                 :   }
    3599                 : 
    3600               0 :   return result;
    3601                 : }
    3602                 : 
    3603                 : nsresult
    3604               0 : nsTextServicesDocument::RemoveInvalidOffsetEntries()
    3605                 : {
    3606                 :   OffsetEntry *entry;
    3607               0 :   PRInt32 i = 0;
    3608                 : 
    3609               0 :   while (PRUint32(i) < mOffsetTable.Length())
    3610                 :   {
    3611               0 :     entry = mOffsetTable[i];
    3612                 : 
    3613               0 :     if (!entry->mIsValid)
    3614                 :     {
    3615               0 :       mOffsetTable.RemoveElementAt(i);
    3616                 : 
    3617               0 :       if (mSelStartIndex >= 0 && mSelStartIndex >= i)
    3618                 :       {
    3619                 :         // We are deleting an entry that comes before
    3620                 :         // mSelStartIndex, decrement mSelStartIndex so
    3621                 :         // that it points to the correct entry!
    3622                 : 
    3623               0 :         NS_ASSERTION(i != mSelStartIndex, "Invalid selection index.");
    3624                 : 
    3625               0 :         --mSelStartIndex;
    3626               0 :         --mSelEndIndex;
    3627                 :       }
    3628                 :     }
    3629                 :     else
    3630               0 :       i++;
    3631                 :   }
    3632                 : 
    3633               0 :   return NS_OK;
    3634                 : }
    3635                 : 
    3636                 : nsresult
    3637               0 : nsTextServicesDocument::ClearOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable)
    3638                 : {
    3639                 :   PRUint32 i;
    3640                 : 
    3641               0 :   for (i = 0; i < aOffsetTable->Length(); i++)
    3642                 :   {
    3643               0 :     delete aOffsetTable->ElementAt(i);
    3644                 :   }
    3645                 : 
    3646               0 :   aOffsetTable->Clear();
    3647                 : 
    3648               0 :   return NS_OK;
    3649                 : }
    3650                 : 
    3651                 : nsresult
    3652               0 : nsTextServicesDocument::SplitOffsetEntry(PRInt32 aTableIndex, PRInt32 aNewEntryLength)
    3653                 : {
    3654               0 :   OffsetEntry *entry = mOffsetTable[aTableIndex];
    3655                 : 
    3656               0 :   NS_ASSERTION((aNewEntryLength > 0), "aNewEntryLength <= 0");
    3657               0 :   NS_ASSERTION((aNewEntryLength < entry->mLength), "aNewEntryLength >= mLength");
    3658                 : 
    3659               0 :   if (aNewEntryLength < 1 || aNewEntryLength >= entry->mLength)
    3660               0 :     return NS_ERROR_FAILURE;
    3661                 : 
    3662               0 :   PRInt32 oldLength = entry->mLength - aNewEntryLength;
    3663                 : 
    3664                 :   OffsetEntry *newEntry = new OffsetEntry(entry->mNode,
    3665                 :                                           entry->mStrOffset + oldLength,
    3666               0 :                                           aNewEntryLength);
    3667                 : 
    3668               0 :   if (!mOffsetTable.InsertElementAt(aTableIndex + 1, newEntry))
    3669                 :   {
    3670               0 :     delete newEntry;
    3671               0 :     return NS_ERROR_FAILURE;
    3672                 :   }
    3673                 : 
    3674                 :    // Adjust entry fields:
    3675                 : 
    3676               0 :    entry->mLength        = oldLength;
    3677               0 :    newEntry->mNodeOffset = entry->mNodeOffset + oldLength;
    3678                 : 
    3679               0 :   return NS_OK;
    3680                 : }
    3681                 : 
    3682                 : nsresult
    3683               0 : nsTextServicesDocument::NodeHasOffsetEntry(nsTArray<OffsetEntry*> *aOffsetTable, nsIDOMNode *aNode, bool *aHasEntry, PRInt32 *aEntryIndex)
    3684                 : {
    3685                 :   OffsetEntry *entry;
    3686                 :   PRUint32 i;
    3687                 : 
    3688               0 :   NS_ENSURE_TRUE(aNode && aHasEntry && aEntryIndex, NS_ERROR_NULL_POINTER);
    3689                 : 
    3690               0 :   for (i = 0; i < aOffsetTable->Length(); i++)
    3691                 :   {
    3692               0 :     entry = (*aOffsetTable)[i];
    3693                 : 
    3694               0 :     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    3695                 : 
    3696               0 :     if (entry->mNode == aNode)
    3697                 :     {
    3698               0 :       *aHasEntry   = true;
    3699               0 :       *aEntryIndex = i;
    3700                 : 
    3701               0 :       return NS_OK;
    3702                 :     }
    3703                 :   }
    3704                 : 
    3705               0 :   *aHasEntry   = false;
    3706               0 :   *aEntryIndex = -1;
    3707                 : 
    3708               0 :   return NS_OK;
    3709                 : }
    3710                 : 
    3711                 : // Spellchecker code has this. See bug 211343
    3712                 : #define IS_NBSP_CHAR(c) (((unsigned char)0xa0)==(c))
    3713                 : 
    3714                 : nsresult
    3715               0 : nsTextServicesDocument::FindWordBounds(nsTArray<OffsetEntry*> *aOffsetTable,
    3716                 :                                        nsString *aBlockStr,
    3717                 :                                        nsIDOMNode *aNode,
    3718                 :                                        PRInt32 aNodeOffset,
    3719                 :                                        nsIDOMNode **aWordStartNode,
    3720                 :                                        PRInt32 *aWordStartOffset,
    3721                 :                                        nsIDOMNode **aWordEndNode,
    3722                 :                                        PRInt32 *aWordEndOffset)
    3723                 : {
    3724                 :   // Initialize return values.
    3725                 : 
    3726               0 :   if (aWordStartNode)
    3727               0 :     *aWordStartNode = nsnull;
    3728               0 :   if (aWordStartOffset)
    3729               0 :     *aWordStartOffset = 0;
    3730               0 :   if (aWordEndNode)
    3731               0 :     *aWordEndNode = nsnull;
    3732               0 :   if (aWordEndOffset)
    3733               0 :     *aWordEndOffset = 0;
    3734                 : 
    3735               0 :   PRInt32 entryIndex = 0;
    3736               0 :   bool hasEntry = false;
    3737                 : 
    3738                 :   // It's assumed that aNode is a text node. The first thing
    3739                 :   // we do is get its index in the offset table so we can
    3740                 :   // calculate the dom point's string offset.
    3741                 : 
    3742               0 :   nsresult result = NodeHasOffsetEntry(aOffsetTable, aNode, &hasEntry, &entryIndex);
    3743               0 :   NS_ENSURE_SUCCESS(result, result);
    3744               0 :   NS_ENSURE_TRUE(hasEntry, NS_ERROR_FAILURE);
    3745                 : 
    3746                 :   // Next we map aNodeOffset into a string offset.
    3747                 : 
    3748               0 :   OffsetEntry *entry = (*aOffsetTable)[entryIndex];
    3749               0 :   PRUint32 strOffset = entry->mStrOffset + aNodeOffset - entry->mNodeOffset;
    3750                 : 
    3751                 :   // Now we use the word breaker to find the beginning and end
    3752                 :   // of the word from our calculated string offset.
    3753                 : 
    3754               0 :   const PRUnichar *str = aBlockStr->get();
    3755               0 :   PRUint32 strLen = aBlockStr->Length();
    3756                 : 
    3757                 :   nsIWordBreaker *aWordBreaker;
    3758                 : 
    3759               0 :   result = CallGetService(NS_WBRK_CONTRACTID, &aWordBreaker);
    3760               0 :   NS_ENSURE_SUCCESS(result, result);
    3761                 : 
    3762               0 :   nsWordRange res = aWordBreaker->FindWord(str, strLen, strOffset);
    3763               0 :   NS_IF_RELEASE(aWordBreaker);
    3764               0 :   if(res.mBegin > strLen)
    3765                 :   {
    3766               0 :     if(!str)
    3767               0 :       return NS_ERROR_NULL_POINTER;
    3768                 :     else
    3769               0 :       return NS_ERROR_ILLEGAL_VALUE;
    3770                 :   }
    3771                 : 
    3772                 :   // Strip out the NBSPs at the ends
    3773               0 :   while ((res.mBegin <= res.mEnd) && (IS_NBSP_CHAR(str[res.mBegin]))) 
    3774               0 :     res.mBegin++;
    3775               0 :   if (str[res.mEnd] == (unsigned char)0x20)
    3776                 :   {
    3777               0 :     PRUint32 realEndWord = res.mEnd - 1;
    3778               0 :     while ((realEndWord > res.mBegin) && (IS_NBSP_CHAR(str[realEndWord]))) 
    3779               0 :       realEndWord--;
    3780               0 :     if (realEndWord < res.mEnd - 1) 
    3781               0 :       res.mEnd = realEndWord + 1;
    3782                 :   }
    3783                 : 
    3784                 :   // Now that we have the string offsets for the beginning
    3785                 :   // and end of the word, run through the offset table and
    3786                 :   // convert them back into dom points.
    3787                 : 
    3788               0 :   PRInt32 i, lastIndex = aOffsetTable->Length() - 1;
    3789                 : 
    3790               0 :   for (i=0; i <= lastIndex; i++)
    3791                 :   {
    3792               0 :     entry = (*aOffsetTable)[i];
    3793                 : 
    3794               0 :     PRInt32 strEndOffset = entry->mStrOffset + entry->mLength;
    3795                 : 
    3796                 :     // Check to see if res.mBegin is within the range covered
    3797                 :     // by this entry. Note that if res.mBegin is after the last
    3798                 :     // character covered by this entry, we will use the next
    3799                 :     // entry if there is one.
    3800                 : 
    3801               0 :     if (PRUint32(entry->mStrOffset) <= res.mBegin &&
    3802                 :         (res.mBegin < PRUint32(strEndOffset) ||
    3803                 :         (res.mBegin == PRUint32(strEndOffset) && i == lastIndex)))
    3804                 :     {
    3805               0 :       if (aWordStartNode)
    3806                 :       {
    3807               0 :         *aWordStartNode = entry->mNode;
    3808               0 :         NS_IF_ADDREF(*aWordStartNode);
    3809                 :       }
    3810                 : 
    3811               0 :       if (aWordStartOffset)
    3812               0 :         *aWordStartOffset = entry->mNodeOffset + res.mBegin - entry->mStrOffset;
    3813                 : 
    3814               0 :       if (!aWordEndNode && !aWordEndOffset)
    3815                 :       {
    3816                 :         // We've found our start entry, but if we're not looking
    3817                 :         // for end entries, we're done.
    3818                 : 
    3819               0 :         break;
    3820                 :       }
    3821                 :     }
    3822                 : 
    3823                 :     // Check to see if res.mEnd is within the range covered
    3824                 :     // by this entry.
    3825                 : 
    3826               0 :     if (PRUint32(entry->mStrOffset) <= res.mEnd && res.mEnd <= PRUint32(strEndOffset))
    3827                 :     {
    3828               0 :       if (res.mBegin == res.mEnd && res.mEnd == PRUint32(strEndOffset) && i != lastIndex)
    3829                 :       {
    3830                 :         // Wait for the next round so that we use the same entry
    3831                 :         // we did for aWordStartNode.
    3832                 : 
    3833               0 :         continue;
    3834                 :       }
    3835                 : 
    3836               0 :       if (aWordEndNode)
    3837                 :       {
    3838               0 :         *aWordEndNode = entry->mNode;
    3839               0 :         NS_IF_ADDREF(*aWordEndNode);
    3840                 :       }
    3841                 : 
    3842               0 :       if (aWordEndOffset)
    3843               0 :         *aWordEndOffset = entry->mNodeOffset + res.mEnd - entry->mStrOffset;
    3844                 : 
    3845               0 :       break;
    3846                 :     }
    3847                 :   }
    3848                 : 
    3849                 : 
    3850               0 :   return NS_OK;
    3851                 : }
    3852                 : 
    3853                 : #ifdef DEBUG_kin
    3854                 : void
    3855                 : nsTextServicesDocument::PrintOffsetTable()
    3856                 : {
    3857                 :   OffsetEntry *entry;
    3858                 :   PRUint32 i;
    3859                 : 
    3860                 :   for (i = 0; i < mOffsetTable.Length(); i++)
    3861                 :   {
    3862                 :     entry = mOffsetTable[i];
    3863                 :     printf("ENTRY %4d: %p  %c  %c  %4d  %4d  %4d\n",
    3864                 :            i, entry->mNode,  entry->mIsValid ? 'V' : 'N',
    3865                 :            entry->mIsInsertedText ? 'I' : 'B',
    3866                 :            entry->mNodeOffset, entry->mStrOffset, entry->mLength);
    3867                 :   }
    3868                 : 
    3869                 :   fflush(stdout);
    3870                 : }
    3871                 : 
    3872                 : void
    3873                 : nsTextServicesDocument::PrintContentNode(nsIContent *aContent)
    3874                 : {
    3875                 :   nsString tmpStr, str;
    3876                 : 
    3877                 :   aContent->Tag()->ToString(tmpStr);
    3878                 :   printf("%s", NS_LossyConvertUTF16toASCII(tmpStr).get());
    3879                 : 
    3880                 :   if (nsIDOMNode::TEXT_NODE == aContent->NodeType())
    3881                 :   {
    3882                 :     aContent->AppendTextTo(str);
    3883                 :     printf(":  \"%s\"", NS_LossyConvertUTF16toASCII(str).get());
    3884                 :   }
    3885                 : 
    3886                 :   printf("\n");
    3887                 :   fflush(stdout);
    3888                 : }
    3889                 : #endif
    3890                 : 
    3891                 : NS_IMETHODIMP
    3892               0 : nsTextServicesDocument::WillInsertNode(nsIDOMNode *aNode,
    3893                 :                               nsIDOMNode *aParent,
    3894                 :                               PRInt32     aPosition)
    3895                 : {
    3896               0 :   return NS_OK;
    3897                 : }
    3898                 : 
    3899                 : NS_IMETHODIMP
    3900               0 : nsTextServicesDocument::WillDeleteNode(nsIDOMNode *aChild)
    3901                 : {
    3902               0 :   return NS_OK;
    3903                 : }
    3904                 : 
    3905                 : NS_IMETHODIMP
    3906               0 : nsTextServicesDocument::WillSplitNode(nsIDOMNode *aExistingRightNode,
    3907                 :                              PRInt32     aOffset)
    3908                 : {
    3909               0 :   return NS_OK;
    3910                 : }
    3911                 : 
    3912                 : NS_IMETHODIMP
    3913               0 : nsTextServicesDocument::WillJoinNodes(nsIDOMNode  *aLeftNode,
    3914                 :                              nsIDOMNode  *aRightNode,
    3915                 :                              nsIDOMNode  *aParent)
    3916                 : {
    3917               0 :   return NS_OK;
    3918                 : }
    3919                 : 
    3920                 : 
    3921                 : // -------------------------------
    3922                 : // stubs for unused listen methods
    3923                 : // -------------------------------
    3924                 : 
    3925                 : NS_IMETHODIMP
    3926               0 : nsTextServicesDocument::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, PRInt32 aPosition)
    3927                 : {
    3928               0 :   return NS_OK;
    3929                 : }
    3930                 : 
    3931                 : NS_IMETHODIMP
    3932               0 : nsTextServicesDocument::DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult)
    3933                 : {
    3934               0 :   return NS_OK;
    3935                 : }
    3936                 : 
    3937                 : NS_IMETHODIMP
    3938               0 : nsTextServicesDocument::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAString &aString)
    3939                 : {
    3940               0 :   return NS_OK;
    3941                 : }
    3942                 : 
    3943                 : NS_IMETHODIMP
    3944               0 : nsTextServicesDocument::DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAString &aString, nsresult aResult)
    3945                 : {
    3946               0 :   return NS_OK;
    3947                 : }
    3948                 : 
    3949                 : NS_IMETHODIMP
    3950               0 : nsTextServicesDocument::WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
    3951                 : {
    3952               0 :   return NS_OK;
    3953                 : }
    3954                 : 
    3955                 : NS_IMETHODIMP
    3956               0 : nsTextServicesDocument::DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength, nsresult aResult)
    3957                 : {
    3958               0 :   return NS_OK;
    3959                 : }
    3960                 : 
    3961                 : NS_IMETHODIMP
    3962               0 : nsTextServicesDocument::WillDeleteSelection(nsISelection *aSelection)
    3963                 : {
    3964               0 :   return NS_OK;
    3965                 : }
    3966                 : 
    3967                 : NS_IMETHODIMP
    3968               0 : nsTextServicesDocument::DidDeleteSelection(nsISelection *aSelection)
    3969                 : {
    3970               0 :   return NS_OK;
    3971            4392 : }
    3972                 : 

Generated by: LCOV version 1.7