LCOV - code coverage report
Current view: directory - editor/libeditor/html - nsWSRunObject.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1037 0 0.0 %
Date: 2012-06-02 Functions: 46 0 0.0 %

       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                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      26                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsTextFragment.h"
      39                 : #include "nsWSRunObject.h"
      40                 : #include "nsIDOMNode.h"
      41                 : #include "nsHTMLEditor.h"
      42                 : #include "nsTextEditUtils.h"
      43                 : #include "nsIContent.h"
      44                 : #include "nsIDOMCharacterData.h"
      45                 : #include "nsCRT.h"
      46                 : #include "nsRange.h"
      47                 : #include "nsContentUtils.h"
      48                 : 
      49                 : const PRUnichar nbsp = 160;
      50                 : 
      51               0 : static bool IsBlockNode(nsIDOMNode* node)
      52                 : {
      53               0 :   bool isBlock (false);
      54               0 :   nsHTMLEditor::NodeIsBlockStatic(node, &isBlock);
      55               0 :   return isBlock;
      56                 : }
      57                 : 
      58                 : //- constructor / destructor -----------------------------------------------
      59               0 : nsWSRunObject::nsWSRunObject(nsHTMLEditor *aEd, nsIDOMNode *aNode, PRInt32 aOffset) :
      60                 : mNode(aNode)
      61                 : ,mOffset(aOffset)
      62                 : ,mPRE(false)
      63                 : ,mStartNode()
      64                 : ,mStartOffset(0)
      65                 : ,mStartReason(0)
      66                 : ,mStartReasonNode()
      67                 : ,mEndNode()
      68                 : ,mEndOffset(0)
      69                 : ,mEndReason(0)
      70                 : ,mEndReasonNode()
      71                 : ,mFirstNBSPNode()
      72                 : ,mFirstNBSPOffset(0)
      73                 : ,mLastNBSPNode()
      74                 : ,mLastNBSPOffset(0)
      75                 : ,mNodeArray()
      76                 : ,mStartRun(nsnull)
      77                 : ,mEndRun(nsnull)
      78               0 : ,mHTMLEditor(aEd)
      79                 : {
      80               0 :   GetWSNodes();
      81               0 :   GetRuns();
      82               0 : }
      83                 : 
      84               0 : nsWSRunObject::~nsWSRunObject()
      85                 : {
      86               0 :   ClearRuns();
      87               0 : }
      88                 : 
      89                 : 
      90                 : 
      91                 : //--------------------------------------------------------------------------------------------
      92                 : //   public static methods
      93                 : //--------------------------------------------------------------------------------------------
      94                 : 
      95                 : nsresult
      96               0 : nsWSRunObject::ScrubBlockBoundary(nsHTMLEditor *aHTMLEd, 
      97                 :                                   nsCOMPtr<nsIDOMNode> *aBlock,
      98                 :                                   BlockBoundary aBoundary,
      99                 :                                   PRInt32 *aOffset)
     100                 : {
     101               0 :   NS_ENSURE_TRUE(aBlock && aHTMLEd, NS_ERROR_NULL_POINTER);
     102               0 :   if ((aBoundary == kBlockStart) || (aBoundary == kBlockEnd))
     103               0 :     return ScrubBlockBoundaryInner(aHTMLEd, aBlock, aBoundary);
     104                 :   
     105                 :   // else we are scrubbing an outer boundary - just before or after
     106                 :   // a block element.
     107               0 :   NS_ENSURE_TRUE(aOffset, NS_ERROR_NULL_POINTER);
     108               0 :   nsAutoTrackDOMPoint tracker(aHTMLEd->mRangeUpdater, aBlock, aOffset);
     109               0 :   nsWSRunObject theWSObj(aHTMLEd, *aBlock, *aOffset);
     110               0 :   return theWSObj.Scrub();
     111                 : }
     112                 : 
     113                 : nsresult 
     114               0 : nsWSRunObject::PrepareToJoinBlocks(nsHTMLEditor *aHTMLEd, 
     115                 :                                    nsIDOMNode *aLeftParent, 
     116                 :                                    nsIDOMNode *aRightParent)
     117                 : {
     118               0 :   NS_ENSURE_TRUE(aLeftParent && aRightParent && aHTMLEd, NS_ERROR_NULL_POINTER);
     119                 :   PRUint32 count;
     120               0 :   aHTMLEd->GetLengthOfDOMNode(aLeftParent, count);
     121               0 :   nsWSRunObject leftWSObj(aHTMLEd, aLeftParent, count);
     122               0 :   nsWSRunObject rightWSObj(aHTMLEd, aRightParent, 0);
     123                 : 
     124               0 :   return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj);
     125                 : }
     126                 : 
     127                 : nsresult 
     128               0 : nsWSRunObject::PrepareToDeleteRange(nsHTMLEditor *aHTMLEd, 
     129                 :                                     nsCOMPtr<nsIDOMNode> *aStartNode,
     130                 :                                     PRInt32 *aStartOffset, 
     131                 :                                     nsCOMPtr<nsIDOMNode> *aEndNode,
     132                 :                                     PRInt32 *aEndOffset)
     133                 : {
     134               0 :   NS_ENSURE_TRUE(aStartNode && aEndNode && *aStartNode && *aEndNode && aStartOffset && aEndOffset && aHTMLEd, NS_ERROR_NULL_POINTER);
     135                 : 
     136               0 :   nsAutoTrackDOMPoint trackerStart(aHTMLEd->mRangeUpdater, aStartNode, aStartOffset);
     137               0 :   nsAutoTrackDOMPoint trackerEnd(aHTMLEd->mRangeUpdater, aEndNode, aEndOffset);
     138                 :   
     139               0 :   nsWSRunObject leftWSObj(aHTMLEd, *aStartNode, *aStartOffset);
     140               0 :   nsWSRunObject rightWSObj(aHTMLEd, *aEndNode, *aEndOffset);
     141                 : 
     142               0 :   return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj);
     143                 : }
     144                 : 
     145                 : nsresult 
     146               0 : nsWSRunObject::PrepareToDeleteNode(nsHTMLEditor *aHTMLEd, 
     147                 :                                    nsIDOMNode *aNode)
     148                 : {
     149               0 :   NS_ENSURE_TRUE(aNode && aHTMLEd, NS_ERROR_NULL_POINTER);
     150               0 :   nsresult res = NS_OK;
     151                 :   
     152               0 :   nsCOMPtr<nsIDOMNode> parent;
     153                 :   PRInt32 offset;
     154               0 :   res = aHTMLEd->GetNodeLocation(aNode, address_of(parent), &offset);
     155               0 :   NS_ENSURE_SUCCESS(res, res);
     156                 :   
     157               0 :   nsWSRunObject leftWSObj(aHTMLEd, parent, offset);
     158               0 :   nsWSRunObject rightWSObj(aHTMLEd, parent, offset+1);
     159                 : 
     160               0 :   return leftWSObj.PrepareToDeleteRangePriv(&rightWSObj);
     161                 : }
     162                 : 
     163                 : nsresult 
     164               0 : nsWSRunObject::PrepareToSplitAcrossBlocks(nsHTMLEditor *aHTMLEd, 
     165                 :                                           nsCOMPtr<nsIDOMNode> *aSplitNode, 
     166                 :                                           PRInt32 *aSplitOffset)
     167                 : {
     168               0 :   NS_ENSURE_TRUE(aSplitNode && aSplitOffset && *aSplitNode && aHTMLEd, NS_ERROR_NULL_POINTER);
     169                 : 
     170               0 :   nsAutoTrackDOMPoint tracker(aHTMLEd->mRangeUpdater, aSplitNode, aSplitOffset);
     171                 :   
     172               0 :   nsWSRunObject wsObj(aHTMLEd, *aSplitNode, *aSplitOffset);
     173                 : 
     174               0 :   return wsObj.PrepareToSplitAcrossBlocksPriv();
     175                 : }
     176                 : 
     177                 : //--------------------------------------------------------------------------------------------
     178                 : //   public instance methods
     179                 : //--------------------------------------------------------------------------------------------
     180                 : 
     181                 : nsresult 
     182               0 : nsWSRunObject::InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent, 
     183                 :                            PRInt32 *aInOutOffset, 
     184                 :                            nsCOMPtr<nsIDOMNode> *outBRNode, 
     185                 :                            nsIEditor::EDirection aSelect)
     186                 : {
     187                 :   // MOOSE: for now, we always assume non-PRE formatting.  Fix this later.
     188                 :   // meanwhile, the pre case is handled in WillInsertText in nsHTMLEditRules.cpp
     189               0 :   NS_ENSURE_TRUE(aInOutParent && aInOutOffset && outBRNode, NS_ERROR_NULL_POINTER);
     190                 : 
     191               0 :   nsresult res = NS_OK;
     192                 :   WSFragment *beforeRun, *afterRun;
     193               0 :   res = FindRun(*aInOutParent, *aInOutOffset, &beforeRun, false);
     194               0 :   res = FindRun(*aInOutParent, *aInOutOffset, &afterRun, true);
     195                 :   
     196                 :   {
     197                 :     // some scoping for nsAutoTrackDOMPoint.  This will track our insertion point
     198                 :     // while we tweak any surrounding whitespace
     199               0 :     nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, aInOutParent, aInOutOffset);
     200                 : 
     201                 :     // handle any changes needed to ws run after inserted br
     202               0 :     if (!afterRun)
     203                 :     {
     204                 :       // don't need to do anything.  just insert break.  ws won't change.
     205                 :     }
     206               0 :     else if (afterRun->mType & eTrailingWS)
     207                 :     {
     208                 :       // don't need to do anything.  just insert break.  ws won't change.
     209                 :     }
     210               0 :     else if (afterRun->mType & eLeadingWS)
     211                 :     {
     212                 :       // delete the leading ws that is after insertion point.  We don't
     213                 :       // have to (it would still not be significant after br), but it's 
     214                 :       // just more aesthetically pleasing to.
     215                 :       res = DeleteChars(*aInOutParent, *aInOutOffset, afterRun->mEndNode, afterRun->mEndOffset,
     216               0 :                         eOutsideUserSelectAll);
     217               0 :       NS_ENSURE_SUCCESS(res, res);
     218                 :     }
     219               0 :     else if (afterRun->mType == eNormalWS)
     220                 :     {
     221                 :       // need to determine if break at front of non-nbsp run.  if so
     222                 :       // convert run to nbsp.
     223               0 :       WSPoint thePoint;
     224               0 :       res = GetCharAfter(*aInOutParent, *aInOutOffset, &thePoint);
     225               0 :       if ( (NS_SUCCEEDED(res)) && thePoint.mTextNode && (nsCRT::IsAsciiSpace(thePoint.mChar)) )
     226                 :       {
     227               0 :         WSPoint prevPoint;
     228               0 :         res = GetCharBefore(thePoint, &prevPoint);
     229               0 :         if ( (NS_FAILED(res)) || (prevPoint.mTextNode && !nsCRT::IsAsciiSpace(prevPoint.mChar)) )
     230                 :         {
     231                 :           // we are at start of non-nbsps.  convert to a single nbsp.
     232               0 :           res = ConvertToNBSP(thePoint);
     233               0 :           NS_ENSURE_SUCCESS(res, res);
     234                 :         }
     235                 :       }
     236                 :     }
     237                 :     
     238                 :     // handle any changes needed to ws run before inserted br
     239               0 :     if (!beforeRun)
     240                 :     {
     241                 :       // don't need to do anything.  just insert break.  ws won't change.
     242                 :     }
     243               0 :     else if (beforeRun->mType & eLeadingWS)
     244                 :     {
     245                 :       // don't need to do anything.  just insert break.  ws won't change.
     246                 :     }
     247               0 :     else if (beforeRun->mType & eTrailingWS)
     248                 :     {
     249                 :       // need to delete the trailing ws that is before insertion point, because it 
     250                 :       // would become significant after break inserted.
     251                 :       res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset,
     252               0 :                         eOutsideUserSelectAll);
     253               0 :       NS_ENSURE_SUCCESS(res, res);
     254                 :     }
     255               0 :     else if (beforeRun->mType == eNormalWS)
     256                 :     {
     257                 :       // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
     258               0 :       res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
     259               0 :       NS_ENSURE_SUCCESS(res, res);
     260                 :     }
     261                 :   }
     262                 :   
     263                 :   // ready, aim, fire!
     264               0 :   return mHTMLEditor->CreateBRImpl(aInOutParent, aInOutOffset, outBRNode, aSelect);
     265                 : }
     266                 : 
     267                 : nsresult 
     268               0 : nsWSRunObject::InsertText(const nsAString& aStringToInsert, 
     269                 :                           nsCOMPtr<nsIDOMNode> *aInOutParent, 
     270                 :                           PRInt32 *aInOutOffset,
     271                 :                           nsIDOMDocument *aDoc)
     272                 : {
     273                 :   // MOOSE: for now, we always assume non-PRE formatting.  Fix this later.
     274                 :   // meanwhile, the pre case is handled in WillInsertText in nsHTMLEditRules.cpp
     275                 : 
     276                 :   // MOOSE: for now, just getting the ws logic straight.  This implementation
     277                 :   // is very slow.  Will need to replace edit rules impl with a more efficient
     278                 :   // text sink here that does the minimal amount of searching/replacing/copying
     279                 : 
     280               0 :   NS_ENSURE_TRUE(aInOutParent && aInOutOffset && aDoc, NS_ERROR_NULL_POINTER);
     281                 : 
     282               0 :   nsresult res = NS_OK;
     283               0 :   if (aStringToInsert.IsEmpty()) return res;
     284                 :   
     285                 :   // string copying sux.  
     286               0 :   nsAutoString theString(aStringToInsert);
     287                 :   
     288                 :   WSFragment *beforeRun, *afterRun;
     289               0 :   res = FindRun(*aInOutParent, *aInOutOffset, &beforeRun, false);
     290               0 :   res = FindRun(*aInOutParent, *aInOutOffset, &afterRun, true);
     291                 :   
     292                 :   {
     293                 :     // some scoping for nsAutoTrackDOMPoint.  This will track our insertion point
     294                 :     // while we tweak any surrounding whitespace
     295               0 :     nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, aInOutParent, aInOutOffset);
     296                 : 
     297                 :     // handle any changes needed to ws run after inserted text
     298               0 :     if (!afterRun)
     299                 :     {
     300                 :       // don't need to do anything.  just insert text.  ws won't change.
     301                 :     }
     302               0 :     else if (afterRun->mType & eTrailingWS)
     303                 :     {
     304                 :       // don't need to do anything.  just insert text.  ws won't change.
     305                 :     }
     306               0 :     else if (afterRun->mType & eLeadingWS)
     307                 :     {
     308                 :       // delete the leading ws that is after insertion point, because it 
     309                 :       // would become significant after text inserted.
     310                 :       res = DeleteChars(*aInOutParent, *aInOutOffset, afterRun->mEndNode, afterRun->mEndOffset,
     311               0 :                          eOutsideUserSelectAll);
     312               0 :       NS_ENSURE_SUCCESS(res, res);
     313                 :     }
     314               0 :     else if (afterRun->mType == eNormalWS)
     315                 :     {
     316                 :       // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
     317               0 :       res = CheckLeadingNBSP(afterRun, *aInOutParent, *aInOutOffset);
     318               0 :       NS_ENSURE_SUCCESS(res, res);
     319                 :     }
     320                 :     
     321                 :     // handle any changes needed to ws run before inserted text
     322               0 :     if (!beforeRun)
     323                 :     {
     324                 :       // don't need to do anything.  just insert text.  ws won't change.
     325                 :     }
     326               0 :     else if (beforeRun->mType & eLeadingWS)
     327                 :     {
     328                 :       // don't need to do anything.  just insert text.  ws won't change.
     329                 :     }
     330               0 :     else if (beforeRun->mType & eTrailingWS)
     331                 :     {
     332                 :       // need to delete the trailing ws that is before insertion point, because it 
     333                 :       // would become significant after text inserted.
     334                 :       res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset,
     335               0 :                         eOutsideUserSelectAll);
     336               0 :       NS_ENSURE_SUCCESS(res, res);
     337                 :     }
     338               0 :     else if (beforeRun->mType == eNormalWS)
     339                 :     {
     340                 :       // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
     341               0 :       res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
     342               0 :       NS_ENSURE_SUCCESS(res, res);
     343                 :     }
     344                 :   }
     345                 :   
     346                 :   // next up, tweak head and tail of string as needed.
     347                 :   // first the head:
     348                 :   // there are a variety of circumstances that would require us to convert a 
     349                 :   // leading ws char into an nbsp:
     350                 :   
     351               0 :   if (nsCRT::IsAsciiSpace(theString[0]))
     352                 :   {
     353                 :     // we have a leading space
     354               0 :     if (beforeRun)
     355                 :     {
     356               0 :       if (beforeRun->mType & eLeadingWS) 
     357                 :       {
     358               0 :         theString.SetCharAt(nbsp, 0);
     359                 :       }
     360               0 :       else if (beforeRun->mType & eNormalWS) 
     361                 :       {
     362               0 :         WSPoint wspoint;
     363               0 :         res = GetCharBefore(*aInOutParent, *aInOutOffset, &wspoint);
     364               0 :         if (NS_SUCCEEDED(res) && wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar))
     365                 :         {
     366               0 :           theString.SetCharAt(nbsp, 0);
     367                 :         }
     368                 :       }
     369                 :     }
     370                 :     else
     371                 :     {
     372               0 :       if ((mStartReason & eBlock) || (mStartReason == eBreak))
     373                 :       {
     374               0 :         theString.SetCharAt(nbsp, 0);
     375                 :       }
     376                 :     }
     377                 :   }
     378                 : 
     379                 :   // then the tail
     380               0 :   PRUint32 lastCharIndex = theString.Length()-1;
     381                 : 
     382               0 :   if (nsCRT::IsAsciiSpace(theString[lastCharIndex]))
     383                 :   {
     384                 :     // we have a leading space
     385               0 :     if (afterRun)
     386                 :     {
     387               0 :       if (afterRun->mType & eTrailingWS)
     388                 :       {
     389               0 :         theString.SetCharAt(nbsp, lastCharIndex);
     390                 :       }
     391               0 :       else if (afterRun->mType & eNormalWS) 
     392                 :       {
     393               0 :         WSPoint wspoint;
     394               0 :         res = GetCharAfter(*aInOutParent, *aInOutOffset, &wspoint);
     395               0 :         if (NS_SUCCEEDED(res) && wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar))
     396                 :         {
     397               0 :           theString.SetCharAt(nbsp, lastCharIndex);
     398                 :         }
     399                 :       }
     400                 :     }
     401                 :     else
     402                 :     {
     403               0 :       if ((mEndReason & eBlock))
     404                 :       {
     405               0 :         theString.SetCharAt(nbsp, lastCharIndex);
     406                 :       }
     407                 :     }
     408                 :   }
     409                 :   
     410                 :   // next scan string for adjacent ws and convert to nbsp/space combos
     411                 :   // MOOSE: don't need to convert tabs here since that is done by WillInsertText() 
     412                 :   // before we are called.  Eventually, all that logic will be pushed down into
     413                 :   // here and made more efficient.
     414                 :   PRUint32 j;
     415               0 :   bool prevWS = false;
     416               0 :   for (j=0; j<=lastCharIndex; j++)
     417                 :   {
     418               0 :     if (nsCRT::IsAsciiSpace(theString[j]))
     419                 :     {
     420               0 :       if (prevWS)
     421                 :       {
     422               0 :         theString.SetCharAt(nbsp, j-1);  // j-1 can't be negative because prevWS starts out false
     423                 :       }
     424                 :       else
     425                 :       {
     426               0 :         prevWS = true;
     427                 :       }
     428                 :     }
     429                 :     else
     430                 :     {
     431               0 :       prevWS = false;
     432                 :     }
     433                 :   }
     434                 :   
     435                 :   // ready, aim, fire!
     436               0 :   res = mHTMLEditor->InsertTextImpl(theString, aInOutParent, aInOutOffset, aDoc);
     437               0 :   return NS_OK;
     438                 : }
     439                 : 
     440                 : nsresult 
     441               0 : nsWSRunObject::DeleteWSBackward()
     442                 : {
     443               0 :   nsresult res = NS_OK;
     444               0 :   WSPoint point;
     445               0 :   res = GetCharBefore(mNode, mOffset, &point);  
     446               0 :   NS_ENSURE_SUCCESS(res, res);
     447               0 :   NS_ENSURE_TRUE(point.mTextNode, NS_OK);  // nothing to delete
     448                 :   
     449               0 :   if (mPRE)  // easy case, preformatted ws
     450                 :   {
     451               0 :     if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar == nbsp))
     452                 :     {
     453               0 :       nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode));
     454               0 :       PRInt32 startOffset = point.mOffset;
     455               0 :       PRInt32 endOffset = point.mOffset+1;
     456               0 :       return DeleteChars(node, startOffset, node, endOffset);
     457                 :     }
     458                 :   }
     459                 :   
     460                 :   // callers job to insure that previous char is really ws.
     461                 :   // If it is normal ws, we need to delete the whole run
     462               0 :   if (nsCRT::IsAsciiSpace(point.mChar))
     463                 :   {
     464               0 :     nsCOMPtr<nsIDOMNode> startNode, endNode, node(do_QueryInterface(point.mTextNode));
     465                 :     PRInt32 startOffset, endOffset;
     466                 :     res = GetAsciiWSBounds(eBoth, node, point.mOffset+1, address_of(startNode), 
     467               0 :                          &startOffset, address_of(endNode), &endOffset);
     468               0 :     NS_ENSURE_SUCCESS(res, res);
     469                 :     
     470                 :     // adjust surrounding ws
     471                 :     res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(startNode), &startOffset, 
     472               0 :                                               address_of(endNode), &endOffset);
     473               0 :     NS_ENSURE_SUCCESS(res, res);
     474                 :     
     475                 :     // finally, delete that ws
     476               0 :     return DeleteChars(startNode, startOffset, endNode, endOffset);
     477                 :   }
     478               0 :   else if (point.mChar == nbsp)
     479                 :   {
     480               0 :     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode));
     481                 :     // adjust surrounding ws
     482               0 :     PRInt32 startOffset = point.mOffset;
     483               0 :     PRInt32 endOffset = point.mOffset+1;
     484                 :     res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(node), &startOffset, 
     485               0 :                                               address_of(node), &endOffset);
     486               0 :     NS_ENSURE_SUCCESS(res, res);
     487                 :     
     488                 :     // finally, delete that ws
     489               0 :     return DeleteChars(node, startOffset, node, endOffset);
     490                 :   
     491                 :   }
     492               0 :   return NS_OK;
     493                 : }
     494                 : 
     495                 : nsresult 
     496               0 : nsWSRunObject::DeleteWSForward()
     497                 : {
     498               0 :   nsresult res = NS_OK;
     499               0 :   WSPoint point;
     500               0 :   res = GetCharAfter(mNode, mOffset, &point);  
     501               0 :   NS_ENSURE_SUCCESS(res, res);
     502               0 :   NS_ENSURE_TRUE(point.mTextNode, NS_OK);  // nothing to delete
     503                 :   
     504               0 :   if (mPRE)  // easy case, preformatted ws
     505                 :   {
     506               0 :     if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar == nbsp))
     507                 :     {
     508               0 :       nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode));
     509               0 :       PRInt32 startOffset = point.mOffset;
     510               0 :       PRInt32 endOffset = point.mOffset+1;
     511               0 :       return DeleteChars(node, startOffset, node, endOffset);
     512                 :     }
     513                 :   }
     514                 :   
     515                 :   // callers job to insure that next char is really ws.
     516                 :   // If it is normal ws, we need to delete the whole run
     517               0 :   if (nsCRT::IsAsciiSpace(point.mChar))
     518                 :   {
     519               0 :     nsCOMPtr<nsIDOMNode> startNode, endNode, node(do_QueryInterface(point.mTextNode));
     520                 :     PRInt32 startOffset, endOffset;
     521                 :     res = GetAsciiWSBounds(eBoth, node, point.mOffset+1, address_of(startNode), 
     522               0 :                          &startOffset, address_of(endNode), &endOffset);
     523               0 :     NS_ENSURE_SUCCESS(res, res);
     524                 :     
     525                 :     // adjust surrounding ws
     526                 :     res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(startNode), &startOffset, 
     527               0 :                                               address_of(endNode), &endOffset);
     528               0 :     NS_ENSURE_SUCCESS(res, res);
     529                 :     
     530                 :     // finally, delete that ws
     531               0 :     return DeleteChars(startNode, startOffset, endNode, endOffset);
     532                 :   }
     533               0 :   else if (point.mChar == nbsp)
     534                 :   {
     535               0 :     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(point.mTextNode));
     536                 :     // adjust surrounding ws
     537               0 :     PRInt32 startOffset = point.mOffset;
     538               0 :     PRInt32 endOffset = point.mOffset+1;
     539                 :     res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(node), &startOffset, 
     540               0 :                                               address_of(node), &endOffset);
     541               0 :     NS_ENSURE_SUCCESS(res, res);
     542                 :     
     543                 :     // finally, delete that ws
     544               0 :     return DeleteChars(node, startOffset, node, endOffset);
     545                 :   
     546                 :   }
     547               0 :   return NS_OK;
     548                 : }
     549                 : 
     550                 : nsresult 
     551               0 : nsWSRunObject::PriorVisibleNode(nsIDOMNode *aNode, 
     552                 :                                 PRInt32 aOffset, 
     553                 :                                 nsCOMPtr<nsIDOMNode> *outVisNode, 
     554                 :                                 PRInt32 *outVisOffset,
     555                 :                                 PRInt16 *outType)
     556                 : {
     557                 :   // Find first visible thing before the point.  position outVisNode/outVisOffset
     558                 :   // just _after_ that thing.  If we don't find anything return start of ws.
     559               0 :   NS_ENSURE_TRUE(aNode && outVisNode && outVisOffset && outType, NS_ERROR_NULL_POINTER);
     560                 :     
     561               0 :   *outType = eNone;
     562                 :   WSFragment *run;
     563               0 :   FindRun(aNode, aOffset, &run, false);
     564                 :   
     565                 :   // is there a visible run there or earlier?
     566               0 :   while (run)
     567                 :   {
     568               0 :     if (run->mType == eNormalWS)
     569                 :     {
     570               0 :       WSPoint point;
     571               0 :       GetCharBefore(aNode, aOffset, &point);
     572               0 :       if (point.mTextNode)
     573                 :       {
     574               0 :         *outVisNode = do_QueryInterface(point.mTextNode);
     575               0 :         *outVisOffset = point.mOffset+1;
     576               0 :         if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar==nbsp))
     577                 :         {
     578               0 :           *outType = eNormalWS;
     579                 :         }
     580               0 :         else if (!point.mChar)
     581                 :         {
     582                 :           // MOOSE: not possible?
     583               0 :           *outType = eNone;
     584                 :         }
     585                 :         else
     586                 :         {
     587               0 :           *outType = eText;
     588                 :         }
     589               0 :         return NS_OK;
     590                 :       }
     591                 :       // else if no text node then keep looking.  We should eventually fall out of loop
     592                 :     }
     593                 : 
     594               0 :     run = run->mLeft;
     595                 :   }
     596                 :   
     597                 :   // if we get here then nothing in ws data to find.  return start reason
     598               0 :   *outVisNode = mStartReasonNode;
     599               0 :   *outVisOffset = mStartOffset;  // this really isn't meaningful if mStartReasonNode!=mStartNode
     600               0 :   *outType = mStartReason;
     601               0 :   return NS_OK;
     602                 : }
     603                 : 
     604                 : 
     605                 : nsresult 
     606               0 : nsWSRunObject::NextVisibleNode (nsIDOMNode *aNode, 
     607                 :                                 PRInt32 aOffset, 
     608                 :                                 nsCOMPtr<nsIDOMNode> *outVisNode, 
     609                 :                                 PRInt32 *outVisOffset,
     610                 :                                 PRInt16 *outType)
     611                 : {
     612                 :   // Find first visible thing after the point.  position outVisNode/outVisOffset
     613                 :   // just _before_ that thing.  If we don't find anything return end of ws.
     614               0 :   NS_ENSURE_TRUE(aNode && outVisNode && outVisOffset && outType, NS_ERROR_NULL_POINTER);
     615                 :     
     616                 :   WSFragment *run;
     617               0 :   FindRun(aNode, aOffset, &run, true);
     618                 :   
     619                 :   // is there a visible run there or later?
     620               0 :   while (run)
     621                 :   {
     622               0 :     if (run->mType == eNormalWS)
     623                 :     {
     624               0 :       WSPoint point;
     625               0 :       GetCharAfter(aNode, aOffset, &point);
     626               0 :       if (point.mTextNode)
     627                 :       {
     628               0 :         *outVisNode = do_QueryInterface(point.mTextNode);
     629               0 :         *outVisOffset = point.mOffset;
     630               0 :         if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar==nbsp))
     631                 :         {
     632               0 :           *outType = eNormalWS;
     633                 :         }
     634               0 :         else if (!point.mChar)
     635                 :         {
     636                 :           // MOOSE: not possible?
     637               0 :           *outType = eNone;
     638                 :         }
     639                 :         else
     640                 :         {
     641               0 :           *outType = eText;
     642                 :         }
     643               0 :         return NS_OK;
     644                 :       }
     645                 :       // else if no text node then keep looking.  We should eventually fall out of loop
     646                 :     }
     647                 : 
     648               0 :     run = run->mRight;
     649                 :   }
     650                 :   
     651                 :   // if we get here then nothing in ws data to find.  return end reason
     652               0 :   *outVisNode = mEndReasonNode;
     653               0 :   *outVisOffset = mEndOffset; // this really isn't meaningful if mEndReasonNode!=mEndNode
     654               0 :   *outType = mEndReason;
     655               0 :   return NS_OK;
     656                 : }
     657                 : 
     658                 : nsresult 
     659               0 : nsWSRunObject::AdjustWhitespace()
     660                 : {
     661                 :   // this routine examines a run of ws and tries to get rid of some unneeded nbsp's,
     662                 :   // replacing them with regualr ascii space if possible.  Keeping things simple
     663                 :   // for now and just trying to fix up the trailing ws in the run.
     664               0 :   if (!mLastNBSPNode) {
     665                 :     // nothing to do!
     666               0 :     return NS_OK;
     667                 :   }
     668               0 :   nsresult res = NS_OK;
     669               0 :   WSFragment *curRun = mStartRun;
     670               0 :   while (curRun)
     671                 :   {
     672                 :     // look for normal ws run
     673               0 :     if (curRun->mType == eNormalWS)
     674                 :     {
     675               0 :       res = CheckTrailingNBSPOfRun(curRun);
     676               0 :       break;
     677                 :     }
     678               0 :     curRun = curRun->mRight;
     679                 :   }
     680               0 :   return res;
     681                 : }
     682                 : 
     683                 : 
     684                 : //--------------------------------------------------------------------------------------------
     685                 : //   protected methods
     686                 : //--------------------------------------------------------------------------------------------
     687                 : 
     688                 : already_AddRefed<nsIDOMNode>
     689               0 : nsWSRunObject::GetWSBoundingParent()
     690                 : {
     691               0 :   NS_ENSURE_TRUE(mNode, nsnull);
     692               0 :   nsCOMPtr<nsIDOMNode> wsBoundingParent = mNode;
     693               0 :   while (!IsBlockNode(wsBoundingParent))
     694                 :   {
     695               0 :     nsCOMPtr<nsIDOMNode> parent;
     696               0 :     wsBoundingParent->GetParentNode(getter_AddRefs(parent));
     697               0 :     if (!parent || !mHTMLEditor->IsEditable(parent))
     698                 :       break;
     699               0 :     wsBoundingParent.swap(parent);
     700                 :   }
     701               0 :   return wsBoundingParent.forget();
     702                 : }
     703                 : 
     704                 : nsresult
     705               0 : nsWSRunObject::GetWSNodes()
     706                 : {
     707                 :   // collect up an array of nodes that are contiguous with the insertion point
     708                 :   // and which contain only whitespace.  Stop if you reach non-ws text or a new 
     709                 :   // block boundary.
     710               0 :   nsresult res = NS_OK;
     711                 :   
     712               0 :   DOMPoint start(mNode, mOffset), end(mNode, mOffset);
     713               0 :   nsCOMPtr<nsIDOMNode> wsBoundingParent = GetWSBoundingParent();
     714                 : 
     715                 :   // first look backwards to find preceding ws nodes
     716               0 :   if (mHTMLEditor->IsTextNode(mNode))
     717                 :   {
     718               0 :     nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNode));
     719               0 :     const nsTextFragment *textFrag = textNode->GetText();
     720                 :     
     721               0 :     res = PrependNodeToList(mNode);
     722               0 :     NS_ENSURE_SUCCESS(res, res);
     723               0 :     if (mOffset)
     724                 :     {
     725                 :       PRInt32 pos;
     726               0 :       for (pos=mOffset-1; pos>=0; pos--)
     727                 :       {
     728                 :         // sanity bounds check the char position.  bug 136165
     729               0 :         if (PRUint32(pos) >= textFrag->GetLength())
     730                 :         {
     731               0 :           NS_NOTREACHED("looking beyond end of text fragment");
     732               0 :           continue;
     733                 :         }
     734               0 :         PRUnichar theChar = textFrag->CharAt(pos);
     735               0 :         if (!nsCRT::IsAsciiSpace(theChar))
     736                 :         {
     737               0 :           if (theChar != nbsp)
     738                 :           {
     739               0 :             mStartNode = mNode;
     740               0 :             mStartOffset = pos+1;
     741               0 :             mStartReason = eText;
     742               0 :             mStartReasonNode = mNode;
     743               0 :             break;
     744                 :           }
     745                 :           // as we look backwards update our earliest found nbsp
     746               0 :           mFirstNBSPNode = mNode;
     747               0 :           mFirstNBSPOffset = pos;
     748                 :           // also keep track of latest nbsp so far
     749               0 :           if (!mLastNBSPNode)
     750                 :           {
     751               0 :             mLastNBSPNode = mNode;
     752               0 :             mLastNBSPOffset = pos;
     753                 :           }
     754                 :         }
     755               0 :         start.SetPoint(mNode,pos);
     756                 :       }
     757                 :     }
     758                 :   }
     759                 : 
     760               0 :   nsCOMPtr<nsIDOMNode> priorNode;
     761               0 :   while (!mStartNode)
     762                 :   {
     763                 :     // we haven't found the start of ws yet.  Keep looking
     764               0 :     res = GetPreviousWSNode(start, wsBoundingParent, address_of(priorNode));
     765               0 :     NS_ENSURE_SUCCESS(res, res);
     766               0 :     if (priorNode)
     767                 :     {
     768               0 :       if (IsBlockNode(priorNode))
     769                 :       {
     770               0 :         start.GetPoint(mStartNode, mStartOffset);
     771               0 :         mStartReason = eOtherBlock;
     772               0 :         mStartReasonNode = priorNode;
     773                 :       }
     774               0 :       else if (mHTMLEditor->IsTextNode(priorNode))
     775                 :       {
     776               0 :         res = PrependNodeToList(priorNode);
     777               0 :         NS_ENSURE_SUCCESS(res, res);
     778               0 :         nsCOMPtr<nsIContent> textNode(do_QueryInterface(priorNode));
     779                 :         const nsTextFragment *textFrag;
     780               0 :         if (!textNode || !(textFrag = textNode->GetText())) {
     781               0 :           return NS_ERROR_NULL_POINTER;
     782                 :         }
     783               0 :         PRUint32 len = textNode->TextLength();
     784                 : 
     785               0 :         if (len < 1)
     786                 :         {
     787                 :           // Zero length text node. Set start point to it
     788                 :           // so we can get past it!
     789               0 :           start.SetPoint(priorNode,0);
     790                 :         }
     791                 :         else
     792                 :         {
     793                 :           PRInt32 pos;
     794               0 :           for (pos=len-1; pos>=0; pos--)
     795                 :           {
     796                 :             // sanity bounds check the char position.  bug 136165
     797               0 :             if (PRUint32(pos) >= textFrag->GetLength())
     798                 :             {
     799               0 :               NS_NOTREACHED("looking beyond end of text fragment");
     800               0 :               continue;
     801                 :             }
     802               0 :             PRUnichar theChar = textFrag->CharAt(pos);
     803               0 :             if (!nsCRT::IsAsciiSpace(theChar))
     804                 :             {
     805               0 :               if (theChar != nbsp)
     806                 :               {
     807               0 :                 mStartNode = priorNode;
     808               0 :                 mStartOffset = pos+1;
     809               0 :                 mStartReason = eText;
     810               0 :                 mStartReasonNode = priorNode;
     811               0 :                 break;
     812                 :               }
     813                 :               // as we look backwards update our earliest found nbsp
     814               0 :               mFirstNBSPNode = priorNode;
     815               0 :               mFirstNBSPOffset = pos;
     816                 :               // also keep track of latest nbsp so far
     817               0 :               if (!mLastNBSPNode)
     818                 :               {
     819               0 :                 mLastNBSPNode = priorNode;
     820               0 :                 mLastNBSPOffset = pos;
     821                 :               }
     822                 :             }
     823               0 :             start.SetPoint(priorNode,pos);
     824                 :           }
     825                 :         }
     826                 :       }
     827                 :       else
     828                 :       {
     829                 :         // it's a break or a special node, like <img>, that is not a block and not
     830                 :         // a break but still serves as a terminator to ws runs.
     831               0 :         start.GetPoint(mStartNode, mStartOffset);
     832               0 :         if (nsTextEditUtils::IsBreak(priorNode))
     833               0 :           mStartReason = eBreak;
     834                 :         else
     835               0 :           mStartReason = eSpecial;
     836               0 :         mStartReasonNode = priorNode;
     837                 :       }
     838                 :     }
     839                 :     else
     840                 :     {
     841                 :       // no prior node means we exhausted wsBoundingParent
     842               0 :       start.GetPoint(mStartNode, mStartOffset);
     843               0 :       mStartReason = eThisBlock;
     844               0 :       mStartReasonNode = wsBoundingParent;
     845                 :     } 
     846                 :   }
     847                 :   
     848                 :   // then look ahead to find following ws nodes
     849               0 :   if (mHTMLEditor->IsTextNode(mNode))
     850                 :   {
     851                 :     // don't need to put it on list. it already is from code above
     852               0 :     nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNode));
     853               0 :     const nsTextFragment *textFrag = textNode->GetText();
     854                 : 
     855               0 :     PRUint32 len = textNode->TextLength();
     856               0 :     if (PRUint16(mOffset)<len)
     857                 :     {
     858                 :       PRInt32 pos;
     859               0 :       for (pos=mOffset; PRUint32(pos)<len; pos++)
     860                 :       {
     861                 :         // sanity bounds check the char position.  bug 136165
     862               0 :         if ((pos<0) || (PRUint32(pos)>=textFrag->GetLength()))
     863                 :         {
     864               0 :           NS_NOTREACHED("looking beyond end of text fragment");
     865               0 :           continue;
     866                 :         }
     867               0 :         PRUnichar theChar = textFrag->CharAt(pos);
     868               0 :         if (!nsCRT::IsAsciiSpace(theChar))
     869                 :         {
     870               0 :           if (theChar != nbsp)
     871                 :           {
     872               0 :             mEndNode = mNode;
     873               0 :             mEndOffset = pos;
     874               0 :             mEndReason = eText;
     875               0 :             mEndReasonNode = mNode;
     876               0 :             break;
     877                 :           }
     878                 :           // as we look forwards update our latest found nbsp
     879               0 :           mLastNBSPNode = mNode;
     880               0 :           mLastNBSPOffset = pos;
     881                 :           // also keep track of earliest nbsp so far
     882               0 :           if (!mFirstNBSPNode)
     883                 :           {
     884               0 :             mFirstNBSPNode = mNode;
     885               0 :             mFirstNBSPOffset = pos;
     886                 :           }
     887                 :         }
     888               0 :         end.SetPoint(mNode,pos+1);
     889                 :       }
     890                 :     }
     891                 :   }
     892                 : 
     893               0 :   nsCOMPtr<nsIDOMNode> nextNode;
     894               0 :   while (!mEndNode)
     895                 :   {
     896                 :     // we haven't found the end of ws yet.  Keep looking
     897               0 :     res = GetNextWSNode(end, wsBoundingParent, address_of(nextNode));
     898               0 :     NS_ENSURE_SUCCESS(res, res);
     899               0 :     if (nextNode)
     900                 :     {
     901               0 :       if (IsBlockNode(nextNode))
     902                 :       {
     903                 :         // we encountered a new block.  therefore no more ws.
     904               0 :         end.GetPoint(mEndNode, mEndOffset);
     905               0 :         mEndReason = eOtherBlock;
     906               0 :         mEndReasonNode = nextNode;
     907                 :       }
     908               0 :       else if (mHTMLEditor->IsTextNode(nextNode))
     909                 :       {
     910               0 :         res = AppendNodeToList(nextNode);
     911               0 :         NS_ENSURE_SUCCESS(res, res);
     912               0 :         nsCOMPtr<nsIContent> textNode(do_QueryInterface(nextNode));
     913                 :         const nsTextFragment *textFrag;
     914               0 :         if (!textNode || !(textFrag = textNode->GetText())) {
     915               0 :           return NS_ERROR_NULL_POINTER;
     916                 :         }
     917               0 :         PRUint32 len = textNode->TextLength();
     918                 : 
     919               0 :         if (len < 1)
     920                 :         {
     921                 :           // Zero length text node. Set end point to it
     922                 :           // so we can get past it!
     923               0 :           end.SetPoint(nextNode,0);
     924                 :         }
     925                 :         else
     926                 :         {
     927                 :           PRInt32 pos;
     928               0 :           for (pos=0; PRUint32(pos)<len; pos++)
     929                 :           {
     930                 :             // sanity bounds check the char position.  bug 136165
     931               0 :             if (PRUint32(pos) >= textFrag->GetLength())
     932                 :             {
     933               0 :               NS_NOTREACHED("looking beyond end of text fragment");
     934               0 :               continue;
     935                 :             }
     936               0 :             PRUnichar theChar = textFrag->CharAt(pos);
     937               0 :             if (!nsCRT::IsAsciiSpace(theChar))
     938                 :             {
     939               0 :               if (theChar != nbsp)
     940                 :               {
     941               0 :                 mEndNode = nextNode;
     942               0 :                 mEndOffset = pos;
     943               0 :                 mEndReason = eText;
     944               0 :                 mEndReasonNode = nextNode;
     945               0 :                 break;
     946                 :               }
     947                 :               // as we look forwards update our latest found nbsp
     948               0 :               mLastNBSPNode = nextNode;
     949               0 :               mLastNBSPOffset = pos;
     950                 :               // also keep track of earliest nbsp so far
     951               0 :               if (!mFirstNBSPNode)
     952                 :               {
     953               0 :                 mFirstNBSPNode = nextNode;
     954               0 :                 mFirstNBSPOffset = pos;
     955                 :               }
     956                 :             }
     957               0 :             end.SetPoint(nextNode,pos+1);
     958                 :           }
     959                 :         }
     960                 :       }
     961                 :       else
     962                 :       {
     963                 :         // we encountered a break or a special node, like <img>, 
     964                 :         // that is not a block and not a break but still 
     965                 :         // serves as a terminator to ws runs.
     966               0 :         end.GetPoint(mEndNode, mEndOffset);
     967               0 :         if (nsTextEditUtils::IsBreak(nextNode))
     968               0 :           mEndReason = eBreak;
     969                 :         else
     970               0 :           mEndReason = eSpecial;
     971               0 :         mEndReasonNode = nextNode;
     972                 :       }
     973                 :     }
     974                 :     else
     975                 :     {
     976                 :       // no next node means we exhausted wsBoundingParent
     977               0 :       end.GetPoint(mEndNode, mEndOffset);
     978               0 :       mEndReason = eThisBlock;
     979               0 :       mEndReasonNode = wsBoundingParent;
     980                 :     } 
     981                 :   }
     982                 : 
     983               0 :   return NS_OK;
     984                 : }
     985                 : 
     986                 : nsresult
     987               0 : nsWSRunObject::GetRuns()
     988                 : {
     989               0 :   ClearRuns();
     990                 :   
     991                 :   // handle some easy cases first
     992               0 :   mHTMLEditor->IsPreformatted(mNode, &mPRE);
     993                 :   // if it's preformatedd, or if we are surrounded by text or special, it's all one
     994                 :   // big normal ws run
     995               0 :   if ( mPRE || (((mStartReason == eText) || (mStartReason == eSpecial)) &&
     996                 :        ((mEndReason == eText) || (mEndReason == eSpecial) || (mEndReason == eBreak))) )
     997                 :   {
     998               0 :     return MakeSingleWSRun(eNormalWS);
     999                 :   }
    1000                 : 
    1001                 :   // if we are before or after a block (or after a break), and there are no nbsp's,
    1002                 :   // then it's all non-rendering ws.
    1003               0 :   if ( !(mFirstNBSPNode || mLastNBSPNode) &&
    1004                 :       ( (mStartReason & eBlock) || (mStartReason == eBreak) || (mEndReason & eBlock) ) )
    1005                 :   {
    1006               0 :     PRInt16 wstype = eNone;
    1007               0 :     if ((mStartReason & eBlock) || (mStartReason == eBreak))
    1008               0 :       wstype = eLeadingWS;
    1009               0 :     if (mEndReason & eBlock) 
    1010               0 :       wstype |= eTrailingWS;
    1011               0 :     return MakeSingleWSRun(wstype);
    1012                 :   }
    1013                 :   
    1014                 :   // otherwise a little trickier.  shucks.
    1015               0 :   mStartRun = new WSFragment();
    1016               0 :   NS_ENSURE_TRUE(mStartRun, NS_ERROR_NULL_POINTER);
    1017               0 :   mStartRun->mStartNode = mStartNode;
    1018               0 :   mStartRun->mStartOffset = mStartOffset;
    1019                 :   
    1020               0 :   if ( (mStartReason & eBlock) || (mStartReason == eBreak) )
    1021                 :   {
    1022                 :     // set up mStartRun
    1023               0 :     mStartRun->mType = eLeadingWS;
    1024               0 :     mStartRun->mEndNode = mFirstNBSPNode;
    1025               0 :     mStartRun->mEndOffset = mFirstNBSPOffset;
    1026               0 :     mStartRun->mLeftType = mStartReason;
    1027               0 :     mStartRun->mRightType = eNormalWS;
    1028                 :     
    1029                 :     // set up next run
    1030               0 :     WSFragment *normalRun = new WSFragment();
    1031               0 :     NS_ENSURE_TRUE(normalRun, NS_ERROR_NULL_POINTER);
    1032               0 :     mStartRun->mRight = normalRun;
    1033               0 :     normalRun->mType = eNormalWS;
    1034               0 :     normalRun->mStartNode = mFirstNBSPNode;
    1035               0 :     normalRun->mStartOffset = mFirstNBSPOffset;
    1036               0 :     normalRun->mLeftType = eLeadingWS;
    1037               0 :     normalRun->mLeft = mStartRun;
    1038               0 :     if (mEndReason != eBlock)
    1039                 :     {
    1040                 :       // then no trailing ws.  this normal run ends the overall ws run.
    1041               0 :       normalRun->mRightType = mEndReason;
    1042               0 :       normalRun->mEndNode   = mEndNode;
    1043               0 :       normalRun->mEndOffset = mEndOffset;
    1044               0 :       mEndRun = normalRun;
    1045                 :     }
    1046                 :     else
    1047                 :     {
    1048                 :       // we might have trailing ws.
    1049                 :       // it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
    1050                 :       // will point to it, even though in general start/end points not
    1051                 :       // guaranteed to be in text nodes.
    1052               0 :       if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
    1053                 :       {
    1054                 :         // normal ws runs right up to adjacent block (nbsp next to block)
    1055               0 :         normalRun->mRightType = mEndReason;
    1056               0 :         normalRun->mEndNode   = mEndNode;
    1057               0 :         normalRun->mEndOffset = mEndOffset;
    1058               0 :         mEndRun = normalRun;
    1059                 :       }
    1060                 :       else
    1061                 :       {
    1062               0 :         normalRun->mEndNode = mLastNBSPNode;
    1063               0 :         normalRun->mEndOffset = mLastNBSPOffset+1;
    1064               0 :         normalRun->mRightType = eTrailingWS;
    1065                 :         
    1066                 :         // set up next run
    1067               0 :         WSFragment *lastRun = new WSFragment();
    1068               0 :         NS_ENSURE_TRUE(lastRun, NS_ERROR_NULL_POINTER);
    1069               0 :         lastRun->mType = eTrailingWS;
    1070               0 :         lastRun->mStartNode = mLastNBSPNode;
    1071               0 :         lastRun->mStartOffset = mLastNBSPOffset+1;
    1072               0 :         lastRun->mEndNode = mEndNode;
    1073               0 :         lastRun->mEndOffset = mEndOffset;
    1074               0 :         lastRun->mLeftType = eNormalWS;
    1075               0 :         lastRun->mLeft = normalRun;
    1076               0 :         lastRun->mRightType = mEndReason;
    1077               0 :         mEndRun = lastRun;
    1078               0 :         normalRun->mRight = lastRun;
    1079                 :       }
    1080               0 :     }
    1081                 :   }
    1082                 :   else // mStartReason is not eBlock or eBreak
    1083                 :   {
    1084                 :     // set up mStartRun
    1085               0 :     mStartRun->mType = eNormalWS;
    1086               0 :     mStartRun->mEndNode = mLastNBSPNode;
    1087               0 :     mStartRun->mEndOffset = mLastNBSPOffset+1;
    1088               0 :     mStartRun->mLeftType = mStartReason;
    1089                 : 
    1090                 :     // we might have trailing ws.
    1091                 :     // it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
    1092                 :     // will point to it, even though in general start/end points not
    1093                 :     // guaranteed to be in text nodes.
    1094               0 :     if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
    1095                 :     {
    1096               0 :       mStartRun->mRightType = mEndReason;
    1097               0 :       mStartRun->mEndNode   = mEndNode;
    1098               0 :       mStartRun->mEndOffset = mEndOffset;
    1099               0 :       mEndRun = mStartRun;
    1100                 :     }
    1101                 :     else
    1102                 :     {
    1103                 :       // set up next run
    1104               0 :       WSFragment *lastRun = new WSFragment();
    1105               0 :       NS_ENSURE_TRUE(lastRun, NS_ERROR_NULL_POINTER);
    1106               0 :       lastRun->mType = eTrailingWS;
    1107               0 :       lastRun->mStartNode = mLastNBSPNode;
    1108               0 :       lastRun->mStartOffset = mLastNBSPOffset+1;
    1109               0 :       lastRun->mLeftType = eNormalWS;
    1110               0 :       lastRun->mLeft = mStartRun;
    1111               0 :       lastRun->mRightType = mEndReason;
    1112               0 :       mEndRun = lastRun;
    1113               0 :       mStartRun->mRight = lastRun;
    1114               0 :       mStartRun->mRightType = eTrailingWS;
    1115                 :     }
    1116                 :   }
    1117                 :   
    1118               0 :   return NS_OK;
    1119                 : }
    1120                 : 
    1121                 : void
    1122               0 : nsWSRunObject::ClearRuns()
    1123                 : {
    1124                 :   WSFragment *tmp, *run;
    1125               0 :   run = mStartRun;
    1126               0 :   while (run)
    1127                 :   {
    1128               0 :     tmp = run->mRight;
    1129               0 :     delete run;
    1130               0 :     run = tmp;
    1131                 :   }
    1132               0 :   mStartRun = 0;
    1133               0 :   mEndRun = 0;
    1134               0 : }
    1135                 : 
    1136                 : nsresult 
    1137               0 : nsWSRunObject::MakeSingleWSRun(PRInt16 aType)
    1138                 : {
    1139               0 :   mStartRun = new WSFragment();
    1140               0 :   NS_ENSURE_TRUE(mStartRun, NS_ERROR_NULL_POINTER);
    1141                 : 
    1142               0 :   mStartRun->mStartNode   = mStartNode;
    1143               0 :   mStartRun->mStartOffset = mStartOffset;
    1144               0 :   mStartRun->mType        = aType;
    1145               0 :   mStartRun->mEndNode     = mEndNode;
    1146               0 :   mStartRun->mEndOffset   = mEndOffset;
    1147               0 :   mStartRun->mLeftType    = mStartReason;
    1148               0 :   mStartRun->mRightType   = mEndReason;
    1149                 :   
    1150               0 :   mEndRun  = mStartRun;
    1151                 :   
    1152               0 :   return NS_OK;
    1153                 : }
    1154                 : 
    1155                 : nsresult 
    1156               0 : nsWSRunObject::PrependNodeToList(nsIDOMNode *aNode)
    1157                 : {
    1158               0 :   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
    1159               0 :   if (!mNodeArray.InsertObjectAt(aNode, 0))
    1160               0 :     return NS_ERROR_FAILURE;
    1161               0 :   return NS_OK;
    1162                 : }
    1163                 : 
    1164                 : nsresult 
    1165               0 : nsWSRunObject::AppendNodeToList(nsIDOMNode *aNode)
    1166                 : {
    1167               0 :   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
    1168               0 :   if (!mNodeArray.AppendObject(aNode))
    1169               0 :     return NS_ERROR_FAILURE;
    1170               0 :   return NS_OK;
    1171                 : }
    1172                 : 
    1173                 : nsresult 
    1174               0 : nsWSRunObject::GetPreviousWSNode(nsIDOMNode *aStartNode, 
    1175                 :                                  nsIDOMNode *aBlockParent, 
    1176                 :                                  nsCOMPtr<nsIDOMNode> *aPriorNode)
    1177                 : {
    1178                 :   // can't really recycle various getnext/prior routines because we
    1179                 :   // have special needs here.  Need to step into inline containers but
    1180                 :   // not block containers.
    1181               0 :   NS_ENSURE_TRUE(aStartNode && aBlockParent && aPriorNode, NS_ERROR_NULL_POINTER);
    1182                 :   
    1183               0 :   nsresult res = aStartNode->GetPreviousSibling(getter_AddRefs(*aPriorNode));
    1184               0 :   NS_ENSURE_SUCCESS(res, res);
    1185               0 :   nsCOMPtr<nsIDOMNode> temp, curNode = aStartNode;
    1186               0 :   while (!*aPriorNode)
    1187                 :   {
    1188                 :     // we have exhausted nodes in parent of aStartNode.
    1189               0 :     res = curNode->GetParentNode(getter_AddRefs(temp));
    1190               0 :     NS_ENSURE_SUCCESS(res, res);
    1191               0 :     NS_ENSURE_TRUE(temp, NS_ERROR_NULL_POINTER);
    1192               0 :     if (temp == aBlockParent)
    1193                 :     {
    1194                 :       // we have exhausted nodes in the block parent.  The convention here is to return null.
    1195               0 :       *aPriorNode = nsnull;
    1196               0 :       return NS_OK;
    1197                 :     }
    1198                 :     // we have a parent: look for previous sibling
    1199               0 :     res = temp->GetPreviousSibling(getter_AddRefs(*aPriorNode));
    1200               0 :     NS_ENSURE_SUCCESS(res, res);
    1201               0 :     curNode = temp;
    1202                 :   }
    1203                 :   // we have a prior node.  If it's a block, return it.
    1204               0 :   if (IsBlockNode(*aPriorNode))
    1205               0 :     return NS_OK;
    1206                 :   // else if it's a container, get deep rightmost child
    1207               0 :   else if (mHTMLEditor->IsContainer(*aPriorNode))
    1208                 :   {
    1209               0 :     temp = mHTMLEditor->GetRightmostChild(*aPriorNode);
    1210               0 :     if (temp)
    1211               0 :       *aPriorNode = temp;
    1212               0 :     return NS_OK;
    1213                 :   }
    1214                 :   // else return the node itself
    1215               0 :   return NS_OK;
    1216                 : }
    1217                 : 
    1218                 : nsresult 
    1219               0 : nsWSRunObject::GetPreviousWSNode(DOMPoint aPoint,
    1220                 :                                  nsIDOMNode *aBlockParent, 
    1221                 :                                  nsCOMPtr<nsIDOMNode> *aPriorNode)
    1222                 : {
    1223               0 :   nsCOMPtr<nsIDOMNode> node;
    1224                 :   PRInt32 offset;
    1225               0 :   aPoint.GetPoint(node, offset);
    1226               0 :   return GetPreviousWSNode(node,offset,aBlockParent,aPriorNode);
    1227                 : }
    1228                 : 
    1229                 : nsresult 
    1230               0 : nsWSRunObject::GetPreviousWSNode(nsIDOMNode *aStartNode,
    1231                 :                                  PRInt16 aOffset, 
    1232                 :                                  nsIDOMNode *aBlockParent, 
    1233                 :                                  nsCOMPtr<nsIDOMNode> *aPriorNode)
    1234                 : {
    1235                 :   // can't really recycle various getnext/prior routines because we
    1236                 :   // have special needs here.  Need to step into inline containers but
    1237                 :   // not block containers.
    1238               0 :   NS_ENSURE_TRUE(aStartNode && aBlockParent && aPriorNode, NS_ERROR_NULL_POINTER);
    1239               0 :   *aPriorNode = 0;
    1240                 : 
    1241               0 :   if (mHTMLEditor->IsTextNode(aStartNode))
    1242               0 :     return GetPreviousWSNode(aStartNode, aBlockParent, aPriorNode);
    1243               0 :   if (!mHTMLEditor->IsContainer(aStartNode))
    1244               0 :     return GetPreviousWSNode(aStartNode, aBlockParent, aPriorNode);
    1245                 :   
    1246               0 :   if (!aOffset)
    1247                 :   {
    1248               0 :     if (aStartNode==aBlockParent)
    1249                 :     {
    1250                 :       // we are at start of the block.
    1251               0 :       return NS_OK;
    1252                 :     }
    1253                 : 
    1254                 :     // we are at start of non-block container
    1255               0 :     return GetPreviousWSNode(aStartNode, aBlockParent, aPriorNode);
    1256                 :   }
    1257                 : 
    1258               0 :   nsCOMPtr<nsIContent> startContent( do_QueryInterface(aStartNode) );
    1259               0 :   NS_ENSURE_STATE(startContent);
    1260               0 :   nsIContent *priorContent = startContent->GetChildAt(aOffset - 1);
    1261               0 :   NS_ENSURE_TRUE(priorContent, NS_ERROR_NULL_POINTER);
    1262               0 :   *aPriorNode = do_QueryInterface(priorContent);
    1263                 :   // we have a prior node.  If it's a block, return it.
    1264               0 :   if (IsBlockNode(*aPriorNode))
    1265               0 :     return NS_OK;
    1266                 :   // else if it's a container, get deep rightmost child
    1267               0 :   else if (mHTMLEditor->IsContainer(*aPriorNode))
    1268                 :   {
    1269               0 :     nsCOMPtr<nsIDOMNode> temp;
    1270               0 :     temp = mHTMLEditor->GetRightmostChild(*aPriorNode);
    1271               0 :     if (temp)
    1272               0 :       *aPriorNode = temp;
    1273               0 :     return NS_OK;
    1274                 :   }
    1275                 :   // else return the node itself
    1276               0 :   return NS_OK;
    1277                 : }
    1278                 : 
    1279                 : nsresult 
    1280               0 : nsWSRunObject::GetNextWSNode(nsIDOMNode *aStartNode, 
    1281                 :                              nsIDOMNode *aBlockParent, 
    1282                 :                              nsCOMPtr<nsIDOMNode> *aNextNode)
    1283                 : {
    1284                 :   // can't really recycle various getnext/prior routines because we
    1285                 :   // have special needs here.  Need to step into inline containers but
    1286                 :   // not block containers.
    1287               0 :   NS_ENSURE_TRUE(aStartNode && aBlockParent && aNextNode, NS_ERROR_NULL_POINTER);
    1288                 :   
    1289               0 :   *aNextNode = 0;
    1290               0 :   nsresult res = aStartNode->GetNextSibling(getter_AddRefs(*aNextNode));
    1291               0 :   NS_ENSURE_SUCCESS(res, res);
    1292               0 :   nsCOMPtr<nsIDOMNode> temp, curNode = aStartNode;
    1293               0 :   while (!*aNextNode)
    1294                 :   {
    1295                 :     // we have exhausted nodes in parent of aStartNode.
    1296               0 :     res = curNode->GetParentNode(getter_AddRefs(temp));
    1297               0 :     NS_ENSURE_SUCCESS(res, res);
    1298               0 :     NS_ENSURE_TRUE(temp, NS_ERROR_NULL_POINTER);
    1299               0 :     if (temp == aBlockParent)
    1300                 :     {
    1301                 :       // we have exhausted nodes in the block parent.  The convention
    1302                 :       // here is to return null.
    1303               0 :       *aNextNode = nsnull;
    1304               0 :       return NS_OK;
    1305                 :     }
    1306                 :     // we have a parent: look for next sibling
    1307               0 :     res = temp->GetNextSibling(getter_AddRefs(*aNextNode));
    1308               0 :     NS_ENSURE_SUCCESS(res, res);
    1309               0 :     curNode = temp;
    1310                 :   }
    1311                 :   // we have a next node.  If it's a block, return it.
    1312               0 :   if (IsBlockNode(*aNextNode))
    1313               0 :     return NS_OK;
    1314                 :   // else if it's a container, get deep leftmost child
    1315               0 :   else if (mHTMLEditor->IsContainer(*aNextNode))
    1316                 :   {
    1317               0 :     temp = mHTMLEditor->GetLeftmostChild(*aNextNode);
    1318               0 :     if (temp)
    1319               0 :       *aNextNode = temp;
    1320               0 :     return NS_OK;
    1321                 :   }
    1322                 :   // else return the node itself
    1323               0 :   return NS_OK;
    1324                 : }
    1325                 : 
    1326                 : nsresult 
    1327               0 : nsWSRunObject::GetNextWSNode(DOMPoint aPoint,
    1328                 :                              nsIDOMNode *aBlockParent, 
    1329                 :                              nsCOMPtr<nsIDOMNode> *aNextNode)
    1330                 : {
    1331               0 :   nsCOMPtr<nsIDOMNode> node;
    1332                 :   PRInt32 offset;
    1333               0 :   aPoint.GetPoint(node, offset);
    1334               0 :   return GetNextWSNode(node,offset,aBlockParent,aNextNode);
    1335                 : }
    1336                 : 
    1337                 : nsresult 
    1338               0 : nsWSRunObject::GetNextWSNode(nsIDOMNode *aStartNode,
    1339                 :                              PRInt16 aOffset, 
    1340                 :                              nsIDOMNode *aBlockParent, 
    1341                 :                              nsCOMPtr<nsIDOMNode> *aNextNode)
    1342                 : {
    1343                 :   // can't really recycle various getnext/prior routines because we have special needs
    1344                 :   // here.  Need to step into inline containers but not block containers.
    1345               0 :   NS_ENSURE_TRUE(aStartNode && aBlockParent && aNextNode, NS_ERROR_NULL_POINTER);
    1346               0 :   *aNextNode = 0;
    1347                 : 
    1348               0 :   if (mHTMLEditor->IsTextNode(aStartNode))
    1349               0 :     return GetNextWSNode(aStartNode, aBlockParent, aNextNode);
    1350               0 :   if (!mHTMLEditor->IsContainer(aStartNode))
    1351               0 :     return GetNextWSNode(aStartNode, aBlockParent, aNextNode);
    1352                 :   
    1353               0 :   nsCOMPtr<nsIContent> startContent( do_QueryInterface(aStartNode) );
    1354               0 :   NS_ENSURE_STATE(startContent);
    1355               0 :   nsIContent *nextContent = startContent->GetChildAt(aOffset);
    1356               0 :   if (!nextContent)
    1357                 :   {
    1358               0 :     if (aStartNode==aBlockParent)
    1359                 :     {
    1360                 :       // we are at end of the block.
    1361               0 :       return NS_OK;
    1362                 :     }
    1363                 : 
    1364                 :     // we are at end of non-block container
    1365               0 :     return GetNextWSNode(aStartNode, aBlockParent, aNextNode);
    1366                 :   }
    1367                 :   
    1368               0 :   *aNextNode = do_QueryInterface(nextContent);
    1369                 :   // we have a next node.  If it's a block, return it.
    1370               0 :   if (IsBlockNode(*aNextNode))
    1371               0 :     return NS_OK;
    1372                 :   // else if it's a container, get deep leftmost child
    1373               0 :   else if (mHTMLEditor->IsContainer(*aNextNode))
    1374                 :   {
    1375               0 :     nsCOMPtr<nsIDOMNode> temp;
    1376               0 :     temp = mHTMLEditor->GetLeftmostChild(*aNextNode);
    1377               0 :     if (temp)
    1378               0 :       *aNextNode = temp;
    1379               0 :     return NS_OK;
    1380                 :   }
    1381                 :   // else return the node itself
    1382               0 :   return NS_OK;
    1383                 : }
    1384                 : 
    1385                 : nsresult 
    1386               0 : nsWSRunObject::PrepareToDeleteRangePriv(nsWSRunObject* aEndObject)
    1387                 : {
    1388                 :   // this routine adjust whitespace before *this* and after aEndObject
    1389                 :   // in preperation for the two areas to become adjacent after the 
    1390                 :   // intervening content is deleted.  It's overly agressive right
    1391                 :   // now.  There might be a block boundary remaining between them after
    1392                 :   // the deletion, in which case these adjstments are unneeded (though
    1393                 :   // I don't think they can ever be harmful?)
    1394                 :   
    1395               0 :   NS_ENSURE_TRUE(aEndObject, NS_ERROR_NULL_POINTER);
    1396               0 :   nsresult res = NS_OK;
    1397                 :   
    1398                 :   // get the runs before and after selection
    1399                 :   WSFragment *beforeRun, *afterRun;
    1400               0 :   res = FindRun(mNode, mOffset, &beforeRun, false);
    1401               0 :   NS_ENSURE_SUCCESS(res, res);
    1402               0 :   res = aEndObject->FindRun(aEndObject->mNode, aEndObject->mOffset, &afterRun, true);
    1403               0 :   NS_ENSURE_SUCCESS(res, res);
    1404                 :   
    1405                 :   // trim after run of any leading ws
    1406               0 :   if (afterRun && (afterRun->mType & eLeadingWS))
    1407                 :   {
    1408                 :     res = aEndObject->DeleteChars(aEndObject->mNode, aEndObject->mOffset, afterRun->mEndNode, afterRun->mEndOffset,
    1409               0 :                                   eOutsideUserSelectAll);
    1410               0 :     NS_ENSURE_SUCCESS(res, res);
    1411                 :   }
    1412                 :   // adjust normal ws in afterRun if needed
    1413               0 :   if (afterRun && (afterRun->mType == eNormalWS) && !aEndObject->mPRE)
    1414                 :   {
    1415               0 :     if ( (beforeRun && (beforeRun->mType & eLeadingWS)) ||
    1416               0 :          (!beforeRun && ((mStartReason & eBlock) || (mStartReason == eBreak))) )
    1417                 :     {
    1418                 :       // make sure leading char of following ws is an nbsp, so that it will show up
    1419               0 :       WSPoint point;
    1420               0 :       aEndObject->GetCharAfter(aEndObject->mNode, aEndObject->mOffset, &point);
    1421               0 :       if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar))
    1422                 :       {
    1423               0 :         res = aEndObject->ConvertToNBSP(point, eOutsideUserSelectAll);
    1424               0 :         NS_ENSURE_SUCCESS(res, res);
    1425                 :       }
    1426                 :     }
    1427                 :   }
    1428                 :   // trim before run of any trailing ws
    1429               0 :   if (beforeRun && (beforeRun->mType & eTrailingWS))
    1430                 :   {
    1431                 :     res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, mNode, mOffset,
    1432               0 :                       eOutsideUserSelectAll);
    1433               0 :     NS_ENSURE_SUCCESS(res, res);
    1434                 :   }
    1435               0 :   else if (beforeRun && (beforeRun->mType == eNormalWS) && !mPRE)
    1436                 :   {
    1437               0 :     if ( (afterRun && (afterRun->mType & eTrailingWS)) ||
    1438                 :          (afterRun && (afterRun->mType == eNormalWS))   ||
    1439               0 :          (!afterRun && ((aEndObject->mEndReason & eBlock))) )
    1440                 :     {
    1441                 :       // make sure trailing char of starting ws is an nbsp, so that it will show up
    1442               0 :       WSPoint point;
    1443               0 :       GetCharBefore(mNode, mOffset, &point);
    1444               0 :       if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar))
    1445                 :       {
    1446               0 :         nsCOMPtr<nsIDOMNode> wsStartNode, wsEndNode;
    1447                 :         PRInt32 wsStartOffset, wsEndOffset;
    1448                 :         res = GetAsciiWSBounds(eBoth, mNode, mOffset, 
    1449                 :                                address_of(wsStartNode), &wsStartOffset, 
    1450               0 :                                address_of(wsEndNode), &wsEndOffset);
    1451               0 :         NS_ENSURE_SUCCESS(res, res);
    1452               0 :         point.mTextNode = do_QueryInterface(wsStartNode);
    1453               0 :         if (!point.mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) {
    1454                 :           // Not sure if this is needed, but it'll maintain the same
    1455                 :           // functionality
    1456               0 :           point.mTextNode = nsnull;
    1457                 :         }
    1458               0 :         point.mOffset = wsStartOffset;
    1459               0 :         res = ConvertToNBSP(point, eOutsideUserSelectAll);
    1460               0 :         NS_ENSURE_SUCCESS(res, res);
    1461                 :       }
    1462                 :     }
    1463                 :   }
    1464               0 :   return res;
    1465                 : }
    1466                 : 
    1467                 : nsresult 
    1468               0 : nsWSRunObject::PrepareToSplitAcrossBlocksPriv()
    1469                 : {
    1470                 :   // used to prepare ws to be split across two blocks.  The main issue 
    1471                 :   // here is make sure normalWS doesn't end up becoming non-significant
    1472                 :   // leading or trailing ws after the split.
    1473               0 :   nsresult res = NS_OK;
    1474                 :   
    1475                 :   // get the runs before and after selection
    1476                 :   WSFragment *beforeRun, *afterRun;
    1477               0 :   res = FindRun(mNode, mOffset, &beforeRun, false);
    1478               0 :   NS_ENSURE_SUCCESS(res, res);
    1479               0 :   res = FindRun(mNode, mOffset, &afterRun, true);
    1480                 :   
    1481                 :   // adjust normal ws in afterRun if needed
    1482               0 :   if (afterRun && (afterRun->mType == eNormalWS))
    1483                 :   {
    1484                 :     // make sure leading char of following ws is an nbsp, so that it will show up
    1485               0 :     WSPoint point;
    1486               0 :     GetCharAfter(mNode, mOffset, &point);
    1487               0 :     if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar))
    1488                 :     {
    1489               0 :       res = ConvertToNBSP(point);
    1490               0 :       NS_ENSURE_SUCCESS(res, res);
    1491                 :     }
    1492                 :   }
    1493                 : 
    1494                 :   // adjust normal ws in beforeRun if needed
    1495               0 :   if (beforeRun && (beforeRun->mType == eNormalWS))
    1496                 :   {
    1497                 :     // make sure trailing char of starting ws is an nbsp, so that it will show up
    1498               0 :     WSPoint point;
    1499               0 :     GetCharBefore(mNode, mOffset, &point);
    1500               0 :     if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar))
    1501                 :     {
    1502               0 :       nsCOMPtr<nsIDOMNode> wsStartNode, wsEndNode;
    1503                 :       PRInt32 wsStartOffset, wsEndOffset;
    1504                 :       res = GetAsciiWSBounds(eBoth, mNode, mOffset, 
    1505                 :                              address_of(wsStartNode), &wsStartOffset, 
    1506               0 :                              address_of(wsEndNode), &wsEndOffset);
    1507               0 :       NS_ENSURE_SUCCESS(res, res);
    1508               0 :       point.mTextNode = do_QueryInterface(wsStartNode);
    1509               0 :       if (!point.mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) {
    1510                 :         // Not sure if this is needed, but it'll maintain the same
    1511                 :         // functionality
    1512               0 :         point.mTextNode = nsnull;
    1513                 :       }
    1514               0 :       point.mOffset = wsStartOffset;
    1515               0 :       res = ConvertToNBSP(point);
    1516               0 :       NS_ENSURE_SUCCESS(res, res);
    1517                 :     }
    1518                 :   }
    1519               0 :   return res;
    1520                 : }
    1521                 : 
    1522                 : nsresult 
    1523               0 : nsWSRunObject::DeleteChars(nsIDOMNode *aStartNode, PRInt32 aStartOffset, 
    1524                 :                            nsIDOMNode *aEndNode, PRInt32 aEndOffset,
    1525                 :                            AreaRestriction aAR)
    1526                 : {
    1527                 :   // MOOSE: this routine needs to be modified to preserve the integrity of the
    1528                 :   // wsFragment info.
    1529               0 :   NS_ENSURE_TRUE(aStartNode && aEndNode, NS_ERROR_NULL_POINTER);
    1530                 : 
    1531               0 :   if (aAR == eOutsideUserSelectAll)
    1532                 :   {
    1533               0 :     nsCOMPtr<nsIDOMNode> san = mHTMLEditor->FindUserSelectAllNode(aStartNode);
    1534               0 :     if (san)
    1535               0 :       return NS_OK;
    1536                 :     
    1537               0 :     if (aStartNode != aEndNode)
    1538                 :     {
    1539               0 :       san = mHTMLEditor->FindUserSelectAllNode(aEndNode);
    1540               0 :       if (san)
    1541               0 :         return NS_OK;
    1542                 :     }
    1543                 :   }
    1544                 : 
    1545               0 :   if ((aStartNode == aEndNode) && (aStartOffset == aEndOffset))
    1546               0 :     return NS_OK;  // nothing to delete
    1547                 :   
    1548               0 :   nsresult res = NS_OK;
    1549               0 :   PRInt32 idx = mNodeArray.IndexOf(aStartNode);
    1550               0 :   if (idx==-1) idx = 0; // if our strarting point wasn't one of our ws text nodes,
    1551                 :                         // then just go through them from the beginning.
    1552               0 :   nsCOMPtr<nsIDOMNode> node;
    1553               0 :   nsCOMPtr<nsIDOMCharacterData> textnode;
    1554               0 :   nsRefPtr<nsRange> range;
    1555                 : 
    1556               0 :   if (aStartNode == aEndNode)
    1557                 :   {
    1558               0 :     textnode = do_QueryInterface(aStartNode);
    1559               0 :     if (textnode)
    1560                 :     {
    1561                 :       return mHTMLEditor->DeleteText(textnode, (PRUint32)aStartOffset, 
    1562               0 :                                      (PRUint32)(aEndOffset-aStartOffset));
    1563                 :     }
    1564                 :   }
    1565                 : 
    1566               0 :   PRInt32 count = mNodeArray.Count();
    1567               0 :   while (idx < count)
    1568                 :   {
    1569               0 :     node = mNodeArray[idx];
    1570               0 :     if (!node)
    1571               0 :       break;  // we ran out of ws nodes; must have been deleting to end
    1572               0 :     if (node == aStartNode)
    1573                 :     {
    1574               0 :       textnode = do_QueryInterface(node);
    1575                 :       PRUint32 len;
    1576               0 :       textnode->GetLength(&len);
    1577               0 :       if (PRUint32(aStartOffset)<len)
    1578                 :       {
    1579               0 :         res = mHTMLEditor->DeleteText(textnode, (PRUint32)aStartOffset, len-aStartOffset);
    1580               0 :         NS_ENSURE_SUCCESS(res, res);
    1581                 :       }
    1582                 :     }
    1583               0 :     else if (node == aEndNode)
    1584                 :     {
    1585               0 :       if (aEndOffset)
    1586                 :       {
    1587               0 :         textnode = do_QueryInterface(node);
    1588               0 :         res = mHTMLEditor->DeleteText(textnode, 0, (PRUint32)aEndOffset);
    1589               0 :         NS_ENSURE_SUCCESS(res, res);
    1590                 :       }
    1591               0 :       break;
    1592                 :     }
    1593                 :     else
    1594                 :     {
    1595               0 :       if (!range)
    1596                 :       {
    1597               0 :         range = new nsRange();
    1598               0 :         res = range->SetStart(aStartNode, aStartOffset);
    1599               0 :         NS_ENSURE_SUCCESS(res, res);
    1600               0 :         res = range->SetEnd(aEndNode, aEndOffset);
    1601               0 :         NS_ENSURE_SUCCESS(res, res);
    1602                 :       }
    1603                 :       bool nodeBefore, nodeAfter;
    1604               0 :       nsCOMPtr<nsIContent> content (do_QueryInterface(node));
    1605               0 :       res = nsRange::CompareNodeToRange(content, range, &nodeBefore, &nodeAfter);
    1606               0 :       NS_ENSURE_SUCCESS(res, res);
    1607               0 :       if (nodeAfter)
    1608                 :       {
    1609                 :         break;
    1610                 :       }
    1611               0 :       if (!nodeBefore)
    1612                 :       {
    1613               0 :         res = mHTMLEditor->DeleteNode(node);
    1614               0 :         NS_ENSURE_SUCCESS(res, res);
    1615               0 :         mNodeArray.RemoveObject(node);
    1616               0 :         --count;
    1617               0 :         --idx;
    1618                 :       }
    1619                 :     }
    1620               0 :     idx++;
    1621                 :   }
    1622               0 :   return res;
    1623                 : }
    1624                 : 
    1625                 : nsresult 
    1626               0 : nsWSRunObject::GetCharAfter(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint)
    1627                 : {
    1628               0 :   NS_ENSURE_TRUE(aNode && outPoint, NS_ERROR_NULL_POINTER);
    1629                 : 
    1630               0 :   PRInt32 idx = mNodeArray.IndexOf(aNode);
    1631               0 :   if (idx == -1) 
    1632                 :   {
    1633                 :     // use range comparisons to get right ws node
    1634               0 :     return GetWSPointAfter(aNode, aOffset, outPoint);
    1635                 :   }
    1636                 :   else
    1637                 :   {
    1638                 :     // use wspoint version of GetCharAfter()
    1639               0 :     WSPoint point(aNode,aOffset,0);
    1640               0 :     return GetCharAfter(point, outPoint);
    1641                 :   }
    1642                 :   
    1643                 :   return NS_ERROR_FAILURE;
    1644                 : }
    1645                 : 
    1646                 : nsresult 
    1647               0 : nsWSRunObject::GetCharBefore(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint)
    1648                 : {
    1649               0 :   NS_ENSURE_TRUE(aNode && outPoint, NS_ERROR_NULL_POINTER);
    1650                 : 
    1651               0 :   PRInt32 idx = mNodeArray.IndexOf(aNode);
    1652               0 :   if (idx == -1) 
    1653                 :   {
    1654                 :     // use range comparisons to get right ws node
    1655               0 :     return GetWSPointBefore(aNode, aOffset, outPoint);
    1656                 :   }
    1657                 :   else
    1658                 :   {
    1659                 :     // use wspoint version of GetCharBefore()
    1660               0 :     WSPoint point(aNode,aOffset,0);
    1661               0 :     return GetCharBefore(point, outPoint);
    1662                 :   }
    1663                 :   
    1664                 :   return NS_ERROR_FAILURE;
    1665                 : }
    1666                 : 
    1667                 : nsresult 
    1668               0 : nsWSRunObject::GetCharAfter(WSPoint &aPoint, WSPoint *outPoint)
    1669                 : {
    1670               0 :   NS_ENSURE_TRUE(aPoint.mTextNode && outPoint, NS_ERROR_NULL_POINTER);
    1671                 :   
    1672               0 :   outPoint->mTextNode = nsnull;
    1673               0 :   outPoint->mOffset = 0;
    1674               0 :   outPoint->mChar = 0;
    1675                 : 
    1676               0 :   nsCOMPtr<nsIDOMNode> pointTextNode(do_QueryInterface(aPoint.mTextNode));
    1677               0 :   PRInt32 idx = mNodeArray.IndexOf(pointTextNode);
    1678               0 :   if (idx == -1) return NS_OK;  // can't find point, but it's not an error
    1679               0 :   PRInt32 numNodes = mNodeArray.Count();
    1680                 :   
    1681               0 :   if (PRUint16(aPoint.mOffset) < aPoint.mTextNode->TextLength())
    1682                 :   {
    1683               0 :     *outPoint = aPoint;
    1684               0 :     outPoint->mChar = GetCharAt(aPoint.mTextNode, aPoint.mOffset);
    1685                 :   }
    1686               0 :   else if (idx < (PRInt32)(numNodes-1))
    1687                 :   {
    1688               0 :     nsIDOMNode* node = mNodeArray[idx+1];
    1689               0 :     NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
    1690               0 :     outPoint->mTextNode = do_QueryInterface(node);
    1691               0 :     if (!outPoint->mTextNode->IsNodeOfType(nsINode::eDATA_NODE)) {
    1692                 :       // Not sure if this is needed, but it'll maintain the same
    1693                 :       // functionality
    1694               0 :       outPoint->mTextNode = nsnull;
    1695                 :     }
    1696               0 :     outPoint->mOffset = 0;
    1697               0 :     outPoint->mChar = GetCharAt(outPoint->mTextNode, 0);
    1698                 :   }
    1699               0 :   return NS_OK;
    1700                 : }
    1701                 : 
    1702                 : nsresult 
    1703               0 : nsWSRunObject::GetCharBefore(WSPoint &aPoint, WSPoint *outPoint)
    1704                 : {
    1705               0 :   NS_ENSURE_TRUE(aPoint.mTextNode && outPoint, NS_ERROR_NULL_POINTER);
    1706                 :   
    1707               0 :   outPoint->mTextNode = nsnull;
    1708               0 :   outPoint->mOffset = 0;
    1709               0 :   outPoint->mChar = 0;
    1710                 :   
    1711               0 :   nsCOMPtr<nsIDOMNode> pointTextNode(do_QueryInterface(aPoint.mTextNode));
    1712               0 :   PRInt32 idx = mNodeArray.IndexOf(pointTextNode);
    1713               0 :   if (idx == -1) return NS_OK;  // can't find point, but it's not an error
    1714                 :   
    1715               0 :   if (aPoint.mOffset != 0)
    1716                 :   {
    1717               0 :     *outPoint = aPoint;
    1718               0 :     outPoint->mOffset--;
    1719               0 :     outPoint->mChar = GetCharAt(aPoint.mTextNode, aPoint.mOffset-1);
    1720                 :   }
    1721               0 :   else if (idx)
    1722                 :   {
    1723               0 :     nsIDOMNode* node = mNodeArray[idx-1];
    1724               0 :     NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
    1725               0 :     outPoint->mTextNode = do_QueryInterface(node);
    1726                 : 
    1727               0 :     PRUint32 len = outPoint->mTextNode->TextLength();
    1728                 : 
    1729               0 :     if (len)
    1730                 :     {
    1731               0 :       outPoint->mOffset = len-1;
    1732               0 :       outPoint->mChar = GetCharAt(outPoint->mTextNode, len-1);
    1733                 :     }
    1734                 :   }
    1735               0 :   return NS_OK;
    1736                 : }
    1737                 : 
    1738                 : nsresult 
    1739               0 : nsWSRunObject::ConvertToNBSP(WSPoint aPoint, AreaRestriction aAR)
    1740                 : {
    1741                 :   // MOOSE: this routine needs to be modified to preserve the integrity of the
    1742                 :   // wsFragment info.
    1743               0 :   NS_ENSURE_TRUE(aPoint.mTextNode, NS_ERROR_NULL_POINTER);
    1744                 : 
    1745               0 :   if (aAR == eOutsideUserSelectAll)
    1746                 :   {
    1747               0 :     nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(aPoint.mTextNode);
    1748               0 :     if (domnode)
    1749                 :     {
    1750               0 :       nsCOMPtr<nsIDOMNode> san = mHTMLEditor->FindUserSelectAllNode(domnode);
    1751               0 :       if (san)
    1752               0 :         return NS_OK;
    1753                 :     }
    1754                 :   }
    1755                 : 
    1756               0 :   nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(aPoint.mTextNode));
    1757               0 :   NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER);
    1758               0 :   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(textNode));
    1759                 :   
    1760                 :   // first, insert an nbsp
    1761               0 :   nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
    1762               0 :   nsAutoString nbspStr(nbsp);
    1763               0 :   nsresult res = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, textNode, aPoint.mOffset, true);
    1764               0 :   NS_ENSURE_SUCCESS(res, res);
    1765                 :   
    1766                 :   // next, find range of ws it will replace
    1767               0 :   nsCOMPtr<nsIDOMNode> startNode, endNode;
    1768               0 :   PRInt32 startOffset=0, endOffset=0;
    1769                 :   
    1770                 :   res = GetAsciiWSBounds(eAfter, node, aPoint.mOffset+1, address_of(startNode), 
    1771               0 :                          &startOffset, address_of(endNode), &endOffset);
    1772               0 :   NS_ENSURE_SUCCESS(res, res);
    1773                 :   
    1774                 :   // finally, delete that replaced ws, if any
    1775               0 :   if (startNode)
    1776                 :   {
    1777               0 :     res = DeleteChars(startNode, startOffset, endNode, endOffset);
    1778                 :   }
    1779                 :   
    1780               0 :   return res;
    1781                 : }
    1782                 : 
    1783                 : nsresult
    1784               0 : nsWSRunObject::GetAsciiWSBounds(PRInt16 aDir, nsIDOMNode *aNode, PRInt32 aOffset,
    1785                 :                                 nsCOMPtr<nsIDOMNode> *outStartNode, PRInt32 *outStartOffset,
    1786                 :                                 nsCOMPtr<nsIDOMNode> *outEndNode, PRInt32 *outEndOffset)
    1787                 : {
    1788               0 :   NS_ENSURE_TRUE(aNode && outStartNode && outEndNode, NS_ERROR_NULL_POINTER);
    1789                 : 
    1790               0 :   nsCOMPtr<nsIDOMNode> startNode, endNode;
    1791               0 :   PRInt32 startOffset=0, endOffset=0;
    1792                 :   
    1793               0 :   nsresult res = NS_OK;
    1794                 :   
    1795               0 :   if (aDir & eAfter)
    1796                 :   {
    1797               0 :     WSPoint point, tmp;
    1798               0 :     res = GetCharAfter(aNode, aOffset, &point);
    1799               0 :     if (NS_SUCCEEDED(res) && point.mTextNode)
    1800                 :     {  // we found a text node, at least
    1801               0 :       endNode = do_QueryInterface(point.mTextNode);
    1802               0 :       endOffset = point.mOffset;
    1803               0 :       startNode = endNode;
    1804               0 :       startOffset = endOffset;
    1805                 :       
    1806                 :       // scan ahead to end of ascii ws
    1807               0 :       while (nsCRT::IsAsciiSpace(point.mChar))
    1808                 :       {
    1809               0 :         endNode = do_QueryInterface(point.mTextNode);
    1810               0 :         point.mOffset++;  // endOffset is _after_ ws
    1811               0 :         endOffset = point.mOffset;
    1812               0 :         tmp = point;
    1813               0 :         res = GetCharAfter(tmp, &point);
    1814               0 :         if (NS_FAILED(res) || !point.mTextNode) break;
    1815                 :       }
    1816                 :     }
    1817                 :   }
    1818                 :   
    1819               0 :   if (aDir & eBefore)
    1820                 :   {
    1821               0 :     WSPoint point, tmp;
    1822               0 :     res = GetCharBefore(aNode, aOffset, &point);
    1823               0 :     if (NS_SUCCEEDED(res) && point.mTextNode)
    1824                 :     {  // we found a text node, at least
    1825               0 :       startNode = do_QueryInterface(point.mTextNode);
    1826               0 :       startOffset = point.mOffset+1;
    1827               0 :       if (!endNode)
    1828                 :       {
    1829               0 :         endNode = startNode;
    1830               0 :         endOffset = startOffset;
    1831                 :       }
    1832                 :       
    1833                 :       // scan back to start of ascii ws
    1834               0 :       while (nsCRT::IsAsciiSpace(point.mChar))
    1835                 :       {
    1836               0 :         startNode = do_QueryInterface(point.mTextNode);
    1837               0 :         startOffset = point.mOffset;
    1838               0 :         tmp = point;
    1839               0 :         res = GetCharBefore(tmp, &point);
    1840               0 :         if (NS_FAILED(res) || !point.mTextNode) break;
    1841                 :       }
    1842                 :     }
    1843                 :   }  
    1844                 :   
    1845               0 :   *outStartNode = startNode;
    1846               0 :   *outStartOffset = startOffset;
    1847               0 :   *outEndNode = endNode;
    1848               0 :   *outEndOffset = endOffset;
    1849                 : 
    1850               0 :   return NS_OK;
    1851                 : }
    1852                 : 
    1853                 : nsresult
    1854               0 : nsWSRunObject::FindRun(nsIDOMNode *aNode, PRInt32 aOffset, WSFragment **outRun, bool after)
    1855                 : {
    1856               0 :   *outRun = nsnull;
    1857                 :   // given a dompoint, find the ws run that is before or after it, as caller needs
    1858               0 :   NS_ENSURE_TRUE(aNode && outRun, NS_ERROR_NULL_POINTER);
    1859                 :     
    1860               0 :   nsresult res = NS_OK;
    1861               0 :   WSFragment *run = mStartRun;
    1862               0 :   while (run)
    1863                 :   {
    1864                 :     PRInt16 comp = nsContentUtils::ComparePoints(aNode, aOffset, run->mStartNode,
    1865               0 :                                                  run->mStartOffset);
    1866               0 :     if (comp <= 0)
    1867                 :     {
    1868               0 :       if (after)
    1869                 :       {
    1870               0 :         *outRun = run;
    1871               0 :         return res;
    1872                 :       }
    1873                 :       else // before
    1874                 :       {
    1875               0 :         *outRun = nsnull;
    1876               0 :         return res;
    1877                 :       }
    1878                 :     }
    1879                 :     comp = nsContentUtils::ComparePoints(aNode, aOffset,
    1880               0 :                                          run->mEndNode, run->mEndOffset);
    1881               0 :     if (comp < 0)
    1882                 :     {
    1883               0 :       *outRun = run;
    1884               0 :       return res;
    1885                 :     }
    1886               0 :     else if (comp == 0)
    1887                 :     {
    1888               0 :       if (after)
    1889                 :       {
    1890               0 :         *outRun = run->mRight;
    1891               0 :         return res;
    1892                 :       }
    1893                 :       else // before
    1894                 :       {
    1895               0 :         *outRun = run;
    1896               0 :         return res;
    1897                 :       }
    1898                 :     }
    1899               0 :     if (!run->mRight)
    1900                 :     {
    1901               0 :       if (after)
    1902                 :       {
    1903               0 :         *outRun = nsnull;
    1904               0 :         return res;
    1905                 :       }
    1906                 :       else // before
    1907                 :       {
    1908               0 :         *outRun = run;
    1909               0 :         return res;
    1910                 :       }
    1911                 :     }
    1912               0 :     run = run->mRight;
    1913                 :   }
    1914               0 :   return res;
    1915                 : }
    1916                 : 
    1917                 : PRUnichar 
    1918               0 : nsWSRunObject::GetCharAt(nsIContent *aTextNode, PRInt32 aOffset)
    1919                 : {
    1920                 :   // return 0 if we can't get a char, for whatever reason
    1921               0 :   NS_ENSURE_TRUE(aTextNode, 0);
    1922                 : 
    1923               0 :   PRInt32 len = PRInt32(aTextNode->TextLength());
    1924               0 :   if (aOffset < 0 || aOffset >= len)
    1925               0 :     return 0;
    1926                 :     
    1927               0 :   return aTextNode->GetText()->CharAt(aOffset);
    1928                 : }
    1929                 : 
    1930                 : nsresult 
    1931               0 : nsWSRunObject::GetWSPointAfter(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint)
    1932                 : {
    1933                 :   // Note: only to be called if aNode is not a ws node.  
    1934                 :   
    1935                 :   // binary search on wsnodes
    1936                 :   PRInt32 numNodes, firstNum, curNum, lastNum;
    1937               0 :   numNodes = mNodeArray.Count();
    1938                 :   
    1939               0 :   NS_ENSURE_TRUE(numNodes, NS_OK); // do nothing if there are no nodes to search
    1940                 : 
    1941               0 :   firstNum = 0;
    1942               0 :   curNum = numNodes/2;
    1943               0 :   lastNum = numNodes;
    1944               0 :   PRInt16 cmp=0;
    1945               0 :   nsCOMPtr<nsIDOMNode>  curNode;
    1946                 :   
    1947                 :   // begin binary search
    1948                 :   // we do this because we need to minimize calls to ComparePoints(),
    1949                 :   // which is mongo expensive
    1950               0 :   while (curNum != lastNum)
    1951                 :   {
    1952               0 :     curNode = mNodeArray[curNum];
    1953               0 :     cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0);
    1954               0 :     if (cmp < 0)
    1955               0 :       lastNum = curNum;
    1956                 :     else
    1957               0 :       firstNum = curNum + 1;
    1958               0 :     curNum = (lastNum - firstNum) / 2 + firstNum;
    1959               0 :     NS_ASSERTION(firstNum <= curNum && curNum <= lastNum, "Bad binary search");
    1960                 :   }
    1961                 : 
    1962                 :   // When the binary search is complete, we always know that the current node
    1963                 :   // is the same as the end node, which is always past our range. Therefore,
    1964                 :   // we've found the node immediately after the point of interest.
    1965               0 :   if (curNum == mNodeArray.Count()) {
    1966                 :     // they asked for past our range (it's after the last node). GetCharAfter
    1967                 :     // will do the work for us when we pass it the last index of the last node.
    1968               0 :     nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum-1]));
    1969               0 :     WSPoint point(textNode, textNode->TextLength(), 0);
    1970               0 :     return GetCharAfter(point, outPoint);
    1971                 :   } else {
    1972                 :     // The char after the point of interest is the first character of our range.
    1973               0 :     nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum]));
    1974               0 :     WSPoint point(textNode, 0, 0);
    1975               0 :     return GetCharAfter(point, outPoint);
    1976                 :   }
    1977                 : }
    1978                 : 
    1979                 : nsresult 
    1980               0 : nsWSRunObject::GetWSPointBefore(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint)
    1981                 : {
    1982                 :   // Note: only to be called if aNode is not a ws node.  
    1983                 :   
    1984                 :   // binary search on wsnodes
    1985                 :   PRInt32 numNodes, firstNum, curNum, lastNum;
    1986               0 :   numNodes = mNodeArray.Count();
    1987                 :   
    1988               0 :   NS_ENSURE_TRUE(numNodes, NS_OK); // do nothing if there are no nodes to search
    1989                 :   
    1990               0 :   firstNum = 0;
    1991               0 :   curNum = numNodes/2;
    1992               0 :   lastNum = numNodes;
    1993               0 :   PRInt16 cmp=0;
    1994               0 :   nsCOMPtr<nsIDOMNode>  curNode;
    1995                 :   
    1996                 :   // begin binary search
    1997                 :   // we do this because we need to minimize calls to ComparePoints(),
    1998                 :   // which is mongo expensive
    1999               0 :   while (curNum != lastNum)
    2000                 :   {
    2001               0 :     curNode = mNodeArray[curNum];
    2002               0 :     cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0);
    2003               0 :     if (cmp < 0)
    2004               0 :       lastNum = curNum;
    2005                 :     else
    2006               0 :       firstNum = curNum + 1;
    2007               0 :     curNum = (lastNum - firstNum) / 2 + firstNum;
    2008               0 :     NS_ASSERTION(firstNum <= curNum && curNum <= lastNum, "Bad binary search");
    2009                 :   }
    2010                 : 
    2011                 :   // When the binary search is complete, we always know that the current node
    2012                 :   // is the same as the end node, which is always past our range. Therefore,
    2013                 :   // we've found the node immediately after the point of interest.
    2014               0 :   if (curNum == mNodeArray.Count()) {
    2015                 :     // get the point before the end of the last node, we can pass the length
    2016                 :     // of the node into GetCharBefore, and it will return the last character.
    2017               0 :     nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum - 1]));
    2018               0 :     WSPoint point(textNode, textNode->TextLength(), 0);
    2019               0 :     return GetCharBefore(point, outPoint);
    2020                 :   } else {
    2021                 :     // we can just ask the current node for the point immediately before it,
    2022                 :     // it will handle moving to the previous node (if any) and returning the
    2023                 :     // appropriate character
    2024               0 :     nsCOMPtr<nsIContent> textNode(do_QueryInterface(mNodeArray[curNum]));
    2025               0 :     WSPoint point(textNode, 0, 0);
    2026               0 :     return GetCharBefore(point, outPoint);
    2027                 :   }
    2028                 : }
    2029                 : 
    2030                 : nsresult
    2031               0 : nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun)
    2032                 : {    
    2033                 :   // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation. 
    2034                 :   // examine what is before and after the trailing nbsp, if any.
    2035               0 :   NS_ENSURE_TRUE(aRun, NS_ERROR_NULL_POINTER);
    2036               0 :   WSPoint thePoint;
    2037               0 :   bool leftCheck = false;
    2038               0 :   bool spaceNBSP = false;
    2039               0 :   bool rightCheck = false;
    2040                 :   
    2041                 :   // confirm run is normalWS
    2042               0 :   if (aRun->mType != eNormalWS) return NS_ERROR_FAILURE;
    2043                 :   
    2044                 :   // first check for trailing nbsp
    2045               0 :   nsresult res = GetCharBefore(aRun->mEndNode, aRun->mEndOffset, &thePoint);
    2046               0 :   if (NS_SUCCEEDED(res) && thePoint.mTextNode && thePoint.mChar == nbsp)
    2047                 :   {
    2048                 :     // now check that what is to the left of it is compatible with replacing nbsp with space
    2049               0 :     WSPoint prevPoint;
    2050               0 :     res = GetCharBefore(thePoint, &prevPoint);
    2051               0 :     if (NS_SUCCEEDED(res) && prevPoint.mTextNode)
    2052                 :     {
    2053               0 :       if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) leftCheck = true;
    2054               0 :       else spaceNBSP = true;
    2055                 :     }
    2056               0 :     else if (aRun->mLeftType == eText)    leftCheck = true;
    2057               0 :     else if (aRun->mLeftType == eSpecial) leftCheck = true;
    2058               0 :     if (leftCheck || spaceNBSP)
    2059                 :     {
    2060                 :       // now check that what is to the right of it is compatible with replacing nbsp with space
    2061               0 :       if (aRun->mRightType == eText)    rightCheck = true;
    2062               0 :       if (aRun->mRightType == eSpecial) rightCheck = true;
    2063               0 :       if (aRun->mRightType == eBreak)   rightCheck = true;
    2064               0 :       if ((aRun->mRightType & eBlock) &&
    2065               0 :           IsBlockNode(nsCOMPtr<nsIDOMNode>(GetWSBoundingParent())))
    2066                 :       {
    2067                 :         // we are at a block boundary.  Insert a <br>.  Why?  Well, first note that
    2068                 :         // the br will have no visible effect since it is up against a block boundary.
    2069                 :         // |foo<br><p>bar|  renders like |foo<p>bar| and similarly
    2070                 :         // |<p>foo<br></p>bar| renders like |<p>foo</p>bar|.  What this <br> addition
    2071                 :         // gets us is the ability to convert a trailing nbsp to a space.  Consider:
    2072                 :         // |<body>foo. '</body>|, where ' represents selection.  User types space attempting
    2073                 :         // to put 2 spaces after the end of their sentence.  We used to do this as:
    2074                 :         // |<body>foo. &nbsp</body>|  This caused problems with soft wrapping: the nbsp
    2075                 :         // would wrap to the next line, which looked attrocious.  If you try to do:
    2076                 :         // |<body>foo.&nbsp </body>| instead, the trailing space is invisible because it 
    2077                 :         // is against a block boundary.  If you do: |<body>foo.&nbsp&nbsp</body>| then
    2078                 :         // you get an even uglier soft wrapping problem, where foo is on one line until
    2079                 :         // you type the final space, and then "foo  " jumps down to the next line.  Ugh.
    2080                 :         // The best way I can find out of this is to throw in a harmless <br>
    2081                 :         // here, which allows us to do: |<body>foo.&nbsp <br></body>|, which doesn't
    2082                 :         // cause foo to jump lines, doesn't cause spaces to show up at the beginning of 
    2083                 :         // soft wrapped lines, and lets the user see 2 spaces when they type 2 spaces.
    2084                 : 
    2085               0 :         nsCOMPtr<nsIDOMNode> brNode;
    2086               0 :         res = mHTMLEditor->CreateBR(aRun->mEndNode, aRun->mEndOffset, address_of(brNode));
    2087               0 :         NS_ENSURE_SUCCESS(res, res);
    2088                 : 
    2089                 :         // refresh thePoint, prevPoint
    2090               0 :         res = GetCharBefore(aRun->mEndNode, aRun->mEndOffset, &thePoint);
    2091               0 :         NS_ENSURE_SUCCESS(res, res);
    2092               0 :         res = GetCharBefore(thePoint, &prevPoint);
    2093               0 :         NS_ENSURE_SUCCESS(res, res);
    2094               0 :         rightCheck = true;
    2095                 :       }
    2096                 :     }
    2097               0 :     if (leftCheck && rightCheck)
    2098                 :     {
    2099                 :       // now replace nbsp with space
    2100                 :       // first, insert a space
    2101               0 :       nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode));
    2102               0 :       NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER);
    2103               0 :       nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
    2104               0 :       nsAutoString spaceStr(PRUnichar(32));
    2105               0 :       res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, thePoint.mOffset, true);
    2106               0 :       NS_ENSURE_SUCCESS(res, res);
    2107                 :   
    2108                 :       // finally, delete that nbsp
    2109               0 :       nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
    2110               0 :       res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
    2111               0 :       NS_ENSURE_SUCCESS(res, res);
    2112                 :     }
    2113               0 :     else if (!mPRE && spaceNBSP && rightCheck)  // don't mess with this preformatted for now.
    2114                 :     {
    2115                 :       // we have a run of ascii whitespace (which will render as one space)
    2116                 :       // followed by an nbsp (which is at the end of the whitespace run).  Let's
    2117                 :       // switch their order.  This will insure that if someone types two spaces
    2118                 :       // after a sentence, and the editor softwraps at this point, the spaces wont
    2119                 :       // be split across lines, which looks ugly and is bad for the moose.
    2120                 :       
    2121               0 :       nsCOMPtr<nsIDOMNode> startNode, endNode, thenode(do_QueryInterface(prevPoint.mTextNode));
    2122                 :       PRInt32 startOffset, endOffset;
    2123                 :       res = GetAsciiWSBounds(eBoth, thenode, prevPoint.mOffset+1, address_of(startNode), 
    2124               0 :                            &startOffset, address_of(endNode), &endOffset);
    2125               0 :       NS_ENSURE_SUCCESS(res, res);
    2126                 :       
    2127                 :       //  delete that nbsp
    2128               0 :       nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
    2129               0 :       res = DeleteChars(delNode, thePoint.mOffset, delNode, thePoint.mOffset+1);
    2130               0 :       NS_ENSURE_SUCCESS(res, res);
    2131                 :       
    2132                 :       // finally, insert that nbsp before the ascii ws run
    2133               0 :       nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
    2134               0 :       nsAutoString nbspStr(nbsp);
    2135               0 :       nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(startNode));
    2136               0 :       res = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, textNode, startOffset, true);
    2137               0 :       NS_ENSURE_SUCCESS(res, res);
    2138                 :     }
    2139                 :   }
    2140               0 :   return NS_OK;
    2141                 : }
    2142                 : 
    2143                 : nsresult
    2144               0 : nsWSRunObject::CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset)
    2145                 : {    
    2146                 :   // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation. 
    2147                 :   // this routine is called when we about to make this point in the ws abut an inserted break
    2148                 :   // or text, so we don't have to worry about what is after it.  What is after it now will 
    2149                 :   // end up after the inserted object.   
    2150               0 :   NS_ENSURE_TRUE(aRun && aNode, NS_ERROR_NULL_POINTER);
    2151               0 :   WSPoint thePoint;
    2152               0 :   bool canConvert = false;
    2153               0 :   nsresult res = GetCharBefore(aNode, aOffset, &thePoint);
    2154               0 :   if (NS_SUCCEEDED(res) && thePoint.mTextNode && thePoint.mChar == nbsp)
    2155                 :   {
    2156               0 :     WSPoint prevPoint;
    2157               0 :     res = GetCharBefore(thePoint, &prevPoint);
    2158               0 :     if (NS_SUCCEEDED(res) && prevPoint.mTextNode)
    2159                 :     {
    2160               0 :       if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) canConvert = true;
    2161                 :     }
    2162               0 :     else if (aRun->mLeftType == eText)    canConvert = true;
    2163               0 :     else if (aRun->mLeftType == eSpecial) canConvert = true;
    2164                 :   }
    2165               0 :   if (canConvert)
    2166                 :   {
    2167                 :     // first, insert a space
    2168               0 :     nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode));
    2169               0 :     NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER);
    2170               0 :     nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
    2171               0 :     nsAutoString spaceStr(PRUnichar(32));
    2172               0 :     res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, thePoint.mOffset, true);
    2173               0 :     NS_ENSURE_SUCCESS(res, res);
    2174                 :   
    2175                 :     // finally, delete that nbsp
    2176               0 :     nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
    2177               0 :     res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
    2178               0 :     NS_ENSURE_SUCCESS(res, res);
    2179                 :   }
    2180               0 :   return NS_OK;
    2181                 : }
    2182                 : 
    2183                 : nsresult
    2184               0 : nsWSRunObject::CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset)
    2185                 : {    
    2186                 :   // try to change an nbsp to a space, if possible, just to prevent nbsp proliferation    
    2187                 :   // this routine is called when we about to make this point in the ws abut an inserted
    2188                 :   // text, so we don't have to worry about what is before it.  What is before it now will 
    2189                 :   // end up before the inserted text.   
    2190               0 :   WSPoint thePoint;
    2191               0 :   bool canConvert = false;
    2192               0 :   nsresult res = GetCharAfter(aNode, aOffset, &thePoint);
    2193               0 :   if (NS_SUCCEEDED(res) && thePoint.mChar == nbsp)
    2194                 :   {
    2195               0 :     WSPoint nextPoint, tmp=thePoint;
    2196               0 :     tmp.mOffset++; // we want to be after thePoint
    2197               0 :     res = GetCharAfter(tmp, &nextPoint);
    2198               0 :     if (NS_SUCCEEDED(res) && nextPoint.mTextNode)
    2199                 :     {
    2200               0 :       if (!nsCRT::IsAsciiSpace(nextPoint.mChar)) canConvert = true;
    2201                 :     }
    2202               0 :     else if (aRun->mRightType == eText)    canConvert = true;
    2203               0 :     else if (aRun->mRightType == eSpecial) canConvert = true;
    2204               0 :     else if (aRun->mRightType == eBreak)   canConvert = true;
    2205                 :   }
    2206               0 :   if (canConvert)
    2207                 :   {
    2208                 :     // first, insert a space
    2209               0 :     nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode));
    2210               0 :     NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER);
    2211               0 :     nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
    2212               0 :     nsAutoString spaceStr(PRUnichar(32));
    2213               0 :     res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, thePoint.mOffset, true);
    2214               0 :     NS_ENSURE_SUCCESS(res, res);
    2215                 :   
    2216                 :     // finally, delete that nbsp
    2217               0 :     nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
    2218               0 :     res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
    2219               0 :     NS_ENSURE_SUCCESS(res, res);
    2220                 :   }
    2221               0 :   return NS_OK;
    2222                 : }
    2223                 : 
    2224                 : 
    2225                 : nsresult
    2226               0 : nsWSRunObject::ScrubBlockBoundaryInner(nsHTMLEditor *aHTMLEd, 
    2227                 :                                        nsCOMPtr<nsIDOMNode> *aBlock,
    2228                 :                                        BlockBoundary aBoundary)
    2229                 : {
    2230               0 :   NS_ENSURE_TRUE(aBlock && aHTMLEd, NS_ERROR_NULL_POINTER);
    2231               0 :   PRInt32 offset=0;
    2232               0 :   if (aBoundary == kBlockEnd)
    2233                 :   {
    2234                 :     PRUint32 uOffset;
    2235               0 :     aHTMLEd->GetLengthOfDOMNode(*aBlock, uOffset); 
    2236               0 :     offset = uOffset;
    2237                 :   }
    2238               0 :   nsWSRunObject theWSObj(aHTMLEd, *aBlock, offset);
    2239               0 :   return theWSObj.Scrub();    
    2240                 : }
    2241                 : 
    2242                 : 
    2243                 : nsresult
    2244               0 : nsWSRunObject::Scrub()
    2245                 : {
    2246               0 :   WSFragment *run = mStartRun;
    2247               0 :   while (run)
    2248                 :   {
    2249               0 :     if (run->mType & (eLeadingWS|eTrailingWS) )
    2250                 :     {
    2251               0 :       nsresult res = DeleteChars(run->mStartNode, run->mStartOffset, run->mEndNode, run->mEndOffset);
    2252               0 :       NS_ENSURE_SUCCESS(res, res);
    2253                 :     }
    2254               0 :     run = run->mRight;
    2255                 :   }
    2256               0 :   return NS_OK;
    2257                 : }
    2258                 : 

Generated by: LCOV version 1.7