Back to index

lightning-sunbird  0.9+nobinonly
nsSelection.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Mats Palmgren <mats.palmgren@bredband.net>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 /*
00040  * Implementation of selection: nsISelection,nsISelectionPrivate and nsIFrameSelection
00041  */
00042 
00043 #include "nsCOMPtr.h"
00044 #include "nsWeakReference.h"
00045 #include "nsIFactory.h"
00046 #include "nsIEnumerator.h"
00047 #include "nsString.h"
00048 #include "nsReadableUtils.h"
00049 #include "nsIDOMRange.h"
00050 #include "nsIFrameSelection.h"
00051 #include "nsISelection.h"
00052 #include "nsISelection2.h"
00053 #include "nsISelectionPrivate.h"
00054 #include "nsISelectionListener.h"
00055 #include "nsIComponentManager.h"
00056 #include "nsContentCID.h"
00057 #include "nsIContent.h"
00058 #include "nsIDOMElement.h"
00059 #include "nsIDOMNode.h"
00060 #include "nsRange.h"
00061 #include "nsCOMArray.h"
00062 #include "nsGUIEvent.h"
00063 #include "nsIDOMKeyEvent.h"
00064 #include "nsITableLayout.h"
00065 #include "nsITableCellLayout.h"
00066 #include "nsIDOMNodeList.h"
00067 #include "nsITextContent.h"
00068 #include "nsTArray.h"
00069 
00070 #include "nsISelectionListener.h"
00071 #include "nsIContentIterator.h"
00072 #include "nsIDocumentEncoder.h"
00073 
00074 // for IBMBIDI
00075 #include "nsFrameTraversal.h"
00076 #include "nsILineIterator.h"
00077 #include "nsLayoutAtoms.h"
00078 #include "nsIFrameTraversal.h"
00079 #include "nsLayoutUtils.h"
00080 #include "nsLayoutCID.h"
00081 static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
00082 
00083 #include "nsIDOMText.h"
00084 
00085 #include "nsContentUtils.h"
00086 
00087 //included for desired x position;
00088 #include "nsPresContext.h"
00089 #include "nsIPresShell.h"
00090 #include "nsICaret.h"
00091 
00092 
00093 // included for view scrolling
00094 #include "nsIViewManager.h"
00095 #include "nsIScrollableView.h"
00096 #include "nsIDeviceContext.h"
00097 #include "nsITimer.h"
00098 #include "nsIServiceManager.h"
00099 #include "nsIEventQueue.h"
00100 #include "nsIEventQueueService.h"
00101 
00102 // notifications
00103 #include "nsIDOMDocument.h"
00104 #include "nsIDocument.h"
00105 
00106 #include "nsISelectionController.h"//for the enums
00107 #include "nsHTMLAtoms.h"
00108 #include "nsAutoCopyListener.h"
00109 #include "nsCopySupport.h"
00110 #include "nsIClipboard.h"
00111 
00112 #define STATUS_CHECK_RETURN_MACRO() {if (!mShell) return NS_ERROR_FAILURE;}
00113 
00114 
00115 
00116 //#define DEBUG_TABLE 1
00117 
00118 // Selection's use of generated content iterators has been turned off
00119 // temporarily since it bogs down selection in large documents. Using
00120 // generated content iterators is slower because it must resolve the style
00121 // for the content to find out if it has any before/after style, and it
00122 // increases the number of calls to GetPrimaryFrame() which is very expensive.
00123 //
00124 // We can reduce the number of calls to GetPrimaryFrame() during selection
00125 // by a good factor (maybe 2-3 times) if we just ignore the generated content
00126 // and NOT hilite it when we cross it.
00127 //
00128 // #1 the output system doesn't handle it right now anyway so selecting
00129 //    has no REAL benefit to generated content.
00130 // #2 there is no available way given to me by troy that can give back the
00131 //    necessary data without a frame to work from.
00132 #ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
00133 static NS_DEFINE_IID(kCGenContentIteratorCID, NS_GENERATEDCONTENTITERATOR_CID);
00134 static NS_DEFINE_IID(kCGenSubtreeIteratorCID, NS_GENERATEDSUBTREEITERATOR_CID);
00135 #else
00136 static NS_DEFINE_IID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
00137 static NS_DEFINE_IID(kCSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
00138 #endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
00139 
00140 #undef OLD_SELECTION
00141 #undef OLD_TABLE_SELECTION
00142 
00143 
00144 //PROTOTYPES
00145 class nsSelectionIterator;
00146 class nsSelection;
00147 class nsAutoScrollTimer;
00148 struct nsScrollSelectionIntoViewEvent;
00149 
00150 PRBool  IsValidSelectionPoint(nsSelection *aFrameSel, nsIContent *aContent);
00151 PRBool  IsValidSelectionPoint(nsSelection *aFrameSel, nsIDOMNode *aDomNode);
00152 
00153 static nsIAtom *GetTag(nsIDOMNode *aNode);
00154 static nsresult ParentOffset(nsIDOMNode *aNode, nsIDOMNode **aParent, PRInt32 *aChildOffset);
00155 static nsIDOMNode *GetCellParent(nsIDOMNode *aDomNode);
00156 
00157 
00158 #ifdef PRINT_RANGE
00159 static void printRange(nsIDOMRange *aDomRange);
00160 #define DEBUG_OUT_RANGE(x)  printRange(x)
00161 #else
00162 #define DEBUG_OUT_RANGE(x)  
00163 #endif //MOZ_DEBUG
00164 
00165 
00166 
00167 //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
00168 //#define DEBUG_NAVIGATION
00169 
00170 
00171 //#define DEBUG_TABLE_SELECTION 1
00172 
00173 
00174 struct CachedOffsetForFrame {
00175   CachedOffsetForFrame()
00176   : mCachedFrameOffset(0, 0) // nsPoint ctor
00177   , mLastCaretFrame(nsnull)
00178   , mLastContentOffset(0)
00179   , mCanCacheFrameOffset(PR_FALSE)
00180   {}
00181 
00182   nsPoint      mCachedFrameOffset;      // cached frame offset
00183   nsIFrame*    mLastCaretFrame;         // store the frame the caret was last drawn in.
00184   PRInt32      mLastContentOffset;      // store last content offset
00185   PRPackedBool mCanCacheFrameOffset;    // cached frame offset is valid?
00186 };
00187 
00188 struct RangeData
00189 {
00190   RangeData(nsIDOMRange* aRange, PRInt32 aEndIndex) :
00191     mRange(aRange), mEndIndex(aEndIndex) {}
00192 
00193   nsCOMPtr<nsIDOMRange> mRange;
00194   PRInt32 mEndIndex; // index into mRangeEndings of this item
00195 };
00196 
00197 class nsTypedSelection : public nsISelection2,
00198                          public nsISelectionPrivate,
00199                          public nsSupportsWeakReference
00200 {
00201 public:
00202   nsTypedSelection();
00203   nsTypedSelection(nsSelection *aList);
00204   virtual ~nsTypedSelection();
00205   
00206   NS_DECL_ISUPPORTS
00207   NS_DECL_NSISELECTION
00208   NS_DECL_NSISELECTION2
00209   NS_DECL_NSISELECTIONPRIVATE
00210 
00211   // utility methods for scrolling the selection into view
00212   nsresult      GetPresContext(nsPresContext **aPresContext);
00213   nsresult      GetPresShell(nsIPresShell **aPresShell);
00214   nsresult      GetRootScrollableView(nsIScrollableView **aScrollableView);
00215   nsresult      GetFrameToScrolledViewOffsets(nsIScrollableView *aScrollableView, nsIFrame *aFrame, nscoord *aXOffset, nscoord *aYOffset);
00216   nsresult      GetPointFromOffset(nsIFrame *aFrame, PRInt32 aContentOffset, nsPoint *aPoint);
00217   nsresult      GetSelectionRegionRectAndScrollableView(SelectionRegion aRegion, nsRect *aRect, nsIScrollableView **aScrollableView);
00218   nsresult      ScrollRectIntoView(nsIScrollableView *aScrollableView, nsRect& aRect, PRIntn  aVPercent, PRIntn  aHPercent, PRBool aScrollParentViews);
00219 
00220   nsresult      PostScrollSelectionIntoViewEvent(SelectionRegion aRegion);
00221   NS_IMETHOD    ScrollIntoView(SelectionRegion aRegion=nsISelectionController::SELECTION_FOCUS_REGION, PRBool aIsSynchronous=PR_TRUE);
00222   nsresult      AddItem(nsIDOMRange *aRange);
00223   nsresult      RemoveItem(nsIDOMRange *aRange);
00224   nsresult      Clear(nsPresContext* aPresContext);
00225 
00226   // methods for convenience. Note, these don't addref
00227   nsIDOMNode*  FetchAnchorNode();  //where did the selection begin
00228   PRInt32      FetchAnchorOffset();
00229 
00230   nsIDOMNode*  FetchOriginalAnchorNode();  //where did the ORIGINAL selection begin
00231   PRInt32      FetchOriginalAnchorOffset();
00232 
00233   nsIDOMNode*  FetchFocusNode();   //where is the carret
00234   PRInt32      FetchFocusOffset();
00235 
00236   nsIDOMNode*  FetchStartParent(nsIDOMRange *aRange);   //skip all the com stuff and give me the start/end
00237   PRInt32      FetchStartOffset(nsIDOMRange *aRange);
00238   nsIDOMNode*  FetchEndParent(nsIDOMRange *aRange);     //skip all the com stuff and give me the start/end
00239   PRInt32      FetchEndOffset(nsIDOMRange *aRange);
00240 
00241   nsDirection  GetDirection(){return mDirection;}
00242   void         SetDirection(nsDirection aDir){mDirection = aDir;}
00243   PRBool       GetTrueDirection() {return mTrueDirection;}
00244   void         SetTrueDirection(PRBool aBool){mTrueDirection = aBool;}
00245   NS_IMETHOD   CopyRangeToAnchorFocus(nsIDOMRange *aRange);
00246   
00247 
00248 //  NS_IMETHOD   GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, PRBool aIsEndNode, nsIFrame **aResultFrame);
00249   NS_IMETHOD   GetPrimaryFrameForAnchorNode(nsIFrame **aResultFrame);
00250   NS_IMETHOD   GetPrimaryFrameForFocusNode(nsIFrame **aResultFrame, PRInt32 *aOffset);
00251   NS_IMETHOD   SetOriginalAnchorPoint(nsIDOMNode *aNode, PRInt32 aOffset);
00252   NS_IMETHOD   GetOriginalAnchorPoint(nsIDOMNode **aNode, PRInt32 *aOffset);
00253   NS_IMETHOD   LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
00254                              SelectionDetails **aReturnDetails, SelectionType aType, PRBool aSlowCheck);
00255   NS_IMETHOD   Repaint(nsPresContext* aPresContext);
00256 
00257   nsresult     StartAutoScrollTimer(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRUint32 aDelay);
00258   nsresult     StopAutoScrollTimer();
00259   nsresult     DoAutoScrollView(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews);
00260   nsresult     ScrollPointIntoClipView(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool *aDidScroll);
00261   nsresult     ScrollPointIntoView(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews, PRBool *aDidScroll);
00262   nsresult     GetViewAncestorOffset(nsIView *aView, nsIView *aAncestorView, nscoord *aXOffset, nscoord *aYOffset);
00263 
00264   SelectionType GetType(){return mType;}
00265   void          SetType(SelectionType aType){mType = aType;}
00266 
00267   nsresult     NotifySelectionListeners();
00268 
00269   void DetachFromPresentation();
00270 
00271 private:
00272   friend class nsSelectionIterator;
00273   friend struct nsScrollSelectionIntoViewEvent;
00274 
00275 
00276   void         setAnchorFocusRange(PRInt32 aIndex); //pass in index into FrameSelection
00277   NS_IMETHOD   selectFrames(nsPresContext* aPresContext, nsIContentIterator *aInnerIter, nsIContent *aContent, nsIDOMRange *aRange, nsIPresShell *aPresShell, PRBool aFlags);
00278   NS_IMETHOD   selectFrames(nsPresContext* aPresContext, nsIDOMRange *aRange, PRBool aSelect);
00279   nsresult     getTableCellLocationFromRange(nsIDOMRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol);
00280   nsresult     addTableCellRange(nsIDOMRange *aRange, PRBool *aDidAddRange);
00281   
00282 #ifdef OLD_SELECTION
00283   NS_IMETHOD   FixupSelectionPoints(nsIDOMRange *aRange, nsDirection *aDir, PRBool *aFixupState);
00284 #endif //OLD_SELECTION
00285 
00286   // These are the ranges inside this selection. They are kept sorted in order
00287   // of DOM position of start and end, respectively (both of these arrays
00288   // should have the same contents, but possibly in different orders).
00289   //
00290   // This data structure is sorted by the range beginnings and the range
00291   // endings. When searching for a range, we can discard ranges whose ends
00292   // are before the point in question, or whose beginnings are after. We can
00293   // find these two sets of ranges on O(log n) time.
00294   //
00295   // Merging these two result sets takes O(n) time, so a a full query is O(log
00296   // n + n) time. This looks worse than brute force O(n) searching, but in
00297   // practice it is much faster. The DOM comparisons used in a brute force
00298   // search are VERY expensive because they actually walk the DOM tree. We only
00299   // do log(n) of these comparisons.
00300   //
00301   // Our O(n) merging step uses very fast integer comparisons, and, since
00302   // we store the range ending index in the structure, we have to merge at
00303   // most half of the results. Timing shows that this algorithm is nearly
00304   // twice as fast doing intersections for 9 ranges, and 18 times faster for
00305   // 250 ranges.
00306   //
00307   // An interval tree would give us O(log n) time lookups, which would be
00308   // better. However, this approach gets us most of the way, and doesn't
00309   // require rebalancing and other  overhead. If this algorithm is found to be
00310   // a bottleneck, it should be replaced with an interval tree.
00311   //
00312   // It has been discussed requiring selections to be disjoint in the future.
00313   // If this requirement is added, the current design makes the most sense, we
00314   // can just remove the array sorted by endings.
00315 
00316   nsTArray<RangeData> mRanges;
00317   nsTArray<PRInt32> mRangeEndings;    // references info mRanges
00318 #ifdef DEBUG
00319   PRBool ValidateRanges();
00320 #endif
00321 
00322   nsresult FindInsertionPoint(
00323       const nsTArray<PRInt32>* aRemappingArray,
00324       nsIDOMNode* aPointNode, PRInt32 aPointOffset,
00325       nsresult (*aComparator)(nsIDOMNode*,PRInt32,nsIDOMRange*,PRInt32*),
00326       PRInt32* aInsertionPoint);
00327   nsresult MoveIndexToFirstMatch(PRInt32* aIndex, nsIDOMNode* aNode,
00328                                  PRInt32 aOffset,
00329                                  const nsTArray<PRInt32>* aArray,
00330                                  PRBool aUseBeginning);
00331   nsresult MoveIndexToNextMismatch(PRInt32* aIndex, nsIDOMNode* aNode,
00332                                    PRInt32 aOffset,
00333                                    const nsTArray<PRInt32>* aRemappingArray,
00334                                    PRBool aUseBeginning);
00335   PRBool FindRangeGivenPoint(nsIDOMNode* aBeginNode, PRInt32 aBeginOffset,
00336                              nsIDOMNode* aEndNode, PRInt32 aEndOffset,
00337                              PRInt32 aStartSearchingHere);
00338 
00339   nsCOMPtr<nsIDOMRange> mAnchorFocusRange;
00340   nsCOMPtr<nsIDOMRange> mOriginalAnchorRange; //used as a point with range gravity for security
00341   nsDirection mDirection; //FALSE = focus, anchor;  TRUE = anchor, focus
00342   PRBool mFixupState; //was there a fixup?
00343 
00344   nsSelection *mFrameSelection;
00345   nsWeakPtr mPresShellWeak; //weak reference to presshell.
00346   SelectionType mType;//type of this nsTypedSelection;
00347   nsAutoScrollTimer *mAutoScrollTimer; // timer for autoscrolling.
00348   nsCOMArray<nsISelectionListener> mSelectionListeners;
00349   PRBool mTrueDirection;
00350   nsCOMPtr<nsIEventQueue> mEventQueue;
00351   PRBool mScrollEventPosted;
00352   CachedOffsetForFrame *mCachedOffsetForFrame;
00353 };
00354 
00355 // Stack-class to turn on/off selection batching for table selection
00356 class nsSelectionBatcher
00357 {
00358 private:
00359   nsCOMPtr<nsISelectionPrivate> mSelection;
00360 public:
00361   nsSelectionBatcher(nsISelectionPrivate *aSelection) : mSelection(aSelection)
00362   {
00363     if (mSelection) mSelection->StartBatchChanges();
00364   }
00365   virtual ~nsSelectionBatcher() 
00366   { 
00367     if (mSelection) mSelection->EndBatchChanges();
00368   }
00369 };
00370 
00371 class nsSelection : public nsIFrameSelection
00372                     
00373 {
00374 public:
00375   /*interfaces for addref and release and queryinterface*/
00376   
00377   NS_DECL_ISUPPORTS
00378 
00379 /*BEGIN nsIFrameSelection interfaces*/
00380   NS_IMETHOD Init(nsIPresShell *aShell, nsIContent *aLimiter);
00381   NS_IMETHOD SetScrollableView(nsIScrollableView *aScrollView);
00382   NS_IMETHOD GetScrollableView(nsIScrollableView **aScrollView) {*aScrollView = mScrollView; return NS_OK;}
00383 
00384   NS_IMETHOD ShutDown();
00385   NS_IMETHOD HandleTextEvent(nsGUIEvent *aGUIEvent);
00386   NS_IMETHOD HandleKeyEvent(nsPresContext* aPresContext, nsGUIEvent *aGuiEvent);
00387   NS_IMETHOD HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset, PRUint32 aContentEndOffset, 
00388                        PRBool aContinueSelection, PRBool aMultipleSelection,PRBool aHint);
00389   NS_IMETHOD HandleDrag(nsPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint);
00390   NS_IMETHOD HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRInt32 aTarget, nsMouseEvent *aMouseEvent);
00391   NS_IMETHOD StartAutoScrollTimer(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRUint32 aDelay);
00392   NS_IMETHOD StopAutoScrollTimer();
00393   NS_IMETHOD EnableFrameNotification(PRBool aEnable){mNotifyFrames = aEnable; return NS_OK;}
00394   NS_IMETHOD LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
00395                              SelectionDetails **aReturnDetails, PRBool aSlowCheck);
00396   NS_IMETHOD SetMouseDownState(PRBool aState);
00397   NS_IMETHOD GetMouseDownState(PRBool *aState);
00398 
00399   NS_IMETHOD GetTableCellSelection(PRBool *aState){if (aState){*aState = mSelectingTableCellMode != 0; return NS_OK;}return NS_ERROR_NULL_POINTER;}
00400   NS_IMETHOD ClearTableCellSelection(){mSelectingTableCellMode = 0; return NS_OK;}
00401   
00402   NS_IMETHOD GetSelection(SelectionType aType, nsISelection **aDomSelection);
00403   NS_IMETHOD ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous);
00404   NS_IMETHOD RepaintSelection(nsPresContext* aPresContext, SelectionType aType);
00405   NS_IMETHOD GetFrameForNodeOffset(nsIContent *aNode, PRInt32 aOffset, HINT aHint, nsIFrame **aReturnFrame, PRInt32 *aReturnOffset);
00406   NS_IMETHOD CommonPageMove(PRBool aForward, PRBool aExtend, nsIScrollableView *aScrollableView, nsIFrameSelection *aFrameSel);
00407 
00408   NS_IMETHOD AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection,
00409         nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset);
00410   
00411   NS_IMETHOD SetHint(HINT aHintRight);
00412   NS_IMETHOD GetHint(HINT *aHintRight);
00413   NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend);
00414   NS_IMETHOD WordMove(PRBool aForward, PRBool aExtend);
00415   NS_IMETHOD LineMove(PRBool aForward, PRBool aExtend);
00416   NS_IMETHOD IntraLineMove(PRBool aForward, PRBool aExtend); 
00417   NS_IMETHOD SelectAll();
00418   NS_IMETHOD SetDisplaySelection(PRInt16 aState);
00419   NS_IMETHOD GetDisplaySelection(PRInt16 *aState);
00420   NS_IMETHOD SetDelayCaretOverExistingSelection(PRBool aDelay);
00421   NS_IMETHOD GetDelayCaretOverExistingSelection(PRBool *aDelay);
00422   NS_IMETHOD SetDelayedCaretData(nsMouseEvent *aMouseEvent);
00423   NS_IMETHOD GetDelayedCaretData(nsMouseEvent **aMouseEvent);
00424   NS_IMETHOD GetLimiter(nsIContent **aLimiterContent);
00425   NS_IMETHOD SetMouseDoubleDown(PRBool aDoubleDown);
00426   NS_IMETHOD GetMouseDoubleDown(PRBool *aDoubleDown);
00427   NS_IMETHOD GetPrevNextBidiLevels(nsPresContext *aPresContext,
00428                                    nsIContent *aNode,
00429                                    PRUint32 aContentOffset,
00430                                    nsIFrame **aPrevFrame,
00431                                    nsIFrame **aNextFrame,
00432                                    PRUint8 *aPrevLevel,
00433                                    PRUint8 *aNextLevel);
00434   NS_IMETHOD GetFrameFromLevel(nsPresContext *aPresContext,
00435                                nsIFrame *aFrameIn,
00436                                nsDirection aDirection,
00437                                PRUint8 aBidiLevel,
00438                                nsIFrame **aFrameOut);
00439   NS_IMETHOD MaintainSelection();
00440   /*END nsIFrameSelection interfaces */
00441 
00442 
00443 
00444   nsSelection();
00445   virtual ~nsSelection();
00446 
00447   NS_IMETHOD    StartBatchChanges();
00448   NS_IMETHOD    EndBatchChanges();
00449   NS_IMETHOD    DeleteFromDocument();
00450 
00451   nsIPresShell *GetShell() {return mShell;}
00452 
00453 private:
00454   NS_IMETHOD TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset, PRUint32 aContentEndOffset, 
00455                        PRBool aContinueSelection, PRBool aMultipleSelection);
00456 
00457   void BidiLevelFromMove(nsPresContext* aContext,
00458                          nsIPresShell* aPresShell,
00459                          nsIContent *aNode,
00460                          PRUint32 aContentOffset,
00461                          PRUint32 aKeycode,
00462                          HINT aHint);
00463   void BidiLevelFromClick(nsIContent *aNewFocus, PRUint32 aContentOffset);
00464   NS_IMETHOD GetPrevNextBidiLevels(nsPresContext *aPresContext,
00465                                    nsIContent *aNode,
00466                                    PRUint32 aContentOffset,
00467                                    HINT aHint,
00468                                    nsIFrame **aPrevFrame,
00469                                    nsIFrame **aNextFrame,
00470                                    PRUint8 *aPrevLevel,
00471                                    PRUint8 *aNextLevel);  
00472 #ifdef VISUALSELECTION
00473   NS_IMETHOD VisualSelectFrames(nsPresContext* aContext,
00474                                 nsIFrame* aCurrentFrame,
00475                                 nsPeekOffsetStruct aPos);
00476   NS_IMETHOD VisualSequence(nsPresContext *aPresContext,
00477                             nsIFrame* aSelectFrame,
00478                             nsIFrame* aCurrentFrame,
00479                             nsPeekOffsetStruct* aPos,
00480                             PRBool* aNeedVisualSelection);
00481   NS_IMETHOD SelectToEdge(nsIFrame *aFrame,
00482                           nsIContent *aContent,
00483                           PRInt32 aOffset,
00484                           PRInt32 aEdge,
00485                           PRBool aMultipleSelection);
00486   NS_IMETHOD SelectLines(nsPresContext *aPresContext,
00487                          nsDirection aSelectionDirection,
00488                          nsIDOMNode *aAnchorNode,
00489                          nsIFrame* aAnchorFrame,
00490                          PRInt32 aAnchorOffset,
00491                          nsIDOMNode *aCurrentNode,
00492                          nsIFrame* aCurrentFrame,
00493                          PRInt32 aCurrentOffset,
00494                          nsPeekOffsetStruct aPos);
00495 #endif // VISUALSELECTION
00496 
00497   PRBool AdjustForMaintainedSelection(nsIContent *aContent, PRInt32 aOffset);
00498 
00499 // post and pop reasons for notifications. we may stack these later
00500   void    PostReason(PRInt16 aReason) { mSelectionChangeReason = aReason; }
00501   PRInt16 PopReason()
00502   {
00503     PRInt16 retval = mSelectionChangeReason;
00504     mSelectionChangeReason = 0;
00505     return retval;
00506   }
00507 
00508   friend class nsTypedSelection; 
00509 #ifdef DEBUG
00510   void printSelection();       // for debugging
00511 #endif /* DEBUG */
00512 
00513   void ResizeBuffer(PRUint32 aNewBufSize);
00514 /*HELPER METHODS*/
00515   nsresult     MoveCaret(PRUint32 aKeycode, PRBool aContinueSelection, nsSelectionAmount aAmount);
00516 
00517   nsresult     FetchDesiredX(nscoord &aDesiredX); //the x position requested by the Key Handling for up down
00518   void         InvalidateDesiredX(); //do not listen to mDesiredX you must get another.
00519   void         SetDesiredX(nscoord aX); //set the mDesiredX
00520 
00521   nsresult     GetRootForContentSubtree(nsIContent *aContent, nsIContent **aParent);
00522   nsresult     ConstrainFrameAndPointToAnchorSubtree(nsPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, nsIFrame **aRetFrame, nsPoint& aRetPoint);
00523 
00524   PRUint32     GetBatching(){return mBatching;}
00525   PRBool       GetNotifyFrames(){return mNotifyFrames;}
00526   void         SetDirty(PRBool aDirty=PR_TRUE){if (mBatching) mChangesDuringBatching = aDirty;}
00527 
00528   nsresult     NotifySelectionListeners(SelectionType aType);     // add parameters to say collapsed etc?
00529 
00530   // utility method to lookup frame style
00531   nsresult      FrameOrParentHasSpecialSelectionStyle(nsIFrame* aFrame, PRUint8 aSelectionStyle, nsIFrame* *foundFrame);
00532 
00533   nsTypedSelection *mDomSelections[nsISelectionController::NUM_SELECTIONTYPES];
00534 
00535   // Table selection support.
00536   // Interfaces that let us get info based on cellmap locations
00537   nsITableLayout* GetTableLayout(nsIContent *aTableContent);
00538   nsITableCellLayout* GetCellLayout(nsIContent *aCellContent);
00539 
00540   nsresult SelectBlockOfCells(nsIContent *aStartNode, nsIContent *aEndNode);
00541   nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
00542   nsresult GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex);
00543 
00544   nsresult GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
00545   nsresult GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
00546   nsresult GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode);
00547   // aTableNode may be null if table isn't needed to be returned
00548   PRBool   IsInSameTable(nsIContent *aContent1, nsIContent *aContent2, nsIContent **aTableNode);
00549   nsresult GetParentTable(nsIContent *aCellNode, nsIContent **aTableNode);
00550   nsresult SelectCellElement(nsIDOMElement* aCellElement);
00551   nsresult CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset);
00552   nsresult ClearNormalSelection();
00553 
00554   nsCOMPtr<nsIDOMNode> mCellParent; //used to snap to table selection
00555   nsCOMPtr<nsIContent> mStartSelectedCell;
00556   nsCOMPtr<nsIContent> mEndSelectedCell;
00557   nsCOMPtr<nsIContent> mAppendStartSelectedCell;
00558   nsCOMPtr<nsIContent> mUnselectCellOnMouseUp;
00559   PRInt32  mSelectingTableCellMode;
00560   PRInt32  mSelectedCellIndex;
00561 
00562   // maintain selection
00563   nsCOMPtr<nsIDOMRange> mMaintainRange;
00564 
00565   //batching
00566   PRInt32 mBatching;
00567     
00568   nsIContent *mLimiter;     //limit selection navigation to a child of this node.
00569   nsIPresShell *mShell;
00570 
00571   PRInt16 mSelectionChangeReason; // reason for notifications of selection changing
00572   PRInt16 mDisplaySelection; //for visual display purposes.
00573 
00574   HINT  mHint;   //hint to tell if the selection is at the end of this line or beginning of next
00575 
00576   PRInt32 mDesiredX;
00577   nsIScrollableView *mScrollView;
00578 
00579   nsMouseEvent mDelayedMouseEvent;
00580 
00581   PRPackedBool mDelayCaretOverExistingSelection;
00582   PRPackedBool mDelayedMouseEventValid;
00583 
00584   PRPackedBool mChangesDuringBatching;
00585   PRPackedBool mNotifyFrames;
00586   PRPackedBool mIsEditor;
00587   PRPackedBool mDragSelectingCells;
00588   PRPackedBool mMouseDownState;   //for drag purposes
00589   PRPackedBool mMouseDoubleDownState; //has the doubleclick down happened
00590   PRPackedBool mDesiredXSet;
00591 };
00592 
00593 class nsSelectionIterator : public nsIBidirectionalEnumerator
00594 {
00595 public:
00596 /*BEGIN nsIEnumerator interfaces
00597 see the nsIEnumerator for more details*/
00598 
00599   NS_DECL_ISUPPORTS
00600 
00601   NS_DECL_NSIENUMERATOR
00602 
00603   NS_DECL_NSIBIDIRECTIONALENUMERATOR
00604 
00605 /*END nsIEnumerator interfaces*/
00606 /*BEGIN Helper Methods*/
00607   NS_IMETHOD CurrentItem(nsIDOMRange **aRange);
00608 /*END Helper Methods*/
00609 private:
00610   friend class nsTypedSelection;
00611 
00612   //lame lame lame if delete from document goes away then get rid of this unless its debug
00613   friend class nsSelection; 
00614 
00615   nsSelectionIterator(nsTypedSelection *);
00616   virtual ~nsSelectionIterator();
00617   PRInt32     mIndex;
00618   nsTypedSelection *mDomSelection;
00619   SelectionType mType;
00620 };
00621 
00622 class nsAutoScrollTimer : public nsITimerCallback
00623 {
00624 public:
00625 
00626   NS_DECL_ISUPPORTS
00627 
00628   nsAutoScrollTimer()
00629       : mSelection(0), mView(0), mPresContext(0), mPoint(0,0), mDelay(30)
00630   {
00631   }
00632 
00633   virtual ~nsAutoScrollTimer()
00634   {
00635    if (mTimer)
00636        mTimer->Cancel();
00637   }
00638 
00639   nsresult Start(nsPresContext *aPresContext, nsIView *aView, nsPoint &aPoint)
00640   {
00641     mView        = aView;
00642     mPresContext = aPresContext;
00643     mPoint       = aPoint;
00644 
00645     if (!mTimer)
00646     {
00647       nsresult result;
00648       mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
00649 
00650       if (NS_FAILED(result))
00651         return result;
00652     }
00653 
00654     return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
00655   }
00656 
00657   nsresult Stop()
00658   {
00659     nsresult result = NS_OK;
00660 
00661     if (mTimer)
00662     {
00663       mTimer->Cancel();
00664       mTimer = 0;
00665     }
00666 
00667     return result;
00668   }
00669 
00670   nsresult Init(nsSelection *aFrameSelection, nsTypedSelection *aSelection)
00671   {
00672     mFrameSelection = aFrameSelection;
00673     mSelection = aSelection;
00674     return NS_OK;
00675   }
00676 
00677   nsresult SetDelay(PRUint32 aDelay)
00678   {
00679     mDelay = aDelay;
00680     return NS_OK;
00681   }
00682 
00683   NS_IMETHOD Notify(nsITimer *timer)
00684   {
00685     if (mSelection && mPresContext && mView)
00686     {
00687       nsIFrame *frame = NS_STATIC_CAST(nsIFrame*, mView->GetClientData());
00688 
00689       if (!frame)
00690         return NS_OK;
00691 
00692       mFrameSelection->HandleDrag(mPresContext, frame, mPoint);
00693 
00694       mSelection->DoAutoScrollView(mPresContext, mView, mPoint, PR_TRUE);
00695     }
00696     return NS_OK;
00697   }
00698 private:
00699   nsSelection    *mFrameSelection;
00700   nsTypedSelection *mSelection;
00701   nsCOMPtr<nsITimer> mTimer;
00702   nsIView        *mView;
00703   nsPresContext *mPresContext;
00704   nsPoint         mPoint;
00705   PRUint32        mDelay;
00706 };
00707 
00708 NS_IMPL_ADDREF(nsAutoScrollTimer)
00709 NS_IMPL_RELEASE(nsAutoScrollTimer)
00710 NS_IMPL_QUERY_INTERFACE1(nsAutoScrollTimer, nsITimerCallback)
00711 
00712 nsresult NS_NewAutoScrollTimer(nsAutoScrollTimer **aResult);
00713 
00714 nsresult NS_NewAutoScrollTimer(nsAutoScrollTimer **aResult)
00715 {
00716   if (!aResult)
00717     return NS_ERROR_NULL_POINTER;
00718 
00719   *aResult = (nsAutoScrollTimer*) new nsAutoScrollTimer;
00720 
00721   if (!aResult)
00722     return NS_ERROR_OUT_OF_MEMORY;
00723 
00724   NS_ADDREF(*aResult);
00725 
00726   return NS_OK;
00727 }
00728 
00729 nsresult NS_NewSelection(nsIFrameSelection **aFrameSelection);
00730 
00731 nsresult NS_NewSelection(nsIFrameSelection **aFrameSelection)
00732 {
00733   nsSelection *rlist = new nsSelection;
00734   if (!rlist)
00735     return NS_ERROR_OUT_OF_MEMORY;
00736   *aFrameSelection = (nsIFrameSelection *)rlist;
00737   NS_ADDREF(rlist);
00738   return NS_OK;
00739 }
00740 
00741 nsresult NS_NewDomSelection(nsISelection **aDomSelection);
00742 
00743 nsresult NS_NewDomSelection(nsISelection **aDomSelection)
00744 {
00745   nsTypedSelection *rlist = new nsTypedSelection;
00746   if (!rlist)
00747     return NS_ERROR_OUT_OF_MEMORY;
00748   *aDomSelection = (nsISelection *)rlist;
00749   NS_ADDREF(rlist);
00750   return NS_OK;
00751 }
00752 
00753 static PRInt8
00754 GetIndexFromSelectionType(SelectionType aType)
00755 {
00756     switch (aType)
00757     {
00758     case nsISelectionController::SELECTION_NORMAL: return 0; break;
00759     case nsISelectionController::SELECTION_SPELLCHECK: return 1; break;
00760     case nsISelectionController::SELECTION_IME_RAWINPUT: return 2; break;
00761     case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT: return 3; break;
00762     case nsISelectionController::SELECTION_IME_CONVERTEDTEXT: return 4; break;
00763     case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: return 5; break;
00764     case nsISelectionController::SELECTION_ACCESSIBILITY: return 6; break;
00765     default:return -1;break;
00766     }
00767     /* NOTREACHED */
00768     return 0;
00769 }
00770 
00771 static SelectionType 
00772 GetSelectionTypeFromIndex(PRInt8 aIndex)
00773 {
00774   switch (aIndex)
00775   {
00776     case 0: return nsISelectionController::SELECTION_NORMAL;break;
00777     case 1: return nsISelectionController::SELECTION_SPELLCHECK;break;
00778     case 2: return nsISelectionController::SELECTION_IME_RAWINPUT;break;
00779     case 3: return nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT;break;
00780     case 4: return nsISelectionController::SELECTION_IME_CONVERTEDTEXT;break;
00781     case 5: return nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT;break;
00782     case 6: return nsISelectionController::SELECTION_ACCESSIBILITY;break;
00783     default:
00784       return nsISelectionController::SELECTION_NORMAL;break;
00785   }
00786   /* NOTREACHED */
00787   return 0;
00788 }
00789 
00790 //utility methods to check the content vs the limiter that will hold selection to a piece of the dom
00791 PRBool       
00792 IsValidSelectionPoint(nsSelection *aFrameSel, nsIDOMNode *aDomNode)
00793 {
00794     nsCOMPtr<nsIContent> passedContent;
00795     passedContent = do_QueryInterface(aDomNode);
00796     if (!passedContent)
00797       return PR_FALSE;
00798     return IsValidSelectionPoint(aFrameSel,passedContent);
00799 }
00800 
00801 /*
00802 The limiter is used specifically for the text areas and textfields
00803 In that case it is the DIV tag that is anonymously created for the text
00804 areas/fields.  Text nodes and BR nodes fall beneath it.  In the case of a 
00805 BR node the limiter will be the parent and the offset will point before or
00806 after the BR node.  In the case of the text node the parent content is 
00807 the text node itself and the offset will be the exact character position.
00808 The offset is not important to check for validity.  Simply look at the 
00809 passed in content.  If it equals the limiter then the selection point is valid.
00810 If its parent it the limiter then the point is also valid.  In the case of 
00811 NO limiter all points are valid since you are in a topmost iframe. (browser
00812 or composer)
00813 */
00814 PRBool       
00815 IsValidSelectionPoint(nsSelection *aFrameSel, nsIContent *aContent)
00816 {
00817   if (!aFrameSel || !aContent)
00818     return PR_FALSE;
00819   if (aFrameSel)
00820   {
00821     nsresult result;
00822     nsCOMPtr<nsIContent> tLimiter;
00823     result = aFrameSel->GetLimiter(getter_AddRefs(tLimiter));
00824     if (NS_FAILED(result))
00825       return PR_FALSE;
00826     if (tLimiter && tLimiter != aContent)
00827     {
00828       if (tLimiter != aContent->GetParent()) //if newfocus == the limiter. thats ok. but if not there and not parent bad
00829         return PR_FALSE; //not in the right content. tLimiter said so
00830     }
00831   }
00832   return PR_TRUE;
00833 }
00834 
00835 
00836 NS_IMPL_ADDREF(nsSelectionIterator)
00837 NS_IMPL_RELEASE(nsSelectionIterator)
00838 
00839 NS_INTERFACE_MAP_BEGIN(nsSelectionIterator)
00840   NS_INTERFACE_MAP_ENTRY(nsIEnumerator)
00841   NS_INTERFACE_MAP_ENTRY(nsIBidirectionalEnumerator)
00842 NS_INTERFACE_MAP_END_AGGREGATED(mDomSelection)
00843 
00844 
00846 
00847 nsSelectionIterator::nsSelectionIterator(nsTypedSelection *aList)
00848 :mIndex(0)
00849 {
00850   if (!aList)
00851   {
00852     NS_NOTREACHED("nsSelection");
00853     return;
00854   }
00855   mDomSelection = aList;
00856 }
00857 
00858 
00859 
00860 nsSelectionIterator::~nsSelectionIterator()
00861 {
00862 }
00863 
00864 
00865 
00867 
00869 
00870 
00871 
00872 NS_IMETHODIMP
00873 nsSelectionIterator::Next()
00874 {
00875   mIndex++;
00876   PRInt32 cnt = mDomSelection->mRanges.Length();
00877   if (mIndex < cnt)
00878     return NS_OK;
00879   return NS_ERROR_FAILURE;
00880 }
00881 
00882 
00883 
00884 NS_IMETHODIMP
00885 nsSelectionIterator::Prev()
00886 {
00887   mIndex--;
00888   if (mIndex >= 0 )
00889     return NS_OK;
00890   return NS_ERROR_FAILURE;
00891 }
00892 
00893 
00894 
00895 NS_IMETHODIMP
00896 nsSelectionIterator::First()
00897 {
00898   if (!mDomSelection)
00899     return NS_ERROR_NULL_POINTER;
00900   mIndex = 0;
00901   return NS_OK;
00902 }
00903 
00904 
00905 
00906 NS_IMETHODIMP
00907 nsSelectionIterator::Last()
00908 {
00909   if (!mDomSelection)
00910     return NS_ERROR_NULL_POINTER;
00911   mIndex = mDomSelection->mRanges.Length() - 1;
00912   return NS_OK;
00913 }
00914 
00915 
00916 
00917 NS_IMETHODIMP 
00918 nsSelectionIterator::CurrentItem(nsISupports **aItem)
00919 {
00920   if (!aItem)
00921     return NS_ERROR_NULL_POINTER;
00922 
00923   if (mIndex < 0 || mIndex >= (PRInt32)mDomSelection->mRanges.Length()) {
00924     return NS_ERROR_FAILURE;
00925   }
00926 
00927   return CallQueryInterface(mDomSelection->mRanges[mIndex].mRange,
00928                             aItem);
00929 }
00930 
00931 
00932 NS_IMETHODIMP 
00933 nsSelectionIterator::CurrentItem(nsIDOMRange **aItem)
00934 {
00935   if (!aItem)
00936     return NS_ERROR_NULL_POINTER;
00937   if (mIndex < 0 || mIndex >= (PRInt32)mDomSelection->mRanges.Length()) {
00938     return NS_ERROR_FAILURE;
00939   }
00940 
00941   *aItem = mDomSelection->mRanges[mIndex].mRange;
00942   NS_IF_ADDREF(*aItem);
00943   return NS_OK;
00944 }
00945 
00946 
00947 
00948 NS_IMETHODIMP
00949 nsSelectionIterator::IsDone()
00950 {
00951   PRInt32 cnt = mDomSelection->mRanges.Length();
00952   if (mIndex >= 0 && mIndex < cnt) {
00953     return NS_ENUMERATOR_FALSE;
00954   }
00955   return NS_OK;
00956 }
00957 
00958 
00960 
00961 #ifdef XP_MAC
00962 #pragma mark -
00963 #endif
00964 
00966 
00967 nsSelection::nsSelection()
00968   : mDelayedMouseEvent(PR_FALSE, 0, nsnull, nsMouseEvent::eReal)
00969 {
00970   PRInt32 i;
00971   for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
00972     mDomSelections[i] = nsnull;
00973   }
00974   for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
00975     mDomSelections[i] = new nsTypedSelection(this);
00976     if (!mDomSelections[i])
00977       return;
00978     NS_ADDREF(mDomSelections[i]);
00979     mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
00980   }
00981   mBatching = 0;
00982   mChangesDuringBatching = PR_FALSE;
00983   mNotifyFrames = PR_TRUE;
00984   mLimiter = nsnull; //no default limiter.
00985   
00986   mMouseDoubleDownState = PR_FALSE;
00987   
00988   mHint = HINTLEFT;
00989   mDragSelectingCells = PR_FALSE;
00990   mSelectingTableCellMode = 0;
00991   mSelectedCellIndex = 0;
00992 
00993   // Check to see if the autocopy pref is enabled
00994   //   and add the autocopy listener if it is
00995   if (nsContentUtils::GetBoolPref("clipboard.autocopy")) {
00996     nsAutoCopyListener *autoCopy = nsAutoCopyListener::GetInstance();
00997 
00998     if (autoCopy) {
00999       PRInt8 index =
01000         GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
01001       if (mDomSelections[index]) {
01002         autoCopy->Listen(mDomSelections[index]);
01003       }
01004     }
01005   }
01006 
01007   mDisplaySelection = nsISelectionController::SELECTION_OFF;
01008 
01009   mDelayCaretOverExistingSelection = PR_TRUE;
01010   mDelayedMouseEventValid = PR_FALSE;
01011   mSelectionChangeReason = nsISelectionListener::NO_REASON;
01012 }
01013 
01014 
01015 nsSelection::~nsSelection() 
01016 {
01017   PRInt32 i;
01018   for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
01019     if (mDomSelections[i]) {
01020       mDomSelections[i]->DetachFromPresentation();
01021       NS_RELEASE(mDomSelections[i]);
01022     }
01023   }
01024 }
01025 
01026 
01027 NS_IMPL_ISUPPORTS1(nsSelection, nsIFrameSelection)
01028 
01029 
01030 nsresult
01031 nsSelection::FetchDesiredX(nscoord &aDesiredX) //the x position requested by the Key Handling for up down
01032 {
01033   if (!mShell)
01034   {
01035     NS_ASSERTION(0,"fetch desired X failed\n");
01036     return NS_ERROR_FAILURE;
01037   }
01038   if (mDesiredXSet)
01039   {
01040     aDesiredX = mDesiredX;
01041     return NS_OK;
01042   }
01043 
01044   nsCOMPtr<nsICaret> caret;
01045   nsresult result = mShell->GetCaret(getter_AddRefs(caret));
01046   if (NS_FAILED(result))
01047     return result;
01048   if (!caret)
01049     return NS_ERROR_NULL_POINTER;
01050 
01051   nsRect coord;
01052   PRBool  collapsed;
01053   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
01054   result = caret->SetCaretDOMSelection(mDomSelections[index]);
01055   if (NS_FAILED(result))
01056     return result;
01057 
01058   result = caret->GetCaretCoordinates(nsICaret::eClosestViewCoordinates, mDomSelections[index], &coord, &collapsed, nsnull);
01059   if (NS_FAILED(result))
01060     return result;
01061    
01062   aDesiredX = coord.x;
01063   return NS_OK;
01064 }
01065 
01066 
01067 
01068 void
01069 nsSelection::InvalidateDesiredX() //do not listen to mDesiredX you must get another.
01070 {
01071   mDesiredXSet = PR_FALSE;
01072 }
01073 
01074 
01075 
01076 void
01077 nsSelection::SetDesiredX(nscoord aX) //set the mDesiredX
01078 {
01079   mDesiredX = aX;
01080   mDesiredXSet = PR_TRUE;
01081 }
01082 
01083 nsresult
01084 nsSelection::GetRootForContentSubtree(nsIContent *aContent, nsIContent **aParent)
01085 {
01086   // This method returns the root of the sub-tree containing aContent.
01087   // We do this by searching up through the parent hierarchy, and stopping
01088   // when there are no more parents, or we hit a situation where the
01089   // parent/child relationship becomes invalid.
01090   //
01091   // An example of an invalid parent/child relationship is anonymous content.
01092   // Anonymous content has a pointer to it's parent, but it is not listed
01093   // as a child of it's parent. In this case, the anonymous content would
01094   // be considered the root of the subtree.
01095 
01096   if (!aContent || !aParent)
01097     return NS_ERROR_NULL_POINTER;
01098 
01099   *aParent = 0;
01100 
01101   nsIContent* child = aContent;
01102 
01103   while (child)
01104   {
01105     nsIContent* parent = child->GetParent();
01106 
01107     if (!parent)
01108       break;
01109 
01110     PRUint32 childCount = parent->GetChildCount();
01111 
01112     if (childCount < 1)
01113       break;
01114 
01115     PRInt32 childIndex = parent->IndexOf(child);
01116 
01117     if (childIndex < 0 || ((PRUint32)childIndex) >= childCount)
01118       break;
01119 
01120     child = parent;
01121   }
01122 
01123   NS_IF_ADDREF(*aParent = child);
01124 
01125   return NS_OK;
01126 }
01127 
01128 nsresult
01129 nsSelection::ConstrainFrameAndPointToAnchorSubtree(nsPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, nsIFrame **aRetFrame, nsPoint& aRetPoint)
01130 {
01131   //
01132   // The whole point of this method is to return a frame and point that
01133   // that lie within the same valid subtree as the anchor node's frame,
01134   // for use with the method GetContentAndOffsetsFromPoint().
01135   //
01136   // A valid subtree is defined to be one where all the content nodes in
01137   // the tree have a valid parent-child relationship.
01138   //
01139   // If the anchor frame and aFrame are in the same subtree, aFrame will
01140   // be returned in aRetFrame. If they are in different subtrees, we
01141   // return the frame for the root of the subtree.
01142   //
01143 
01144   if (!aFrame || !aRetFrame)
01145     return NS_ERROR_NULL_POINTER;
01146 
01147   *aRetFrame = aFrame;
01148   aRetPoint  = aPoint;
01149 
01150   //
01151   // Get the frame and content for the selection's anchor point!
01152   //
01153 
01154   nsresult result;
01155   nsCOMPtr<nsIDOMNode> anchorNode;
01156   PRInt32 anchorOffset = 0;
01157   PRInt32 anchorFrameOffset = 0;
01158 
01159   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
01160   if (! mDomSelections[index])
01161     return NS_ERROR_NULL_POINTER;
01162 
01163   result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode));
01164 
01165   if (NS_FAILED(result))
01166     return result;
01167 
01168   if (!anchorNode)
01169     return NS_OK;
01170 
01171   result = mDomSelections[index]->GetAnchorOffset(&anchorOffset);
01172 
01173   if (NS_FAILED(result))
01174     return result;
01175 
01176   nsIFrame *anchorFrame = 0;
01177   nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
01178 
01179   if (!anchorContent)
01180     return NS_ERROR_FAILURE;
01181   
01182   result = GetFrameForNodeOffset(anchorContent, anchorOffset, mHint, &anchorFrame, &anchorFrameOffset);
01183 
01184   //
01185   // Now find the root of the subtree containing the anchor's content.
01186   //
01187 
01188   nsCOMPtr<nsIContent> anchorRoot;
01189   result = GetRootForContentSubtree(anchorContent, getter_AddRefs(anchorRoot));
01190 
01191   if (NS_FAILED(result))
01192     return result;
01193 
01194   //
01195   // Now find the root of the subtree containing aFrame's content.
01196   //
01197 
01198   nsIContent* content = aFrame->GetContent();
01199 
01200   if (content)
01201   {
01202     nsCOMPtr<nsIContent> contentRoot;
01203 
01204     result = GetRootForContentSubtree(content, getter_AddRefs(contentRoot));
01205 
01206     if (anchorRoot == contentRoot)
01207     {
01208       //
01209       // The anchor and AFrame's root are the same. There
01210       // is no need to constrain, simply return aFrame.
01211       //
01212       *aRetFrame = aFrame;
01213       return NS_OK;
01214     }
01215   }
01216 
01217   //
01218   // aFrame's root does not match the anchor's root, or there is no
01219   // content associated with aFrame. Just return the primary frame
01220   // for the anchor's root. We'll let GetContentAndOffsetsFromPoint()
01221   // find the closest frame aPoint.
01222   //
01223 
01224   result = mShell->GetPrimaryFrameFor(anchorRoot, aRetFrame);
01225 
01226   if (NS_FAILED(result))
01227     return result;
01228 
01229   if (! *aRetFrame)
01230     return NS_ERROR_FAILURE;
01231 
01232   //
01233   // Now make sure that aRetPoint is converted to the same coordinate
01234   // system used by aRetFrame.
01235   //
01236 
01237   aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
01238 
01239   return NS_OK;
01240 }
01241 
01242 #ifdef XP_MAC
01243 #pragma mark -
01244 #endif
01245 
01246 #ifdef PRINT_RANGE
01247 void printRange(nsIDOMRange *aDomRange)
01248 {
01249   if (!aDomRange)
01250   {
01251     printf("NULL nsIDOMRange\n");
01252   }
01253   nsCOMPtr<nsIDOMNode> startNode;
01254   nsCOMPtr<nsIDOMNode> endNode;
01255   PRInt32 startOffset;
01256   PRInt32 endOffset;
01257   aDomRange->GetStartParent(getter_AddRefs(startNode));
01258   aDomRange->GetStartOffset(&startOffset);
01259   aDomRange->GetEndParent(getter_AddRefs(endNode));
01260   aDomRange->GetEndOffset(&endOffset);
01261   
01262   printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
01263          (unsigned long)aDomRange,
01264          (unsigned long)(nsIDOMNode*)startNode, (long)startOffset,
01265          (unsigned long)(nsIDOMNode*)endNode, (long)endOffset);
01266          
01267 }
01268 #endif /* PRINT_RANGE */
01269 
01270 static
01271 nsIAtom *GetTag(nsIDOMNode *aNode)
01272 {
01273   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
01274   if (!content) 
01275   {
01276     NS_NOTREACHED("bad node passed to GetTag()");
01277     return nsnull;
01278   }
01279   
01280   return content->Tag();
01281 }
01282 
01283 nsresult
01284 ParentOffset(nsIDOMNode *aNode, nsIDOMNode **aParent, PRInt32 *aChildOffset)
01285 {
01286   if (!aNode || !aParent || !aChildOffset)
01287     return NS_ERROR_NULL_POINTER;
01288 
01289   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
01290   if (content)
01291   {
01292     nsIContent* parent = content->GetParent();
01293     if (parent)
01294     {
01295       *aChildOffset = parent->IndexOf(content);
01296 
01297       return CallQueryInterface(parent, aParent);
01298     }
01299   }
01300 
01301   return NS_OK;
01302 }
01303 
01304 nsIDOMNode *
01305 GetCellParent(nsIDOMNode *aDomNode)
01306 {
01307     if (!aDomNode)
01308       return 0;
01309     nsCOMPtr<nsIDOMNode> parent(aDomNode);
01310     nsCOMPtr<nsIDOMNode> current(aDomNode);
01311     PRInt32 childOffset;
01312     nsIAtom *tag;
01313     // Start with current node and look for a table cell
01314     while(current)
01315     {
01316       tag = GetTag(current);
01317       if (tag == nsHTMLAtoms::td || tag == nsHTMLAtoms::th)
01318         return current;
01319       if (NS_FAILED(ParentOffset(current,getter_AddRefs(parent),&childOffset)) || !parent)
01320         return 0;
01321       current = parent;
01322     }
01323     return 0;
01324 }
01325 
01326 
01327 NS_IMETHODIMP
01328 nsSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
01329 {
01330   mShell = aShell;
01331   mMouseDownState = PR_FALSE;
01332   mDesiredXSet = PR_FALSE;
01333   mLimiter = aLimiter;
01334   mScrollView = nsnull;
01335   return NS_OK;
01336 }
01337 
01338 NS_IMETHODIMP
01339 nsSelection::SetScrollableView(nsIScrollableView *aScrollView)
01340 {
01341   mScrollView = aScrollView;
01342   return NS_OK;
01343 }
01344 
01345 
01346 NS_IMETHODIMP
01347 nsSelection::ShutDown()
01348 {
01349   return NS_OK;
01350 }
01351 
01352   
01353   
01354 NS_IMETHODIMP
01355 nsSelection::HandleTextEvent(nsGUIEvent *aGUIEvent)
01356 {
01357   if (!aGUIEvent)
01358     return NS_ERROR_NULL_POINTER;
01359 
01360 #ifdef DEBUG_TAGUE
01361   printf("nsSelection: HandleTextEvent\n");
01362 #endif
01363   nsresult result(NS_OK);
01364   if (NS_TEXT_TEXT == aGUIEvent->message) {
01365     PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
01366     result = mDomSelections[index]->ScrollIntoView();
01367   }
01368   return result;
01369 }
01370 
01371 
01372 nsresult
01373 nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinueSelection, nsSelectionAmount aAmount)
01374 {
01375   nsPresContext *context = mShell->GetPresContext();
01376   if (!context)
01377     return NS_ERROR_FAILURE;
01378 
01379   nsCOMPtr<nsIDOMNode> weakNodeUsed;
01380   PRInt32 offsetused = 0;
01381 
01382   PRBool isCollapsed;
01383   nscoord desiredX = 0; //we must keep this around and revalidate it when its just UP/DOWN
01384 
01385   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
01386   nsresult result = mDomSelections[index]->GetIsCollapsed(&isCollapsed);
01387   if (NS_FAILED(result))
01388     return result;
01389   if (aKeycode == nsIDOMKeyEvent::DOM_VK_UP || aKeycode == nsIDOMKeyEvent::DOM_VK_DOWN)
01390   {
01391     result = FetchDesiredX(desiredX);
01392     if (NS_FAILED(result))
01393       return result;
01394     SetDesiredX(desiredX);
01395   }
01396 
01397   PRInt32 caretStyle = nsContentUtils::GetIntPref("layout.selection.caret_style", 0);
01398 #ifdef XP_MACOSX
01399   if (caretStyle == 0) {
01400     caretStyle = 2; // put caret at the selection edge in the |aKeycode| direction
01401   }
01402 #endif
01403 
01404   if (!isCollapsed && !aContinueSelection && caretStyle == 2) {
01405     switch (aKeycode){
01406       case nsIDOMKeyEvent::DOM_VK_LEFT  :
01407       case nsIDOMKeyEvent::DOM_VK_UP    :
01408           if (mDomSelections[index]->GetDirection() == eDirPrevious) { //f,a
01409             offsetused = mDomSelections[index]->FetchFocusOffset();
01410             weakNodeUsed = mDomSelections[index]->FetchFocusNode();
01411           }
01412           else {
01413             offsetused = mDomSelections[index]->FetchAnchorOffset();
01414             weakNodeUsed = mDomSelections[index]->FetchAnchorNode();
01415           }
01416           result = mDomSelections[index]->Collapse(weakNodeUsed, offsetused);
01417           mDomSelections[index]->ScrollIntoView();
01418           mHint = HINTRIGHT;
01419           return NS_OK;
01420 
01421       case nsIDOMKeyEvent::DOM_VK_RIGHT :
01422       case nsIDOMKeyEvent::DOM_VK_DOWN  :
01423           if (mDomSelections[index]->GetDirection() == eDirPrevious) { //f,a
01424             offsetused = mDomSelections[index]->FetchAnchorOffset();
01425             weakNodeUsed = mDomSelections[index]->FetchAnchorNode();
01426           }
01427           else {
01428             offsetused = mDomSelections[index]->FetchFocusOffset();
01429             weakNodeUsed = mDomSelections[index]->FetchFocusNode();
01430           }
01431           result = mDomSelections[index]->Collapse(weakNodeUsed, offsetused);
01432           mDomSelections[index]->ScrollIntoView();
01433           mHint = HINTLEFT;
01434           return NS_OK;
01435     }
01436   }
01437 
01438   offsetused = mDomSelections[index]->FetchFocusOffset();
01439   weakNodeUsed = mDomSelections[index]->FetchFocusNode();
01440 
01441     
01442   nsIFrame *frame;
01443   result = mDomSelections[index]->GetPrimaryFrameForFocusNode(&frame, &offsetused);
01444 
01445   if (NS_FAILED(result) || !frame)
01446     return result?result:NS_ERROR_FAILURE;
01447   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(frame->GetContent());
01448   nsCOMPtr<nsIDOMNode> parentNode;
01449   nsPeekOffsetStruct pos;
01450 
01451   //set data using mLimiter to stop on scroll views.  If we have a limiter then we stop peeking
01452   //when we hit scrollable views.  If no limiter then just let it go ahead
01453   pos.SetData(mShell, desiredX, aAmount, eDirPrevious, offsetused, PR_FALSE,
01454               PR_TRUE, PR_TRUE, mLimiter != nsnull, PR_TRUE);
01455 
01456   HINT tHint(mHint); //temporary variable so we dont set mHint until it is necessary
01457   switch (aKeycode){
01458     case nsIDOMKeyEvent::DOM_VK_RIGHT : 
01459         InvalidateDesiredX();
01460         pos.mDirection = eDirNext;
01461         tHint = HINTLEFT;//stick to this line
01462         PostReason(nsISelectionListener::KEYPRESS_REASON);
01463       break;
01464     case nsIDOMKeyEvent::DOM_VK_LEFT  : //no break
01465         InvalidateDesiredX();
01466         tHint = HINTRIGHT;//stick to opposite of movement
01467         PostReason(nsISelectionListener::KEYPRESS_REASON);
01468       break;
01469     case nsIDOMKeyEvent::DOM_VK_DOWN : 
01470         pos.mAmount = eSelectLine;
01471         pos.mDirection = eDirNext;//no break here
01472         PostReason(nsISelectionListener::KEYPRESS_REASON);
01473       break;
01474     case nsIDOMKeyEvent::DOM_VK_UP : 
01475         pos.mAmount = eSelectLine;
01476         PostReason(nsISelectionListener::KEYPRESS_REASON);
01477       break;
01478     case nsIDOMKeyEvent::DOM_VK_HOME :
01479         InvalidateDesiredX();
01480         pos.mAmount = eSelectBeginLine;
01481         tHint = HINTRIGHT;//stick to opposite of movement
01482         PostReason(nsISelectionListener::KEYPRESS_REASON);
01483       break;
01484     case nsIDOMKeyEvent::DOM_VK_END :
01485         InvalidateDesiredX();
01486         pos.mAmount = eSelectEndLine;
01487         tHint = HINTLEFT;//stick to this line
01488         PostReason(nsISelectionListener::KEYPRESS_REASON);
01489      break;
01490   default :return NS_ERROR_FAILURE;
01491   }
01492   pos.mPreferLeft = tHint;
01493   if (NS_SUCCEEDED(result) && NS_SUCCEEDED(result = frame->PeekOffset(context, &pos)) && pos.mResultContent)
01494   {
01495     tHint = (HINT)pos.mPreferLeft;
01496     if (context->BidiEnabled())
01497     {
01498       nsIFrame *theFrame;
01499       PRInt32 currentOffset, frameStart, frameEnd;
01500 
01501       // XXX - I expected to be able to use pos.mResultFrame, but when we move from frame to frame
01502       //       and |PeekOffset| is called recursively, pos.mResultFrame on exit is sometimes set to the original
01503       //       frame, not the frame that we ended up in, so I need this call to |GetFrameForNodeOffset|.
01504       //       I don't know if that could or should be changed or if it would break something else.
01505       GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset, tHint, &theFrame, &currentOffset);
01506       theFrame->GetOffsets(frameStart, frameEnd);
01507 
01508       tHint = (HINT)pos.mPreferLeft;
01509       if (frameStart !=0 || frameEnd !=0) // Otherwise the frame is not a text frame, so nothing more to do
01510       {
01511         switch (aKeycode) {
01512           case nsIDOMKeyEvent::DOM_VK_HOME:
01513           case nsIDOMKeyEvent::DOM_VK_END:
01514     
01515             // force the offset to the logical beginning (for HOME) or end (for END) of the frame
01516             // (if it is an RTL frame it will be at the visual beginning or end, which we don't want in this case)
01517             if (nsIDOMKeyEvent::DOM_VK_HOME == aKeycode)
01518               pos.mContentOffset = frameStart;
01519             else
01520               pos.mContentOffset = frameEnd;
01521     
01522             // set the cursor Bidi level to the paragraph embedding level
01523             mShell->SetCaretBidiLevel(NS_GET_BASE_LEVEL(theFrame));
01524             break;
01525     
01526           default:
01527             // If the current position is not a frame boundary, it's enough just to take the Bidi level of the current frame
01528             if ((pos.mContentOffset != frameStart && pos.mContentOffset != frameEnd)
01529                 || (eSelectDir == aAmount)
01530                 || (eSelectLine == aAmount))
01531             {
01532               mShell->SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame));
01533             }
01534             else
01535               BidiLevelFromMove(context, mShell, pos.mResultContent, pos.mContentOffset, aKeycode, tHint);
01536         }
01537       }
01538 #ifdef VISUALSELECTION
01539       // Handle visual selection
01540       if (aContinueSelection)
01541       {
01542         result = VisualSelectFrames(context, theFrame, pos);
01543         if (NS_FAILED(result)) // Back out by collapsing the selection to the current position
01544           result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, PR_FALSE, PR_FALSE);
01545       }    
01546       else
01547         result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinueSelection, PR_FALSE);
01548     }
01549     else
01550 #else
01551     }
01552 #endif // VISUALSELECTION
01553     result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinueSelection, PR_FALSE);
01554   } else if (aKeycode == nsIDOMKeyEvent::DOM_VK_RIGHT && !aContinueSelection) {
01555     // Collapse selection if PeekOffset failed because we bumped into the BRFrame, bug 207623.
01556     mDomSelections[index]->Collapse(weakNodeUsed, offsetused);
01557     tHint = mHint; // make the line below restore the original hint
01558     result = NS_OK;
01559   }
01560   if (NS_SUCCEEDED(result))
01561   {
01562     mHint = tHint; //save the hint parameter now for the next time
01563     result = mDomSelections[index]->ScrollIntoView();
01564   }
01565 
01566   return result;
01567 }
01568 
01569 
01570 
01574 NS_IMETHODIMP
01575 nsSelection::HandleKeyEvent(nsPresContext* aPresContext, nsGUIEvent *aGuiEvent)
01576 {
01577   if (!aGuiEvent)
01578     return NS_ERROR_NULL_POINTER;
01579   STATUS_CHECK_RETURN_MACRO();
01580 
01581   nsresult result = NS_ERROR_FAILURE;
01582   if (NS_KEY_PRESS == aGuiEvent->message) {
01583     nsKeyEvent *keyEvent = (nsKeyEvent *)aGuiEvent; //this is ok. It really is a keyevent
01584     switch (keyEvent->keyCode)
01585     {
01586         case nsIDOMKeyEvent::DOM_VK_LEFT  : 
01587         case nsIDOMKeyEvent::DOM_VK_UP    :
01588         case nsIDOMKeyEvent::DOM_VK_DOWN  : 
01589         case nsIDOMKeyEvent::DOM_VK_RIGHT    :
01590         case nsIDOMKeyEvent::DOM_VK_HOME  : 
01591         case nsIDOMKeyEvent::DOM_VK_END    :
01592           break;
01593         default:
01594            return NS_ERROR_FAILURE;
01595     }
01596 
01597 //XXX Need xp way get platfrom specific behavior into key navigation.
01598 //XXX This really shouldn't have to use an ifdef
01599 #ifdef _WIN32
01600     if (keyEvent->isAlt) {
01601       return NS_ERROR_FAILURE;
01602     }
01603 #endif
01604     nsSelectionAmount amount = eSelectCharacter;
01605     if (keyEvent->isControl)
01606       amount = eSelectWord;
01607     return MoveCaret(keyEvent->keyCode, keyEvent->isShift, amount);
01608   }
01609   return result;
01610 }
01611 
01612 //END nsSelection methods
01613 
01614 
01615 //BEGIN nsIFrameSelection methods
01616 
01617 NS_IMETHODIMP
01618 nsTypedSelection::ToString(PRUnichar **aReturn)
01619 {
01620   return ToStringWithFormat("text/plain", 0, 0, aReturn);
01621 }
01622 
01623 
01624 NS_IMETHODIMP
01625 nsTypedSelection::ToStringWithFormat(const char * aFormatType, PRUint32 aFlags, 
01626                                    PRInt32 aWrapCol, PRUnichar **aReturn)
01627 {
01628   nsresult rv = NS_OK;
01629   if (!aReturn)
01630     return NS_ERROR_NULL_POINTER;
01631   
01632   nsCAutoString formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
01633   formatType.Append(aFormatType);
01634   nsCOMPtr<nsIDocumentEncoder> encoder =
01635            do_CreateInstance(formatType.get(), &rv);
01636   NS_ENSURE_SUCCESS(rv, rv);
01637 
01638   nsCOMPtr<nsIPresShell> shell;
01639   rv = GetPresShell(getter_AddRefs(shell));
01640   if (NS_FAILED(rv) || !shell) {
01641     return NS_ERROR_FAILURE;
01642   }
01643 
01644   nsIDocument *doc = shell->GetDocument();
01645   NS_ENSURE_SUCCESS(rv, rv);
01646 
01647   // Flags should always include OutputSelectionOnly if we're coming from here:
01648   aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
01649   nsAutoString readstring;
01650   readstring.AssignASCII(aFormatType);
01651   rv = encoder->Init(doc, readstring, aFlags);
01652   NS_ENSURE_SUCCESS(rv, rv);
01653 
01654   encoder->SetSelection(this);
01655   if (aWrapCol != 0)
01656     encoder->SetWrapColumn(aWrapCol);
01657 
01658   nsAutoString tmp;
01659   rv = encoder->EncodeToString(tmp);
01660   *aReturn = ToNewUnicode(tmp);//get the unicode pointer from it. this is temporary
01661   return rv;
01662 }
01663 
01664 NS_IMETHODIMP
01665 nsTypedSelection::SetInterlinePosition(PRBool aHintRight)
01666 {
01667   if (!mFrameSelection)
01668     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
01669   nsIFrameSelection::HINT hint;
01670   if (aHintRight)
01671     hint = nsIFrameSelection::HINTRIGHT;
01672   else
01673     hint = nsIFrameSelection::HINTLEFT;
01674   return mFrameSelection->SetHint(hint);
01675 }
01676 
01677 NS_IMETHODIMP
01678 nsTypedSelection::GetInterlinePosition(PRBool *aHintRight)
01679 {
01680   if (!mFrameSelection)
01681     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
01682   nsIFrameSelection::HINT hint;
01683   nsresult rv = mFrameSelection->GetHint(&hint);
01684   if (hint == nsIFrameSelection::HINTRIGHT)
01685     *aHintRight = PR_TRUE;
01686   else
01687     *aHintRight = PR_FALSE;
01688   return rv;
01689 }
01690 
01691 #ifdef VISUALSELECTION
01692 
01693 static nsDirection
01694 ReverseDirection(nsDirection aDirection)
01695 {
01696   return (eDirNext == aDirection) ? eDirPrevious : eDirNext;
01697 }
01698 
01699 static nsresult
01700 FindLineContaining(nsIFrame* aFrame, nsIFrame** aBlock, PRInt32* aLine)
01701 {
01702   nsIFrame *blockFrame = aFrame;
01703   nsIFrame *thisBlock = nsnull;
01704   nsCOMPtr<nsILineIteratorNavigator> it; 
01705   nsresult result = NS_ERROR_FAILURE;
01706   while (NS_FAILED(result) && blockFrame)
01707   {
01708     thisBlock = blockFrame;
01709     blockFrame = blockFrame->GetParent();
01710     if (blockFrame) {
01711       it = do_QueryInterface(blockFrame, &result);
01712     }
01713   }
01714   if (!blockFrame || !it)
01715     return NS_ERROR_FAILURE;
01716   *aBlock = blockFrame;
01717   return it->FindLineContaining(thisBlock, aLine);  
01718 }
01719 
01720 NS_IMETHODIMP
01721 nsSelection::VisualSequence(nsPresContext *aPresContext,
01722                             nsIFrame* aSelectFrame,
01723                             nsIFrame* aCurrentFrame,
01724                             nsPeekOffsetStruct* aPos,
01725                             PRBool* aNeedVisualSelection)
01726 {
01727   nsVoidArray frameArray;
01728   PRInt32 frameStart, frameEnd;
01729   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
01730   nsresult result = nsnull;
01731   
01732   PRUint8 currentLevel = NS_GET_EMBEDDING_LEVEL(aCurrentFrame);
01733   result = aSelectFrame->PeekOffset(aPresContext, aPos);
01734   while (aCurrentFrame != (aSelectFrame = aPos->mResultFrame))
01735   {
01736     if (NS_FAILED(result))
01737       return NS_OK; // we have passed the end of the line, and we will carry on from there
01738     if (!aSelectFrame)
01739       return NS_ERROR_FAILURE;
01740     if (frameArray.IndexOf(aSelectFrame) > -1)
01741       // If we have already seen this frame, we must be in an infinite loop
01742       return NS_OK;
01743     else
01744       frameArray.AppendElement(aSelectFrame);
01745 
01746     aSelectFrame->GetOffsets(frameStart, frameEnd);
01747     PRUint8 bidiLevel = NS_GET_EMBEDDING_LEVEL(aSelectFrame);
01748     
01749     if (currentLevel != bidiLevel)
01750       *aNeedVisualSelection = PR_TRUE;
01751     if ((eDirNext == aPos->mDirection) == (bidiLevel & 1))
01752     {
01753       mDomSelections[index]->SetDirection(eDirPrevious);
01754       result = TakeFocus(aPos->mResultContent, frameEnd, frameStart, PR_FALSE, PR_TRUE);
01755     }
01756     else
01757     {
01758       mDomSelections[index]->SetDirection(eDirNext);
01759       result = TakeFocus(aPos->mResultContent, frameStart, frameEnd, PR_FALSE, PR_TRUE);
01760     }
01761     if (NS_FAILED(result))
01762       return result;
01763 
01764     aPos->mAmount = eSelectDir; // reset this because PeekOffset will have changed it to eSelectNoAmount
01765     aPos->mContentOffset = 0;
01766     result = aSelectFrame->PeekOffset(aPresContext, aPos);
01767   }
01768   
01769   return NS_OK;
01770 }
01771 
01772 NS_IMETHODIMP
01773 nsSelection::SelectToEdge(nsIFrame *aFrame, nsIContent *aContent, PRInt32 aOffset, PRInt32 aEdge, PRBool aMultipleSelection)
01774 {
01775   PRInt32 frameStart, frameEnd;
01776   
01777   aFrame->GetOffsets(frameStart, frameEnd);
01778   if (0 == aEdge)
01779     aEdge = frameStart;
01780   else if (-1 == aEdge)
01781     aEdge = frameEnd;
01782   if (0 == aOffset)
01783     aOffset = frameStart;
01784   else if (-1 == aOffset)
01785     aOffset = frameEnd;
01786   return TakeFocus(aContent, aOffset, aEdge, PR_FALSE, aMultipleSelection);
01787 }
01788 
01789 NS_IMETHODIMP
01790 nsSelection::SelectLines(nsPresContext *aPresContext,
01791                          nsDirection aSelectionDirection,
01792                          nsIDOMNode *aAnchorNode,
01793                          nsIFrame* aAnchorFrame,
01794                          PRInt32 aAnchorOffset,
01795                          nsIDOMNode *aCurrentNode,
01796                          nsIFrame* aCurrentFrame,
01797                          PRInt32 aCurrentOffset,
01798                          nsPeekOffsetStruct aPos)
01799 {
01800   nsIFrame *startFrame, *endFrame;
01801   PRInt32 startOffset, endOffset;
01802   PRInt32 relativePosition;
01803   nsCOMPtr<nsIDOMNode> startNode;
01804   nsCOMPtr<nsIDOMNode> endNode;
01805   nsIContent *startContent;
01806   nsIContent *endContent;
01807   nsresult result;
01808 
01809   // normalize the order before we start to avoid piles of conditions later
01810   relativePosition = nsRange::ComparePoints(aAnchorNode, aAnchorOffset,
01811                                             aCurrentNode, aCurrentOffset);
01812   if (0 == relativePosition)
01813     return NS_ERROR_FAILURE;
01814   else if (relativePosition < 0)
01815   {
01816     startNode = aAnchorNode;
01817     startFrame = aAnchorFrame;
01818     startOffset = aAnchorOffset;
01819     endNode = aCurrentNode;
01820     endFrame = aCurrentFrame;
01821     endOffset = aCurrentOffset;
01822   }
01823   else
01824   {
01825     startNode = aCurrentNode;
01826     startFrame = aCurrentFrame;
01827     startOffset = aCurrentOffset;
01828     endNode = aAnchorNode;
01829     endFrame = aAnchorFrame;
01830     endOffset = aAnchorOffset;
01831   }
01832 
01833   aPos.mStartOffset = startOffset;
01834   aPos.mDirection = eDirNext;
01835   aPos.mAmount = eSelectLine;
01836   result = startFrame->PeekOffset(aPresContext, &aPos);
01837   if (NS_FAILED(result))
01838     return result;
01839   startFrame = aPos.mResultFrame;
01840   
01841   aPos.mStartOffset = aPos.mContentOffset;
01842   aPos.mAmount = eSelectBeginLine;
01843   result = startFrame->PeekOffset(aPresContext, &aPos);
01844   if (NS_FAILED(result))
01845     return result;
01846   
01847   nsIFrame *theFrame;
01848   PRInt32 currentOffset, frameStart, frameEnd;
01849   
01850   result = GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTLEFT, &theFrame, &currentOffset);
01851   if (NS_FAILED(result))
01852     return result;
01853   theFrame->GetOffsets(frameStart, frameEnd);
01854   startOffset = frameStart;
01855   startContent = aPos.mResultContent;
01856   startNode = do_QueryInterface(startContent);
01857 
01858   // If we have already overshot the endpoint, back out
01859   if (nsRange::ComparePoints(startNode, startOffset, endNode, endOffset) >= 0)
01860     return NS_ERROR_FAILURE;
01861 
01862   aPos.mStartOffset = endOffset;
01863   aPos.mDirection = eDirPrevious;
01864   aPos.mAmount = eSelectLine;
01865   result = endFrame->PeekOffset(aPresContext, &aPos);
01866   if (NS_FAILED(result))
01867     return result;
01868   endFrame = aPos.mResultFrame;
01869 
01870   aPos.mStartOffset = aPos.mContentOffset;
01871   aPos.mAmount = eSelectEndLine;
01872   result = endFrame->PeekOffset(aPresContext, &aPos);
01873   if (NS_FAILED(result))
01874     return result;
01875 
01876   result = GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTRIGHT, &theFrame, &currentOffset);
01877   if (NS_FAILED(result))
01878     return result;
01879   theFrame->GetOffsets(frameStart, frameEnd);
01880   endOffset = frameEnd;
01881   endContent = aPos.mResultContent;
01882   endNode = do_QueryInterface(endContent);
01883 
01884   if (nsRange::ComparePoints(startNode, startOffset, endNode, endOffset) < 0)
01885   {
01886     TakeFocus(startContent, startOffset, startOffset, PR_FALSE, PR_TRUE);
01887     return TakeFocus(endContent, endOffset, endOffset, PR_TRUE, PR_TRUE);
01888   }
01889   else
01890     return NS_ERROR_FAILURE;
01891 }
01892 
01893 NS_IMETHODIMP
01894 nsSelection::VisualSelectFrames(nsPresContext *aPresContext,
01895                                 nsIFrame* aCurrentFrame,
01896                                 nsPeekOffsetStruct aPos)
01897 {
01898   nsCOMPtr<nsIContent> anchorContent;
01899   nsCOMPtr<nsIDOMNode> anchorNode;
01900   PRInt32 anchorOffset;
01901   nsIFrame* anchorFrame;
01902   nsCOMPtr<nsIContent> focusContent;
01903   nsCOMPtr<nsIDOMNode> focusNode;
01904   PRInt32 focusOffset;
01905   nsIFrame* focusFrame;
01906   nsCOMPtr<nsIContent> currentContent;
01907   nsCOMPtr<nsIDOMNode> currentNode;
01908   PRInt32 currentOffset;
01909   nsresult result;
01910   nsIFrame* startFrame;
01911   PRBool needVisualSelection = PR_FALSE;
01912   nsDirection selectionDirection;
01913   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
01914 
01915   result = mDomSelections[index]->GetOriginalAnchorPoint(getter_AddRefs(anchorNode), &anchorOffset);
01916   if (NS_FAILED(result))
01917     return result;
01918   anchorContent = do_QueryInterface(anchorNode);
01919   result = GetFrameForNodeOffset(anchorContent, anchorOffset, mHint, &anchorFrame, &anchorOffset);
01920   if (NS_FAILED(result))
01921     return result;
01922   PRUint8 anchorLevel = NS_GET_EMBEDDING_LEVEL(anchorFrame);
01923   
01924   currentContent = aPos.mResultContent;
01925   currentNode = do_QueryInterface(currentContent);
01926   currentOffset = aPos.mContentOffset;
01927   PRUint8 currentLevel = NS_GET_EMBEDDING_LEVEL(aCurrentFrame);
01928 
01929   // Moving from simplest case to more complicated:
01930   // case 1: selection starts and ends in the same frame: no special treatment
01931   if (anchorFrame == aCurrentFrame) {
01932     mDomSelections[index]->SetTrueDirection(!(anchorLevel & 1));
01933     return TakeFocus(currentContent, anchorOffset, currentOffset, PR_FALSE, PR_FALSE);
01934   }
01935 
01936   focusOffset = mDomSelections[index]->FetchFocusOffset();
01937   focusNode = mDomSelections[index]->FetchFocusNode();
01938   focusContent = do_QueryInterface(focusNode);
01939   HINT hint;
01940   if ((HINTLEFT == mHint) == (currentLevel & 1))
01941     hint = HINTRIGHT;
01942   else
01943     hint = HINTLEFT;
01944 
01945   result = GetFrameForNodeOffset(focusContent, focusOffset, hint, &focusFrame, &focusOffset);
01946   if (NS_FAILED(result))
01947     return result;
01948   PRUint8 focusLevel = NS_GET_EMBEDDING_LEVEL(focusFrame);
01949 
01950   if (currentLevel != anchorLevel)
01951     needVisualSelection = PR_TRUE;
01952 
01953   // Make sure of the selection direction
01954   selectionDirection = mDomSelections[index]->GetDirection();
01955   if (!mDomSelections[index]->GetTrueDirection()) {
01956     selectionDirection = ReverseDirection(selectionDirection);
01957     mDomSelections[index]->SetDirection(selectionDirection);
01958   }
01959 
01960   PRInt32 anchorLine, currentLine;
01961   nsIFrame* anchorBlock  = nsnull;
01962   nsIFrame* currentBlock = nsnull;
01963   FindLineContaining(anchorFrame, &anchorBlock, &anchorLine);
01964   FindLineContaining(aCurrentFrame, &currentBlock, &currentLine);
01965 
01966   if (anchorBlock==currentBlock && anchorLine==currentLine)
01967   {
01968     // case 2: selection starts and ends in the same line
01969 
01970     // Select from the anchor point to the edge of the frame
01971     // Which edge? If the selection direction is forward the right edge, if it is backward the left edge
01972     // For rtl frames the right edge is the begining of the frame, for ltr frames it is the end and vice versa
01973     if ((eDirNext == selectionDirection) == (anchorLevel & 1))
01974       result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, 0, PR_FALSE);
01975     else
01976       result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE);
01977     if (NS_FAILED(result))
01978       return result;
01979 
01980     // Walk the frames in visual order until we reach the current frame, selecting each frame as we go
01981     InvalidateDesiredX();
01982     aPos.mAmount = eSelectDir;
01983     aPos.mStartOffset = anchorOffset;
01984     aPos.mDirection = selectionDirection;
01985 
01986     result = anchorFrame->PeekOffset(aPresContext, &aPos);
01987     if (NS_FAILED(result))
01988       return result;
01989     
01990     startFrame = aPos.mResultFrame;
01991     result = VisualSequence(aPresContext, startFrame, aCurrentFrame, &aPos, &needVisualSelection);
01992     if (NS_FAILED(result))
01993       return result;
01994 
01995     if (!needVisualSelection)
01996     {
01997       if (currentLevel & 1)
01998         mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection));
01999       // all the frames we passed through had the same Bidi level, so we can back out and do an ordinary selection
02000       result = TakeFocus(anchorContent, anchorOffset, anchorOffset, PR_FALSE, PR_FALSE);
02001       if (NS_FAILED(result))
02002         return result;
02003       result = TakeFocus(currentContent, currentOffset, currentOffset, PR_TRUE, PR_FALSE);
02004       if (NS_FAILED(result))
02005         return result;
02006     }
02007     else {
02008       if ((currentLevel & 1) != (focusLevel & 1))
02009         mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection));
02010       // Select from the current point to the edge of the frame
02011       if ((eDirNext == selectionDirection) == (currentLevel & 1))
02012         result = SelectToEdge(aCurrentFrame, currentContent, -1, currentOffset, PR_TRUE);
02013       else
02014         result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE);
02015       if (NS_FAILED(result))
02016         return result;
02017     }
02018   }
02019   else {
02020 
02021     // case 3: selection starts and ends in different lines
02022 
02023     // If selection direction is forwards:
02024     // Select from the anchor point to the edge of the frame in the direction of the end of the line
02025     // i.e. the rightmost character if the current paragraph embedding level is even (LTR paragraph)
02026     // or the leftmost character if the current paragraph embedding level is odd (RTL paragraph)
02027     //
02028     // As before, for rtl frames the right edge is the begining of the frame, for ltr frames it is the end and vice versa
02029     //
02030     // If selection direction is backwards, vice versa throughout
02031     //
02032     PRUint8 anchorBaseLevel = NS_GET_BASE_LEVEL(anchorFrame);
02033     if ((eDirNext == selectionDirection) != ((anchorLevel & 1) == (anchorBaseLevel & 1)))
02034       result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, 0, PR_FALSE);
02035     else
02036       result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE);
02037     if (NS_FAILED(result))
02038       return result;
02039 
02040     // Walk the frames in visual order until we reach the end of the line
02041     aPos.mJumpLines = PR_FALSE;
02042     aPos.mAmount = eSelectDir;
02043     aPos.mStartOffset = anchorOffset;
02044     aPos.mDirection = selectionDirection;
02045     if (anchorBaseLevel & 1)
02046       aPos.mDirection = ReverseDirection(aPos.mDirection);
02047     result = VisualSequence(aPresContext, anchorFrame, aCurrentFrame, &aPos, &needVisualSelection);
02048     if (NS_FAILED(result))
02049       return result;
02050 
02051     // Select all the lines between the line containing the anchor point and the line containing the current point
02052     aPos.mJumpLines = PR_TRUE;
02053     result = SelectLines(aPresContext, selectionDirection,
02054                          anchorNode, anchorFrame, anchorOffset,
02055                          currentNode, aCurrentFrame, currentOffset, aPos);
02056     if (NS_FAILED(result))
02057       return result;
02058 
02059     // Go to the current point
02060     PRUint8 currentBaseLevel = NS_GET_BASE_LEVEL(aCurrentFrame);
02061     // Walk the frames in visual order until we reach the beginning of the line
02062     aPos.mJumpLines = PR_FALSE;
02063     if ((currentBaseLevel & 1) == (anchorBaseLevel & 1))
02064       aPos.mDirection = ReverseDirection(aPos.mDirection);
02065     aPos.mStartOffset = currentOffset;
02066     result = VisualSequence(aPresContext, aCurrentFrame, anchorFrame, &aPos, &needVisualSelection);
02067     if (NS_FAILED(result))
02068       return result;
02069 
02070     // Select from the current point to the edge of the frame
02071     if (currentLevel & 1)
02072       mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection));
02073 
02074     if ((eDirPrevious == selectionDirection) != ((currentLevel & 1) == (currentBaseLevel & 1)))
02075       result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE);
02076     else
02077       result = SelectToEdge(aCurrentFrame, currentContent, -1, currentOffset, PR_TRUE);
02078     if (NS_FAILED(result))
02079       return result;
02080     
02081     // restore original selection direction
02082 //    mDomSelections[index]->SetDirection(selectionDirection);
02083   }
02084 
02085   // Sometimes we have to lie about the selection direction, so we will have to remember when we are doing so
02086   mDomSelections[index]->SetTrueDirection(mDomSelections[index]->GetDirection() == selectionDirection);
02087   
02088   mDomSelections[index]->SetOriginalAnchorPoint(anchorNode, anchorOffset);
02089   NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
02090   return NS_OK;
02091 }
02092 #endif // VISUALSELECTION
02093 
02094 NS_IMETHODIMP
02095 nsSelection::GetPrevNextBidiLevels(nsPresContext *aPresContext,
02096                                    nsIContent *aNode,
02097                                    PRUint32 aContentOffset,
02098                                    nsIFrame **aPrevFrame,
02099                                    nsIFrame **aNextFrame,
02100                                    PRUint8 *aPrevLevel,
02101                                    PRUint8 *aNextLevel)
02102 {
02103   return GetPrevNextBidiLevels(aPresContext, aNode, aContentOffset, mHint,
02104                                aPrevFrame, aNextFrame, aPrevLevel, aNextLevel);
02105 }
02106 
02107 NS_IMETHODIMP
02108 nsSelection::GetPrevNextBidiLevels(nsPresContext *aPresContext,
02109                                    nsIContent *aNode,
02110                                    PRUint32 aContentOffset,
02111                                    HINT aHint,
02112                                    nsIFrame **aPrevFrame,
02113                                    nsIFrame **aNextFrame,
02114                                    PRUint8 *aPrevLevel,
02115                                    PRUint8 *aNextLevel)
02116 {
02117   if (!aPrevFrame || !aNextFrame)
02118     return NS_ERROR_NULL_POINTER;
02119   // Get the level of the frames on each side
02120   nsIFrame    *currentFrame;
02121   PRInt32     currentOffset;
02122   PRInt32     frameStart, frameEnd;
02123   nsDirection direction;
02124   nsresult    result;
02125 
02126   *aPrevLevel = *aNextLevel = 0;
02127 
02128   result = GetFrameForNodeOffset(aNode, aContentOffset, aHint, &currentFrame, &currentOffset);
02129   if (NS_FAILED(result))
02130     return result;
02131   currentFrame->GetOffsets(frameStart, frameEnd);
02132 
02133   if (0 == frameStart && 0 == frameEnd)
02134     direction = eDirPrevious;
02135   else if (frameStart == currentOffset)
02136     direction = eDirPrevious;
02137   else if (frameEnd == currentOffset)
02138     direction = eDirNext;
02139   else {
02140     // we are neither at the beginning nor at the end of the frame, so we have no worries
02141     *aPrevFrame = *aNextFrame = currentFrame;
02142     *aPrevLevel = *aNextLevel = NS_GET_EMBEDDING_LEVEL(currentFrame);
02143     return NS_OK;
02144   }
02145 
02146   /*
02147   we have to find the next or previous *logical* frame.
02148 
02149   Unfortunately |GetFrameFromDirection| has already been munged to return the next/previous *visual* frame, so we can't use that.
02150   The following code is taken from there without the Bidi changes.
02151 
02152   XXX is there a simpler way to do this? 
02153   */
02154 
02155   nsIFrame *blockFrame = currentFrame;
02156   nsIFrame *thisBlock = nsnull;
02157   PRInt32   thisLine;
02158   nsILineIteratorNavigator* it;  // This is qi'd off a frame, and those aren't
02159                                  // refcounted
02160   result = NS_ERROR_FAILURE;
02161   while (NS_FAILED(result) && blockFrame)
02162   {
02163     thisBlock = blockFrame;
02164     blockFrame = blockFrame->GetParent();
02165     if (blockFrame) {
02166       result = CallQueryInterface(blockFrame, &it);
02167     }
02168   }
02169   if (!blockFrame || !it)
02170     return NS_ERROR_FAILURE;
02171   result = it->FindLineContaining(thisBlock, &thisLine);
02172   if (NS_FAILED(result))
02173     return result;
02174 
02175   if (thisLine < 0) 
02176     return NS_ERROR_FAILURE;
02177 
02178   nsIFrame *firstFrame;
02179   nsIFrame *lastFrame;
02180   nsRect    nonUsedRect;
02181   PRInt32   lineFrameCount;
02182   PRUint32  lineFlags;
02183 
02184   result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
02185                        &lineFlags);
02186   if (NS_FAILED(result))
02187     return result;
02188 
02189   lastFrame = firstFrame;
02190 
02191   for (;lineFrameCount > 1;lineFrameCount --) {
02192     lastFrame = lastFrame->GetNextSibling();
02193   }
02194 
02195   // GetFirstLeaf
02196   nsIFrame *lookahead;
02197   while (1) {
02198     lookahead = firstFrame->GetFirstChild(nsnull);
02199     if (!lookahead)
02200       break; //nothing to do
02201     firstFrame = lookahead;
02202   }
02203 
02204   // GetLastLeaf
02205   while (1) {
02206     lookahead = lastFrame->GetFirstChild(nsnull);
02207     if (!lookahead)
02208       break; //nothing to do
02209     lastFrame = lookahead;
02210     while ((lookahead = lastFrame->GetNextSibling()) != nsnull)
02211       lastFrame = lookahead;
02212   }
02213   //END LINE DATA CODE
02214 
02215   if (direction == eDirNext && lastFrame == currentFrame) { // End of line: set aPrevFrame to the current frame
02216                                                             //              set aPrevLevel to the embedding level of the current frame
02217                                                             //              set aNextFrame to null
02218                                                             //              set aNextLevel to the paragraph embedding level
02219     *aPrevFrame = currentFrame;
02220     *aPrevLevel = NS_GET_EMBEDDING_LEVEL(currentFrame);
02221     *aNextLevel = NS_GET_BASE_LEVEL(currentFrame);
02222     *aNextFrame = nsnull;
02223     return NS_OK;
02224   }
02225 
02226   if (direction == eDirPrevious && firstFrame == currentFrame) { // Beginning of line: set aPrevFrame to null
02227                                                                  //                    set aPrevLevel to the paragraph embedding level
02228                                                                  //                    set aNextFrame to the current frame
02229                                                                  //                    set aNextLevel to the embedding level of the current frame
02230     *aNextFrame = currentFrame;
02231     *aNextLevel = NS_GET_EMBEDDING_LEVEL(currentFrame);
02232     *aPrevLevel = NS_GET_BASE_LEVEL(currentFrame);
02233     *aPrevFrame = nsnull;
02234     return NS_OK;
02235   }
02236 
02237   // Find the adjacent frame
02238 
02239   nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
02240   nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
02241   if (NS_FAILED(result))
02242     return result;
02243 
02244   result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, currentFrame);
02245   if (NS_FAILED(result))
02246     return result;
02247   nsISupports *isupports = nsnull;
02248   if (direction == eDirNext)
02249     result = frameTraversal->Next();
02250   else 
02251     result = frameTraversal->Prev();
02252 
02253   if (NS_FAILED(result))
02254     return result;
02255   result = frameTraversal->CurrentItem(&isupports);
02256   if (NS_FAILED(result))
02257     return result;
02258   if (!isupports)
02259     return NS_ERROR_NULL_POINTER;
02260   //we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules
02261   //for speed reasons
02262   nsIFrame *newFrame = (nsIFrame *)isupports;
02263 
02264   if (direction == eDirNext) {
02265     *aPrevFrame = currentFrame;
02266     *aPrevLevel = NS_GET_EMBEDDING_LEVEL(currentFrame);
02267     *aNextFrame = newFrame;
02268     *aNextLevel = NS_GET_EMBEDDING_LEVEL(newFrame);
02269   }
02270   else {
02271     *aNextFrame = currentFrame;
02272     *aNextLevel = NS_GET_EMBEDDING_LEVEL(currentFrame);
02273     *aPrevFrame = newFrame;
02274     *aPrevLevel = NS_GET_EMBEDDING_LEVEL(newFrame);
02275   }
02276 
02277   return NS_OK;
02278 
02279 }
02280 
02281 NS_IMETHODIMP nsSelection::GetFrameFromLevel(nsPresContext *aPresContext,
02282                                              nsIFrame *aFrameIn,
02283                                              nsDirection aDirection,
02284                                              PRUint8 aBidiLevel,
02285                                              nsIFrame **aFrameOut)
02286 {
02287   PRUint8 foundLevel = 0;
02288   nsIFrame *foundFrame = aFrameIn;
02289 
02290   nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
02291   nsresult result;
02292   nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
02293   if (NS_FAILED(result))
02294       return result;
02295 
02296   result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, aFrameIn);
02297   if (NS_FAILED(result))
02298     return result;
02299   nsISupports *isupports = nsnull;
02300 
02301   do {
02302     *aFrameOut = foundFrame;
02303     if (aDirection == eDirNext)
02304       result = frameTraversal->Next();
02305     else 
02306       result = frameTraversal->Prev();
02307 
02308     if (NS_FAILED(result))
02309       return result;
02310     result = frameTraversal->CurrentItem(&isupports);
02311     if (NS_FAILED(result))
02312       return result;
02313     if (!isupports)
02314       return NS_ERROR_NULL_POINTER;
02315     //we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules
02316     //for speed reasons
02317     foundFrame = (nsIFrame *)isupports;
02318     foundLevel = NS_GET_EMBEDDING_LEVEL(foundFrame);
02319 
02320   } while (foundLevel > aBidiLevel);
02321 
02322   return NS_OK;
02323 }
02324 
02325 
02326 NS_IMETHODIMP 
02327 nsSelection::MaintainSelection()
02328 {
02329   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
02330   nsCOMPtr<nsIDOMRange> range;
02331   nsresult rv = mDomSelections[index]->GetRangeAt(0, getter_AddRefs(range));
02332   if (NS_FAILED(rv))
02333     return rv;
02334   if (!range)
02335     return NS_ERROR_FAILURE;
02336 
02337   nsCOMPtr<nsIDOMNode> startNode;
02338   nsCOMPtr<nsIDOMNode> endNode;
02339   PRInt32 startOffset;
02340   PRInt32 endOffset;
02341   range->GetStartContainer(getter_AddRefs(startNode));
02342   range->GetEndContainer(getter_AddRefs(endNode));
02343   range->GetStartOffset(&startOffset);
02344   range->GetEndOffset(&endOffset);
02345 
02346   mMaintainRange = nsnull;
02347   NS_NewRange(getter_AddRefs(mMaintainRange));
02348   if (!mMaintainRange)
02349     return NS_ERROR_OUT_OF_MEMORY;
02350 
02351   mMaintainRange->SetStart(startNode, startOffset);
02352   return mMaintainRange->SetEnd(endNode, endOffset);
02353 }
02354 
02355 
02373 void nsSelection::BidiLevelFromMove(nsPresContext* aContext,
02374                                     nsIPresShell* aPresShell,
02375                                     nsIContent *aNode,
02376                                     PRUint32 aContentOffset,
02377                                     PRUint32 aKeycode,
02378                                     HINT aHint)
02379 {
02380   PRUint8 firstLevel;
02381   PRUint8 secondLevel;
02382   PRUint8 currentLevel;
02383   nsIFrame* firstFrame=nsnull;
02384   nsIFrame* secondFrame=nsnull;
02385 
02386   aPresShell->GetCaretBidiLevel(&currentLevel);
02387 
02388   switch (aKeycode) {
02389 
02390     // Right and Left: the new cursor Bidi level is the level of the character moved over
02391     case nsIDOMKeyEvent::DOM_VK_RIGHT:
02392     case nsIDOMKeyEvent::DOM_VK_LEFT:
02393       GetPrevNextBidiLevels(aContext, aNode, aContentOffset, aHint, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
02394       if (HINTLEFT == aHint)
02395         aPresShell->SetCaretBidiLevel(firstLevel);
02396       else
02397         aPresShell->SetCaretBidiLevel(secondLevel);
02398       break;
02399 
02400       /*
02401     // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters      
02402     case nsIDOMKeyEvent::DOM_VK_UP:
02403     case nsIDOMKeyEvent::DOM_VK_DOWN:
02404       GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
02405       aPresShell->SetCaretBidiLevel(PR_MIN(firstLevel, secondLevel));
02406       break;
02407       */
02408 
02409     default:
02410       aPresShell->UndefineCaretBidiLevel();
02411   }
02412 }
02413 
02420 void nsSelection::BidiLevelFromClick(nsIContent *aNode, PRUint32 aContentOffset)
02421 {
02422   nsIFrame* clickInFrame=nsnull;
02423   PRInt32 OffsetNotUsed;
02424 
02425   nsresult result = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &clickInFrame, &OffsetNotUsed);
02426   if (NS_FAILED(result))
02427     return;
02428 
02429   mShell->SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(clickInFrame));
02430 }
02431 
02432 
02433 PRBool
02434 nsSelection::AdjustForMaintainedSelection(nsIContent *aContent, PRInt32 aOffset)
02435 {
02436   // Is the desired content and offset currently in selection?
02437   // If the double click flag is set then don't continue selection if the 
02438   // desired content and offset are currently inside a selection.
02439   // This will stop double click then mouse-drag from undoing the desired
02440   // selecting of a word.
02441   if (!mMaintainRange)
02442     return PR_FALSE;
02443 
02444   nsCOMPtr<nsIDOMNode> rangenode;
02445   PRInt32 rangeOffset;
02446   mMaintainRange->GetStartContainer(getter_AddRefs(rangenode));
02447   mMaintainRange->GetStartOffset(&rangeOffset);
02448 
02449   nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aContent);
02450   if (domNode)
02451   {
02452     PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
02453     nsCOMPtr<nsIDOMNSRange> nsrange = do_QueryInterface(mMaintainRange);
02454     if (nsrange)
02455     {
02456       PRBool insideSelection = PR_FALSE;
02457       nsrange->IsPointInRange(domNode, aOffset, &insideSelection);
02458 
02459       // Done when we find a range that we are in
02460       if (insideSelection)
02461       {
02462         mDomSelections[index]->Collapse(rangenode, rangeOffset);
02463         mMaintainRange->GetEndContainer(getter_AddRefs(rangenode));
02464         mMaintainRange->GetEndOffset(&rangeOffset);
02465         mDomSelections[index]->Extend(rangenode,rangeOffset);
02466         return PR_TRUE; // dragging in selection aborted
02467       }
02468     }
02469 
02470     PRInt32 relativePosition = nsRange::ComparePoints(rangenode, rangeOffset,
02471                                                       domNode, aOffset);
02472     // if == 0 or -1 do nothing if < 0 then we need to swap direction
02473     if (relativePosition > 0
02474         && (mDomSelections[index]->GetDirection() == eDirNext))
02475     {
02476       mMaintainRange->GetEndContainer(getter_AddRefs(rangenode));
02477       mMaintainRange->GetEndOffset(&rangeOffset);
02478       mDomSelections[index]->Collapse(rangenode, rangeOffset);
02479     }
02480     else if (relativePosition < 0
02481              && (mDomSelections[index]->GetDirection() == eDirPrevious))
02482       mDomSelections[index]->Collapse(rangenode, rangeOffset);
02483   }
02484 
02485   return PR_FALSE;
02486 }
02487 
02488 
02489 NS_IMETHODIMP
02490 nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset, 
02491                        PRUint32 aContentEndOffset, PRBool aContinueSelection, 
02492                        PRBool aMultipleSelection, PRBool aHint) 
02493 {
02494   if (!aNewFocus)
02495     return NS_ERROR_INVALID_ARG;
02496 
02497   InvalidateDesiredX();
02498 
02499   if (!aContinueSelection)
02500     mMaintainRange = nsnull;
02501 
02502   mHint = HINT(aHint);
02503   // Don't take focus when dragging off of a table
02504   if (!mDragSelectingCells)
02505   {
02506     BidiLevelFromClick(aNewFocus, aContentOffset);
02507     PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
02508     if (aContinueSelection &&
02509         AdjustForMaintainedSelection(aNewFocus, aContentOffset))
02510       return NS_OK; //shift clicked to maintained selection. rejected.
02511 
02512     return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection);
02513   }
02514   
02515   return NS_OK;
02516 }
02517 
02518 NS_IMETHODIMP
02519 nsSelection::HandleDrag(nsPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint)
02520 {
02521   if (!aPresContext || !aFrame)
02522     return NS_ERROR_NULL_POINTER;
02523 
02524   nsresult result;
02525   nsIFrame *newFrame = 0;
02526   nsPoint   newPoint;
02527 
02528   result = ConstrainFrameAndPointToAnchorSubtree(aPresContext, aFrame, aPoint, &newFrame, newPoint);
02529   if (NS_FAILED(result))
02530     return result;
02531   if (!newFrame)
02532     return NS_ERROR_FAILURE;
02533 
02534   PRInt32 startPos = 0;
02535   PRInt32 contentOffsetEnd = 0;
02536   PRBool  beginOfContent;
02537   nsCOMPtr<nsIContent> newContent;
02538 
02539   result = newFrame->GetContentAndOffsetsFromPoint(aPresContext, newPoint,
02540                                                    getter_AddRefs(newContent), 
02541                                                    startPos, contentOffsetEnd, beginOfContent);
02542 
02543   if ((newFrame->GetStateBits() & NS_FRAME_SELECTED_CONTENT) &&
02544        AdjustForMaintainedSelection(newContent, startPos))
02545     return NS_OK;
02546 
02547   // do we have CSS that changes selection behaviour?
02548   {
02549     //add scope for nsCOMPtr
02550     PRBool    changeSelection;
02551     nsCOMPtr<nsIContent>  selectContent;
02552     PRInt32   newStart, newEnd;
02553     if (NS_SUCCEEDED(AdjustOffsetsFromStyle(newFrame, &changeSelection, getter_AddRefs(selectContent), &newStart, &newEnd))
02554       && changeSelection)
02555     {
02556       newContent = selectContent;
02557       startPos = newStart;
02558       contentOffsetEnd = newEnd;
02559     }
02560   }
02561 
02562   if (NS_SUCCEEDED(result))
02563   {
02564 #ifdef VISUALSELECTION
02565     if (aPresContext->BidiEnabled()) {
02566       PRUint8 level;
02567       nsPeekOffsetStruct pos;
02568       //set data using mLimiter to stop on scroll views.  If we have a limiter then we stop peeking
02569       //when we hit scrollable views.  If no limiter then just let it go ahead
02570       pos.SetData(mShell, 0, eSelectDir, eDirNext, startPos, PR_FALSE,
02571                   PR_TRUE, PR_TRUE, mLimiter != nsnull, PR_FALSE);
02572       mHint = HINT(beginOfContent);
02573       HINT saveHint = mHint;
02574       if (NS_GET_EMBEDDING_LEVEL(newFrame) & 1)
02575         mHint = (mHint==HINTLEFT) ? HINTRIGHT : HINTLEFT;
02576       pos.mResultContent = newContent;
02577       pos.mContentOffset = contentOffsetEnd;
02578       result = VisualSelectFrames(aPresContext, newFrame, pos);
02579       if (NS_FAILED(result))
02580         result = HandleClick(newContent, startPos, contentOffsetEnd, PR_TRUE,
02581                              PR_FALSE, beginOfContent);
02582       mHint = saveHint;
02583     }
02584     else
02585 #endif // VISUALSELECTION
02586       result = HandleClick(newContent, startPos, contentOffsetEnd, PR_TRUE,
02587                            PR_FALSE, beginOfContent);
02588   }
02589 
02590   return result;
02591 }
02592 
02593 NS_IMETHODIMP
02594 nsSelection::StartAutoScrollTimer(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRUint32 aDelay)
02595 {
02596   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
02597   return mDomSelections[index]->StartAutoScrollTimer(aPresContext, aView, aPoint, aDelay);
02598 }
02599 
02600 NS_IMETHODIMP
02601 nsSelection::StopAutoScrollTimer()
02602 {
02603   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
02604   return mDomSelections[index]->StopAutoScrollTimer();
02605 }
02606 
02610 NS_IMETHODIMP
02611 nsSelection::TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset, 
02612                        PRUint32 aContentEndOffset, PRBool aContinueSelection, PRBool aMultipleSelection)
02613 {
02614   if (!aNewFocus)
02615     return NS_ERROR_NULL_POINTER;
02616 
02617   STATUS_CHECK_RETURN_MACRO();
02618 
02619   if (!IsValidSelectionPoint(this,aNewFocus))
02620     return NS_ERROR_FAILURE;
02621 
02622   // Clear all table selection data
02623   mSelectingTableCellMode = 0;
02624   mDragSelectingCells = PR_FALSE;
02625   mStartSelectedCell = nsnull;
02626   mEndSelectedCell = nsnull;
02627   mAppendStartSelectedCell = nsnull;
02628 
02629   //HACKHACKHACK
02630   if (!aNewFocus->GetParent())
02631     return NS_ERROR_FAILURE;
02632   //END HACKHACKHACK /checking for root frames/content
02633 
02634   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
02635   nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aNewFocus);
02636   //traverse through document and unselect crap here
02637   if (!aContinueSelection){ //single click? setting cursor down
02638     PRUint32 batching = mBatching;//hack to use the collapse code.
02639     PRBool changes = mChangesDuringBatching;
02640     mBatching = 1;
02641 
02642     if (aMultipleSelection){
02643       nsCOMPtr<nsIDOMRange> newRange;
02644       NS_NewRange(getter_AddRefs(newRange));
02645 
02646       newRange->SetStart(domNode,aContentOffset);
02647       newRange->SetEnd(domNode,aContentOffset);
02648       mDomSelections[index]->AddRange(newRange);
02649       mBatching = batching;
02650       mChangesDuringBatching = changes;
02651       mDomSelections[index]->SetOriginalAnchorPoint(domNode,aContentOffset);
02652     }
02653     else
02654     {
02655       PRBool oldDesiredXSet = mDesiredXSet; //need to keep old desired X if it was set.
02656       mDomSelections[index]->Collapse(domNode, aContentOffset);
02657       mDesiredXSet = oldDesiredXSet; //now reset desired X back.
02658       mBatching = batching;
02659       mChangesDuringBatching = changes;
02660     }
02661     if (aContentEndOffset != aContentOffset)
02662       mDomSelections[index]->Extend(domNode,aContentEndOffset);
02663 
02664     //find out if we are inside a table. if so, find out which one and which cell
02665     //once we do that, the next time we get a takefocus, check the parent tree. 
02666     //if we are no longer inside same table ,cell then switch to table selection mode.
02667     // BUT only do this in an editor
02668 
02669     PRInt16 displaySelection;
02670     nsresult result = mShell->GetSelectionFlags(&displaySelection);
02671     if (NS_FAILED(result))
02672       return result;
02673 
02674     // Editor has DISPLAY_ALL selection type
02675     if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
02676     {
02677       mCellParent = GetCellParent(domNode);
02678 #ifdef DEBUG_TABLE_SELECTION
02679       if (mCellParent)
02680         printf(" * TakeFocus - Collapsing into new cell\n");
02681 #endif
02682     }
02683   }
02684   else {
02685     // Now update the range list:
02686     if (aContinueSelection && domNode)
02687     {
02688       PRInt32 offset;
02689       nsIDOMNode *cellparent = GetCellParent(domNode);
02690       if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode
02691       {
02692 #ifdef DEBUG_TABLE_SELECTION
02693 printf(" * TakeFocus - moving into new cell\n");
02694 #endif
02695         nsCOMPtr<nsIDOMNode> parent;
02696         nsCOMPtr<nsIContent> parentContent;
02697         nsMouseEvent event(PR_FALSE, 0, nsnull, nsMouseEvent::eReal);
02698         nsresult result;
02699 
02700         // Start selecting in the cell we were in before
02701         result = ParentOffset(mCellParent, getter_AddRefs(parent),&offset);
02702         parentContent = do_QueryInterface(parent);
02703         if (parentContent)
02704           result = HandleTableSelection(parentContent, offset, nsISelectionPrivate::TABLESELECTION_CELL, &event);
02705 
02706         // Find the parent of this new cell and extend selection to it
02707         result = ParentOffset(cellparent,getter_AddRefs(parent),&offset);
02708         parentContent = do_QueryInterface(parent);
02709 
02710         // XXXX We need to REALLY get the current key shift state
02711         //  (we'd need to add event listener -- let's not bother for now)
02712         event.isShift = PR_FALSE; //aContinueSelection;
02713         if (parentContent)
02714         {
02715           mCellParent = cellparent;
02716           // Continue selection into next cell
02717           result = HandleTableSelection(parentContent, offset, nsISelectionPrivate::TABLESELECTION_CELL, &event);
02718         }
02719       }
02720       else
02721       {
02722         // XXXX Problem: Shift+click in browser is appending text selection to selected table!!!
02723         //   is this the place to erase seleced cells ?????
02724         if (mDomSelections[index]->GetDirection() == eDirNext && aContentEndOffset > aContentOffset) //didnt go far enough 
02725         {
02726           mDomSelections[index]->Extend(domNode, aContentEndOffset);//this will only redraw the diff 
02727         }
02728         else
02729           mDomSelections[index]->Extend(domNode, aContentOffset);
02730       }
02731     }
02732   }
02733 
02734   // Don't notify selection listeners if batching is on:
02735   if (GetBatching())
02736     return NS_OK;
02737   return NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
02738 }
02739 
02740 
02741 
02742 NS_METHOD
02743 nsSelection::LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
02744                              SelectionDetails **aReturnDetails, PRBool aSlowCheck)
02745 {
02746   if (!aContent || !aReturnDetails)
02747     return NS_ERROR_NULL_POINTER;
02748 
02749   STATUS_CHECK_RETURN_MACRO();
02750 
02751   *aReturnDetails = nsnull;
02752 
02753   for (PRInt32 j = 0; j < nsISelectionController::NUM_SELECTIONTYPES; j++) {
02754     if (mDomSelections[j])
02755       mDomSelections[j]->LookUpSelection(aContent, aContentOffset, aContentLength, aReturnDetails, (SelectionType)(1<<j), aSlowCheck);
02756   }
02757   return NS_OK;
02758 }
02759 
02760 
02761 
02762 NS_METHOD 
02763 nsSelection::SetMouseDownState(PRBool aState)
02764 {
02765   if (mMouseDownState == aState)
02766     return NS_OK;
02767   mMouseDownState = aState;
02768   if (!mMouseDownState)
02769   {
02770     PRInt16 reason;
02771     if (aState)
02772       reason = nsISelectionListener::MOUSEDOWN_REASON;
02773     else
02774       reason = nsISelectionListener::MOUSEUP_REASON;
02775     PostReason(reason);//not a drag reason
02776     NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);//notify that reason is mouse up please.
02777   }
02778   return NS_OK;
02779 }
02780 
02781 
02782 
02783 NS_METHOD
02784 nsSelection::GetMouseDownState(PRBool *aState)
02785 {
02786   if (!aState)
02787     return NS_ERROR_NULL_POINTER;
02788   *aState = mMouseDownState;
02789   return NS_OK;
02790 }
02791 
02792 NS_IMETHODIMP
02793 nsSelection::GetSelection(SelectionType aType, nsISelection **aDomSelection)
02794 {
02795   if (!aDomSelection)
02796     return NS_ERROR_NULL_POINTER;
02797   PRInt8 index = GetIndexFromSelectionType(aType);
02798   if (index < 0)
02799     return NS_ERROR_INVALID_ARG;
02800   *aDomSelection = NS_REINTERPRET_CAST(nsISelection *,mDomSelections[index]);
02801   NS_ADDREF(*aDomSelection);
02802   return NS_OK;
02803 }
02804 
02805 NS_IMETHODIMP
02806 nsSelection::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous)
02807 {
02808   PRInt8 index = GetIndexFromSelectionType(aType);
02809   if (index < 0)
02810     return NS_ERROR_INVALID_ARG;
02811 
02812   if (!mDomSelections[index])
02813     return NS_ERROR_NULL_POINTER;
02814 
02815   return mDomSelections[index]->ScrollIntoView(aRegion, aIsSynchronous);
02816 }
02817 
02818 NS_IMETHODIMP
02819 nsSelection::RepaintSelection(nsPresContext* aPresContext, SelectionType aType)
02820 {
02821   PRInt8 index = GetIndexFromSelectionType(aType);
02822   if (index < 0)
02823     return NS_ERROR_INVALID_ARG;
02824   if (!mDomSelections[index])
02825     return NS_ERROR_NULL_POINTER;
02826   return mDomSelections[index]->Repaint(aPresContext);
02827 }
02828  
02829 NS_IMETHODIMP
02830 nsSelection::GetFrameForNodeOffset(nsIContent *aNode, PRInt32 aOffset, HINT aHint, nsIFrame **aReturnFrame, PRInt32 *aReturnOffset)
02831 {
02832   if (!aNode || !aReturnFrame || !aReturnOffset)
02833     return NS_ERROR_NULL_POINTER;
02834 
02835   if (aOffset < 0)
02836     return NS_ERROR_FAILURE;
02837 
02838   *aReturnOffset = aOffset;
02839 
02840   nsresult result = NS_OK;
02841 
02842   nsCOMPtr<nsIContent> theNode = aNode;
02843 
02844   if (aNode->IsContentOfType(nsIContent::eELEMENT))
02845   {
02846     PRInt32 childIndex  = 0;
02847     PRInt32 numChildren = 0;
02848 
02849     if (aHint == HINTLEFT)
02850     {
02851       if (aOffset > 0)
02852         childIndex = aOffset - 1;
02853       else
02854         childIndex = aOffset;
02855     }
02856     else // HINTRIGHT
02857     {
02858       numChildren = theNode->GetChildCount();
02859 
02860       if (aOffset >= numChildren)
02861       {
02862         if (numChildren > 0)
02863           childIndex = numChildren - 1;
02864         else
02865           childIndex = 0;
02866       }
02867       else
02868         childIndex = aOffset;
02869     }
02870     
02871     nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex);
02872 
02873     if (!childNode)
02874       return NS_ERROR_FAILURE;
02875 
02876     theNode = childNode;
02877 
02878 #ifdef DONT_DO_THIS_YET
02879     // XXX: We can't use this code yet because the hinting
02880     //      can cause us to attatch to the wrong line frame.
02881 
02882     // Now that we have the child node, check if it too
02883     // can contain children. If so, call this method again!
02884 
02885     if (theNode->IsContentOfType(nsIContent::eELEMENT))
02886     {
02887       PRInt32 newOffset = 0;
02888 
02889       if (aOffset > childIndex)
02890       {
02891         numChildren = theNode->GetChildCount();
02892 
02893         newOffset = numChildren;
02894       }
02895 
02896       return GetFrameForNodeOffset(theNode, newOffset, aHint, aReturnFrame,aReturnOffset);
02897     }
02898     else
02899 #endif // DONT_DO_THIS_YET
02900     {
02901       // Check to see if theNode is a text node. If it is, translate
02902       // aOffset into an offset into the text node.
02903 
02904       nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode);
02905 
02906       if (textNode)
02907       {
02908         if (aOffset > childIndex)
02909         {
02910           PRUint32 textLength = 0;
02911 
02912           result = textNode->GetLength(&textLength);
02913 
02914           if (NS_FAILED(result))
02915             return NS_ERROR_FAILURE;
02916 
02917           *aReturnOffset = (PRInt32)textLength;
02918         }
02919         else
02920           *aReturnOffset = 0;
02921       }
02922     }
02923   }
02924   
02925   result = mShell->GetPrimaryFrameFor(theNode, aReturnFrame);
02926   if (NS_FAILED(result))
02927     return result;
02928 
02929   if (!*aReturnFrame)
02930     return NS_ERROR_UNEXPECTED;
02931 
02932   // find the child frame containing the offset we want
02933   result = (*aReturnFrame)->GetChildFrameContainingOffset(*aReturnOffset, aHint, &aOffset, aReturnFrame);
02934   return result;
02935 }
02936 
02937 NS_IMETHODIMP
02938 nsSelection::CommonPageMove(PRBool aForward, 
02939                           PRBool aExtend, 
02940                           nsIScrollableView *aScrollableView,
02941                           nsIFrameSelection *aFrameSel)
02942 {
02943   if ( !aScrollableView || !aFrameSel)
02944     return NS_ERROR_NULL_POINTER;
02945   // expected behavior for PageMove is to scroll AND move the caret
02946   // and remain relative position of the caret in view. see Bug 4302.
02947 
02948   nsresult result;
02949   //get the frame from the scrollable view
02950 
02951   nsIFrame* mainframe = nsnull;
02952 
02953   // The view's client data points back to its frame
02954   nsIView *scrolledView;
02955   result = aScrollableView->GetScrolledView(scrolledView);
02956 
02957   if (NS_FAILED(result))
02958     return result;
02959 
02960   if (scrolledView)
02961     mainframe = NS_STATIC_CAST(nsIFrame*, scrolledView->GetClientData());
02962 
02963   if (!mainframe)
02964     return NS_ERROR_FAILURE;
02965 
02966   // find out where the caret is.
02967   // we should know mDesiredX value of nsSelection, but I havent seen that behavior in other windows applications yet.
02968   nsCOMPtr<nsISelection> domSel;
02969   aFrameSel->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
02970   
02971   if (!domSel) 
02972     return NS_ERROR_UNEXPECTED;
02973   
02974   nsCOMPtr<nsICaret> caret;
02975   nsRect caretPos;
02976   PRBool isCollapsed;
02977   result = mShell->GetCaret(getter_AddRefs(caret));
02978   
02979   if (NS_FAILED(result)) 
02980     return result;
02981   
02982   nsIView *caretView;
02983   result = caret->GetCaretCoordinates(nsICaret::eClosestViewCoordinates, domSel, &caretPos, &isCollapsed, &caretView);
02984   
02985   if (NS_FAILED(result)) 
02986     return result;
02987   
02988   //need to adjust caret jump by percentage scroll
02989   nsSize scrollDelta;
02990   aScrollableView->GetPageScrollDistances(&scrollDelta);
02991 
02992   if (aForward)
02993     caretPos.y += scrollDelta.height;
02994   else
02995     caretPos.y -= scrollDelta.height;
02996 
02997   
02998   if (caretView)
02999   {
03000     caretPos += caretView->GetOffsetTo(scrolledView);
03001   }
03002     
03003   // get a content at desired location
03004   nsCOMPtr<nsIContent> content;
03005   PRInt32 startOffset, endOffset;
03006   PRBool beginFrameContent;
03007   nsPoint desiredPoint;
03008   desiredPoint.x = caretPos.x;
03009   desiredPoint.y = caretPos.y + caretPos.height/2;
03010   nsPresContext *context = mShell->GetPresContext();
03011   result = mainframe->GetContentAndOffsetsFromPoint(context, desiredPoint, getter_AddRefs(content), startOffset, endOffset, beginFrameContent);
03012   
03013   if (NS_FAILED(result)) 
03014     return result;
03015   
03016   if (!content) 
03017     return NS_ERROR_UNEXPECTED;
03018 
03019   // scroll one page
03020 
03021   aScrollableView->ScrollByPages(0, aForward ? 1 : -1);
03022   
03023   // place the caret
03024   result = aFrameSel->HandleClick(content, startOffset, startOffset, aExtend, PR_FALSE, PR_TRUE);
03025   
03026   return result;
03027 }
03028 
03029 NS_IMETHODIMP 
03030 nsSelection::CharacterMove(PRBool aForward, PRBool aExtend)
03031 {
03032   if (aForward)
03033     return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectCharacter);
03034   else
03035     return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectCharacter);
03036 }
03037 
03038 NS_IMETHODIMP
03039 nsSelection::WordMove(PRBool aForward, PRBool aExtend)
03040 {
03041   if (aForward)
03042     return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectWord);
03043   else
03044     return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectWord);
03045 }
03046 
03047 NS_IMETHODIMP
03048 nsSelection::LineMove(PRBool aForward, PRBool aExtend)
03049 {
03050   if (aForward)
03051     return MoveCaret(nsIDOMKeyEvent::DOM_VK_DOWN,aExtend,eSelectLine);
03052   else
03053     return MoveCaret(nsIDOMKeyEvent::DOM_VK_UP,aExtend,eSelectLine);
03054 }
03055 
03056 NS_IMETHODIMP
03057 nsSelection::IntraLineMove(PRBool aForward, PRBool aExtend)
03058 {
03059   if (aForward)
03060     return MoveCaret(nsIDOMKeyEvent::DOM_VK_END,aExtend,eSelectLine);
03061   else
03062     return MoveCaret(nsIDOMKeyEvent::DOM_VK_HOME,aExtend,eSelectLine);
03063 }
03064 
03065 NS_IMETHODIMP nsSelection::SelectAll()
03066 {
03067   nsCOMPtr<nsIContent> rootContent;
03068   if (mLimiter)
03069   {
03070     rootContent = mLimiter;//addrefit
03071   }
03072   else
03073   {
03074     nsIDocument *doc = mShell->GetDocument();
03075     if (!doc)
03076       return NS_ERROR_FAILURE;
03077     rootContent = doc->GetRootContent();
03078     if (!rootContent)
03079       return NS_ERROR_FAILURE;
03080   }
03081   PRInt32 numChildren = rootContent->GetChildCount();
03082   PostReason(nsISelectionListener::NO_REASON);
03083   return TakeFocus(mLimiter, 0, numChildren, PR_FALSE, PR_FALSE);
03084 }
03085 
03087 
03088 NS_IMETHODIMP
03089 nsSelection::StartBatchChanges()
03090 {
03091   nsresult result(NS_OK);
03092   mBatching++;
03093   return result;
03094 }
03095 
03096  
03097 NS_IMETHODIMP
03098 nsSelection::EndBatchChanges()
03099 {
03100   nsresult result(NS_OK);
03101   mBatching--;
03102   NS_ASSERTION(mBatching >=0,"Bad mBatching");
03103   if (mBatching == 0 && mChangesDuringBatching){
03104     mChangesDuringBatching = PR_FALSE;
03105     NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
03106   }
03107   return result;
03108 }
03109 
03110 
03111 nsresult
03112 nsSelection::NotifySelectionListeners(SelectionType aType)
03113 {
03114   PRInt8 index = GetIndexFromSelectionType(aType);
03115   if (index >=0)
03116   {
03117     return mDomSelections[index]->NotifySelectionListeners();
03118   }
03119   return NS_ERROR_FAILURE;
03120 }
03121 
03122 nsresult
03123 nsSelection::FrameOrParentHasSpecialSelectionStyle(nsIFrame* aFrame, PRUint8 aSelectionStyle, nsIFrame* *foundFrame)
03124 {
03125   nsIFrame* thisFrame = aFrame;
03126   
03127   while (thisFrame)
03128   {
03129     if (thisFrame->GetStyleUIReset()->mUserSelect == aSelectionStyle)
03130     {
03131       *foundFrame = thisFrame;
03132       return NS_OK;
03133     }
03134   
03135     thisFrame = thisFrame->GetParent();
03136   }
03137   
03138   *foundFrame = nsnull;
03139   return NS_OK;
03140 }
03141 
03142 
03143 // Start of Table Selection methods
03144 
03145 static PRBool IsCell(nsIContent *aContent)
03146 {
03147   return ((aContent->Tag() == nsHTMLAtoms::td ||
03148            aContent->Tag() == nsHTMLAtoms::th) &&
03149           aContent->IsContentOfType(nsIContent::eHTML));
03150 }
03151 
03152 nsITableCellLayout* 
03153 nsSelection::GetCellLayout(nsIContent *aCellContent)
03154 {
03155   // Get frame for cell
03156   nsIFrame *cellFrame = nsnull;
03157   mShell->GetPrimaryFrameFor(aCellContent, &cellFrame);
03158   if (!cellFrame)
03159     return nsnull;
03160 
03161   nsITableCellLayout *cellLayoutObject = nsnull;
03162   CallQueryInterface(cellFrame, &cellLayoutObject);
03163 
03164   return cellLayoutObject;
03165 }
03166 
03167 nsITableLayout* 
03168 nsSelection::GetTableLayout(nsIContent *aTableContent)
03169 {
03170   // Get frame for table
03171   nsIFrame *tableFrame = nsnull;
03172   mShell->GetPrimaryFrameFor(aTableContent, &tableFrame);
03173   if (!tableFrame)
03174     return nsnull;
03175 
03176   nsITableLayout *tableLayoutObject = nsnull;
03177   CallQueryInterface(tableFrame, &tableLayoutObject);
03178 
03179   return tableLayoutObject;
03180 }
03181 
03182 nsresult
03183 nsSelection::ClearNormalSelection()
03184 {
03185   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
03186   return mDomSelections[index]->RemoveAllRanges();
03187 }
03188 
03189 // Table selection support.
03190 // TODO: Separate table methods into a separate nsITableSelection interface
03191 nsresult
03192 nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRInt32 aTarget, nsMouseEvent *aMouseEvent)
03193 {
03194   NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER);
03195   NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER);
03196 
03197   if (mMouseDownState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE))
03198   {
03199     // We were selecting cells and user drags mouse in table border or inbetween cells,
03200     //  just do nothing
03201       return NS_OK;
03202   }
03203 
03204   nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(aParentContent);
03205   if (!parentNode)
03206     return NS_ERROR_FAILURE;
03207 
03208   nsresult result = NS_OK;
03209 
03210   nsIContent *childContent = aParentContent->GetChildAt(aContentOffset);
03211   nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
03212   if (!childNode)
03213     return NS_ERROR_FAILURE;
03214 
03215   // When doing table selection, always set the direction to next so
03216   // we can be sure that anchorNode's offset always points to the
03217   // selected cell
03218   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
03219   mDomSelections[index]->SetDirection(eDirNext);
03220 
03221   // Stack-class to wrap all table selection changes in 
03222   //  BeginBatchChanges() / EndBatchChanges()
03223   nsSelectionBatcher selectionBatcher(mDomSelections[index]);
03224 
03225   PRInt32 startRowIndex, startColIndex, curRowIndex, curColIndex;
03226   if (mMouseDownState && mDragSelectingCells)
03227   {
03228     // We are drag-selecting
03229     if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE)
03230     {
03231       // If dragging in the same cell as last event, do nothing
03232       if (mEndSelectedCell == childContent)
03233         return NS_OK;
03234 
03235 #ifdef DEBUG_TABLE_SELECTION
03236 printf(" mStartSelectedCell = %x, mEndSelectedCell = %x, childContent = %x \n", mStartSelectedCell, mEndSelectedCell, childContent);
03237 #endif
03238       // aTarget can be any "cell mode",
03239       //  so we can easily drag-select rows and columns 
03240       // Once we are in row or column mode,
03241       //  we can drift into any cell to stay in that mode
03242       //  even if aTarget = TABLESELECTION_CELL
03243 
03244       if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW ||
03245           mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN)
03246       {
03247         if (mEndSelectedCell)
03248         {
03249           // Also check if cell is in same row/col
03250           result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
03251           if (NS_FAILED(result)) return result;
03252           result = GetCellIndexes(childContent, curRowIndex, curColIndex);
03253           if (NS_FAILED(result)) return result;
03254         
03255 #ifdef DEBUG_TABLE_SELECTION
03256 printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex);
03257 #endif
03258           if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
03259               (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex)) 
03260             return NS_OK;
03261         }
03262 #ifdef DEBUG_TABLE_SELECTION
03263 printf(" Dragged into a new column or row\n");
03264 #endif
03265         // Continue dragging row or column selection
03266         return SelectRowOrColumn(childContent, mSelectingTableCellMode);
03267       }
03268       else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL)
03269       {
03270 #ifdef DEBUG_TABLE_SELECTION
03271 printf("HandleTableSelection: Dragged into a new cell\n");
03272 #endif
03273         // Trick for quick selection of rows and columns
03274         // Hold down shift, then start selecting in one direction
03275         // If next cell dragged into is in same row, select entire row,
03276         //   if next cell is in same column, select entire column
03277         if (mStartSelectedCell && aMouseEvent->isShift)
03278         {
03279           result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
03280           if (NS_FAILED(result)) return result;
03281           result = GetCellIndexes(childContent, curRowIndex, curColIndex);
03282           if (NS_FAILED(result)) return result;
03283           
03284           if (startRowIndex == curRowIndex || 
03285               startColIndex == curColIndex)
03286           {
03287             // Force new selection block
03288             mStartSelectedCell = nsnull;
03289             mDomSelections[index]->RemoveAllRanges();
03290 
03291             if (startRowIndex == curRowIndex)
03292               mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW;
03293             else
03294               mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN;
03295 
03296             return SelectRowOrColumn(childContent, mSelectingTableCellMode);
03297           }
03298         }
03299         
03300         // Reselect block of cells to new end location
03301         return SelectBlockOfCells(mStartSelectedCell, childContent);
03302       }
03303     }
03304     // Do nothing if dragging in table, but outside a cell
03305     return NS_OK;
03306   }
03307   else 
03308   {
03309     // Not dragging  -- mouse event is down or up
03310     if (mMouseDownState)
03311     {
03312 #ifdef DEBUG_TABLE_SELECTION
03313 printf("HandleTableSelection: Mouse down event\n");
03314 #endif
03315       // Clear cell we stored in mouse-down
03316       mUnselectCellOnMouseUp = nsnull;
03317       
03318       if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL)
03319       {
03320         PRBool isSelected = PR_FALSE;
03321 
03322         // Check if we have other selected cells
03323         nsCOMPtr<nsIDOMNode> previousCellNode;
03324         GetFirstSelectedCellAndRange(getter_AddRefs(previousCellNode), nsnull);
03325         if (previousCellNode)
03326         {
03327           // We have at least 1 other selected cell
03328 
03329           // Check if new cell is already selected
03330           nsIFrame  *cellFrame = nsnull;
03331           result = mShell->GetPrimaryFrameFor(childContent, &cellFrame);
03332           if (NS_FAILED(result)) return result;
03333           if (!cellFrame) return NS_ERROR_NULL_POINTER;
03334           result = cellFrame->GetSelected(&isSelected);
03335           if (NS_FAILED(result)) return result;
03336         }
03337         else
03338         {
03339           // No cells selected -- remove non-cell selection
03340           mDomSelections[index]->RemoveAllRanges();
03341         }
03342         mDragSelectingCells = PR_TRUE;    // Signal to start drag-cell-selection
03343         mSelectingTableCellMode = aTarget;
03344         // Set start for new drag-selection block (not appended)
03345         mStartSelectedCell = childContent;
03346         // The initial block end is same as the start
03347         mEndSelectedCell = childContent;
03348         
03349         if (isSelected)
03350         {
03351           // Remember this cell to (possibly) unselect it on mouseup
03352           mUnselectCellOnMouseUp = childContent;
03353 #ifdef DEBUG_TABLE_SELECTION
03354 printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
03355 #endif
03356         }
03357         else
03358         {
03359           // Select an unselected cell
03360           // but first remove existing selection if not in same table
03361           nsCOMPtr<nsIContent> previousCellContent = do_QueryInterface(previousCellNode);
03362           if (previousCellContent && !IsInSameTable(previousCellContent, childContent, nsnull))
03363           {
03364             mDomSelections[index]->RemoveAllRanges();
03365             // Reset selection mode that is cleared in RemoveAllRanges
03366             mSelectingTableCellMode = aTarget;
03367           }
03368 
03369           nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childContent);
03370           return SelectCellElement(cellElement);
03371         }
03372 
03373         return NS_OK;
03374       }
03375       else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE)
03376       {
03377         //TODO: We currently select entire table when clicked between cells,
03378         //  should we restrict to only around border?
03379         //  *** How do we get location data for cell and click?
03380         mDragSelectingCells = PR_FALSE;
03381         mStartSelectedCell = nsnull;
03382         mEndSelectedCell = nsnull;
03383 
03384         // Remove existing selection and select the table
03385         mDomSelections[index]->RemoveAllRanges();
03386         return CreateAndAddRange(parentNode, aContentOffset);
03387       }
03388       else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
03389       {
03390 #ifdef DEBUG_TABLE_SELECTION
03391 printf("aTarget == %d\n", aTarget);
03392 #endif
03393 
03394         // Start drag-selecting mode so multiple rows/cols can be selected
03395         // Note: Currently, nsFrame::GetDataForTableSelection
03396         //       will never call us for row or column selection on mouse down
03397         mDragSelectingCells = PR_TRUE;
03398       
03399         // Force new selection block
03400         mStartSelectedCell = nsnull;
03401         mDomSelections[index]->RemoveAllRanges();
03402         // Always do this AFTER RemoveAllRanges
03403         mSelectingTableCellMode = aTarget;
03404         return SelectRowOrColumn(childContent, aTarget);
03405       }
03406     }
03407     else
03408     {
03409 #ifdef DEBUG_TABLE_SELECTION
03410 printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%d\n", mDragSelectingCells, mStartSelectedCell);
03411 #endif
03412       // First check if we are extending a block selection
03413       PRInt32 rangeCount;
03414       result = mDomSelections[index]->GetRangeCount(&rangeCount);
03415       if (NS_FAILED(result)) 
03416         return result;
03417 
03418       if (rangeCount > 0 && aMouseEvent->isShift && 
03419           mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
03420       {
03421         // Shift key is down: append a block selection
03422         mDragSelectingCells = PR_FALSE;
03423         return SelectBlockOfCells(mAppendStartSelectedCell, childContent);
03424       }
03425 
03426       if (mDragSelectingCells)
03427         mAppendStartSelectedCell = mStartSelectedCell;
03428         
03429       mDragSelectingCells = PR_FALSE;
03430       mStartSelectedCell = nsnull;
03431       mEndSelectedCell = nsnull;
03432 
03433       // Any other mouseup actions require that Ctrl or Cmd key is pressed
03434       //  else stop table selection mode
03435       PRBool doMouseUpAction = PR_FALSE;
03436 #if defined(XP_MAC) || defined(XP_MACOSX)
03437       doMouseUpAction = aMouseEvent->isMeta;
03438 #else
03439       doMouseUpAction = aMouseEvent->isControl;
03440 #endif
03441       if (!doMouseUpAction)
03442       {
03443 #ifdef DEBUG_TABLE_SELECTION
03444 printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%d\n", mAppendStartSelectedCell);
03445 #endif
03446         return NS_OK;
03447       }
03448       // Unselect a cell only if it wasn't
03449       //  just selected on mousedown
03450       if( childContent == mUnselectCellOnMouseUp)
03451       {
03452         // Scan ranges to find the cell to unselect (the selection range to remove)
03453         nsCOMPtr<nsIDOMNode> previousCellParent;
03454         nsCOMPtr<nsIDOMRange> range;
03455         PRInt32 offset;
03456 #ifdef DEBUG_TABLE_SELECTION
03457 printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
03458 #endif
03459         for( PRInt32 i = 0; i < rangeCount; i++)
03460         {
03461           result = mDomSelections[index]->GetRangeAt(i, getter_AddRefs(range));
03462           if (NS_FAILED(result)) return result;
03463           if (!range) return NS_ERROR_NULL_POINTER;
03464 
03465           nsCOMPtr<nsIDOMNode> parent;
03466           result = range->GetStartContainer(getter_AddRefs(parent));
03467           if (NS_FAILED(result)) return result;
03468           if (!parent) return NS_ERROR_NULL_POINTER;
03469 
03470           range->GetStartOffset(&offset);
03471           // Be sure previous selection is a table cell
03472           nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
03473           nsCOMPtr<nsIContent> child = parentContent->GetChildAt(offset);
03474           if (child && IsCell(child))
03475             previousCellParent = parent;
03476 
03477           // We're done if we didn't find parent of a previously-selected cell
03478           if (!previousCellParent) break;
03479         
03480           if (previousCellParent == parentNode && offset == aContentOffset)
03481           {
03482             // Cell is already selected
03483             if (rangeCount == 1)
03484             {
03485 #ifdef DEBUG_TABLE_SELECTION
03486 printf("HandleTableSelection: Unselecting single selected cell\n");
03487 #endif
03488               // This was the only cell selected.
03489               // Collapse to "normal" selection inside the cell
03490               mStartSelectedCell = nsnull;
03491               mEndSelectedCell = nsnull;
03492               mAppendStartSelectedCell = nsnull;
03493               //TODO: We need a "Collapse to just before deepest child" routine
03494               // Even better, should we collapse to just after the LAST deepest child
03495               //  (i.e., at the end of the cell's contents)?
03496               return mDomSelections[index]->Collapse(childNode, 0);
03497             }
03498 #ifdef DEBUG_TABLE_SELECTION
03499 printf("HandleTableSelection: Removing cell from multi-cell selection\n");
03500 #endif
03501             // Unselecting the start of previous block 
03502             // XXX What do we use now!
03503             if (childContent == mAppendStartSelectedCell)
03504                mAppendStartSelectedCell = nsnull;
03505 
03506             // Deselect cell by removing its range from selection
03507             return mDomSelections[index]->RemoveRange(range);
03508           }
03509         }
03510         mUnselectCellOnMouseUp = nsnull;
03511       }
03512     }
03513   }
03514   return result;
03515 }
03516 
03517 nsresult
03518 nsSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell)
03519 {
03520   NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER);
03521   NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER);
03522   mEndSelectedCell = aEndCell;
03523 
03524   nsCOMPtr<nsIContent> startCell;
03525   nsresult result = NS_OK;
03526 
03527   // If new end cell is in a different table, do nothing
03528   nsCOMPtr<nsIContent> table;
03529   if (!IsInSameTable(aStartCell, aEndCell, getter_AddRefs(table)))
03530     return NS_OK;
03531 
03532   // Get starting and ending cells' location in the cellmap
03533   PRInt32 startRowIndex, startColIndex, endRowIndex, endColIndex;
03534   result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
03535   if(NS_FAILED(result)) return result;
03536   result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
03537   if(NS_FAILED(result)) return result;
03538 
03539   // Get TableLayout interface to access cell data based on cellmap location
03540   // frames are not ref counted, so don't use an nsCOMPtr
03541   nsITableLayout *tableLayoutObject = GetTableLayout(table);
03542   if (!tableLayoutObject) return NS_ERROR_FAILURE;
03543 
03544   PRInt32 curRowIndex, curColIndex;
03545 
03546   if (mDragSelectingCells)
03547   {
03548     // Drag selecting: remove selected cells outside of new block limits
03549 
03550     PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
03551 
03552     nsCOMPtr<nsIDOMNode> cellNode;
03553     nsCOMPtr<nsIDOMRange> range;
03554     result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
03555     if (NS_FAILED(result)) return result;
03556 
03557     PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
03558     PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
03559     PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
03560     PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
03561 
03562     while (cellNode)
03563     {
03564       nsCOMPtr<nsIContent> childContent = do_QueryInterface(cellNode);
03565       result = GetCellIndexes(childContent, curRowIndex, curColIndex);
03566       if (NS_FAILED(result)) return result;
03567 
03568 #ifdef DEBUG_TABLE_SELECTION
03569 if (!range)
03570 printf("SelectBlockOfCells -- range is null\n");
03571 #endif
03572       if (range &&
03573           (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
03574            curColIndex < minColIndex || curColIndex > maxColIndex))
03575       {
03576         mDomSelections[index]->RemoveRange(range);
03577         // Since we've removed the range, decrement pointer to next range
03578         mSelectedCellIndex--;
03579       }    
03580       result = GetNextSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
03581       if (NS_FAILED(result)) return result;
03582     }
03583   }
03584 
03585   nsCOMPtr<nsIDOMElement> cellElement;
03586   PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
03587   PRBool  isSelected;
03588 
03589   // Note that we select block in the direction of user's mouse dragging,
03590   //  which means start cell may be after the end cell in either row or column
03591   PRInt32 row = startRowIndex;
03592   while(PR_TRUE)
03593   {
03594     PRInt32 col = startColIndex;
03595     while(PR_TRUE)
03596     {
03597       result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
03598                                                 curRowIndex, curColIndex, rowSpan, colSpan, 
03599                                                 actualRowSpan, actualColSpan, isSelected);
03600       if (NS_FAILED(result)) return result;
03601 
03602       NS_ASSERTION(actualColSpan, "!actualColSpan is 0!");
03603 
03604       // Skip cells that are spanned from previous locations or are already selected
03605       if (!isSelected && cellElement && row == curRowIndex && col == curColIndex)
03606       {
03607         result = SelectCellElement(cellElement);
03608         if (NS_FAILED(result)) return result;
03609       }
03610       // Done when we reach end column
03611       if (col == endColIndex) break;
03612 
03613       if (startColIndex < endColIndex)
03614         col ++;
03615       else
03616         col--;
03617     };
03618     if (row == endRowIndex) break;
03619 
03620     if (startRowIndex < endRowIndex)
03621       row++;
03622     else
03623       row--;
03624   };
03625   return result;
03626 }
03627 
03628 nsresult
03629 nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
03630 {
03631   if (!aCellContent) return NS_ERROR_NULL_POINTER;
03632 
03633   nsCOMPtr<nsIContent> table;
03634   nsresult result = GetParentTable(aCellContent, getter_AddRefs(table));
03635   if (NS_FAILED(result)) return PR_FALSE;
03636   if (!table) return NS_ERROR_NULL_POINTER;
03637 
03638   // Get table and cell layout interfaces to access 
03639   //   cell data based on cellmap location
03640   // Frames are not ref counted, so don't use an nsCOMPtr
03641   nsITableLayout *tableLayout = GetTableLayout(table);
03642   if (!tableLayout) return NS_ERROR_FAILURE;
03643   nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
03644   if (!cellLayout) return NS_ERROR_FAILURE;
03645 
03646   // Get location of target cell:      
03647   PRInt32 rowIndex, colIndex, curRowIndex, curColIndex;
03648   result = cellLayout->GetCellIndexes(rowIndex, colIndex);
03649   if (NS_FAILED(result)) return result;
03650 
03651   // Be sure we start at proper beginning
03652   // (This allows us to select row or col given ANY cell!)
03653   if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
03654     colIndex = 0;
03655   if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
03656     rowIndex = 0;
03657 
03658   nsCOMPtr<nsIDOMElement> cellElement;
03659   nsCOMPtr<nsIDOMElement> firstCell;
03660   nsCOMPtr<nsIDOMElement> lastCell;
03661   PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
03662   PRBool isSelected;
03663 
03664   do {
03665     // Loop through all cells in column or row to find first and last
03666     result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
03667                                         curRowIndex, curColIndex, rowSpan, colSpan, 
03668                                         actualRowSpan, actualColSpan, isSelected);
03669     if (NS_FAILED(result)) return result;
03670     if (cellElement)
03671     {
03672       NS_ASSERTION(actualRowSpan > 0 && actualColSpan> 0, "SelectRowOrColumn: Bad rowspan or colspan\n");
03673       if (!firstCell)
03674         firstCell = cellElement;
03675 
03676       lastCell = cellElement;
03677 
03678       // Move to next cell in cellmap, skipping spanned locations
03679       if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
03680         colIndex += actualColSpan;
03681       else
03682         rowIndex += actualRowSpan;
03683     }
03684   }
03685   while (cellElement);
03686 
03687   // Use SelectBlockOfCells:
03688   // This will replace existing selection,
03689   //  but allow unselecting by dragging out of selected region
03690   if (firstCell && lastCell)
03691   {
03692     if (!mStartSelectedCell)
03693     {
03694       // We are starting a new block, so select the first cell
03695       result = SelectCellElement(firstCell);
03696       if (NS_FAILED(result)) return result;
03697       mStartSelectedCell = do_QueryInterface(firstCell);
03698     }
03699     nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
03700     result = SelectBlockOfCells(mStartSelectedCell, lastCellContent);
03701 
03702     // This gets set to the cell at end of row/col, 
03703     //   but we need it to be the cell under cursor
03704     mEndSelectedCell = aCellContent;
03705     return result;
03706   }
03707 
03708 #if 0
03709 // This is a more efficient strategy that appends row to current selection,
03710 //  but doesn't allow dragging OFF of an existing selection to unselect!
03711   do {
03712     // Loop through all cells in column or row
03713     result = tableLayout->GetCellDataAt(rowIndex, colIndex,
03714                                         getter_AddRefs(cellElement),
03715                                         curRowIndex, curColIndex,
03716                                         rowSpan, colSpan,
03717                                         actualRowSpan, actualColSpan,
03718                                         isSelected);
03719     if (NS_FAILED(result)) return result;
03720     // We're done when cell is not found
03721     if (!cellElement) break;
03722 
03723 
03724     // Check spans else we infinitely loop
03725     NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
03726     NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
03727     
03728     // Skip cells that are already selected or span from outside our region
03729     if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
03730     {
03731       result = SelectCellElement(cellElement);
03732       if (NS_FAILED(result)) return result;
03733     }
03734     // Move to next row or column in cellmap, skipping spanned locations
03735     if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
03736       colIndex += actualColSpan;
03737     else
03738       rowIndex += actualRowSpan;
03739   }
03740   while (cellElement);
03741 #endif
03742 
03743   return NS_OK;
03744 }
03745 
03746 nsresult 
03747 nsSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode)
03748 {
03749   if (!aRange || !aCellNode) return NS_ERROR_NULL_POINTER;
03750 
03751   *aCellNode = nsnull;
03752 
03753   nsCOMPtr<nsIDOMNode> startParent;
03754   nsresult result = aRange->GetStartContainer(getter_AddRefs(startParent));
03755   if (NS_FAILED(result))
03756     return result;
03757   if (!startParent)
03758     return NS_ERROR_FAILURE;
03759 
03760   PRInt32 offset;
03761   result = aRange->GetStartOffset(&offset);
03762   if (NS_FAILED(result))
03763     return result;
03764 
03765   nsCOMPtr<nsIContent> parentContent = do_QueryInterface(startParent);
03766   nsCOMPtr<nsIContent> childContent = parentContent->GetChildAt(offset);
03767   if (!childContent)
03768     return NS_ERROR_NULL_POINTER;
03769   // Don't return node if not a cell
03770   if (!IsCell(childContent)) return NS_OK;
03771 
03772   nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
03773   if (childNode)
03774   {
03775     *aCellNode = childNode;
03776     NS_ADDREF(*aCellNode);
03777   }
03778   return NS_OK;
03779 }
03780 
03781 nsresult 
03782 nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
03783 {
03784   if (!aCell) return NS_ERROR_NULL_POINTER;
03785   *aCell = nsnull;
03786 
03787   // aRange is optional
03788   if (aRange)
03789     *aRange = nsnull;
03790 
03791   nsCOMPtr<nsIDOMRange> firstRange;
03792   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
03793   nsresult result = mDomSelections[index]->GetRangeAt(0, getter_AddRefs(firstRange));
03794   if (NS_FAILED(result)) return result;
03795   if (!firstRange) return NS_ERROR_FAILURE;
03796 
03797   nsCOMPtr<nsIDOMNode> cellNode;
03798   result = GetFirstCellNodeInRange(firstRange, getter_AddRefs(cellNode));
03799   if (NS_FAILED(result)) return result;
03800   if (!cellNode) return NS_OK;
03801 
03802   *aCell = cellNode;
03803   NS_ADDREF(*aCell);
03804   if (aRange)
03805   {
03806     *aRange = firstRange;
03807     NS_ADDREF(*aRange);
03808   }
03809 
03810   // Setup for next cell
03811   mSelectedCellIndex = 1;
03812 
03813   return NS_OK;
03814 }
03815 
03816 nsresult
03817 nsSelection::GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
03818 {
03819   if (!aCell) return NS_ERROR_NULL_POINTER;
03820   *aCell = nsnull;
03821 
03822   // aRange is optional
03823   if (aRange)
03824     *aRange = nsnull;
03825 
03826   PRInt32 rangeCount;
03827   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
03828   nsresult result = mDomSelections[index]->GetRangeCount(&rangeCount);
03829   if (NS_FAILED(result)) return result;
03830 
03831   // Don't even try if index exceeds range count
03832   if (mSelectedCellIndex >= rangeCount) 
03833   {
03834     // Should we reset index? 
03835     // Maybe better to force recalling GetFirstSelectedCell()
03836     //mSelectedCellIndex = 0;
03837     return NS_OK;
03838   }
03839 
03840   // Get first node in next range of selection - test if it's a cell
03841   nsCOMPtr<nsIDOMRange> range;
03842   result = mDomSelections[index]->GetRangeAt(mSelectedCellIndex, getter_AddRefs(range));
03843   if (NS_FAILED(result)) return result;
03844   if (!range) return NS_ERROR_FAILURE;
03845 
03846   nsCOMPtr<nsIDOMNode> cellNode;
03847   result = GetFirstCellNodeInRange(range, getter_AddRefs(cellNode));
03848   if (NS_FAILED(result)) return result;
03849   // No cell in selection range
03850   if (!cellNode) return NS_OK;
03851 
03852   *aCell = cellNode;
03853   NS_ADDREF(*aCell);
03854   if (aRange)
03855   {
03856     *aRange = range;
03857     NS_ADDREF(*aRange);
03858   }
03859 
03860   // Setup for next cell
03861   mSelectedCellIndex++;
03862 
03863   return NS_OK;
03864 }
03865 
03866 nsresult
03867 nsSelection::GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex)
03868 {
03869   if (!aCell) return NS_ERROR_NULL_POINTER;
03870 
03871   aColIndex=0; // initialize out params
03872   aRowIndex=0;
03873 
03874   nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
03875   if (!cellLayoutObject)  return NS_ERROR_FAILURE;
03876   return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
03877 }
03878 
03879 PRBool 
03880 nsSelection::IsInSameTable(nsIContent *aContent1, nsIContent *aContent2, nsIContent **aTable)
03881 {
03882   if (!aContent1 || !aContent2) return PR_FALSE;
03883   
03884   // aTable is optional:
03885   if(aTable) *aTable = nsnull;
03886   
03887   nsCOMPtr<nsIContent> tableNode1;
03888   nsCOMPtr<nsIContent> tableNode2;
03889 
03890   nsresult result = GetParentTable(aContent1, getter_AddRefs(tableNode1));
03891   if (NS_FAILED(result)) return PR_FALSE;
03892   result = GetParentTable(aContent2, getter_AddRefs(tableNode2));
03893   if (NS_FAILED(result)) return PR_FALSE;
03894 
03895   // Must be in the same table
03896   if (tableNode1 && (tableNode1 == tableNode2))
03897   {
03898     if (aTable)
03899     {
03900       *aTable = tableNode1;
03901       NS_ADDREF(*aTable);
03902     }
03903     return PR_TRUE;;
03904   }
03905   return PR_FALSE;
03906 }
03907 
03908 nsresult
03909 nsSelection::GetParentTable(nsIContent *aCell, nsIContent **aTable)
03910 {
03911   if (!aCell || !aTable) {
03912     return NS_ERROR_NULL_POINTER;
03913   }
03914 
03915   for (nsIContent* parent = aCell->GetParent(); parent;
03916        parent = parent->GetParent()) {
03917     if (parent->Tag() == nsHTMLAtoms::table &&
03918         parent->IsContentOfType(nsIContent::eHTML)) {
03919       *aTable = parent;
03920       NS_ADDREF(*aTable);
03921 
03922       return NS_OK;
03923     }
03924   }
03925 
03926   return NS_OK;
03927 }
03928 
03929 nsresult
03930 nsSelection::SelectCellElement(nsIDOMElement *aCellElement)
03931 {
03932   nsCOMPtr<nsIContent> cellContent = do_QueryInterface(aCellElement);
03933 
03934   if (!cellContent) {
03935     return NS_ERROR_FAILURE;
03936   }
03937 
03938   nsIContent *parent = cellContent->GetParent();
03939   nsCOMPtr<nsIDOMNode> parentNode(do_QueryInterface(parent));
03940   if (!parentNode) {
03941     return NS_ERROR_FAILURE;
03942   }
03943 
03944   // Get child offset
03945   PRInt32 offset = parent->IndexOf(cellContent);
03946 
03947   return CreateAndAddRange(parentNode, offset);
03948 }
03949 
03950 nsresult
03951 nsTypedSelection::getTableCellLocationFromRange(nsIDOMRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol)
03952 {
03953   if (!aRange || !aSelectionType || !aRow || !aCol)
03954     return NS_ERROR_NULL_POINTER;
03955 
03956   *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
03957   *aRow = 0;
03958   *aCol = 0;
03959 
03960   // Must have access to frame selection to get cell info
03961   if (!mFrameSelection) return NS_OK;
03962 
03963   nsresult result = GetTableSelectionType(aRange, aSelectionType);
03964   if (NS_FAILED(result)) return result;
03965   
03966   // Don't fail if range does not point to a single table cell,
03967   //  let aSelectionType tell user if we don't have a cell
03968   if (*aSelectionType  != nsISelectionPrivate::TABLESELECTION_CELL)
03969     return NS_OK;
03970 
03971   // Get the child content (the cell) pointed to by starting node of range
03972   // We do minimal checking since GetTableSelectionType assures
03973   //   us that this really is a table cell
03974   nsCOMPtr<nsIDOMNode> startNode;
03975   result = aRange->GetStartContainer(getter_AddRefs(startNode));
03976   if (NS_FAILED(result))
03977     return result;
03978 
03979   nsCOMPtr<nsIContent> content(do_QueryInterface(startNode));
03980   if (!content)
03981     return NS_ERROR_FAILURE;
03982   PRInt32 startOffset;
03983   result = aRange->GetStartOffset(&startOffset);
03984   if (NS_FAILED(result))
03985     return result;
03986 
03987   nsIContent *child = content->GetChildAt(startOffset);
03988   if (!child)
03989     return NS_ERROR_FAILURE;
03990 
03991   //Note: This is a non-ref-counted pointer to the frame
03992   nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
03993   if (NS_FAILED(result))
03994     return result;
03995   if (!cellLayout)
03996     return NS_ERROR_FAILURE;
03997 
03998   return cellLayout->GetCellIndexes(*aRow, *aCol);
03999 }
04000 
04001 nsresult
04002 nsTypedSelection::addTableCellRange(nsIDOMRange *aRange, PRBool *aDidAddRange)
04003 {
04004   if (!aDidAddRange)
04005     return NS_ERROR_NULL_POINTER;
04006 
04007   *aDidAddRange = PR_FALSE;
04008 
04009   if (!mFrameSelection)
04010     return NS_OK;
04011 
04012   if (!aRange)
04013     return NS_ERROR_NULL_POINTER;
04014 
04015   nsresult result;
04016 
04017   // Get if we are adding a cell selection and the row, col of cell if we are
04018   PRInt32 newRow, newCol, tableMode;
04019   result = getTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
04020   if (NS_FAILED(result)) return result;
04021   
04022   // If not adding a cell range, we are done here
04023   if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
04024   {
04025     mFrameSelection->mSelectingTableCellMode = tableMode;
04026     // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
04027     return NS_OK;
04028   }
04029   
04030   // Set frame selection mode only if not already set to a table mode
04031   //  so we don't loose the select row and column flags (not detected by getTableCellLocation)
04032   if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE)
04033     mFrameSelection->mSelectingTableCellMode = tableMode;
04034 
04035   return AddItem(aRange);
04036 }
04037 
04038 //TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
04039 NS_IMETHODIMP
04040 nsTypedSelection::GetTableSelectionType(nsIDOMRange* aRange, PRInt32* aTableSelectionType)
04041 {
04042   if (!aRange || !aTableSelectionType)
04043     return NS_ERROR_NULL_POINTER;
04044   
04045   *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
04046  
04047   // Must have access to frame selection to get cell info
04048   if(!mFrameSelection) return NS_OK;
04049 
04050   nsCOMPtr<nsIDOMNode> startNode;
04051   nsresult result = aRange->GetStartContainer(getter_AddRefs(startNode));
04052   if (NS_FAILED(result)) return result;
04053   if (!startNode) return NS_ERROR_FAILURE;
04054   
04055   nsCOMPtr<nsIDOMNode> endNode;
04056   result = aRange->GetEndContainer(getter_AddRefs(endNode));
04057   if (NS_FAILED(result)) return result;
04058   if (!endNode) return NS_ERROR_FAILURE;
04059 
04060   // Not a single selected node
04061   if (startNode != endNode) return NS_OK;
04062 
04063   nsCOMPtr<nsIContent> content = do_QueryInterface(startNode);
04064   if (!content) return NS_ERROR_FAILURE;
04065 
04066   // if we simply cannot have children, return NS_OK as a non-failing,
04067   // non-completing case for table selection
04068   if (!content->IsContentOfType(nsIContent::eELEMENT))
04069     return NS_OK; //got to be a text node, definately not a table row/cell
04070   
04071   PRInt32 startOffset;
04072   PRInt32 endOffset;
04073   result = aRange->GetEndOffset(&endOffset);
04074   if (NS_FAILED(result)) return result;
04075   result = aRange->GetStartOffset(&startOffset);
04076   if (NS_FAILED(result)) return result;
04077 
04078   // Not a single selected node
04079   if ((endOffset - startOffset) != 1)
04080     return NS_OK;
04081 
04082   if (!content->IsContentOfType(nsIContent::eHTML)) {
04083     return NS_OK;
04084   }
04085 
04086   nsIAtom *tag = content->Tag();
04087 
04088   if (tag == nsHTMLAtoms::tr)
04089   {
04090     *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
04091   }
04092   else //check to see if we are selecting a table or row (column and all cells not done yet)
04093   {
04094     nsIContent *child = content->GetChildAt(startOffset);
04095     if (!child)
04096       return NS_ERROR_FAILURE;
04097 
04098     tag = child->Tag();
04099 
04100     if (tag == nsHTMLAtoms::table)
04101       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
04102     else if (tag == nsHTMLAtoms::tr)
04103       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
04104   }
04105 
04106   return result;
04107 }
04108 
04109 nsresult
04110 nsSelection::CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset)
04111 {
04112   if (!aParentNode) return NS_ERROR_NULL_POINTER;
04113   nsCOMPtr<nsIDOMRange> range;
04114   NS_NewRange(getter_AddRefs(range));
04115   if (!range) return NS_ERROR_OUT_OF_MEMORY;
04116 
04117   // Set range around child at given offset
04118   nsresult result = range->SetStart(aParentNode, aOffset);
04119   if (NS_FAILED(result)) return result;
04120   result = range->SetEnd(aParentNode, aOffset+1);
04121   if (NS_FAILED(result)) return result;
04122   
04123   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
04124   return mDomSelections[index]->AddRange(range);
04125 }
04126 
04127 // End of Table Selection
04128 
04129 NS_IMETHODIMP
04130 nsSelection::AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection,
04131       nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset)
04132 {
04133   
04134   *changeSelection = PR_FALSE;
04135   *outContent = nsnull;
04136   
04137   nsresult  rv;  
04138   nsIFrame*   selectAllFrame;
04139   rv = FrameOrParentHasSpecialSelectionStyle(aFrame, NS_STYLE_USER_SELECT_ALL, &selectAllFrame);
04140   if (NS_FAILED(rv)) return rv;
04141   
04142   if (!selectAllFrame)
04143     return NS_OK;
04144   
04145   nsIContent* selectAllContent = selectAllFrame->GetContent();
04146   if (selectAllContent)
04147   {
04148     nsCOMPtr<nsIContent> parentContent = selectAllContent->GetParent();
04149     if (parentContent)
04150     {
04151       PRInt32 startOffset = parentContent->IndexOf(selectAllContent);
04152 
04153       if (startOffset < 0)
04154       {
04155         // hrmm, this is probably anonymous content. Let's go up another level
04156         // do we need to do this if we get the right frameSelection to start with?
04157         nsCOMPtr<nsIContent> superParent = parentContent->GetParent();
04158         if (superParent)
04159         {
04160           PRInt32 superStartOffset = superParent->IndexOf(parentContent);
04161           if (superStartOffset < 0)
04162             return NS_ERROR_FAILURE;    // give up
04163         
04164           parentContent = superParent;
04165           startOffset = superStartOffset;
04166         }
04167       }
04168       
04169       NS_IF_ADDREF(*outContent = parentContent);
04170 
04171       *outStartOffset = startOffset;
04172       *outEndOffset = startOffset + 1;
04173 
04174       *changeSelection = PR_TRUE;
04175     }    
04176   }
04177 
04178   return NS_OK;
04179 }
04180 
04181 
04182 NS_IMETHODIMP
04183 nsSelection::SetHint(HINT aHintRight)
04184 {
04185   mHint = aHintRight;
04186   return NS_OK;
04187 }
04188 
04189 NS_IMETHODIMP
04190 nsSelection::GetHint(HINT *aHintRight)
04191 {
04192   *aHintRight = mHint;
04193   return NS_OK; 
04194 }
04195 
04196 
04197 //END nsIFrameSelection methods
04198 
04199 
04200 #ifdef XP_MAC
04201 #pragma mark -
04202 #endif
04203 
04204 //BEGIN nsISelection interface implementations
04205 
04206 
04207 
04208 NS_IMETHODIMP
04209 nsSelection::DeleteFromDocument()
04210 {
04211   nsresult res;
04212 
04213   // If we're already collapsed, then set ourselves to include the
04214   // last item BEFORE the current range, rather than the range itself,
04215   // before we do the delete.
04216   PRBool isCollapsed;
04217   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
04218   mDomSelections[index]->GetIsCollapsed( &isCollapsed);
04219   if (isCollapsed)
04220   {
04221     // If the offset is positive, then it's easy:
04222     if (mDomSelections[index]->FetchFocusOffset() > 0)
04223     {
04224       mDomSelections[index]->Extend(mDomSelections[index]->FetchFocusNode(), mDomSelections[index]->FetchFocusOffset() - 1);
04225     }
04226     else
04227     {
04228       // Otherwise it's harder, have to find the previous node
04229       printf("Sorry, don't know how to delete across frame boundaries yet\n");
04230       return NS_ERROR_NOT_IMPLEMENTED;
04231     }
04232   }
04233 
04234   // Get an iterator
04235   nsSelectionIterator iter(mDomSelections[index]);
04236   res = iter.First();
04237   if (NS_FAILED(res))
04238     return res;
04239 
04240   nsCOMPtr<nsIDOMRange> range;
04241   while (iter.IsDone())
04242   {
04243     res = iter.CurrentItem(NS_STATIC_CAST(nsIDOMRange**, getter_AddRefs(range)));
04244     if (NS_FAILED(res))
04245       return res;
04246     res = range->DeleteContents();
04247     if (NS_FAILED(res))
04248       return res;
04249     iter.Next();
04250   }
04251 
04252   // Collapse to the new location.
04253   // If we deleted one character, then we move back one element.
04254   // FIXME  We don't know how to do this past frame boundaries yet.
04255   if (isCollapsed)
04256     mDomSelections[index]->Collapse(mDomSelections[index]->FetchAnchorNode(), mDomSelections[index]->FetchAnchorOffset()-1);
04257   else if (mDomSelections[index]->FetchAnchorOffset() > 0)
04258     mDomSelections[index]->Collapse(mDomSelections[index]->FetchAnchorNode(), mDomSelections[index]->FetchAnchorOffset());
04259 #ifdef DEBUG
04260   else
04261     printf("Don't know how to set selection back past frame boundary\n");
04262 #endif
04263 
04264   return NS_OK;
04265 }
04266 
04267 
04268 NS_IMETHODIMP
04269 nsSelection::SetDisplaySelection(PRInt16 aToggle)
04270 {
04271   mDisplaySelection = aToggle;
04272   return NS_OK;
04273 }
04274 
04275 NS_IMETHODIMP
04276 nsSelection::GetDisplaySelection(PRInt16 *aToggle)
04277 {
04278   if (!aToggle)
04279     return NS_ERROR_INVALID_ARG;
04280   *aToggle = mDisplaySelection;
04281   return NS_OK;
04282 }
04283 
04284 NS_IMETHODIMP
04285 nsSelection::SetDelayCaretOverExistingSelection(PRBool aDelay)
04286 {
04287   mDelayCaretOverExistingSelection = aDelay;
04288   
04289   if (! aDelay)
04290     mDelayedMouseEventValid = PR_FALSE;
04291 
04292   return NS_OK;
04293 }
04294 
04295 NS_IMETHODIMP
04296 nsSelection::GetDelayCaretOverExistingSelection(PRBool *aDelay)
04297 {
04298   if (!aDelay)
04299     return NS_ERROR_NULL_POINTER;
04300 
04301   *aDelay =   mDelayCaretOverExistingSelection;
04302 
04303   return NS_OK;
04304 }
04305 
04306 NS_IMETHODIMP
04307 nsSelection::SetDelayedCaretData(nsMouseEvent *aMouseEvent)
04308 {
04309   if (aMouseEvent)
04310   {
04311     mDelayedMouseEventValid = PR_TRUE;
04312     mDelayedMouseEvent      = *aMouseEvent;
04313 
04314     // Don't cache the widget.  We don't need it and it could go away.
04315     mDelayedMouseEvent.widget = nsnull;
04316   }
04317   else
04318     mDelayedMouseEventValid = PR_FALSE;
04319 
04320   return NS_OK;
04321 }
04322 
04323 NS_IMETHODIMP
04324 nsSelection::GetDelayedCaretData(nsMouseEvent **aMouseEvent)
04325 {
04326   if (!aMouseEvent)
04327     return NS_ERROR_NULL_POINTER;
04328 
04329   if (mDelayedMouseEventValid)
04330     *aMouseEvent = &mDelayedMouseEvent;
04331   else
04332     *aMouseEvent = 0;
04333 
04334   return NS_OK;
04335 }
04336 
04337 // Frame needs to get the limiting content node for parent node searches
04338 NS_IMETHODIMP
04339 nsSelection::GetLimiter(nsIContent **aLimiterContent)
04340 {
04341   if (!aLimiterContent) return NS_ERROR_NULL_POINTER;
04342   *aLimiterContent = mLimiter;
04343   NS_IF_ADDREF(*aLimiterContent);
04344 
04345   return NS_OK;
04346 }
04347 
04348 NS_IMETHODIMP
04349 nsSelection::SetMouseDoubleDown(PRBool aDoubleDown)
04350 {
04351   mMouseDoubleDownState = aDoubleDown;
04352   return NS_OK;
04353 }
04354 
04355 NS_IMETHODIMP
04356 nsSelection::GetMouseDoubleDown(PRBool *aDoubleDown)
04357 {
04358   *aDoubleDown = mMouseDoubleDownState;
04359   return NS_OK;
04360 }
04361  
04362 //END nsISelection interface implementations
04363 
04364 #if 0
04365 #pragma mark -
04366 #endif
04367 
04368 // nsTypedSelection implementation
04369 
04370 // note: this can return a nil anchor node
04371 
04372 nsTypedSelection::nsTypedSelection(nsSelection *aList)
04373 {
04374   mFrameSelection = aList;
04375   mFixupState = PR_FALSE;
04376   mDirection = eDirNext;
04377   mAutoScrollTimer = nsnull;
04378   mScrollEventPosted = PR_FALSE;
04379   mCachedOffsetForFrame = nsnull;
04380 }
04381 
04382 
04383 nsTypedSelection::nsTypedSelection()
04384 {
04385   mFrameSelection = nsnull;
04386   mFixupState = PR_FALSE;
04387   mDirection = eDirNext;
04388   mAutoScrollTimer = nsnull;
04389   mScrollEventPosted = PR_FALSE;
04390   mCachedOffsetForFrame = nsnull;
04391 }
04392 
04393 
04394 
04395 nsTypedSelection::~nsTypedSelection()
04396 {
04397   DetachFromPresentation();
04398 }
04399   
04400 void nsTypedSelection::DetachFromPresentation() {
04401   setAnchorFocusRange(-1);
04402 
04403   if (mAutoScrollTimer) {
04404     mAutoScrollTimer->Stop();
04405     NS_RELEASE(mAutoScrollTimer);
04406   }
04407 
04408   if (mEventQueue && mScrollEventPosted) {
04409     mEventQueue->RevokeEvents(this);
04410     mScrollEventPosted = PR_FALSE;
04411   }
04412 
04413   if (mCachedOffsetForFrame) {
04414     delete mCachedOffsetForFrame;
04415     mCachedOffsetForFrame = nsnull;
04416   }
04417 
04418   mFrameSelection = nsnull;
04419 }
04420 
04421 
04422 // QueryInterface implementation for nsRange
04423 NS_INTERFACE_MAP_BEGIN(nsTypedSelection)
04424   NS_INTERFACE_MAP_ENTRY(nsISelection)
04425   NS_INTERFACE_MAP_ENTRY(nsISelection2)
04426   NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
04427   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
04428   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
04429   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(Selection)
04430 NS_INTERFACE_MAP_END
04431 
04432 
04433 NS_IMPL_ADDREF(nsTypedSelection)
04434 NS_IMPL_RELEASE(nsTypedSelection)
04435 
04436 
04437 NS_IMETHODIMP
04438 nsTypedSelection::SetPresShell(nsIPresShell *aPresShell)
04439 {
04440   mPresShellWeak = do_GetWeakReference(aPresShell);
04441   return NS_OK;
04442 }
04443 
04444 
04445 
04446 NS_IMETHODIMP
04447 nsTypedSelection::GetAnchorNode(nsIDOMNode** aAnchorNode)
04448 {
04449   if (!aAnchorNode)
04450     return NS_ERROR_NULL_POINTER;
04451   *aAnchorNode = nsnull;
04452   if(!mAnchorFocusRange)
04453     return NS_OK;
04454    
04455   nsresult result;
04456   if (GetDirection() == eDirNext){
04457     result = mAnchorFocusRange->GetStartContainer(aAnchorNode);
04458   }
04459   else{
04460     result = mAnchorFocusRange->GetEndContainer(aAnchorNode);
04461   }
04462   return result;
04463 }
04464 
04465 NS_IMETHODIMP
04466 nsTypedSelection::GetAnchorOffset(PRInt32* aAnchorOffset)
04467 {
04468   if (!aAnchorOffset)
04469     return NS_ERROR_NULL_POINTER;
04470   *aAnchorOffset = nsnull;
04471   if(!mAnchorFocusRange)
04472     return NS_OK;
04473 
04474   nsresult result;
04475   if (GetDirection() == eDirNext){
04476     result = mAnchorFocusRange->GetStartOffset(aAnchorOffset);
04477   }
04478   else{
04479     result = mAnchorFocusRange->GetEndOffset(aAnchorOffset);
04480   }
04481   return result;
04482 }
04483 
04484 // note: this can return a nil focus node
04485 NS_IMETHODIMP
04486 nsTypedSelection::GetFocusNode(nsIDOMNode** aFocusNode)
04487 {
04488   if (!aFocusNode)
04489     return NS_ERROR_NULL_POINTER;
04490   *aFocusNode = nsnull;
04491   if(!mAnchorFocusRange)
04492     return NS_OK;
04493 
04494   nsresult result;
04495   if (GetDirection() == eDirNext){
04496     result = mAnchorFocusRange->GetEndContainer(aFocusNode);
04497   }
04498   else{
04499     result = mAnchorFocusRange->GetStartContainer(aFocusNode);
04500   }
04501 
04502   return result;
04503 }
04504 
04505 NS_IMETHODIMP nsTypedSelection::GetFocusOffset(PRInt32* aFocusOffset)
04506 {
04507   if (!aFocusOffset)
04508     return NS_ERROR_NULL_POINTER;
04509   *aFocusOffset = nsnull;
04510   if(!mAnchorFocusRange)
04511     return NS_OK;
04512 
04513    nsresult result;
04514   if (GetDirection() == eDirNext){
04515     result = mAnchorFocusRange->GetEndOffset(aFocusOffset);
04516   }
04517   else{
04518     result = mAnchorFocusRange->GetStartOffset(aFocusOffset);
04519   }
04520   return result;
04521 }
04522 
04523 
04524 void nsTypedSelection::setAnchorFocusRange(PRInt32 indx)
04525 {
04526   if (indx >= (PRInt32)mRanges.Length())
04527     return;
04528   if (indx < 0) //release all
04529   {
04530     mAnchorFocusRange = nsnull;
04531   }
04532   else{
04533     mAnchorFocusRange = mRanges[indx].mRange;
04534   }
04535 }
04536 
04537 
04538 
04539 nsIDOMNode*
04540 nsTypedSelection::FetchAnchorNode()
04541 {  //where did the selection begin
04542   nsCOMPtr<nsIDOMNode>returnval;
04543   GetAnchorNode(getter_AddRefs(returnval));//this queries
04544   return returnval;
04545 }//at end it will release, no addreff was called
04546 
04547 
04548 
04549 PRInt32
04550 nsTypedSelection::FetchAnchorOffset()
04551 {
04552   PRInt32 returnval;
04553   if (NS_SUCCEEDED(GetAnchorOffset(&returnval)))//this queries
04554     return returnval;
04555   return 0;
04556 }
04557 
04558 
04559 
04560 nsIDOMNode*
04561 nsTypedSelection::FetchOriginalAnchorNode()  //where did the ORIGINAL selection begin
04562 {
04563   nsCOMPtr<nsIDOMNode>returnval;
04564   PRInt32 unused;
04565   GetOriginalAnchorPoint(getter_AddRefs(returnval),  &unused);//this queries
04566   return returnval;
04567 }
04568 
04569 
04570 
04571 PRInt32
04572 nsTypedSelection::FetchOriginalAnchorOffset()
04573 {
04574   nsCOMPtr<nsIDOMNode>unused;
04575   PRInt32 returnval;
04576   if (NS_SUCCEEDED(GetOriginalAnchorPoint(getter_AddRefs(unused), &returnval)))//this queries
04577     return returnval;
04578   return NS_OK;
04579 }
04580 
04581 
04582 
04583 nsIDOMNode*
04584 nsTypedSelection::FetchFocusNode()
04585 {   //where is the carret
04586   nsCOMPtr<nsIDOMNode>returnval;
04587   GetFocusNode(getter_AddRefs(returnval));//this queries
04588   return returnval;
04589 }//at end it will release, no addreff was called
04590 
04591 
04592 
04593 PRInt32
04594 nsTypedSelection::FetchFocusOffset()
04595 {
04596   PRInt32 returnval;
04597   if (NS_SUCCEEDED(GetFocusOffset(&returnval)))//this queries
04598     return returnval;
04599   return NS_OK;
04600 }
04601 
04602 
04603 
04604 nsIDOMNode*
04605 nsTypedSelection::FetchStartParent(nsIDOMRange *aRange)   //skip all the com stuff and give me the start/end
04606 {
04607   if (!aRange)
04608     return nsnull;
04609   nsCOMPtr<nsIDOMNode> returnval;
04610   aRange->GetStartContainer(getter_AddRefs(returnval));
04611   return returnval;
04612 }
04613 
04614 
04615 
04616 PRInt32
04617 nsTypedSelection::FetchStartOffset(nsIDOMRange *aRange)
04618 {
04619   if (!aRange)
04620     return nsnull;
04621   PRInt32 returnval;
04622   if (NS_SUCCEEDED(aRange->GetStartOffset(&returnval)))
04623     return returnval;
04624   return 0;
04625 }
04626 
04627 
04628 
04629 nsIDOMNode*
04630 nsTypedSelection::FetchEndParent(nsIDOMRange *aRange)     //skip all the com stuff and give me the start/end
04631 {
04632   if (!aRange)
04633     return nsnull;
04634   nsCOMPtr<nsIDOMNode> returnval;
04635   aRange->GetEndContainer(getter_AddRefs(returnval));
04636   return returnval;
04637 }
04638 
04639 
04640 
04641 PRInt32
04642 nsTypedSelection::FetchEndOffset(nsIDOMRange *aRange)
04643 {
04644   if (!aRange)
04645     return nsnull;
04646   PRInt32 returnval;
04647   if (NS_SUCCEEDED(aRange->GetEndOffset(&returnval)))
04648     return returnval;
04649   return 0;
04650 }
04651 
04652 static nsresult
04653 CompareToRangeStart(nsIDOMNode* aCompareNode, PRInt32 aCompareOffset,
04654                     nsIDOMRange* aRange, PRInt32* cmp)
04655 {
04656   nsCOMPtr<nsIDOMNode> startNode;
04657   nsresult rv = aRange->GetStartContainer(getter_AddRefs(startNode));
04658   NS_ENSURE_SUCCESS(rv, rv);
04659 
04660   PRInt32 startOffset;
04661   rv = aRange->GetStartOffset(&startOffset);
04662   NS_ENSURE_SUCCESS(rv, rv);
04663 
04664   *cmp = nsRange::ComparePoints(aCompareNode, aCompareOffset,
04665                                 startNode, startOffset);
04666   return NS_OK;
04667 }
04668 
04669 static nsresult
04670 CompareToRangeEnd(nsIDOMNode* aCompareNode, PRInt32 aCompareOffset,
04671                   nsIDOMRange* aRange, PRInt32* cmp)
04672 {
04673   nsCOMPtr<nsIDOMNode> endNode;
04674   nsresult rv = aRange->GetEndContainer(getter_AddRefs(endNode));
04675   NS_ENSURE_SUCCESS(rv, rv);
04676 
04677   PRInt32 endOffset;
04678   rv = aRange->GetEndOffset(&endOffset);
04679   NS_ENSURE_SUCCESS(rv, rv);
04680 
04681   *cmp = nsRange::ComparePoints(aCompareNode, aCompareOffset,
04682                                 endNode, endOffset);
04683   return NS_OK;
04684 }
04685 
04686 #ifdef DEBUG
04687 // checks the indices to make sure the range bookkeeping is up-to-date, useful
04688 // for debugging to make sure everything is consistent
04689 PRBool
04690 nsTypedSelection::ValidateRanges()
04691 {
04692   if (mRanges.Length() != mRangeEndings.Length()) {
04693     NS_NOTREACHED("Lengths don't match");
04694     return PR_FALSE;
04695   }
04696 
04697   // check the main array and make sure they refer to the correct sorted ones
04698   PRUint32 i;
04699   for (i = 0; i < mRanges.Length(); i ++) {
04700     if (mRanges[i].mEndIndex < 0 ||
04701         mRanges[i].mEndIndex >= (PRInt32)mRangeEndings.Length()) {
04702       NS_NOTREACHED("Sorted index is out of bounds");
04703       return PR_FALSE;
04704     }
04705     if (mRangeEndings[mRanges[i].mEndIndex] != (PRInt32)i) {
04706       NS_NOTREACHED("Beginning or ending isn't correct");
04707       return PR_FALSE;
04708     }
04709   }
04710 
04711   // check the other array to make sure they all refer to valid indices
04712   for (i = 0; i < mRangeEndings.Length(); i ++) {
04713     if (mRangeEndings[i] < 0 ||
04714         mRangeEndings[i] >= (PRInt32)mRanges.Length()) {
04715       NS_NOTREACHED("Ending refers to invalid index");
04716       return PR_FALSE;
04717     }
04718   }
04719 
04720   return PR_TRUE;
04721 }
04722 #endif
04723 
04724 // nsTypedSelection::FindInsertionPoint
04725 //
04726 //    Binary searches the sorted array of ranges for the insertion point for
04727 //    the given node/offset. The given comparator is used, and the index where
04728 //    the point should appear in the array is placed in *aInsertionPoint;
04729 //
04730 //    If the remapping array is given, we'll find the position in that array
04731 //    for the insertion, where that array contains indices that reference
04732 //    the range array. This can be NULL to not use a remapping array.
04733 //
04734 //    If there is item(s) in the array equal to the input point, we will return
04735 //    a random index between or adjacent to this item(s).
04736 
04737 nsresult
04738 nsTypedSelection::FindInsertionPoint(
04739     const nsTArray<PRInt32>* aRemappingArray,
04740     nsIDOMNode* aPointNode, PRInt32 aPointOffset,
04741     nsresult (*aComparator)(nsIDOMNode*,PRInt32,nsIDOMRange*,PRInt32*),
04742     PRInt32* aInsertionPoint)
04743 {
04744   nsresult rv;
04745   NS_ASSERTION(!aRemappingArray || aRemappingArray->Length() == mRanges.Length(),
04746                "Remapping array must have the same entries as the range array");
04747 
04748   PRInt32 beginSearch = 0;
04749   PRInt32 endSearch = mRanges.Length(); // one beyond what to check
04750   while (endSearch - beginSearch > 0) {
04751     PRInt32 center = (endSearch - beginSearch) / 2 + beginSearch;
04752 
04753     nsIDOMRange* range;
04754     if (aRemappingArray)
04755       range = mRanges[(*aRemappingArray)[center]].mRange;
04756     else
04757       range = mRanges[center].mRange;
04758 
04759     PRInt32 cmp;
04760     rv = aComparator(aPointNode, aPointOffset, range, &cmp);
04761     NS_ENSURE_SUCCESS(rv, rv);
04762 
04763     if (cmp < 0) {        // point < cur
04764       endSearch = center;
04765     } else if (cmp > 0) { // point > cur
04766       beginSearch = center + 1;
04767     } else {              // found match, done
04768       beginSearch = center;
04769       break;
04770     }
04771   }
04772   *aInsertionPoint = beginSearch;
04773   return NS_OK;
04774 }
04775 
04776 nsresult
04777 nsTypedSelection::AddItem(nsIDOMRange *aItem)
04778 {
04779   nsresult rv;
04780   if (!aItem)
04781     return NS_ERROR_NULL_POINTER;
04782 
04783   NS_ASSERTION(ValidateRanges(), "Ranges out of sync");
04784 
04785   // a common case is that we have no ranges yet
04786   if (mRanges.Length() == 0) {
04787     if (! mRanges.AppendElement(RangeData(aItem, 0)))
04788       return NS_ERROR_OUT_OF_MEMORY;
04789     if (! mRangeEndings.AppendElement(0)) {
04790       mRanges.Clear();
04791       return NS_ERROR_OUT_OF_MEMORY;
04792     }
04793     return NS_OK;
04794   }
04795 
04796   nsCOMPtr<nsIDOMNode> beginNode;
04797   PRInt32 beginOffset;
04798   rv = aItem->GetStartContainer(getter_AddRefs(beginNode));
04799   NS_ENSURE_SUCCESS(rv, rv);
04800   rv = aItem->GetStartOffset(&beginOffset);
04801   NS_ENSURE_SUCCESS(rv, rv);
04802 
04803   PRInt32 beginInsertionPoint;
04804   rv = FindInsertionPoint(nsnull, beginNode, beginOffset,
04805                           CompareToRangeStart, &beginInsertionPoint);
04806   NS_ENSURE_SUCCESS(rv, rv);
04807 
04808   // XXX Performance: 99% of the time, the beginning array and the ending array
04809   // will be the same because the ranges do not overlap. We could save a few
04810   // compares (which can be expensive) in this common case by special casing
04811   // this.
04812 
04813   nsCOMPtr<nsIDOMNode> endNode;
04814   PRInt32 endOffset;
04815   rv = aItem->GetEndContainer(getter_AddRefs(endNode));
04816   NS_ENSURE_SUCCESS(rv, rv);
04817   rv = aItem->GetEndOffset(&endOffset);
04818   NS_ENSURE_SUCCESS(rv, rv);
04819 
04820   // make sure that this range is not already in the selection
04821   if (FindRangeGivenPoint(beginNode, beginOffset, endNode, endOffset,
04822                           beginInsertionPoint)) {
04823     // silently succeed, this range is already in the selection
04824     return NS_OK;
04825   }
04826 
04827   PRInt32 endInsertionPoint;
04828   rv = FindInsertionPoint(&mRangeEndings, endNode, endOffset,
04829                           CompareToRangeEnd, &endInsertionPoint);
04830   NS_ENSURE_SUCCESS(rv, rv);
04831 
04832   // insert the range, being careful to revert everything on error to keep
04833   // consistency
04834   if (! mRanges.InsertElementAt(beginInsertionPoint,
04835         RangeData(aItem, endInsertionPoint))) {
04836     return NS_ERROR_OUT_OF_MEMORY;
04837   }
04838   if (! mRangeEndings.InsertElementAt(endInsertionPoint, beginInsertionPoint)) {
04839     mRanges.RemoveElementAt(beginInsertionPoint);
04840     return NS_ERROR_OUT_OF_MEMORY;
04841   }
04842 
04843   // adjust the end indices that point to the main list
04844   PRUint32 i;
04845   for (i = 0; i < mRangeEndings.Length(); i ++) {
04846     if (mRangeEndings[i] >= beginInsertionPoint)
04847       mRangeEndings[i] ++;
04848   }
04849 
04850   // the last loop updated the inserted index as well, so we need to put it
04851   // back (this saves a comparison in that loop)
04852   mRangeEndings[endInsertionPoint] = beginInsertionPoint;
04853 
04854   // adjust the begin/end indices
04855   for (i = endInsertionPoint + 1; i < mRangeEndings.Length(); i ++)
04856     mRanges[mRangeEndings[i]].mEndIndex = i;
04857 
04858   NS_ASSERTION(ValidateRanges(), "Ranges out of sync");
04859   return NS_OK;
04860 }
04861 
04862 nsresult
04863 nsTypedSelection::RemoveItem(nsIDOMRange *aItem)
04864 {
04865   if (!aItem)
04866     return NS_ERROR_NULL_POINTER;
04867   NS_ASSERTION(ValidateRanges(), "Ranges out of sync");
04868 
04869   // Find the range's index & remove it. We could use FindInsertionPoint to
04870   // get O(log n) time, but that requires many expensive DOM comparisons.
04871   // For even several thousand items, this is probably faster because the
04872   // comparisons are so fast.
04873   PRInt32 idx = -1;
04874   PRUint32 i;
04875   for (i = 0; i < mRanges.Length(); i ++) {
04876     if (mRanges[i].mRange == aItem) {
04877       idx = (PRInt32)i;
04878       break;
04879     }
04880   }
04881   if (idx < 0)
04882     return NS_ERROR_INVALID_ARG;
04883   mRanges.RemoveElementAt(idx);
04884 
04885   // need to update the range ending list to reflect the removed item
04886   PRInt32 endingIndex = -1;
04887   for (i = 0; i < mRangeEndings.Length(); i ++) {
04888     if (mRangeEndings[i] == idx)
04889       endingIndex = i;
04890     if (mRangeEndings[i] > idx)
04891       mRangeEndings[i] --;
04892   }
04893   NS_ASSERTION(endingIndex >= 0 && endingIndex < (PRInt32)mRangeEndings.Length(),
04894                "Index not found in ending list");
04895 
04896   // remove from the sorted lists
04897   mRangeEndings.RemoveElementAt(endingIndex);
04898 
04899   // adjust indices of the RangeData structures
04900   for (i = endingIndex; i < mRangeEndings.Length(); i ++)
04901     mRanges[mRangeEndings[i]].mEndIndex = i;
04902 
04903   NS_ASSERTION(ValidateRanges(), "Ranges out of sync");
04904   return NS_OK;
04905 }
04906 
04907 
04908 nsresult
04909 nsTypedSelection::Clear(nsPresContext* aPresContext)
04910 {
04911   setAnchorFocusRange(-1);
04912 
04913   for (PRInt32 i = 0; i < (PRInt32)mRanges.Length(); i ++)
04914     selectFrames(aPresContext, mRanges[i].mRange, 0);
04915   mRanges.Clear();
04916   mRangeEndings.Clear();
04917 
04918   // Reset direction so for more dependable table selection range handling
04919   SetDirection(eDirNext);
04920 
04921   // If this was an ATTENTION selection, change it back to normal now
04922   PRInt16 displayresult = nsISelectionController::SELECTION_OFF;
04923   mFrameSelection->GetDisplaySelection(&displayresult);
04924   if (displayresult == nsISelectionController::SELECTION_ATTENTION) {
04925     mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
04926   }
04927 
04928   return NS_OK;
04929 }
04930 
04931 // nsTypedSelection::MoveIndexToFirstMatch
04932 //
04933 //    This adjusts the given index backwards in the range array so that it
04934 //    points to the first match of (node,offset). There might be any number
04935 //    of identical sequencial items in the array and the index can initially
04936 //    point to any one of them. When complete, the index will be moved to
04937 //    point to the first one of these. If the index does not point to a
04938 //    match of (node,offset) or there is only one match, the index will be
04939 //    untouched.
04940 //
04941 //    If there are multiple ranges beginning at the requested position, we'll
04942 //    get a random index in between those from FindInsertionPoint. We want the
04943 //    index to be at the first match in the array.
04944 //
04945 //    If the remapping array (sorted list containing indices into mRanges)
04946 //    is given, we'll use that for sorting and consider the index into
04947 //    that array. If NULL, we'll just use mRanges directly.
04948 //
04949 //    If aUseBeginning is set we'll compare to the range beginnings in the
04950 //    selection. If false, we'll compare to range endings.
04951 
04952 nsresult
04953 nsTypedSelection::MoveIndexToFirstMatch(PRInt32* aIndex, nsIDOMNode* aNode,
04954                                         PRInt32 aOffset,
04955                                         const nsTArray<PRInt32>* aRemappingArray,
04956                                         PRBool aUseBeginning)
04957 {
04958   nsresult rv;
04959   nsCOMPtr<nsIDOMNode> curNode;
04960   PRInt32 curOffset;
04961   while (*aIndex > 0) {
04962     nsIDOMRange* range;
04963     if (aRemappingArray)
04964       range = mRanges[(*aRemappingArray)[(*aIndex) - 1]].mRange;
04965     else
04966       range = mRanges[(*aIndex) - 1].mRange;
04967 
04968     if (aUseBeginning) {
04969       rv = range->GetStartContainer(getter_AddRefs(curNode));
04970       NS_ENSURE_SUCCESS(rv, rv);
04971       rv = range->GetStartOffset(&curOffset);
04972       NS_ENSURE_SUCCESS(rv, rv);
04973     } else {
04974       rv = range->GetEndContainer(getter_AddRefs(curNode));
04975       NS_ENSURE_SUCCESS(rv, rv);
04976       rv = range->GetEndOffset(&curOffset);
04977       NS_ENSURE_SUCCESS(rv, rv);
04978     }
04979 
04980     if (curNode != aNode)
04981       break; // not a match
04982     if (curOffset != aOffset)
04983       break; // not a match
04984 
04985     // the previous node matches, go back one
04986     (*aIndex) --;
04987   }
04988   return NS_OK;
04989 }
04990 
04991 // nsTypedSelection::MoveIndexToNextMismatch
04992 //
04993 //    The same as MoveIndexToFirstMatch but increments the index until it
04994 //    points to something that doesn't match (node,offset).
04995 //
04996 //    If there is one or more range ending at the requested position, we
04997 //    want to start checking the first one that ends inside the range. This
04998 //    moves the given index to point to the first index inside the range.
04999 //    It may be one past the last item in the array.
05000 //
05001 //    If the requested DOM position is '9', and the caller got index 3
05002 //    from FindInsertionPoint with points in the range array:
05003 //       6  8  9  9  9  13  19
05004 //                ^
05005 //                input
05006 //    The input point will point to a random '9' in the array, or possibly
05007 //    the '13', which are all valid points for a new '9' to live in this array.
05008 //    This function will move the index to point to the '13' in this case,
05009 //    which is the first offset not matching the input position.
05010 //
05011 //    See MoveIndexToFirstMatch for the meaning of aRemappingArray and
05012 //    aUseBeginning.
05013 
05014 nsresult
05015 nsTypedSelection::MoveIndexToNextMismatch(PRInt32* aIndex, nsIDOMNode* aNode,
05016                                           PRInt32 aOffset,
05017                                           const nsTArray<PRInt32>* aRemappingArray,
05018                                           PRBool aUseBeginning)
05019 {
05020   nsresult rv;
05021   nsCOMPtr<nsIDOMNode> curNode;
05022   PRInt32 curOffset;
05023   while (*aIndex < (PRInt32)mRanges.Length()) {
05024     nsIDOMRange* range;
05025     if (aRemappingArray)
05026       range = mRanges[(*aRemappingArray)[*aIndex]].mRange;
05027     else
05028       range = mRanges[*aIndex].mRange;
05029 
05030     if (aUseBeginning) {
05031       rv = range->GetStartContainer(getter_AddRefs(curNode));
05032       NS_ENSURE_SUCCESS(rv, rv);
05033       rv = range->GetStartOffset(&curOffset);
05034       NS_ENSURE_SUCCESS(rv, rv);
05035     } else {
05036       rv = range->GetEndContainer(getter_AddRefs(curNode));
05037       NS_ENSURE_SUCCESS(rv, rv);
05038       rv = range->GetEndOffset(&curOffset);
05039       NS_ENSURE_SUCCESS(rv, rv);
05040     }
05041 
05042     if (curNode != aNode)
05043       break; // mismatch
05044     if (curOffset != aOffset)
05045       break; // mismatch
05046 
05047     // this node matches, go to the next one
05048     (*aIndex) ++;
05049   }
05050   return NS_OK;
05051 }
05052 
05053 // nsTypedSelection::GetRangesForInterval
05054 //
05055 //    XPCOM wrapper for the COMArray version
05056 
05057 NS_IMETHODIMP
05058 nsTypedSelection::GetRangesForInterval(nsIDOMNode* aBeginNode, PRInt32 aBeginOffset,
05059                                        nsIDOMNode* aEndNode, PRInt32 aEndOffset,
05060                                        PRBool aAllowAdjacent,
05061                                        PRUint32 *aResultCount,
05062                                        nsIDOMRange ***aResults)
05063 {
05064   nsresult rv;
05065   *aResultCount = 0;
05066   *aResults = nsnull;
05067   if (! aBeginNode || ! aEndNode || ! aResultCount || ! aResults)
05068     return NS_ERROR_NULL_POINTER;
05069 
05070   nsCOMArray<nsIDOMRange> results;
05071   rv = GetRangesForIntervalCOMArray(aBeginNode, aBeginOffset,
05072                                     aEndNode, aEndOffset,
05073                                     aAllowAdjacent,
05074                                     &results);
05075   NS_ENSURE_SUCCESS(rv, rv);
05076   if (results.Count() == 0)
05077     return NS_OK;
05078 
05079   *aResults = NS_STATIC_CAST(nsIDOMRange**,
05080       nsMemory::Alloc(sizeof(nsIDOMRange*) * results.Count()));
05081   NS_ENSURE_TRUE(*aResults, NS_ERROR_OUT_OF_MEMORY);
05082 
05083   *aResultCount = results.Count();
05084   for (PRInt32 i = 0; i < results.Count(); i ++)
05085     NS_ADDREF((*aResults)[i] = results[i]);
05086   return NS_OK;
05087 }
05088 
05089 // nsTypedSelection::GetRangesForIntervalCOMArray
05090 //
05091 //    Fills a COM array with the ranges overlapping the range specified by
05092 //    the given endpoints. Ranges in the selection exactly adjacent to the
05093 //    input range are not returned unless aAllowAdjacent is set.
05094 
05095 NS_IMETHODIMP
05096 nsTypedSelection::GetRangesForIntervalCOMArray(nsIDOMNode* aBeginNode, PRInt32 aBeginOffset,
05097                                                nsIDOMNode* aEndNode, PRInt32 aEndOffset,
05098                                                PRBool aAllowAdjacent,
05099                                                nsCOMArray<nsIDOMRange>* aRanges)
05100 {
05101   nsresult rv;
05102   NS_ASSERTION(ValidateRanges(), "Ranges out of sync");
05103   aRanges->Clear();
05104   if (mRanges.Length() == 0)
05105     return NS_OK;
05106 
05107   // Ranges that begin after the checked range, and ranges that end before
05108   // the checked range can be discarded. The beginning index is the offset
05109   // into the beginning array of the first item we DON'T have to check.
05110 
05111   // ...index into the beginning array that is the FIRST ITEM OUTSIDE
05112   //    OUR RANGE
05113   PRInt32 beginningIndex;
05114   rv = FindInsertionPoint(nsnull, aEndNode, aEndOffset,
05115                           &CompareToRangeStart, &beginningIndex);
05116   NS_ENSURE_SUCCESS(rv, rv);
05117   if (beginningIndex == 0)
05118     return NS_OK; // optimization: all ranges are after us
05119 
05120   // ...index into the ending array that is the FIRST ITEM WE WANT TO CHECK
05121   PRInt32 endingIndex;
05122   rv = FindInsertionPoint(&mRangeEndings, aBeginNode, aBeginOffset,
05123                           &CompareToRangeEnd, &endingIndex);
05124   NS_ENSURE_SUCCESS(rv, rv);
05125   if (endingIndex == (PRInt32)mRangeEndings.Length())
05126     return NS_OK; // optimization: all ranges are before us
05127 
05128   // adjust the indices in case of exact matches, FindInsertionPoint will
05129   // give us a random index that would still be sorted if there is a match
05130   if (aAllowAdjacent) {
05131     // include adjacent points
05132     //
05133     // Recall that we want all things in mRangeEndings (indexed by endingIndex)
05134     // before the requested beginning, and everything in mRanges (indexed by
05135     // beginningIndex) after the requested ending.
05136     //
05137     // 1 3 5 5 5 8 9 10 10 10 11 12  <-- imaginary DOM positions of ranges
05138     //       ^          ^            <-- we have this
05139     //     ^                  ^      <-- compute this range
05140     //  endingIndex   beginningIndex
05141     rv = MoveIndexToFirstMatch(&endingIndex, aBeginNode, aBeginOffset,
05142                                &mRangeEndings, PR_FALSE);
05143     NS_ENSURE_SUCCESS(rv, rv);
05144     rv = MoveIndexToNextMismatch(&beginningIndex, aEndNode, aEndOffset,
05145                                  nsnull, PR_TRUE);
05146     NS_ENSURE_SUCCESS(rv, rv);
05147   } else {
05148     // exclude adjacent points, see previous case
05149     // 1 3 5 5 5 8 9 10 10 10 11 12  <-- imaginary DOM positions of ranges
05150     //       ^          ^            <-- we have this
05151     //           ^   ^               <-- compute this range
05152     // endingIndex   beginningIndex
05153     rv = MoveIndexToNextMismatch(&endingIndex, aBeginNode, aBeginOffset,
05154                                  &mRangeEndings, PR_FALSE);
05155     NS_ENSURE_SUCCESS(rv, rv);
05156     rv = MoveIndexToFirstMatch(&beginningIndex, aEndNode, aEndOffset,
05157                                nsnull, PR_TRUE);
05158     NS_ENSURE_SUCCESS(rv, rv);
05159   }
05160 
05161   // check for overlaps in the two ranges by linearly searching the smallest
05162   // set of matches
05163   if (beginningIndex > (PRInt32)mRangeEndings.Length() - endingIndex) {
05164     // check ending array because its smaller
05165     for (PRInt32 i = endingIndex; i < (PRInt32)mRangeEndings.Length(); i ++) {
05166       if (mRangeEndings[i] < beginningIndex) {
05167         if (! aRanges->AppendObject(mRanges[mRangeEndings[i]].mRange))
05168           return NS_ERROR_OUT_OF_MEMORY;
05169       }
05170     }
05171   } else {
05172     for (PRInt32 i = 0; i < beginningIndex; i ++) {
05173       if (mRanges[i].mEndIndex >= endingIndex) {
05174         if (! aRanges->AppendObject(mRanges[i].mRange))
05175           return NS_ERROR_OUT_OF_MEMORY;
05176       }
05177     }
05178   }
05179   return NS_OK;
05180 }
05181 
05182 // RangeMatches*Point
05183 //
05184 //    Compares the range beginning or ending point, and returns true if it
05185 //    exactly matches the given DOM point.
05186 
05187 static PRBool
05188 RangeMatchesBeginPoint(nsIDOMRange* aRange, nsIDOMNode* aNode, PRInt32 aOffset)
05189 {
05190   PRInt32 offset;
05191   nsresult rv = aRange->GetStartOffset(&offset);
05192   if (NS_FAILED(rv) || offset != aOffset)
05193     return PR_FALSE;
05194 
05195   nsCOMPtr<nsIDOMNode> node;
05196   rv = aRange->GetStartContainer(getter_AddRefs(node));
05197   if (NS_FAILED(rv) || node != aNode)
05198     return PR_FALSE;
05199 
05200   return PR_TRUE;
05201 }
05202 
05203 static PRBool
05204 RangeMatchesEndPoint(nsIDOMRange* aRange, nsIDOMNode* aNode, PRInt32 aOffset)
05205 {
05206   PRInt32 offset;
05207   nsresult rv = aRange->GetEndOffset(&offset);
05208   if (NS_FAILED(rv) || offset != aOffset)
05209     return PR_FALSE;
05210 
05211   nsCOMPtr<nsIDOMNode> node;
05212   rv = aRange->GetEndContainer(getter_AddRefs(node));
05213   if (NS_FAILED(rv) || node != aNode)
05214     return PR_FALSE;
05215 
05216   return PR_TRUE;
05217 }
05218 
05219 
05220 // nsTypedSelection::FindRangeGivenPoint
05221 //
05222 //    Searches for the range matching the exact given range points. We search
05223 //    in the array of beginnings, and start from the given index. This index
05224 //    should be the result of FindInsertionPoint, which will return any index
05225 //    within a range of identical ones.
05226 //
05227 //    Therefore, this function searches backwards and forwards from that point
05228 //    of all matching beginning points, and then compares the ending points to
05229 //    find a match. Returns true if a match was found, false if not.
05230 
05231 PRBool
05232 nsTypedSelection::FindRangeGivenPoint(
05233     nsIDOMNode* aBeginNode, PRInt32 aBeginOffset,
05234     nsIDOMNode* aEndNode, PRInt32 aEndOffset,
05235     PRInt32 aStartSearchingHere)
05236 {
05237   PRInt32 i;
05238   NS_ASSERTION(aStartSearchingHere >= 0 && aStartSearchingHere <= (PRInt32)mRanges.Length(),
05239                "Input searching seed is not in range.");
05240 
05241   // search backwards for a begin match
05242   for (i = aStartSearchingHere; i >= 0 && i < (PRInt32)mRanges.Length(); i --) {
05243     if (RangeMatchesBeginPoint(mRanges[i].mRange, aBeginNode, aBeginOffset)) {
05244       if (RangeMatchesEndPoint(mRanges[i].mRange, aEndNode, aEndOffset))
05245         return PR_TRUE;
05246     } else {
05247       // done with matches going backwards
05248       break;
05249     }
05250   }
05251 
05252   // search forwards for a begin match
05253   for (i = aStartSearchingHere + 1; i < (PRInt32)mRanges.Length(); i ++) {
05254     if (RangeMatchesBeginPoint(mRanges[i].mRange, aBeginNode, aBeginOffset)) {
05255       if (RangeMatchesEndPoint(mRanges[i].mRange, aEndNode, aEndOffset))
05256         return PR_TRUE;
05257     } else {
05258       // done with matches going forwards
05259       break;
05260     }
05261   }
05262 
05263   // match not found
05264   return PR_FALSE;
05265 }
05266 
05267 //utility method to get the primary frame of node or use the offset to get frame of child node
05268 
05269 #if 0
05270 NS_IMETHODIMP
05271 nsTypedSelection::GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, PRBool aIsEndNode, nsIFrame **aReturnFrame)
05272 {
05273   if (!aNode || !aReturnFrame)
05274     return NS_ERROR_NULL_POINTER;
05275   
05276   if (aOffset < 0)
05277     return NS_ERROR_FAILURE;
05278 
05279   *aReturnFrame = 0;
05280   
05281   nsresult  result = NS_OK;
05282   
05283   nsCOMPtr<nsIDOMNode> node = aNode;
05284 
05285   if (!node)
05286     return NS_ERROR_NULL_POINTER;
05287   
05288   nsCOMPtr<nsIContent> content = do_QueryInterface(node, &result);
05289 
05290   if (NS_FAILED(result))
05291     return result;
05292 
05293   if (!content)
05294     return NS_ERROR_NULL_POINTER;
05295   
05296   if (content->IsContentOfType(nsIContent::eELEMENT))
05297   {
05298     if (aIsEndNode)
05299       aOffset--;
05300 
05301     if (aOffset >= 0)
05302     {
05303       nsIContent *child = content->GetChildAt(aOffset);
05304       if (!child) //out of bounds?
05305         return NS_ERROR_FAILURE;
05306 
05307       content = child; // releases the focusnode
05308     }
05309   }
05310   result = mFrameSelection->GetShell()->GetPrimaryFrameFor(content,aReturnFrame);
05311   return result;
05312 }
05313 #endif
05314 
05315 
05316 NS_IMETHODIMP
05317 nsTypedSelection::GetPrimaryFrameForAnchorNode(nsIFrame **aReturnFrame)
05318 {
05319   if (!aReturnFrame)
05320     return NS_ERROR_NULL_POINTER;
05321   
05322   PRInt32 frameOffset = 0;
05323   *aReturnFrame = 0;
05324   nsCOMPtr<nsIContent> content = do_QueryInterface(FetchAnchorNode());
05325   if (content && mFrameSelection)
05326   {
05327     nsIFrameSelection::HINT hint;
05328     mFrameSelection->GetHint(&hint);
05329     return mFrameSelection->GetFrameForNodeOffset(content, FetchAnchorOffset(),hint,aReturnFrame, &frameOffset);
05330   }
05331   return NS_ERROR_FAILURE;
05332 }
05333 
05334 NS_IMETHODIMP
05335 nsTypedSelection::GetPrimaryFrameForFocusNode(nsIFrame **aReturnFrame, PRInt32 *aOffsetUsed)
05336 {
05337   if (!aReturnFrame)
05338     return NS_ERROR_NULL_POINTER;
05339   
05340   nsCOMPtr<nsIContent> content = do_QueryInterface(FetchFocusNode());
05341   if (!content || !mFrameSelection)
05342     return NS_ERROR_FAILURE;
05343   
05344   nsIPresShell *presShell = mFrameSelection->GetShell();
05345 
05346   nsCOMPtr<nsICaret> caret;
05347   nsresult result = presShell->GetCaret(getter_AddRefs(caret));
05348   if (NS_FAILED(result) || !caret)
05349     return NS_ERROR_FAILURE;
05350 
05351   PRInt32 frameOffset = 0;
05352   *aReturnFrame = 0;
05353   if (!aOffsetUsed)
05354     aOffsetUsed = &frameOffset;
05355 
05356   nsIFrameSelection::HINT hint;
05357   mFrameSelection->GetHint(&hint);
05358   
05359   PRUint8 caretBidiLevel;
05360   presShell->GetCaretBidiLevel(&caretBidiLevel);
05361 
05362   return caret->GetCaretFrameForNodeOffset(content, FetchFocusOffset(), hint, caretBidiLevel,
05363                                            aReturnFrame, aOffsetUsed);
05364 }
05365 
05366 
05367 
05368 //select all content children of aContent
05369 NS_IMETHODIMP
05370 nsTypedSelection::selectFrames(nsPresContext* aPresContext,
05371                              nsIContentIterator *aInnerIter,
05372                              nsIContent *aContent,
05373                              nsIDOMRange *aRange,
05374                              nsIPresShell *aPresShell,
05375                              PRBool aFlags)
05376 {
05377   if (!mFrameSelection)
05378     return NS_OK;//nothing to do
05379   nsresult result;
05380   if (!aInnerIter)
05381     return NS_ERROR_NULL_POINTER;
05382 #ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
05383   nsCOMPtr<nsIGeneratedContentIterator> genericiter = do_QueryInterface(aInnerIter);
05384   if (genericiter && aPresShell)
05385   {
05386     result = genericiter->Init(aPresShell,aContent);
05387   }
05388   else
05389 #endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
05390     result = aInnerIter->Init(aContent);
05391   nsIFrame *frame;
05392   if (NS_SUCCEEDED(result))
05393   {
05394     // First select frame of content passed in
05395     result = mFrameSelection->GetShell()->GetPrimaryFrameFor(aContent, &frame);
05396     if (NS_SUCCEEDED(result) && frame)
05397     {
05398       //NOTE: eSpreadDown is now IGNORED. Selected state is set only for given frame
05399       frame->SetSelected(aPresContext, nsnull, aFlags, eSpreadDown);
05400 #ifndef OLD_TABLE_SELECTION
05401       PRBool tablesel;
05402       mFrameSelection->GetTableCellSelection(&tablesel);
05403       if (tablesel)
05404       {
05405         nsITableCellLayout *tcl = nsnull;
05406         CallQueryInterface(frame, &tcl);
05407         if (tcl)
05408         {
05409           return NS_OK;
05410         }
05411       }
05412 #endif //OLD_TABLE_SELECTION
05413     }
05414     // Now iterated through the child frames and set them
05415     while (!aInnerIter->IsDone())
05416     {
05417       nsIContent *innercontent = aInnerIter->GetCurrentNode();
05418 
05419       result = mFrameSelection->GetShell()->GetPrimaryFrameFor(innercontent, &frame);
05420       if (NS_SUCCEEDED(result) && frame)
05421       {
05422         //NOTE: eSpreadDown is now IGNORED. Selected state is set only
05423         //for given frame
05424 
05425         //spread from here to hit all frames in flow
05426         frame->SetSelected(aPresContext, nsnull,aFlags,eSpreadDown);
05427         nsRect frameRect = frame->GetRect();
05428 
05429         //if a rect is 0 height/width then try to notify next
05430         //available in flow of selection status.
05431         while (!frameRect.width || !frameRect.height)
05432         {
05433           //try to notify next in flow that its content is selected.
05434           frame = frame->GetNextInFlow();
05435           if (frame)
05436           {
05437             frameRect = frame->GetRect();
05438             frame->SetSelected(aPresContext, nsnull,aFlags,eSpreadDown);
05439           }
05440           else
05441             break;
05442         }
05443         //if the frame is splittable and this frame is 0,0 then set
05444         //the next in flow frame to be selected also
05445       }
05446 
05447       aInnerIter->Next();
05448     }
05449 
05450 #if 0
05451     result = mFrameSelection->GetShell()->GetPrimaryFrameFor(content, &frame);
05452     if (NS_SUCCEEDED(result) && frame)
05453       frame->SetSelected(aRange,aFlags,eSpreadDown);//spread from here to hit all frames in flow
05454 #endif
05455 
05456     return NS_OK;
05457   }
05458 
05459   return NS_ERROR_FAILURE;
05460 }
05461 
05462 
05463 
05464 //the idea of this helper method is to select, deselect "top to bottom" traversing through the frames
05465 NS_IMETHODIMP
05466 nsTypedSelection::selectFrames(nsPresContext* aPresContext, nsIDOMRange *aRange, PRBool aFlags)
05467 {
05468   if (!mFrameSelection)
05469     return NS_OK;//nothing to do
05470   if (!aRange || !aPresContext) 
05471     return NS_ERROR_NULL_POINTER;
05472 
05473   nsresult result;
05474   nsCOMPtr<nsIContentIterator> iter = do_CreateInstance(
05475 #ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
05476                                               kCGenSubtreeIteratorCID,
05477 #else
05478                                               kCSubtreeIteratorCID,
05479 #endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
05480                                               &result);
05481   if (NS_FAILED(result))
05482     return result;
05483 
05484   nsCOMPtr<nsIContentIterator> inneriter = do_CreateInstance(
05485 #ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
05486                                               kCGenContentIteratorCID,
05487 #else
05488                                               kCContentIteratorCID,
05489 #endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
05490                                               &result);
05491 
05492   if ((NS_SUCCEEDED(result)) && iter && inneriter)
05493   {
05494     nsIPresShell *presShell = aPresContext->GetPresShell();
05495 #ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
05496     nsCOMPtr<nsIGeneratedContentIterator> genericiter = do_QueryInterface(iter);
05497     if (genericiter && presShell)
05498       result = genericiter->Init(presShell,aRange);
05499     else
05500 #endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
05501       result = iter->Init(aRange);
05502 
05503     // loop through the content iterator for each content node
05504     // for each text node:
05505     // get the frame for the content, and from it the style context
05506     // ask the style context about the property
05507     nsCOMPtr<nsIContent> content;
05508     nsIFrame *frame;
05509 //we must call first one explicitly
05510     content = do_QueryInterface(FetchStartParent(aRange), &result);
05511     if (NS_FAILED(result) || !content)
05512       return result;
05513 
05514     if (!content->IsContentOfType(nsIContent::eELEMENT))
05515     {
05516       result = mFrameSelection->GetShell()->GetPrimaryFrameFor(content, &frame);
05517       if (NS_SUCCEEDED(result) && frame)
05518         frame->SetSelected(aPresContext, aRange,aFlags,eSpreadDown);//spread from here to hit all frames in flow
05519     }
05520 //end start content
05521     iter->First();
05522 
05523     while (!iter->IsDone())
05524     {
05525       content = iter->GetCurrentNode();
05526 
05527       selectFrames(aPresContext, inneriter, content, aRange, presShell,aFlags);
05528 
05529       iter->Next();
05530     }
05531 //we must now do the last one  if it is not the same as the first
05532     if (FetchEndParent(aRange) != FetchStartParent(aRange))
05533     {
05534       content = do_QueryInterface(FetchEndParent(aRange), &result);
05535       if (NS_FAILED(result) || !content)
05536         return result;
05537 
05538       if (!content->IsContentOfType(nsIContent::eELEMENT))
05539       {
05540         result = mFrameSelection->GetShell()->GetPrimaryFrameFor(content, &frame);
05541         if (NS_SUCCEEDED(result) && frame)
05542            frame->SetSelected(aPresContext, aRange,aFlags,eSpreadDown);//spread from here to hit all frames in flow
05543       }
05544     }
05545 //end end parent
05546   }
05547   return result;
05548 }
05549 
05550 // nsTypedSelection::LookUpSelection
05551 //
05552 //    This function is called when a node wants to know where the selection is
05553 //    over itself.
05554 //
05555 //    Usually, this is called when we already know there is a selection over
05556 //    the node in question, and we only need to find the boundaries of it on
05557 //    that node. This is when slowCheck is false--a strict test is not needed.
05558 //    Other times, the caller has no idea, and wants us to test everything,
05559 //    so we are supposed to determine whether there is a selection over the
05560 //    node at all.
05561 //
05562 //    A previous version of this code used this flag to do less work when
05563 //    inclusion was already known (slowCheck=false). However, our tree
05564 //    structure allows us to quickly determine ranges overlapping the node,
05565 //    so we just ignore the slowCheck flag and do the full test every time.
05566 //
05567 //    PERFORMANCE: a common case is that we are doing a fast check with exactly
05568 //    one range in the selection. In this case, this function is slower than
05569 //    brute force because of the overhead of checking the tree. We can optimize
05570 //    this case to make it faster by doing the same thing the previous version
05571 //    of this function did in the case of 1 range. This would also mean that
05572 //    the aSlowCheck flag would have meaning again.
05573 
05574 NS_IMETHODIMP
05575 nsTypedSelection::LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset,
05576                                   PRInt32 aContentLength,
05577                                   SelectionDetails **aReturnDetails,
05578                                   SelectionType aType, PRBool aSlowCheck)
05579 {
05580   nsresult rv;
05581   if (! aContent || ! aReturnDetails)
05582     return NS_ERROR_NULL_POINTER;
05583 
05584   // it is common to have no ranges, to optimize that
05585   if (mRanges.Length() == 0)
05586     return NS_OK;
05587 
05588   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aContent, &rv);
05589   NS_ENSURE_SUCCESS(rv, rv);
05590 
05591   nsCOMArray<nsIDOMRange> overlappingRanges;
05592   rv = GetRangesForIntervalCOMArray(node, aContentOffset,
05593                                     node, aContentOffset + aContentLength,
05594                                     PR_FALSE,
05595                                     &overlappingRanges);
05596   NS_ENSURE_SUCCESS(rv, rv);
05597   if (overlappingRanges.Count() == 0)
05598     return NS_OK;
05599 
05600   for (PRInt32 i = 0; i < overlappingRanges.Count(); i ++) {
05601     nsCOMPtr<nsIDOMNode> startNode, endNode;
05602     PRInt32 startOffset, endOffset;
05603     nsIDOMRange* range = overlappingRanges[i];
05604     range->GetStartContainer(getter_AddRefs(startNode));
05605     range->GetStartOffset(&startOffset);
05606     range->GetEndContainer(getter_AddRefs(endNode));
05607     range->GetEndOffset(&endOffset);
05608 
05609     PRInt32 start = -1, end = -1;
05610     if (startNode == node && endNode == node) {
05611       if (startOffset < (aContentOffset + aContentLength)  &&
05612           endOffset > aContentOffset) {
05613         // this range is totally inside the requested content range
05614         start = PR_MAX(0, startOffset - aContentOffset);
05615         end = PR_MIN(aContentLength, endOffset - aContentOffset);
05616       }
05617       // otherwise, range is inside the requested node, but does not intersect
05618       // the requested content range, so ignore it
05619     } else if (startNode == node) {
05620       if (startOffset < (aContentOffset + aContentLength)) {
05621         // the beginning of the range is inside the requested node, but the
05622         // end is outside, select everything from there to the end
05623         start = PR_MAX(0, startOffset - aContentOffset);
05624         end = aContentLength;
05625       }
05626     } else if (endNode == node) {
05627       if (endOffset > aContentOffset) {
05628         // the end of the range is inside the requested node, but the beginning
05629         // is outside, select everything from the beginning to there
05630         start = 0;
05631         end = PR_MIN(aContentLength, endOffset - aContentOffset);
05632       }
05633     } else {
05634       // this range does not begin or end in the requested node, but since
05635       // GetRangesForInterval returned this range, we know it overlaps.
05636       // Therefore, this node is enclosed in the range, and we select all
05637       // of it.
05638       start = 0;
05639       end = aContentLength;
05640     }
05641     if (start < 0)
05642       continue; // the ranges do not overlap the input range
05643 
05644     SelectionDetails* details = new SelectionDetails;
05645     if (! details)
05646       return NS_ERROR_OUT_OF_MEMORY;
05647 
05648     details->mNext = *aReturnDetails;
05649     details->mStart = start;
05650     details->mEnd = end;
05651     details->mType = aType;
05652     *aReturnDetails = details;
05653   }
05654   return NS_OK;
05655 }
05656 
05657 NS_IMETHODIMP
05658 nsTypedSelection::Repaint(nsPresContext* aPresContext)
05659 {
05660   PRInt32 arrCount = (PRInt32)mRanges.Length();
05661 
05662   if (arrCount < 1)
05663     return NS_OK;
05664 
05665   PRInt32 i;
05666   nsIDOMRange* range;
05667   
05668   for (i = 0; i < arrCount; i++)
05669   {
05670     range = mRanges[i].mRange;
05671 
05672     if (!range)
05673       return NS_ERROR_UNEXPECTED;
05674 
05675     nsresult rv = selectFrames(aPresContext, range, PR_TRUE);
05676 
05677     if (NS_FAILED(rv)) {
05678       return rv;
05679     }
05680   }
05681 
05682   return NS_OK;
05683 }
05684 
05685 NS_IMETHODIMP
05686 nsTypedSelection::GetCanCacheFrameOffset(PRBool *aCanCacheFrameOffset)
05687 { 
05688   NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
05689 
05690   if (mCachedOffsetForFrame)
05691     *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
05692   else
05693     *aCanCacheFrameOffset = PR_FALSE;
05694 
05695   return NS_OK;
05696 }
05697 
05698 NS_IMETHODIMP    
05699 nsTypedSelection::SetCanCacheFrameOffset(PRBool aCanCacheFrameOffset)
05700 {
05701   if (!mCachedOffsetForFrame) {
05702     mCachedOffsetForFrame = new CachedOffsetForFrame;
05703   }
05704 
05705   mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
05706 
05707   // clean up cached frame when turn off cache
05708   // fix bug 207936
05709   if (!aCanCacheFrameOffset) {
05710     mCachedOffsetForFrame->mLastCaretFrame = nsnull;
05711   }
05712 
05713   return NS_OK;
05714 }
05715 
05716 NS_IMETHODIMP    
05717 nsTypedSelection::GetCachedFrameOffset(nsIFrame *aFrame, PRInt32 inOffset, nsPoint& aPoint)
05718 {
05719   if (!mCachedOffsetForFrame) {
05720     mCachedOffsetForFrame = new CachedOffsetForFrame;
05721   }
05722 
05723   if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
05724       mCachedOffsetForFrame->mLastCaretFrame &&
05725       (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
05726       (inOffset == mCachedOffsetForFrame->mLastContentOffset))
05727   {
05728      // get cached frame offset
05729      aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
05730   } 
05731   else
05732   {
05733      // recalculate frame offset and cache it
05734      GetPointFromOffset(aFrame, inOffset, &aPoint);
05735      if (mCachedOffsetForFrame->mCanCacheFrameOffset) {
05736        mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
05737        mCachedOffsetForFrame->mLastCaretFrame = aFrame;
05738        mCachedOffsetForFrame->mLastContentOffset = inOffset; 
05739      }
05740   }
05741 
05742   return NS_OK;
05743 }
05744 
05745 NS_IMETHODIMP
05746 nsTypedSelection::GetFrameSelection(nsIFrameSelection **aFrameSelection) {
05747   NS_ENSURE_ARG_POINTER(aFrameSelection);
05748   *aFrameSelection = mFrameSelection;
05749   NS_ADDREF(*aFrameSelection);
05750   return NS_OK;
05751 }
05752 
05753 nsresult
05754 nsTypedSelection::StartAutoScrollTimer(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRUint32 aDelay)
05755 {
05756   nsresult result;
05757   if (!mFrameSelection)
05758     return NS_OK;//nothing to do
05759 
05760   if (!mAutoScrollTimer)
05761   {
05762     result = NS_NewAutoScrollTimer(&mAutoScrollTimer);
05763 
05764     if (NS_FAILED(result))
05765       return result;
05766 
05767     if (!mAutoScrollTimer)
05768       return NS_ERROR_OUT_OF_MEMORY;
05769 
05770     result = mAutoScrollTimer->Init(mFrameSelection, this);
05771 
05772     if (NS_FAILED(result))
05773       return result;
05774   }
05775 
05776   result = mAutoScrollTimer->SetDelay(aDelay);
05777 
05778   if (NS_FAILED(result))
05779     return result;
05780 
05781   return DoAutoScrollView(aPresContext, aView, aPoint, PR_TRUE);
05782 }
05783 
05784 nsresult
05785 nsTypedSelection::StopAutoScrollTimer()
05786 {
05787   if (mAutoScrollTimer)
05788     return mAutoScrollTimer->Stop();
05789 
05790   return NS_OK; 
05791 }
05792 
05793 nsresult
05794 nsTypedSelection::GetViewAncestorOffset(nsIView *aView, nsIView *aAncestorView, nscoord *aXOffset, nscoord *aYOffset)
05795 {
05796   // Note: A NULL aAncestorView pointer means that the caller wants
05797   //       the view's global offset.
05798 
05799   if (!aView || !aXOffset || !aYOffset)
05800     return NS_ERROR_FAILURE;
05801 
05802   nsPoint offset = aView->GetOffsetTo(aAncestorView);
05803 
05804   *aXOffset = offset.x;
05805   *aYOffset = offset.y;
05806 
05807   return NS_OK;
05808 }
05809 
05810 nsresult
05811 nsTypedSelection::ScrollPointIntoClipView(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool *aDidScroll)
05812 {
05813   nsresult result;
05814 
05815   if (!aPresContext || !aView || !aDidScroll)
05816     return NS_ERROR_NULL_POINTER;
05817 
05818   *aDidScroll = PR_FALSE;
05819 
05820   //
05821   // Get aView's scrollable view.
05822   //
05823 
05824   nsIScrollableView *scrollableView =
05825     nsLayoutUtils::GetNearestScrollingView(aView, nsLayoutUtils::eEither);
05826 
05827   if (!scrollableView)
05828     return NS_OK; // Nothing to do!
05829 
05830   //
05831   // Get the view that is being scrolled.
05832   //
05833 
05834   nsIView *scrolledView = 0;
05835 
05836   result = scrollableView->GetScrolledView(scrolledView);
05837   
05838   //
05839   // Now walk up aView's hierarchy, this time keeping track of
05840   // the view offsets until you hit the scrolledView.
05841   //
05842 
05843   nsPoint viewOffset(0,0);
05844 
05845   result = GetViewAncestorOffset(aView, scrolledView, &viewOffset.x, &viewOffset.y);
05846 
05847   if (NS_FAILED(result))
05848     return result;
05849 
05850   //
05851   // See if aPoint is outside the clip view's boundaries.
05852   // If it is, scroll the view till it is inside the visible area!
05853   //
05854 
05855   nsRect bounds = scrollableView->View()->GetBounds();
05856 
05857   result = scrollableView->GetScrollPosition(bounds.x,bounds.y);
05858 
05859   if (NS_FAILED(result))
05860     return result;
05861 
05862   //
05863   // Calculate the amount we would have to scroll in
05864   // the vertical and horizontal directions to get the point
05865   // within the clip area.
05866   //
05867 
05868   nscoord dx = 0, dy = 0;
05869 
05870   nsPresContext::ScrollbarStyles ss =
05871     nsLayoutUtils::ScrollbarStylesOfView(scrollableView);
05872 
05873   if (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
05874     nscoord e = aPoint.x + viewOffset.x;
05875   
05876     nscoord x1 = bounds.x;
05877     nscoord x2 = bounds.x + bounds.width;
05878 
05879     if (e < x1)
05880       dx = e - x1;
05881     else if (e > x2)
05882       dx = e - x2;
05883   }
05884 
05885   if (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN) {
05886     nscoord e = aPoint.y + viewOffset.y;
05887 
05888     nscoord y1 = bounds.y;
05889     nscoord y2 = bounds.y + bounds.height;
05890 
05891     if (e < y1)
05892       dy = e - y1;
05893     else if (e > y2)
05894       dy = e - y2;
05895   }
05896 
05897   //
05898   // Now clip the scroll amounts so that we don't scroll
05899   // beyond the ends of the document.
05900   //
05901 
05902   nscoord scrollX = 0, scrollY = 0;
05903   nscoord docWidth = 0, docHeight = 0;
05904 
05905   result = scrollableView->GetScrollPosition(scrollX, scrollY);
05906 
05907   if (NS_SUCCEEDED(result))
05908     result = scrollableView->GetContainerSize(&docWidth, &docHeight);
05909 
05910   if (NS_SUCCEEDED(result))
05911   {
05912     if (dx < 0 && scrollX == 0)
05913       dx = 0;
05914     else if (dx > 0)
05915     {
05916       nscoord x1 = scrollX + dx + bounds.width;
05917 
05918       if (x1 > docWidth)
05919         dx -= x1 - docWidth;
05920     }
05921 
05922 
05923     if (dy < 0 && scrollY == 0)
05924       dy = 0;
05925     else if (dy > 0)
05926     {
05927       nscoord y1 = scrollY + dy + bounds.height;
05928 
05929       if (y1 > docHeight)
05930         dy -= y1 - docHeight;
05931     }
05932 
05933     //
05934     // Now scroll the view if necessary.
05935     //
05936 
05937     if (dx != 0 || dy != 0)
05938     {
05939       // Make sure latest bits are available before we scroll them.
05940       aPresContext->GetViewManager()->Composite();
05941 
05942       // Now scroll the view!
05943 
05944       result = scrollableView->ScrollTo(scrollX + dx, scrollY + dy, NS_VMREFRESH_NO_SYNC);
05945 
05946       if (NS_FAILED(result))
05947         return result;
05948 
05949       nsPoint newPos;
05950 
05951       result = scrollableView->GetScrollPosition(newPos.x, newPos.y);
05952 
05953       if (NS_FAILED(result))
05954         return result;
05955 
05956       *aDidScroll = (bounds.x != newPos.x || bounds.y != newPos.y);
05957     }
05958   }
05959 
05960   return result;
05961 }
05962 
05963 nsresult
05964 nsTypedSelection::ScrollPointIntoView(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews, PRBool *aDidScroll)
05965 {
05966   if (!aPresContext || !aView || !aDidScroll)
05967     return NS_ERROR_NULL_POINTER;
05968 
05969   nsresult result;
05970 
05971   *aDidScroll = PR_FALSE;
05972 
05973   //
05974   // Calculate the global offset of the view.
05975   //
05976 
05977   nsPoint globalOffset;
05978 
05979   result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
05980 
05981   if (NS_FAILED(result))
05982     return result;
05983 
05984   //
05985   // Convert aPoint into global coordinates so it is easier to map
05986   // into other views.
05987   //
05988 
05989   nsPoint globalPoint = aPoint + globalOffset;
05990 
05991   //
05992   // Scroll the point into the visible rect of the closest
05993   // scrollable view.
05994   //
05995   result = ScrollPointIntoClipView(aPresContext, aView, aPoint, aDidScroll);
05996 
05997   if (NS_FAILED(result))
05998     return result;
05999 
06000   //
06001   // Now scroll the parent scrollable views.
06002   //
06003 
06004   if (aScrollParentViews)
06005   {
06006     //
06007     // Find aView's parent scrollable view.
06008     //
06009 
06010     nsIScrollableView *scrollableView =
06011       nsLayoutUtils::GetNearestScrollingView(aView, nsLayoutUtils::eEither);
06012 
06013     if (scrollableView)
06014     {
06015       //
06016       // Convert scrollableView to nsIView.
06017       //
06018 
06019       nsIView *scrolledView = 0;
06020       nsIView *view = scrollableView->View();
06021 
06022       if (view)
06023       {
06024         //
06025         // Now get the scrollableView's parent, then search for it's
06026         // closest scrollable view.
06027         //
06028 
06029         view = view->GetParent();
06030 
06031         while (view)
06032         {
06033           scrollableView =
06034             nsLayoutUtils::GetNearestScrollingView(view,
06035                                                    nsLayoutUtils::eEither);
06036 
06037           if (!scrollableView)
06038             break;
06039 
06040           scrolledView = 0;
06041           result = scrollableView->GetScrolledView(scrolledView);
06042           
06043           if (NS_FAILED(result))
06044             return result;
06045 
06046           //
06047           // Map the global point into this scrolledView's coordinate space.
06048           //
06049 
06050           result = GetViewAncestorOffset(scrolledView, nsnull, &globalOffset.x, &globalOffset.y);
06051 
06052           if (NS_FAILED(result))
06053             return result;
06054 
06055           nsPoint newPoint = globalPoint - globalOffset;
06056 
06057           //
06058           // Scroll the point into the visible rect of the scrolled view.
06059           //
06060 
06061           PRBool parentDidScroll = PR_FALSE;
06062 
06063           result = ScrollPointIntoClipView(aPresContext, scrolledView, newPoint, &parentDidScroll);
06064 
06065           if (NS_FAILED(result))
06066             return result;
06067 
06068           *aDidScroll = *aDidScroll || parentDidScroll;
06069 
06070           //
06071           // Now get the parent of this scrollable view so we
06072           // can scroll the next parent view.
06073           //
06074 
06075           view = scrollableView->View()->GetParent();
06076         }
06077       }
06078     }
06079   }
06080 
06081   return NS_OK;
06082 }
06083 
06084 nsresult
06085 nsTypedSelection::DoAutoScrollView(nsPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews)
06086 {
06087   if (!aPresContext || !aView)
06088     return NS_ERROR_NULL_POINTER;
06089 
06090   nsresult result;
06091 
06092   if (mAutoScrollTimer)
06093     result = mAutoScrollTimer->Stop();
06094 
06095   //
06096   // Calculate the global offset of the view.
06097   //
06098 
06099   nsPoint globalOffset;
06100 
06101   result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
06102 
06103   if (NS_FAILED(result))
06104     return result;
06105 
06106   //
06107   // Convert aPoint into global coordinates so we can get back
06108   // to the same point after all the parent views have scrolled.
06109   //
06110 
06111   nsPoint globalPoint = aPoint + globalOffset;
06112 
06113   //
06114   // Now scroll aPoint into view.
06115   //
06116 
06117   PRBool didScroll = PR_FALSE;
06118 
06119   result = ScrollPointIntoView(aPresContext, aView, aPoint, aScrollParentViews, &didScroll);
06120 
06121   if (NS_FAILED(result))
06122     return result;
06123 
06124   //
06125   // Start the AutoScroll timer if necessary.
06126   //
06127 
06128   if (didScroll && mAutoScrollTimer)
06129   {
06130     //
06131     // Map the globalPoint back into aView's coordinate system. We
06132     // have to get the globalOffsets again because aView's
06133     // window and it's parents may have changed their offsets.
06134     //
06135     result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
06136 
06137     if (NS_FAILED(result))
06138       return result;
06139 
06140     nsPoint svPoint = globalPoint - globalOffset;
06141 
06142     result = mAutoScrollTimer->Start(aPresContext, aView, svPoint);
06143   }
06144 
06145   return NS_OK;
06146 }
06147 
06148 NS_IMETHODIMP
06149 nsTypedSelection::GetEnumerator(nsIEnumerator **aIterator)
06150 {
06151   nsresult status = NS_ERROR_OUT_OF_MEMORY;
06152   nsSelectionIterator *iterator =  new nsSelectionIterator(this);
06153   if ( iterator && NS_FAILED(status = CallQueryInterface(iterator, aIterator)) )
06154     delete iterator;
06155   return status;
06156 }
06157 
06158 
06159 
06162 NS_IMETHODIMP
06163 nsTypedSelection::RemoveAllRanges()
06164 {
06165   if (!mFrameSelection)
06166     return NS_OK;//nothing to do
06167   nsCOMPtr<nsPresContext>  presContext;
06168   GetPresContext(getter_AddRefs(presContext));
06169 
06170 
06171   nsresult  result = Clear(presContext);
06172   if (NS_FAILED(result))
06173     return result;
06174   
06175   // Turn off signal for table selection
06176   mFrameSelection->ClearTableCellSelection();
06177 
06178   return mFrameSelection->NotifySelectionListeners(GetType());
06179   // Also need to notify the frames!
06180   // PresShell::CharacterDataChanged should do that on DocumentChanged
06181 }
06182 
06186 NS_IMETHODIMP
06187 nsTypedSelection::AddRange(nsIDOMRange* aRange)
06188 {
06189   if (!aRange) return NS_ERROR_NULL_POINTER;
06190 
06191   // This inserts a table cell range in proper document order
06192   //  and returns NS_ERROR_FAILURE if range doesn't contain just one table cell
06193   PRBool didAddRange;
06194   nsresult result = addTableCellRange(aRange, &didAddRange);
06195   if (NS_FAILED(result)) return result;
06196 
06197   if (!didAddRange)
06198   {
06199     result = AddItem(aRange);
06200     if (NS_FAILED(result)) return result;
06201   }
06202 
06203   PRInt32 count;
06204   result = GetRangeCount(&count);
06205   if (NS_FAILED(result)) return result;
06206 
06207   if (count <= 0)
06208   {
06209     NS_ASSERTION(0,"bad count after additem\n");
06210     return NS_ERROR_FAILURE;
06211   }
06212   setAnchorFocusRange(count -1);
06213   
06214   nsCOMPtr<nsPresContext>  presContext;
06215   GetPresContext(getter_AddRefs(presContext));
06216   selectFrames(presContext, aRange, PR_TRUE);        
06217 
06218   //ScrollIntoView(); this should not happen automatically
06219   if (!mFrameSelection)
06220     return NS_OK;//nothing to do
06221 
06222   return mFrameSelection->NotifySelectionListeners(GetType());
06223 }
06224 
06225 // nsTypedSelection::RemoveRange
06226 //
06227 //    Removes the given range from the selection. The tricky part is updating
06228 //    the flags on the frames that indicate whether they have a selection or
06229 //    not. There could be several selection ranges on the frame, and clearing
06230 //    the bit would cause the selection to not be drawn, even when there is
06231 //    another range on the frame (bug 346185).
06232 //
06233 //    We therefore find any ranges that intersect the same nodes as the range
06234 //    being removed, and cause them to set the selected bits back on their
06235 //    selected frames after we've cleared the bit from ours.
06236 
06237 NS_IMETHODIMP
06238 nsTypedSelection::RemoveRange(nsIDOMRange* aRange)
06239 {
06240   if (!aRange)
06241     return NS_ERROR_INVALID_ARG;
06242   nsresult rv = RemoveItem(aRange);
06243   if (NS_FAILED(rv))
06244     return rv;
06245 
06246   nsCOMPtr<nsIDOMNode> beginNode, endNode;
06247   rv = aRange->GetStartContainer(getter_AddRefs(beginNode));
06248   NS_ENSURE_SUCCESS(rv, rv);
06249   rv = aRange->GetEndContainer(getter_AddRefs(endNode));
06250   NS_ENSURE_SUCCESS(rv, rv);
06251 
06252   // find out the length of the end node, so we can select all of it
06253   PRInt32 beginOffset, endOffset;
06254   PRUint16 endNodeType = nsIDOMNode::ELEMENT_NODE;
06255   endNode->GetNodeType(&endNodeType);
06256   if (endNodeType == nsIDOMNode::TEXT_NODE) {
06257     // Get the length of the text. We can't just use the offset because
06258     // another range could be touching this text node but not intersect our
06259     // range.
06260     beginOffset = 0;
06261     nsAutoString endNodeValue;
06262     endNode->GetNodeValue(endNodeValue);
06263     endOffset = endNodeValue.Length();
06264   } else {
06265     // For non-text nodes, the given offsets should be sufficient.
06266     aRange->GetStartOffset(&beginOffset);
06267     aRange->GetEndOffset(&endOffset);
06268   }
06269 
06270   // clear the selected bit from the removed range's frames
06271   nsCOMPtr<nsPresContext>  presContext;
06272   GetPresContext(getter_AddRefs(presContext));
06273   selectFrames(presContext, aRange, PR_FALSE);
06274 
06275   // add back the selected bit for each range touching our nodes
06276   nsCOMArray<nsIDOMRange> affectedRanges;
06277   rv = GetRangesForIntervalCOMArray(beginNode, beginOffset,
06278                                     endNode, endOffset,
06279                                     PR_TRUE, &affectedRanges);
06280   NS_ENSURE_SUCCESS(rv, rv);
06281   for (PRInt32 i = 0; i < affectedRanges.Count(); i ++)
06282     selectFrames(presContext, affectedRanges[i], PR_TRUE);
06283 
06284   // When the selection is user-created it makes sense to scroll the range
06285   // into view. The spell-check selection, however, is created and destroyed
06286   // in the background. We don't want to scroll in this case or the view
06287   // might appear to be moving randomly (bug 337871).
06288   if (mType != nsISelectionController::SELECTION_SPELLCHECK &&
06289       aRange == mAnchorFocusRange.get())
06290   {
06291     PRInt32 cnt = mRanges.Length();
06292     if (cnt > 0)
06293     {
06294       setAnchorFocusRange(cnt - 1);//reset anchor to LAST range.
06295       ScrollIntoView();
06296     }
06297   }
06298   if (!mFrameSelection)
06299     return NS_OK;//nothing to do
06300   return mFrameSelection->NotifySelectionListeners(GetType());
06301 }
06302 
06303 
06304 
06305 /*
06306  * Collapse sets the whole selection to be one point.
06307  */
06308 NS_IMETHODIMP
06309 nsTypedSelection::Collapse(nsIDOMNode* aParentNode, PRInt32 aOffset)
06310 {
06311   if (!aParentNode)
06312     return NS_ERROR_INVALID_ARG;
06313   if (!mFrameSelection)
06314     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
06315   mFrameSelection->InvalidateDesiredX();
06316   if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
06317     return NS_ERROR_FAILURE;
06318   nsresult result;
06319   // Delete all of the current ranges
06320   if (NS_FAILED(SetOriginalAnchorPoint(aParentNode,aOffset)))
06321     return NS_ERROR_FAILURE; //???
06322   nsCOMPtr<nsPresContext>  presContext;
06323   GetPresContext(getter_AddRefs(presContext));
06324   Clear(presContext);
06325 
06326   // Turn off signal for table selection
06327   if (mFrameSelection)
06328     mFrameSelection->ClearTableCellSelection();
06329 
06330   nsCOMPtr<nsIDOMRange> range;
06331   NS_NewRange(getter_AddRefs(range));
06332   if (! range){
06333     NS_ASSERTION(PR_FALSE,"Couldn't make a range - nsSelection::Collapse");
06334     return NS_ERROR_UNEXPECTED;
06335   }
06336   result = range->SetEnd(aParentNode, aOffset);
06337   if (NS_FAILED(result))
06338     return result;
06339   result = range->SetStart(aParentNode, aOffset);
06340   if (NS_FAILED(result))
06341     return result;
06342 
06343 #ifdef DEBUG_SELECTION
06344   if (aParentNode)
06345   {
06346     nsCOMPtr<nsIContent>content;
06347     content = do_QueryInterface(aParentNode);
06348     if (!content)
06349       return NS_ERROR_FAILURE;
06350 
06351     const char *tagString;
06352     content->Tag()->GetUTF8String(&tagString);
06353     printf ("Sel. Collapse to %p %s %d\n", content.get(), tagString, aOffset);
06354   }
06355   else {
06356     printf ("Sel. Collapse set to null parent.\n");
06357   }
06358 #endif
06359 
06360 
06361   result = AddItem(range);
06362   setAnchorFocusRange(0);
06363   selectFrames(presContext, range,PR_TRUE);
06364   if (NS_FAILED(result))
06365     return result;
06366   if (!mFrameSelection)
06367     return NS_OK;//nothing to do
06368   return mFrameSelection->NotifySelectionListeners(GetType());
06369 }
06370 
06371 /*
06372  * Sets the whole selection to be one point
06373  * at the start of the current selection
06374  */
06375 NS_IMETHODIMP
06376 nsTypedSelection::CollapseToStart()
06377 {
06378   PRInt32 cnt;
06379   nsresult rv = GetRangeCount(&cnt);
06380   if (NS_FAILED(rv) || cnt <= 0)
06381     return NS_ERROR_FAILURE;
06382 
06383   // Get the first range
06384   nsIDOMRange* firstRange = mRanges[0].mRange;
06385   if (!firstRange)
06386     return NS_ERROR_FAILURE;
06387 
06388   nsCOMPtr<nsIDOMNode> parent;
06389   rv = firstRange->GetStartContainer(getter_AddRefs(parent));
06390   if (NS_SUCCEEDED(rv))
06391   {
06392     if (parent)
06393     {
06394       PRInt32 startOffset;
06395       firstRange->GetStartOffset(&startOffset);
06396       rv = Collapse(parent, startOffset);
06397     } else {
06398       // not very likely!
06399       rv = NS_ERROR_FAILURE;
06400     }
06401   }
06402   return rv;
06403 }
06404 
06405 /*
06406  * Sets the whole selection to be one point
06407  * at the end of the current selection
06408  */
06409 NS_IMETHODIMP
06410 nsTypedSelection::CollapseToEnd()
06411 {
06412   PRInt32 cnt;
06413   nsresult rv = GetRangeCount(&cnt);
06414   if (NS_FAILED(rv) || cnt <= 0)
06415     return NS_ERROR_FAILURE;
06416 
06417   // Get the last range
06418   nsIDOMRange* lastRange = mRanges[cnt-1].mRange;
06419   if (!lastRange)
06420     return NS_ERROR_FAILURE;
06421 
06422   nsCOMPtr<nsIDOMNode> parent;
06423   rv = lastRange->GetEndContainer(getter_AddRefs(parent));
06424   if (NS_SUCCEEDED(rv))
06425   {
06426     if (parent)
06427     {
06428       PRInt32 endOffset;
06429       lastRange->GetEndOffset(&endOffset);
06430       rv = Collapse(parent, endOffset);
06431     } else {
06432       // not very likely!
06433       rv = NS_ERROR_FAILURE;
06434     }
06435   }
06436   return rv;
06437 }
06438 
06439 /*
06440  * IsCollapsed -- is the whole selection just one point, or unset?
06441  */
06442 NS_IMETHODIMP
06443 nsTypedSelection::GetIsCollapsed(PRBool* aIsCollapsed)
06444 {
06445   if (!aIsCollapsed)
06446     return NS_ERROR_NULL_POINTER;
06447 
06448   PRInt32 cnt = (PRInt32)mRanges.Length();;
06449   if (cnt == 0)
06450   {
06451     *aIsCollapsed = PR_TRUE;
06452     return NS_OK;
06453   }
06454   
06455   if (cnt != 1)
06456   {
06457     *aIsCollapsed = PR_FALSE;
06458     return NS_OK;
06459   }
06460   
06461   return mRanges[0].mRange->GetCollapsed(aIsCollapsed);
06462 }
06463 
06464 NS_IMETHODIMP
06465 nsTypedSelection::GetRangeCount(PRInt32* aRangeCount)
06466 {
06467   if (!aRangeCount) 
06468     return NS_ERROR_NULL_POINTER;
06469 
06470   *aRangeCount = (PRInt32)mRanges.Length();
06471   NS_ASSERTION(ValidateRanges(), "Ranges out of sync");
06472 
06473   return NS_OK;
06474 }
06475 
06476 NS_IMETHODIMP
06477 nsTypedSelection::GetRangeAt(PRInt32 aIndex, nsIDOMRange** aReturn)
06478 {
06479   if (!aReturn)
06480     return NS_ERROR_NULL_POINTER;
06481   NS_ASSERTION(ValidateRanges(), "Ranges out of sync");
06482 
06483   PRInt32 cnt = (PRInt32)mRanges.Length();
06484   if (aIndex < 0 || aIndex >= cnt)
06485     return NS_ERROR_INVALID_ARG;
06486 
06487   *aReturn = mRanges[aIndex].mRange;
06488   NS_IF_ADDREF(*aReturn);
06489 
06490   return NS_OK;
06491 }
06492 
06493 #ifdef OLD_SELECTION
06494 
06495 //may change parameters may not.
06496 //return NS_ERROR_FAILURE if invalid new selection between anchor and passed in parameters
06497 NS_IMETHODIMP
06498 nsTypedSelection::FixupSelectionPoints(nsIDOMRange *aRange , nsDirection *aDir, PRBool *aFixupState)
06499 {
06500   if (!aRange || !aFixupState)
06501     return NS_ERROR_NULL_POINTER;
06502   *aFixupState = PR_FALSE;
06503   nsresult res;
06504 
06505   //startNode is the beginning or "anchor" of the range
06506   //end Node is the end or "focus of the range
06507   nsCOMPtr<nsIDOMNode> startNode;
06508   nsCOMPtr<nsIDOMNode> endNode;
06509   PRInt32 startOffset;
06510   PRInt32 endOffset;
06511   nsresult result;
06512   if (*aDir == eDirNext)
06513   {
06514     if (NS_FAILED(GetOriginalAnchorPoint(getter_AddRefs(startNode), &startOffset)))
06515     {
06516       aRange->GetStartParent(getter_AddRefs(startNode));
06517       aRange->GetStartOffset(&startOffset);
06518     }
06519     aRange->GetEndParent(getter_AddRefs(endNode));
06520     aRange->GetEndOffset(&endOffset);
06521   }
06522   else
06523   {
06524     if (NS_FAILED(GetOriginalAnchorPoint(getter_AddRefs(startNode), &startOffset)))
06525     {
06526       aRange->GetEndParent(getter_AddRefs(startNode));
06527       aRange->GetEndOffset(&startOffset);
06528     }
06529     aRange->GetStartParent(getter_AddRefs(endNode));
06530     aRange->GetStartOffset(&endOffset);
06531   }
06532   if (!startNode || !endNode)
06533     return NS_ERROR_FAILURE;
06534 
06535   // if end node is a tbody then all bets are off we cannot select "rows"
06536   nsIAtom *atom = GetTag(endNode);
06537   if (atom == nsHTMLAtoms::tbody)
06538     return NS_ERROR_FAILURE; //cannot select INTO row node ony cells
06539 
06540   //get common parent
06541   nsCOMPtr<nsIDOMNode> parent;
06542   nsCOMPtr<nsIDOMRange> subRange;
06543   NS_NewRange(getter_AddRefs(subRange));
06544   if (!subRange) return NS_ERROR_OUT_OF_MEMORY
06545 
06546   result = subRange->SetStart(startNode,startOffset);
06547   if (NS_FAILED(result))
06548     return result;
06549   result = subRange->SetEnd(endNode,endOffset);
06550   if (NS_FAILED(result))
06551   {
06552     result = subRange->SetEnd(startNode,startOffset);
06553     if (NS_FAILED(result))
06554       return result;
06555     result = subRange->SetStart(endNode,endOffset);
06556     if (NS_FAILED(result))
06557       return result;
06558   }
06559 
06560   res = subRange->GetCommonParent(getter_AddRefs(parent));
06561   if (NS_FAILED(res) || !parent)
06562     return res;
06563  
06564   //look for dest. if you see a cell you are in "cell mode"
06565   //if you see a table you select "whole" table
06566 
06567   //src first 
06568   nsCOMPtr<nsIDOMNode> tempNode;
06569   nsCOMPtr<nsIDOMNode> tempNode2;
06570   PRBool cellMode = PR_FALSE;
06571   PRBool dirtystart = PR_FALSE;
06572   PRBool dirtyend = PR_FALSE;
06573   if (startNode != endNode)
06574   {
06575     if (parent != startNode)
06576     {
06577       result = startNode->GetParentNode(getter_AddRefs(tempNode));
06578       if (NS_FAILED(result) || !tempNode)
06579         return NS_ERROR_FAILURE;
06580       while (tempNode != parent)
06581       {
06582         atom = GetTag(tempNode);
06583         if (atom == nsHTMLAtoms::table) //select whole table  if in cell mode, wait for cell
06584         {
06585           result = ParentOffset(tempNode, getter_AddRefs(startNode), &startOffset);
06586           if (NS_FAILED(result))
06587             return NS_ERROR_FAILURE;
06588           if (*aDir == eDirPrevious) //select after
06589             startOffset++;
06590           dirtystart = PR_TRUE;
06591           cellMode = PR_FALSE;
06592         }
06593         else if (atom == nsHTMLAtoms::td ||
06594                  atom == nsHTMLAtoms::th) //you are in "cell" mode put selection to end of cell
06595         {
06596           cellMode = PR_TRUE;
06597           result = ParentOffset(tempNode, getter_AddRefs(startNode), &startOffset);
06598           if (NS_FAILED(result))
06599             return result;
06600           if (*aDir == eDirPrevious) //select after
06601             startOffset++;
06602           dirtystart = PR_TRUE;
06603         }
06604         result = tempNode->GetParentNode(getter_AddRefs(tempNode2));
06605         if (NS_FAILED(result) || !tempNode2)
06606           return NS_ERROR_FAILURE;
06607         tempNode = tempNode2;
06608       }
06609     }
06610   
06611   //now for dest node
06612     if (parent != endNode)
06613     {
06614       result = endNode->GetParentNode(getter_AddRefs(tempNode));
06615       PRBool found = !cellMode;
06616       if (NS_FAILED(result) || !tempNode)
06617         return NS_ERROR_FAILURE;
06618       while (tempNode != parent)
06619       {
06620         atom = GetTag(tempNode);
06621         if (atom == nsHTMLAtoms::table) //select whole table  if in cell mode, wait for cell
06622         {
06623           if (!cellMode)
06624           {
06625             result = ParentOffset(tempNode, getter_AddRefs(endNode), &endOffset);
06626             if (NS_FAILED(result))
06627               return result;
06628             if (*aDir == eDirNext) //select after
06629               endOffset++;
06630             dirtyend = PR_TRUE;
06631           }
06632           else
06633             found = PR_FALSE; //didnt find the right cell yet
06634         }
06635         else if (atom == nsHTMLAtoms::td ||
06636                  atom == nsHTMLAtoms::th) //you are in "cell" mode put selection to end of cell
06637         {
06638           result = ParentOffset(tempNode, getter_AddRefs(endNode), &endOffset);
06639           if (NS_FAILED(result))
06640             return result;
06641           if (*aDir == eDirNext) //select after
06642             endOffset++;
06643           found = PR_TRUE;
06644           dirtyend = PR_TRUE;
06645         }
06646         result = tempNode->GetParentNode(getter_AddRefs(tempNode2));
06647         if (NS_FAILED(result) || !tempNode2)
06648           return NS_ERROR_FAILURE;
06649         tempNode = tempNode2;
06650       }
06651       if (!found)
06652         return NS_ERROR_FAILURE;
06653     }
06654   }
06655   if (*aDir == eDirNext)
06656   {
06657     if (FetchAnchorNode() == startNode.get() && FetchFocusNode() == endNode.get() &&
06658       FetchAnchorOffset() == startOffset && FetchFocusOffset() == endOffset)
06659     {
06660       *aFixupState = PR_FALSE;
06661       return NS_ERROR_FAILURE;//nothing to do
06662     }
06663   }
06664   else
06665   {
06666     if (FetchAnchorNode() == endNode.get() && FetchFocusNode() == startNode.get() &&
06667       FetchAnchorOffset() == endOffset && FetchFocusOffset() == startOffset)
06668     {
06669       *aFixupState = PR_FALSE;
06670       return NS_ERROR_FAILURE;//nothing to do
06671     }
06672   }
06673   if (mFixupState && !dirtyend && !dirtystart)//no mor fixup! all bets off
06674   {
06675     dirtystart = PR_TRUE;//force a reset of anchor positions
06676     dirtystart = PR_TRUE;
06677     *aFixupState = PR_TRUE;//redraw all selection here
06678     mFixupState = PR_FALSE;//no more fixup for next time
06679   }
06680   else
06681   if ((dirtystart || dirtyend) && *aDir != mDirection) //fixup took place but new direction all bets are off
06682   {
06683     *aFixupState = PR_TRUE;
06684     //mFixupState = PR_FALSE;
06685   }
06686   else
06687   if (dirtystart && (FetchAnchorNode() != startNode.get() || FetchAnchorOffset() != startOffset))
06688   {
06689     *aFixupState = PR_TRUE;
06690     mFixupState  = PR_TRUE;
06691   }
06692   else
06693   if (dirtyend && (FetchFocusNode() != endNode.get() || FetchFocusOffset() != endOffset))
06694   {
06695     *aFixupState = PR_TRUE;
06696     mFixupState  = PR_TRUE;
06697   }
06698   else
06699   {
06700     mFixupState = dirtystart || dirtyend;
06701     *aFixupState = PR_FALSE;
06702   }
06703   if (dirtystart || dirtyend){
06704     if (*aDir == eDirNext)
06705     {
06706       if (NS_FAILED(aRange->SetStart(startNode,startOffset)) || NS_FAILED(aRange->SetEnd(endNode, endOffset)))
06707       {
06708         *aDir = eDirPrevious;
06709         aRange->SetStart(endNode, endOffset);
06710         aRange->SetEnd(startNode, startOffset);
06711       }
06712     }
06713     else
06714     {
06715       if (NS_FAILED(aRange->SetStart(endNode,endOffset)) || NS_FAILED(aRange->SetEnd(startNode, startOffset)))
06716       {
06717         *aDir = eDirNext;
06718         aRange->SetStart(startNode, startOffset);
06719         aRange->SetEnd(endNode, endOffset);
06720       }
06721     }
06722   }
06723   return NS_OK;
06724 }
06725 #endif //OLD_SELECTION
06726 
06727 
06728 
06729 
06730 NS_IMETHODIMP
06731 nsTypedSelection::SetOriginalAnchorPoint(nsIDOMNode *aNode, PRInt32 aOffset)
06732 {
06733   if (!aNode){
06734     mOriginalAnchorRange = 0;
06735     return NS_OK;
06736   }
06737   nsCOMPtr<nsIDOMRange> newRange;
06738   nsresult result;
06739   NS_NewRange(getter_AddRefs(newRange));
06740   if (!newRange) return NS_ERROR_OUT_OF_MEMORY;
06741 
06742   result = newRange->SetStart(aNode,aOffset);
06743   if (NS_FAILED(result))
06744     return result;
06745   result = newRange->SetEnd(aNode,aOffset);
06746   if (NS_FAILED(result))
06747     return result;
06748 
06749   mOriginalAnchorRange = newRange;
06750   return result;
06751 }
06752 
06753 
06754 
06755 NS_IMETHODIMP
06756 nsTypedSelection::GetOriginalAnchorPoint(nsIDOMNode **aNode, PRInt32 *aOffset)
06757 {
06758   if (!aNode || !aOffset || !mOriginalAnchorRange)
06759     return NS_ERROR_NULL_POINTER;
06760   nsresult result;
06761   result = mOriginalAnchorRange->GetStartContainer(aNode);
06762   if (NS_FAILED(result))
06763     return result;
06764   result = mOriginalAnchorRange->GetStartOffset(aOffset);
06765   return result;
06766 }
06767 
06768 
06769 /*
06770 utility function
06771 */
06772 NS_IMETHODIMP
06773 nsTypedSelection::CopyRangeToAnchorFocus(nsIDOMRange *aRange)
06774 {
06775   nsCOMPtr<nsIDOMNode> startNode;
06776   nsCOMPtr<nsIDOMNode> endNode;
06777   PRInt32 startOffset;
06778   PRInt32 endOffset;
06779   aRange->GetStartContainer(getter_AddRefs(startNode));
06780   aRange->GetEndContainer(getter_AddRefs(endNode));
06781   aRange->GetStartOffset(&startOffset);
06782   aRange->GetEndOffset(&endOffset);
06783   if (NS_FAILED(mAnchorFocusRange->SetStart(startNode,startOffset)))
06784   {
06785     if (NS_FAILED(mAnchorFocusRange->SetEnd(endNode,endOffset)))
06786       return NS_ERROR_FAILURE;//???
06787     if (NS_FAILED(mAnchorFocusRange->SetStart(startNode,startOffset)))
06788       return NS_ERROR_FAILURE;//???
06789   }
06790   else if (NS_FAILED(mAnchorFocusRange->SetEnd(endNode,endOffset)))
06791           return NS_ERROR_FAILURE;//???
06792   return NS_OK;
06793 }
06794 
06795 /*
06796 Notes which might come in handy for extend:
06797 
06798 We can tell the direction of the selection by asking for the anchors selection
06799 if the begin is less than the end then we know the selection is to the "right".
06800 else it is a backwards selection.
06801 a = anchor
06802 1 = old cursor
06803 2 = new cursor
06804 
06805   if (a <= 1 && 1 <=2)    a,1,2  or (a1,2)
06806   if (a < 2 && 1 > 2)     a,2,1
06807   if (1 < a && a <2)      1,a,2
06808   if (a > 2 && 2 >1)      1,2,a
06809   if (2 < a && a <1)      2,a,1
06810   if (a > 1 && 1 >2)      2,1,a
06811 then execute
06812 a  1  2 select from 1 to 2
06813 a  2  1 deselect from 2 to 1
06814 1  a  2 deselect from 1 to a select from a to 2
06815 1  2  a deselect from 1 to 2
06816 2  1  a = continue selection from 2 to 1
06817 */
06818 
06819 
06820 /*
06821  * Extend extends the selection away from the anchor.
06822  * We don't need to know the direction, because we always change the focus.
06823  */
06824 NS_IMETHODIMP
06825 nsTypedSelection::Extend(nsIDOMNode* aParentNode, PRInt32 aOffset)
06826 {
06827   if (!aParentNode)
06828     return NS_ERROR_INVALID_ARG;
06829 
06830   // First, find the range containing the old focus point:
06831   if (!mAnchorFocusRange)
06832     return NS_ERROR_NOT_INITIALIZED;
06833 
06834   if (!mFrameSelection)
06835     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
06836 
06837   nsresult res;
06838   if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
06839     return NS_ERROR_FAILURE;
06840 
06841   //mFrameSelection->InvalidateDesiredX();
06842   nsCOMPtr<nsIDOMRange> difRange;
06843   NS_NewRange(getter_AddRefs(difRange));
06844   nsCOMPtr<nsIDOMRange> range;
06845 
06846   if (FetchFocusNode() ==  aParentNode && FetchFocusOffset() == aOffset)
06847     return NS_ERROR_FAILURE;//same node nothing to do!
06848 
06849   res = mAnchorFocusRange->CloneRange(getter_AddRefs(range));
06850   //range = mAnchorFocusRange;
06851 
06852   nsCOMPtr<nsIDOMNode> startNode;
06853   nsCOMPtr<nsIDOMNode> endNode;
06854   PRInt32 startOffset;
06855   PRInt32 endOffset;
06856 
06857   range->GetStartContainer(getter_AddRefs(startNode));
06858   range->GetEndContainer(getter_AddRefs(endNode));
06859   range->GetStartOffset(&startOffset);
06860   range->GetEndOffset(&endOffset);
06861 
06862 
06863   nsDirection dir = GetDirection();
06864   PRBool fixupState = PR_FALSE; //if there was a previous fixup the optimal drawing erasing will NOT work
06865   if (NS_FAILED(res))
06866     return res;
06867 
06868   NS_NewRange(getter_AddRefs(difRange));
06869   //compare anchor to old cursor.
06870 
06871   if (NS_FAILED(res))
06872     return res;
06873   PRInt32 result1 = nsRange::ComparePoints(FetchAnchorNode(),
06874                                            FetchAnchorOffset(),
06875                                            FetchFocusNode(),
06876                                            FetchFocusOffset());
06877   //compare old cursor to new cursor
06878   PRInt32 result2 = nsRange::ComparePoints(FetchFocusNode(),
06879                                            FetchFocusOffset(),
06880                                            aParentNode, aOffset);
06881   //compare anchor to new cursor
06882   PRInt32 result3 = nsRange::ComparePoints(FetchAnchorNode(),
06883                                            FetchAnchorOffset(),
06884                                            aParentNode, aOffset);
06885 
06886   if (result2 == 0) //not selecting anywhere
06887     return NS_OK;
06888 
06889   nsCOMPtr<nsPresContext>  presContext;
06890   GetPresContext(getter_AddRefs(presContext));
06891   if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2  a,1,2
06892     //select from 1 to 2 unless they are collapsed
06893     res = range->SetEnd(aParentNode,aOffset);
06894     if (NS_FAILED(res))
06895       return res;
06896     dir = eDirNext;
06897     res = difRange->SetEnd(FetchEndParent(range), FetchEndOffset(range));
06898     res |= difRange->SetStart(FetchFocusNode(), FetchFocusOffset());
06899     if (NS_FAILED(res))
06900       return res;
06901 #ifdef OLD_SELECTION
06902     res = FixupSelectionPoints(range, &dir, &fixupState);
06903 #endif
06904     if (NS_FAILED(res))
06905       return res;
06906     if (fixupState) 
06907     {
06908 #ifdef OLD_SELECTION
06909       selectFrames(mAnchorFocusRange, PR_FALSE);
06910       selectFrames(range, PR_TRUE);
06911 #endif
06912     }
06913     else{
06914       selectFrames(presContext, difRange , PR_TRUE);
06915     }
06916     res = CopyRangeToAnchorFocus(range);
06917     if (NS_FAILED(res))
06918       return res;
06919   }
06920   else if (result1 == 0 && result3 > 0){//2, a1
06921     //select from 2 to 1a
06922     dir = eDirPrevious;
06923     res = range->SetStart(aParentNode,aOffset);
06924     if (NS_FAILED(res))
06925       return res;
06926 #ifdef OLD_SELECTION
06927     res = FixupSelectionPoints(range, &dir, &fixupState);
06928     if (NS_FAILED(res))
06929       return res;
06930     if (fixupState) //unselect previous and select new state has changed to not fixed up
06931     {
06932       selectFrames(mAnchorFocusRange, PR_FALSE);
06933       selectFrames(range, PR_TRUE);
06934     }
06935     else
06936 #endif
06937       selectFrames(presContext, range, PR_TRUE);
06938     res = CopyRangeToAnchorFocus(range);
06939     if (NS_FAILED(res))
06940       return res;
06941   }
06942   else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
06943     //deselect from 2 to 1
06944     res = difRange->SetEnd(FetchFocusNode(), FetchFocusOffset());
06945     res |= difRange->SetStart(aParentNode, aOffset);
06946     if (NS_FAILED(res))
06947       return res;
06948 
06949     res = range->SetEnd(aParentNode,aOffset);
06950     if (NS_FAILED(res))
06951       return res;
06952 #ifdef OLD_SELECTION    
06953     dir = eDirNext;
06954     res = FixupSelectionPoints(range, &dir, &fixupState);
06955 #endif
06956     if (NS_FAILED(res))
06957       return res;
06958     if (fixupState) //unselect previous and select new state has changed to not fixed up
06959     {
06960 #ifdef OLD_SELECTION    
06961       selectFrames(mAnchorFocusRange, PR_FALSE);
06962       selectFrames(range, PR_TRUE);
06963 #endif
06964     }
06965     else 
06966     {
06967       res = CopyRangeToAnchorFocus(range);
06968       if (NS_FAILED(res))
06969         return res;
06970       RemoveItem(mAnchorFocusRange);
06971       selectFrames(presContext, difRange, PR_FALSE);//deselect now if fixup succeeded
06972       AddItem(mAnchorFocusRange);
06973       difRange->SetEnd(FetchEndParent(range),FetchEndOffset(range));
06974       selectFrames(presContext, difRange, PR_TRUE);//must reselect last node maybe more if fixup did something
06975     }
06976   }
06977   else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
06978     if (GetDirection() == eDirPrevious){
06979       res = range->SetStart(endNode,endOffset);
06980       if (NS_FAILED(res))
06981         return res;
06982     }
06983     dir = eDirNext;
06984     res = range->SetEnd(aParentNode,aOffset);
06985     if (NS_FAILED(res))
06986       return res;
06987 #ifdef OLD_SELECTION
06988     res = FixupSelectionPoints(range, &dir, &fixupState);
06989     if (NS_FAILED(res))
06990       return res;
06991 
06992     if (fixupState) //unselect previous and select new state has changed to not fixed up
06993     {
06994       selectFrames(mAnchorFocusRange, PR_FALSE);
06995       selectFrames(range, PR_TRUE);
06996     }
06997     else 
06998 #endif
06999     {
07000       if (FetchFocusNode() != FetchAnchorNode() || FetchFocusOffset() != FetchAnchorOffset() ){//if collapsed diff dont do anything
07001         res = difRange->SetStart(FetchFocusNode(), FetchFocusOffset());
07002         res |= difRange->SetEnd(FetchAnchorNode(), FetchAnchorOffset());
07003         if (NS_FAILED(res))
07004           return res;
07005         res = CopyRangeToAnchorFocus(range);
07006         if (NS_FAILED(res))
07007           return res;
07008         //deselect from 1 to a
07009         RemoveItem(mAnchorFocusRange);
07010         selectFrames(presContext, difRange , PR_FALSE);
07011         AddItem(mAnchorFocusRange);
07012       }
07013       else
07014       {
07015         res = CopyRangeToAnchorFocus(range);
07016         if (NS_FAILED(res))
07017           return res;
07018       }
07019       //select from a to 2
07020       selectFrames(presContext, range , PR_TRUE);
07021     }
07022   }
07023   else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
07024     //deselect from 1 to 2
07025     res = difRange->SetEnd(aParentNode, aOffset);
07026     res |= difRange->SetStart(FetchFocusNode(), FetchFocusOffset());
07027     if (NS_FAILED(res))
07028       return res;
07029     dir = eDirPrevious;
07030     res = range->SetStart(aParentNode,aOffset);
07031     if (NS_FAILED(res))
07032       return res;
07033 
07034 #ifdef OLD_SELECTION
07035     res = FixupSelectionPoints(range, &dir, &fixupState);
07036 #endif
07037     if (NS_FAILED(res))
07038       return res;
07039     if (fixupState) //unselect previous and select new state has changed to not fixed up
07040     {
07041 #ifdef OLD_SELECTION
07042       selectFrames(mAnchorFocusRange, PR_FALSE);
07043       selectFrames(range, PR_TRUE);
07044 #endif
07045     }
07046     else 
07047     {
07048       res = CopyRangeToAnchorFocus(range);
07049       if (NS_FAILED(res))
07050         return res;
07051       RemoveItem(mAnchorFocusRange);
07052       selectFrames(presContext, difRange , PR_FALSE);
07053       AddItem(mAnchorFocusRange);
07054       difRange->SetStart(FetchStartParent(range),FetchStartOffset(range));
07055       selectFrames(presContext, difRange, PR_TRUE);//must reselect last node
07056     }
07057   }
07058   else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
07059     if (GetDirection() == eDirNext){
07060       range->SetEnd(startNode,startOffset);
07061     }
07062     dir = eDirPrevious;
07063     res = range->SetStart(aParentNode,aOffset);
07064     if (NS_FAILED(res))
07065       return res;
07066 #ifdef OLD_SELECTION
07067     res = FixupSelectionPoints(range, &dir, &fixupState);
07068     if (NS_FAILED(res))
07069       return res;
07070     if (fixupState) //unselect previous and select new state has changed to not fixed up
07071     {
07072       selectFrames(mAnchorFocusRange, PR_FALSE);
07073       selectFrames(range, PR_TRUE);
07074     }
07075     else
07076 #endif
07077     {
07078       //deselect from a to 1
07079       if (FetchFocusNode() != FetchAnchorNode() || FetchFocusOffset() != FetchAnchorOffset() ){//if collapsed diff dont do anything
07080         res = difRange->SetStart(FetchAnchorNode(), FetchAnchorOffset());
07081         res |= difRange->SetEnd(FetchFocusNode(), FetchFocusOffset());
07082         res = CopyRangeToAnchorFocus(range);
07083         if (NS_FAILED(res))
07084           return res;
07085         RemoveItem(mAnchorFocusRange);
07086         selectFrames(presContext, difRange, 0);
07087         AddItem(mAnchorFocusRange);
07088       }
07089       else
07090       {
07091         res = CopyRangeToAnchorFocus(range);
07092         if (NS_FAILED(res))
07093           return res;
07094       }
07095       //select from 2 to a
07096       selectFrames(presContext, range , PR_TRUE);
07097     }
07098   }
07099   else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
07100     //select from 2 to 1
07101     res = range->SetStart(aParentNode,aOffset);
07102     if (NS_FAILED(res))
07103       return res;
07104     dir = eDirPrevious;
07105     res = difRange->SetEnd(FetchFocusNode(), FetchFocusOffset());
07106     res |= difRange->SetStart(FetchStartParent(range), FetchStartOffset(range));
07107     if (NS_FAILED(res))
07108       return res;
07109 
07110 #ifdef OLD_SELECTION
07111     res = FixupSelectionPoints(range, &dir, &fixupState);
07112 #endif
07113     if (NS_FAILED(res))
07114       return res;
07115     if (fixupState) //unselect previous and select new state has changed to not fixed up
07116     {
07117 #ifdef OLD_SELECTION
07118       selectFrames(mAnchorFocusRange, PR_FALSE);
07119       selectFrames(range, PR_TRUE);
07120 #endif
07121     }
07122     else {
07123       selectFrames(presContext, difRange, PR_TRUE);
07124     }
07125     res = CopyRangeToAnchorFocus(range);
07126     if (NS_FAILED(res))
07127       return res;
07128   }
07129 
07130   DEBUG_OUT_RANGE(range);
07131 #if 0
07132   if (eDirNext == mDirection)
07133     printf("    direction = 1  LEFT TO RIGHT\n");
07134   else
07135     printf("    direction = 0  RIGHT TO LEFT\n");
07136 #endif
07137   SetDirection(dir);
07138 #ifdef DEBUG_SELECTION
07139   if (aParentNode)
07140   {
07141     nsCOMPtr<nsIContent>content;
07142     content = do_QueryInterface(aParentNode);
07143 
07144     const char *tagString;
07145     content->Tag()->GetUTF8String(&tagString);
07146     printf ("Sel. Extend to %p %s %d\n", content.get(), tagString, aOffset);
07147   }
07148   else {
07149     printf ("Sel. Extend set to null parent.\n");
07150   }
07151 #endif
07152   if (!mFrameSelection)
07153     return NS_OK;//nothing to do
07154   return mFrameSelection->NotifySelectionListeners(GetType());
07155 }
07156 
07157 static nsresult
07158 GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
07159 {
07160   NS_ASSERTION((aChild && aParent), "bad args");
07161   nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
07162   nsCOMPtr<nsIContent> cChild = do_QueryInterface(aChild);
07163 
07164   if (!cChild || !content)
07165     return NS_ERROR_NULL_POINTER;
07166 
07167   aOffset = content->IndexOf(cChild);
07168 
07169   return NS_OK;
07170 }
07171 
07172 NS_IMETHODIMP
07173 nsTypedSelection::SelectAllChildren(nsIDOMNode* aParentNode)
07174 {
07175   NS_ENSURE_ARG_POINTER(aParentNode);
07176   
07177   if (mFrameSelection) 
07178   {
07179     mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
07180   }
07181   nsresult result = Collapse(aParentNode, 0);
07182   if (NS_SUCCEEDED(result))
07183   {
07184     nsCOMPtr<nsIDOMNode>lastChild;
07185     result = aParentNode->GetLastChild(getter_AddRefs(lastChild));
07186     if ((NS_SUCCEEDED(result)) && lastChild)
07187     {
07188       PRInt32 numBodyChildren=0;
07189       GetChildOffset(lastChild, aParentNode, numBodyChildren);
07190       if (mFrameSelection) 
07191       {
07192         mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
07193       }
07194       result = Extend(aParentNode, numBodyChildren+1);
07195     }
07196   }
07197   return result;
07198 }
07199 
07200 NS_IMETHODIMP
07201 nsTypedSelection::ContainsNode(nsIDOMNode* aNode, PRBool aAllowPartial,
07202                                PRBool* aYes)
07203 {
07204   nsresult rv;
07205   if (!aYes)
07206     return NS_ERROR_NULL_POINTER;
07207   NS_ASSERTION(ValidateRanges(), "Ranges out of sync");
07208   *aYes = PR_FALSE;
07209 
07210   if (mRanges.Length() == 0 || !aNode)
07211     return NS_OK;
07212 
07213   PRUint16 nodeType;
07214   aNode->GetNodeType(&nodeType);
07215   PRUint32 nodeLength;
07216   if (nodeType == nsIDOMNode::TEXT_NODE) {
07217     nsAutoString nodeValue;
07218     rv = aNode->GetNodeValue(nodeValue);
07219     NS_ENSURE_SUCCESS(rv, rv);
07220     nodeLength = nodeValue.Length();
07221   } else {
07222     nsCOMPtr<nsIDOMNodeList> aChildNodes;
07223     rv = aNode->GetChildNodes(getter_AddRefs(aChildNodes));
07224     NS_ENSURE_SUCCESS(rv, rv);
07225     rv = aChildNodes->GetLength(&nodeLength);
07226     NS_ENSURE_SUCCESS(rv, rv);
07227   }
07228 
07229   nsCOMArray<nsIDOMRange> overlappingRanges;
07230   rv = GetRangesForIntervalCOMArray(aNode, 0, aNode, nodeLength,
07231                                     PR_FALSE, &overlappingRanges);
07232   NS_ENSURE_SUCCESS(rv, rv);
07233   if (overlappingRanges.Count() == 0)
07234     return NS_OK; // no ranges overlap
07235 
07236   // if the caller said partial intersections are OK, we're done
07237   if (aAllowPartial) {
07238     *aYes = PR_TRUE;
07239     return NS_OK;
07240   }
07241 
07242   // text nodes always count as inside
07243   if (nodeType == nsIDOMNode::TEXT_NODE) {
07244     *aYes = PR_TRUE;
07245     return NS_OK;
07246   }
07247 
07248   // The caller wants to know if the node is entirely within the given range,
07249   // so we have to check all intersecting ranges.
07250   nsCOMPtr<nsIContent> content (do_QueryInterface(aNode, &rv));
07251   NS_ENSURE_SUCCESS(rv, rv);
07252   for (PRInt32 i = 0; i < overlappingRanges.Count(); i ++) {
07253     PRBool nodeStartsBeforeRange, nodeEndsAfterRange;
07254     if (NS_SUCCEEDED(nsRange::CompareNodeToRange(content, overlappingRanges[i],
07255                                                  &nodeStartsBeforeRange,
07256                                                  &nodeEndsAfterRange))) {
07257       if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
07258         *aYes = PR_TRUE;
07259         return NS_OK;
07260       }
07261     }
07262   }
07263   return NS_OK;
07264 }
07265 
07266 
07267 nsresult
07268 nsTypedSelection::GetPresContext(nsPresContext **aPresContext)
07269 {
07270   if (!mFrameSelection)
07271     return NS_ERROR_FAILURE;//nothing to do
07272   nsIPresShell *shell = mFrameSelection->GetShell();
07273 
07274   if (!shell)
07275     return NS_ERROR_NULL_POINTER;
07276 
07277   NS_IF_ADDREF(*aPresContext = shell->GetPresContext());
07278   return NS_OK;
07279 }
07280 
07281 nsresult
07282 nsTypedSelection::GetPresShell(nsIPresShell **aPresShell)
07283 {
07284   if (mPresShellWeak)
07285   {
07286     nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShellWeak);
07287     if (presShell)
07288       NS_ADDREF(*aPresShell = presShell);
07289     return NS_OK;
07290   }
07291   nsresult rv = NS_OK;
07292   if (!mFrameSelection)
07293     return NS_ERROR_FAILURE;//nothing to do
07294 
07295   nsIPresShell *shell = mFrameSelection->GetShell();
07296 
07297   mPresShellWeak = do_GetWeakReference(shell);    // the presshell owns us, so no addref
07298   if (mPresShellWeak)
07299     NS_ADDREF(*aPresShell = shell);
07300   return rv;
07301 }
07302 
07303 nsresult
07304 nsTypedSelection::GetRootScrollableView(nsIScrollableView **aScrollableView)
07305 {
07306   //
07307   // NOTE: This method returns a NON-AddRef'd pointer
07308   //       to the scrollable view!
07309   //
07310   NS_ENSURE_ARG_POINTER(aScrollableView);
07311 
07312   if (!mFrameSelection)
07313     return NS_ERROR_FAILURE;//nothing to do
07314   nsresult rv;
07315   nsIScrollableView *scrollView;
07316   rv = mFrameSelection->GetScrollableView(&scrollView);
07317   if ( NS_FAILED(rv))
07318     return rv;
07319 
07320   if (!scrollView)
07321   {
07322 
07323     nsCOMPtr<nsIPresShell> presShell;
07324 
07325     rv = GetPresShell(getter_AddRefs(presShell));
07326 
07327     if (NS_FAILED(rv))
07328       return rv;
07329 
07330     if (!presShell)
07331       return NS_ERROR_NULL_POINTER;
07332 
07333     nsIViewManager* viewManager = presShell->GetViewManager();
07334 
07335     if (!viewManager)
07336       return NS_ERROR_NULL_POINTER;
07337 
07338     //
07339     // nsIViewManager::GetRootScrollableView() does not
07340     // AddRef the pointer it returns.
07341     //
07342     return viewManager->GetRootScrollableView(aScrollableView);
07343   }
07344   else //SCROLLVIEW_FROM_FRAME
07345   {
07346     *aScrollableView = scrollView;
07347   }
07348 
07349   return rv;
07350 }
07351 
07352 nsresult
07353 nsTypedSelection::GetFrameToScrolledViewOffsets(nsIScrollableView *aScrollableView, nsIFrame *aFrame, nscoord *aX, nscoord *aY)
07354 {
07355   nsresult rv = NS_OK;
07356   if (!mFrameSelection)
07357     return NS_ERROR_FAILURE;//nothing to do
07358 
07359   if (!aScrollableView || !aFrame || !aX || !aY) {
07360     return NS_ERROR_NULL_POINTER;
07361   }
07362 
07363   *aX = 0;
07364   *aY = 0;
07365 
07366   nsIView*  scrolledView;
07367   nsPoint   offset;
07368   nsIView*  closestView;
07369           
07370   // Determine the offset from aFrame to the scrolled view. We do that by
07371   // getting the offset from its closest view and then walking up
07372   aScrollableView->GetScrolledView(scrolledView);
07373   nsIPresShell *shell = mFrameSelection->GetShell();
07374 
07375   if (!shell)
07376     return NS_ERROR_NULL_POINTER;
07377 
07378   aFrame->GetOffsetFromView(offset, &closestView);
07379 
07380   // XXX Deal with the case where there is a scrolled element, e.g., a
07381   // DIV in the middle...
07382   offset += closestView->GetOffsetTo(scrolledView);
07383 
07384   *aX = offset.x;
07385   *aY = offset.y;
07386 
07387   return rv;
07388 }
07389 
07390 nsresult
07391 nsTypedSelection::GetPointFromOffset(nsIFrame *aFrame, PRInt32 aContentOffset, nsPoint *aPoint)
07392 {
07393   nsresult rv = NS_OK;
07394   if (!mFrameSelection)
07395     return NS_ERROR_FAILURE;//nothing to do
07396   if (!aFrame || !aPoint)
07397     return NS_ERROR_NULL_POINTER;
07398 
07399   aPoint->x = 0;
07400   aPoint->y = 0;
07401 
07402   //
07403   // Retrieve the device context. We need one to create
07404   // a rendering context.
07405   //
07406 
07407   nsIPresShell *shell = mFrameSelection->GetShell();
07408   if (!shell)
07409     return NS_ERROR_NULL_POINTER;
07410 
07411   nsPresContext *presContext = shell->GetPresContext();
07412   if (!presContext)
07413     return NS_ERROR_NULL_POINTER;
07414   
07415   //
07416   // Now get the closest view with a widget so we can create
07417   // a rendering context.
07418   //
07419 
07420   nsIWidget* widget = nsnull;
07421   nsIView *closestView = nsnull;
07422   nsPoint offset(0, 0);
07423 
07424   rv = aFrame->GetOffsetFromView(offset, &closestView);
07425 
07426   while (!widget && closestView)
07427   {
07428     widget = closestView->GetWidget();
07429 
07430     if (!widget)
07431     {
07432       closestView = closestView->GetParent();
07433     }
07434   }
07435 
07436   if (!closestView)
07437     return NS_ERROR_FAILURE;
07438 
07439   //
07440   // Create a rendering context. This context is used by text frames
07441   // to calculate text widths so it can figure out where the point is
07442   // in the frame.
07443   //
07444 
07445   nsCOMPtr<nsIRenderingContext> rendContext;
07446 
07447   rv = presContext->DeviceContext()->
07448     CreateRenderingContext(closestView, *getter_AddRefs(rendContext));
07449   
07450   if (NS_FAILED(rv))
07451     return rv;