LCOV - app.info - editor/libeditor/html/nsWSRunObject.cpp

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