LCOV - code coverage report
Current view: directory - layout/generic - nsSelection.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 2521 9 0.4 %
Date: 2012-06-02 Functions: 224 5 2.2 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim: set ts=2 sw=2 et tw=78: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications Corporation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Mats Palmgren <matspal@gmail.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : /*
      41                 :  * Implementation of selection: nsISelection,nsISelectionPrivate and nsFrameSelection
      42                 :  */
      43                 : 
      44                 : #include "mozilla/Attributes.h"
      45                 : 
      46                 : #include "nsCOMPtr.h"
      47                 : #include "nsWeakReference.h"
      48                 : #include "nsIFactory.h"
      49                 : #include "nsIEnumerator.h"
      50                 : #include "nsString.h"
      51                 : #include "nsReadableUtils.h"
      52                 : #include "nsFrameSelection.h"
      53                 : #include "nsISelectionPrivate.h"
      54                 : #include "nsISelectionListener.h"
      55                 : #include "nsIComponentManager.h"
      56                 : #include "nsContentCID.h"
      57                 : #include "nsIContent.h"
      58                 : #include "nsIDOMElement.h"
      59                 : #include "nsIDOMNode.h"
      60                 : #include "nsRange.h"
      61                 : #include "nsCOMArray.h"
      62                 : #include "nsGUIEvent.h"
      63                 : #include "nsIDOMKeyEvent.h"
      64                 : #include "nsITableLayout.h"
      65                 : #include "nsITableCellLayout.h"
      66                 : #include "nsIDOMNodeList.h"
      67                 : #include "nsTArray.h"
      68                 : #include "nsIScrollableFrame.h"
      69                 : #include "nsCCUncollectableMarker.h"
      70                 : #include "nsIContentIterator.h"
      71                 : #include "nsIDocumentEncoder.h"
      72                 : 
      73                 : // for IBMBIDI
      74                 : #include "nsFrameTraversal.h"
      75                 : #include "nsILineIterator.h"
      76                 : #include "nsGkAtoms.h"
      77                 : #include "nsIFrameTraversal.h"
      78                 : #include "nsLayoutUtils.h"
      79                 : #include "nsLayoutCID.h"
      80                 : #include "nsBidiPresUtils.h"
      81                 : static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
      82                 : #include "nsTextFrame.h"
      83                 : 
      84                 : #include "nsIDOMText.h"
      85                 : 
      86                 : #include "nsContentUtils.h"
      87                 : #include "nsThreadUtils.h"
      88                 : #include "mozilla/Preferences.h"
      89                 : 
      90                 : //included for desired x position;
      91                 : #include "nsPresContext.h"
      92                 : #include "nsIPresShell.h"
      93                 : #include "nsCaret.h"
      94                 : 
      95                 : 
      96                 : #include "nsITimer.h"
      97                 : #include "nsIServiceManager.h"
      98                 : #include "nsFrameManager.h"
      99                 : // notifications
     100                 : #include "nsIDOMDocument.h"
     101                 : #include "nsIDocument.h"
     102                 : 
     103                 : #include "nsISelectionController.h"//for the enums
     104                 : #include "nsAutoCopyListener.h"
     105                 : #include "nsCopySupport.h"
     106                 : #include "nsIClipboard.h"
     107                 : 
     108                 : #ifdef IBMBIDI
     109                 : #include "nsIBidiKeyboard.h"
     110                 : #endif // IBMBIDI
     111                 : 
     112                 : #include "nsDOMError.h"
     113                 : #include "mozilla/dom/Element.h"
     114                 : 
     115                 : using namespace mozilla;
     116                 : 
     117                 : //#define DEBUG_TABLE 1
     118                 : 
     119                 : static NS_DEFINE_IID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
     120                 : static NS_DEFINE_IID(kCSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
     121                 : 
     122                 : //PROTOTYPES
     123                 : class nsSelectionIterator;
     124                 : class nsFrameSelection;
     125                 : class nsAutoScrollTimer;
     126                 : 
     127                 : static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
     128                 : 
     129                 : static nsIAtom *GetTag(nsINode *aNode);
     130                 : // returns the parent
     131                 : static nsINode* ParentOffset(nsINode *aNode, PRInt32 *aChildOffset);
     132                 : static nsINode* GetCellParent(nsINode *aDomNode);
     133                 : 
     134                 : #ifdef PRINT_RANGE
     135                 : static void printRange(nsRange *aDomRange);
     136                 : #define DEBUG_OUT_RANGE(x)  printRange(x)
     137                 : #else
     138                 : #define DEBUG_OUT_RANGE(x)  
     139                 : #endif //MOZ_DEBUG
     140                 : 
     141                 : 
     142                 : 
     143                 : //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
     144                 : //#define DEBUG_NAVIGATION
     145                 : 
     146                 : 
     147                 : //#define DEBUG_TABLE_SELECTION 1
     148                 : 
     149                 : struct CachedOffsetForFrame {
     150               0 :   CachedOffsetForFrame()
     151                 :   : mCachedFrameOffset(0, 0) // nsPoint ctor
     152                 :   , mLastCaretFrame(nsnull)
     153                 :   , mLastContentOffset(0)
     154               0 :   , mCanCacheFrameOffset(false)
     155               0 :   {}
     156                 : 
     157                 :   nsPoint      mCachedFrameOffset;      // cached frame offset
     158                 :   nsIFrame*    mLastCaretFrame;         // store the frame the caret was last drawn in.
     159                 :   PRInt32      mLastContentOffset;      // store last content offset
     160                 :   bool mCanCacheFrameOffset;    // cached frame offset is valid?
     161                 : };
     162                 : 
     163                 : struct RangeData
     164            1487 : {
     165            1464 :   RangeData(nsRange* aRange)
     166            1464 :     : mRange(aRange)
     167            1464 :   {}
     168                 : 
     169                 :   nsRefPtr<nsRange> mRange;
     170                 :   nsTextRangeStyle mTextRangeStyle;
     171                 : };
     172                 : 
     173            1464 : static RangeData sEmptyData(nsnull);
     174                 : 
     175                 : // Note, the ownership of nsTypedSelection depends on which way the object is
     176                 : // created. When nsFrameSelection has created nsTypedSelection,
     177                 : // addreffing/releasing nsTypedSelection object is aggregated to
     178                 : // nsFrameSelection. Otherwise normal addref/release is used.
     179                 : // This ensures that nsFrameSelection is never deleted before its
     180                 : // nsTypedSelections.
     181                 : 
     182                 : class nsTypedSelection : public nsISelectionPrivate,
     183                 :                          public nsSupportsWeakReference
     184                 : {
     185                 : public:
     186                 :   nsTypedSelection();
     187                 :   nsTypedSelection(nsFrameSelection *aList);
     188                 :   virtual ~nsTypedSelection();
     189                 :   
     190               0 :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     191            1464 :   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTypedSelection, nsISelectionPrivate)
     192                 :   NS_DECL_NSISELECTION
     193                 :   NS_DECL_NSISELECTIONPRIVATE
     194                 : 
     195                 :   // utility methods for scrolling the selection into view
     196                 :   nsresult      GetPresContext(nsPresContext **aPresContext);
     197                 :   nsresult      GetPresShell(nsIPresShell **aPresShell);
     198                 :   // Returns a rect containing the selection region, and frame that that
     199                 :   // position is relative to. For SELECTION_ANCHOR_REGION or
     200                 :   // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
     201                 :   // SELECTION_WHOLE_SELECTION the rect contains both the anchor and focus
     202                 :   // region rects.
     203                 :   nsIFrame*     GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect *aRect);
     204                 :   // Returns the position of the region (SELECTION_ANCHOR_REGION or
     205                 :   // SELECTION_FOCUS_REGION only), and frame that that position is relative to.
     206                 :   // The 'position' is a zero-width rectangle.
     207                 :   nsIFrame*     GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect *aRect);
     208                 : 
     209                 :   nsresult      PostScrollSelectionIntoViewEvent(SelectionRegion aRegion,
     210                 :                                                  bool aFirstAncestorOnly,
     211                 :                                                  PRInt16 aVPercent,
     212                 :                                                  PRInt16 aHPercent);
     213                 :   enum {
     214                 :     SCROLL_SYNCHRONOUS = 1<<1,
     215                 :     SCROLL_FIRST_ANCESTOR_ONLY = 1<<2,
     216                 :     SCROLL_DO_FLUSH = 1<<3
     217                 :   };
     218                 :   // aDoFlush only matters if aIsSynchronous is true.  If not, we'll just flush
     219                 :   // when the scroll event fires so we make sure to scroll to the right place.
     220                 :   nsresult      ScrollIntoView(SelectionRegion aRegion,
     221                 :                                PRInt16 aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE,
     222                 :                                PRInt16 aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE,
     223                 :                                PRInt32 aFlags = 0);
     224                 :   nsresult      SubtractRange(RangeData* aRange, nsRange* aSubtract,
     225                 :                               nsTArray<RangeData>* aOutput);
     226                 :   nsresult      AddItem(nsRange *aRange, PRInt32* aOutIndex = nsnull);
     227                 :   nsresult      RemoveItem(nsRange *aRange);
     228                 :   nsresult      RemoveCollapsedRanges();
     229                 :   nsresult      Clear(nsPresContext* aPresContext);
     230                 :   nsresult      Collapse(nsINode* aParentNode, PRInt32 aOffset);
     231                 :   nsresult      Extend(nsINode* aParentNode, PRInt32 aOffset);
     232                 :   nsRange*      GetRangeAt(PRInt32 aIndex);
     233                 : 
     234                 :   // methods for convenience. Note, these don't addref
     235                 :   nsINode*     GetAnchorNode();
     236                 :   PRInt32      GetAnchorOffset();
     237                 : 
     238                 :   nsINode*     GetFocusNode();
     239                 :   PRInt32      GetFocusOffset();
     240                 : 
     241                 :   // Get the anchor-to-focus range if we don't care which end is
     242                 :   // anchor and which end is focus.
     243               0 :   const nsRange* GetAnchorFocusRange() const {
     244               0 :     return mAnchorFocusRange;
     245                 :   }
     246                 : 
     247               0 :   nsDirection  GetDirection(){return mDirection;}
     248               0 :   void         SetDirection(nsDirection aDir){mDirection = aDir;}
     249                 :   nsresult     CopyRangeToAnchorFocus(nsRange *aRange);
     250                 :   void         ReplaceAnchorFocusRange(nsRange *aRange);
     251                 : 
     252                 :   //  NS_IMETHOD   GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, bool aIsEndNode, nsIFrame **aResultFrame);
     253                 :   NS_IMETHOD   GetPrimaryFrameForAnchorNode(nsIFrame **aResultFrame);
     254                 :   NS_IMETHOD   GetPrimaryFrameForFocusNode(nsIFrame **aResultFrame, PRInt32 *aOffset, bool aVisual);
     255                 :   NS_IMETHOD   LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
     256                 :                              SelectionDetails **aReturnDetails, SelectionType aType, bool aSlowCheck);
     257                 :   NS_IMETHOD   Repaint(nsPresContext* aPresContext);
     258                 : 
     259                 :   // Note: StartAutoScrollTimer might destroy arbitrary frames etc.
     260                 :   nsresult     StartAutoScrollTimer(nsIFrame *aFrame,
     261                 :                                     nsPoint& aPoint,
     262                 :                                     PRUint32 aDelay);
     263                 : 
     264                 :   nsresult     StopAutoScrollTimer();
     265                 : 
     266                 : private:
     267                 :   friend class nsAutoScrollTimer;
     268                 : 
     269                 :   // Note: DoAutoScroll might destroy arbitrary frames etc.
     270                 :   nsresult DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint);
     271                 : 
     272                 : public:
     273               0 :   SelectionType GetType(){return mType;}
     274               0 :   void          SetType(SelectionType aType){mType = aType;}
     275                 : 
     276                 :   nsresult     NotifySelectionListeners();
     277                 : 
     278                 : private:
     279                 :   friend class nsSelectionIterator;
     280                 : 
     281                 :   class ScrollSelectionIntoViewEvent;
     282                 :   friend class ScrollSelectionIntoViewEvent;
     283                 : 
     284               0 :   class ScrollSelectionIntoViewEvent : public nsRunnable {
     285                 :   public:
     286                 :     NS_DECL_NSIRUNNABLE
     287               0 :     ScrollSelectionIntoViewEvent(nsTypedSelection *aTypedSelection,
     288                 :                                  SelectionRegion aRegion,
     289                 :                                  PRInt16 aVScroll,
     290                 :                                  PRInt16 aHScroll,
     291                 :                                  bool aFirstAncestorOnly)
     292                 :       : mTypedSelection(aTypedSelection),
     293                 :         mRegion(aRegion),
     294                 :         mVerticalScroll(aVScroll),
     295                 :         mHorizontalScroll(aHScroll),
     296               0 :         mFirstAncestorOnly(aFirstAncestorOnly) {
     297               0 :       NS_ASSERTION(aTypedSelection, "null parameter");
     298               0 :     }
     299               0 :     void Revoke() { mTypedSelection = nsnull; }
     300                 :   private:
     301                 :     nsTypedSelection *mTypedSelection;
     302                 :     SelectionRegion   mRegion;
     303                 :     PRInt16           mVerticalScroll;
     304                 :     PRInt16           mHorizontalScroll;
     305                 :     bool              mFirstAncestorOnly;
     306                 :   };
     307                 : 
     308                 :   void setAnchorFocusRange(PRInt32 aIndex); // pass in index into mRanges;
     309                 :                                             // negative value clears
     310                 :                                             // mAnchorFocusRange
     311                 :   nsresult     SelectAllFramesForContent(nsIContentIterator *aInnerIter,
     312                 :                                nsIContent *aContent,
     313                 :                                bool aSelected);
     314                 :   nsresult     selectFrames(nsPresContext* aPresContext, nsRange *aRange, bool aSelect);
     315                 :   nsresult     getTableCellLocationFromRange(nsRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol);
     316                 :   nsresult     addTableCellRange(nsRange *aRange, bool *aDidAddRange, PRInt32 *aOutIndex);
     317                 : 
     318                 :   nsresult FindInsertionPoint(
     319                 :       nsTArray<RangeData>* aElementArray,
     320                 :       nsINode* aPointNode, PRInt32 aPointOffset,
     321                 :       nsresult (*aComparator)(nsINode*,PRInt32,nsRange*,PRInt32*),
     322                 :       PRInt32* aPoint);
     323                 :   bool EqualsRangeAtPoint(nsINode* aBeginNode, PRInt32 aBeginOffset,
     324                 :                             nsINode* aEndNode, PRInt32 aEndOffset,
     325                 :                             PRInt32 aRangeIndex);
     326                 :   void GetIndicesForInterval(nsINode* aBeginNode, PRInt32 aBeginOffset,
     327                 :                              nsINode* aEndNode, PRInt32 aEndOffset,
     328                 :                              bool aAllowAdjacent,
     329                 :                              PRInt32 *aStartIndex, PRInt32 *aEndIndex);
     330                 :   RangeData* FindRangeData(nsIDOMRange* aRange);
     331                 : 
     332                 :   // These are the ranges inside this selection. They are kept sorted in order
     333                 :   // of DOM start position.
     334                 :   //
     335                 :   // This data structure is sorted by the range beginnings. As the ranges are
     336                 :   // disjoint, it is also implicitly sorted by the range endings. This allows
     337                 :   // us to perform binary searches when searching for existence of a range,
     338                 :   // giving us O(log n) search time.
     339                 :   //
     340                 :   // Inserting a new range requires finding the overlapping interval, requiring
     341                 :   // two binary searches plus up to an additional 6 DOM comparisons. If this
     342                 :   // proves to be a performance concern, then an interval tree may be a
     343                 :   // possible solution, allowing the calculation of the overlap interval in
     344                 :   // O(log n) time, though this would require rebalancing and other overhead.
     345                 :   nsTArray<RangeData> mRanges;
     346                 : 
     347                 :   nsRefPtr<nsRange> mAnchorFocusRange;
     348                 :   nsRefPtr<nsFrameSelection> mFrameSelection;
     349                 :   nsWeakPtr mPresShellWeak;
     350                 :   nsRefPtr<nsAutoScrollTimer> mAutoScrollTimer;
     351                 :   nsCOMArray<nsISelectionListener> mSelectionListeners;
     352                 :   nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
     353                 :   CachedOffsetForFrame *mCachedOffsetForFrame;
     354                 :   nsDirection mDirection;
     355                 :   SelectionType mType;
     356                 : };
     357                 : 
     358                 : // Stack-class to turn on/off selection batching for table selection
     359                 : class NS_STACK_CLASS nsSelectionBatcher MOZ_FINAL
     360                 : {
     361                 : private:
     362                 :   nsCOMPtr<nsISelectionPrivate> mSelection;
     363                 : public:
     364               0 :   nsSelectionBatcher(nsISelectionPrivate *aSelection) : mSelection(aSelection)
     365                 :   {
     366               0 :     if (mSelection) mSelection->StartBatchChanges();
     367               0 :   }
     368               0 :   ~nsSelectionBatcher() 
     369               0 :   { 
     370               0 :     if (mSelection) mSelection->EndBatchChanges();
     371               0 :   }
     372                 : };
     373                 : 
     374                 : class nsSelectionIterator : public nsIBidirectionalEnumerator
     375                 : {
     376                 : public:
     377                 : /*BEGIN nsIEnumerator interfaces
     378                 : see the nsIEnumerator for more details*/
     379                 : 
     380                 :   NS_DECL_ISUPPORTS
     381                 : 
     382                 :   NS_DECL_NSIENUMERATOR
     383                 : 
     384                 :   NS_DECL_NSIBIDIRECTIONALENUMERATOR
     385                 : 
     386                 : /*END nsIEnumerator interfaces*/
     387                 : /*BEGIN Helper Methods*/
     388                 :   nsRange* CurrentItem();
     389                 : /*END Helper Methods*/
     390                 : private:
     391                 :   friend class nsTypedSelection;
     392                 : 
     393                 :   //lame lame lame if delete from document goes away then get rid of this unless its debug
     394                 :   friend class nsFrameSelection;
     395                 : 
     396                 :   nsSelectionIterator(nsTypedSelection *);
     397                 :   virtual ~nsSelectionIterator();
     398                 :   PRInt32     mIndex;
     399                 :   nsTypedSelection *mDomSelection;
     400                 :   SelectionType mType;
     401                 : };
     402                 : 
     403                 : class nsAutoScrollTimer : public nsITimerCallback
     404                 : {
     405                 : public:
     406                 : 
     407                 :   NS_DECL_ISUPPORTS
     408                 : 
     409               0 :   nsAutoScrollTimer()
     410               0 :   : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30)
     411                 :   {
     412               0 :   }
     413                 : 
     414               0 :   virtual ~nsAutoScrollTimer()
     415               0 :   {
     416               0 :    if (mTimer)
     417               0 :        mTimer->Cancel();
     418               0 :   }
     419                 : 
     420                 :   // aPoint is relative to aPresContext's root frame
     421               0 :   nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
     422                 :   {
     423               0 :     mPoint = aPoint;
     424                 : 
     425                 :     // Store the presentation context. The timer will be
     426                 :     // stopped by the selection if the prescontext is destroyed.
     427               0 :     mPresContext = aPresContext;
     428                 : 
     429               0 :     mContent = nsIPresShell::GetCapturingContent();
     430                 : 
     431               0 :     if (!mTimer)
     432                 :     {
     433                 :       nsresult result;
     434               0 :       mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
     435                 : 
     436               0 :       if (NS_FAILED(result))
     437               0 :         return result;
     438                 :     }
     439                 : 
     440               0 :     return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
     441                 :   }
     442                 : 
     443               0 :   nsresult Stop()
     444                 :   {
     445               0 :     if (mTimer)
     446                 :     {
     447               0 :       mTimer->Cancel();
     448               0 :       mTimer = 0;
     449                 :     }
     450                 : 
     451               0 :     mContent = nsnull;
     452               0 :     return NS_OK;
     453                 :   }
     454                 : 
     455               0 :   nsresult Init(nsFrameSelection *aFrameSelection, nsTypedSelection *aSelection)
     456                 :   {
     457               0 :     mFrameSelection = aFrameSelection;
     458               0 :     mSelection = aSelection;
     459               0 :     return NS_OK;
     460                 :   }
     461                 : 
     462               0 :   nsresult SetDelay(PRUint32 aDelay)
     463                 :   {
     464               0 :     mDelay = aDelay;
     465               0 :     return NS_OK;
     466                 :   }
     467                 : 
     468               0 :   NS_IMETHOD Notify(nsITimer *timer)
     469                 :   {
     470               0 :     if (mSelection && mPresContext)
     471                 :     {
     472                 :       nsWeakFrame frame =
     473               0 :         mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nsnull;
     474               0 :       if (!frame)
     475               0 :         return NS_OK;
     476               0 :       mContent = nsnull;
     477                 : 
     478                 :       nsPoint pt = mPoint -
     479               0 :         frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
     480               0 :       mFrameSelection->HandleDrag(frame, pt);
     481               0 :       if (!frame.IsAlive())
     482               0 :         return NS_OK;
     483                 : 
     484               0 :       NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
     485               0 :       mSelection->DoAutoScroll(frame, pt);
     486                 :     }
     487               0 :     return NS_OK;
     488                 :   }
     489                 : private:
     490                 :   nsFrameSelection *mFrameSelection;
     491                 :   nsTypedSelection *mSelection;
     492                 :   nsPresContext *mPresContext;
     493                 :   // relative to mPresContext's root frame
     494                 :   nsPoint mPoint;
     495                 :   nsCOMPtr<nsITimer> mTimer;
     496                 :   nsCOMPtr<nsIContent> mContent;
     497                 :   PRUint32 mDelay;
     498                 : };
     499                 : 
     500               0 : NS_IMPL_ISUPPORTS1(nsAutoScrollTimer, nsITimerCallback)
     501                 : 
     502               0 : nsresult NS_NewDomSelection(nsISelection **aDomSelection)
     503                 : {
     504               0 :   nsTypedSelection *rlist = new nsTypedSelection;
     505               0 :   *aDomSelection = (nsISelection *)rlist;
     506               0 :   NS_ADDREF(rlist);
     507               0 :   return NS_OK;
     508                 : }
     509                 : 
     510                 : static PRInt8
     511               0 : GetIndexFromSelectionType(SelectionType aType)
     512                 : {
     513               0 :     switch (aType)
     514                 :     {
     515               0 :     case nsISelectionController::SELECTION_NORMAL: return 0; break;
     516               0 :     case nsISelectionController::SELECTION_SPELLCHECK: return 1; break;
     517               0 :     case nsISelectionController::SELECTION_IME_RAWINPUT: return 2; break;
     518               0 :     case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT: return 3; break;
     519               0 :     case nsISelectionController::SELECTION_IME_CONVERTEDTEXT: return 4; break;
     520               0 :     case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: return 5; break;
     521               0 :     case nsISelectionController::SELECTION_ACCESSIBILITY: return 6; break;
     522               0 :     case nsISelectionController::SELECTION_FIND: return 7; break;
     523               0 :     case nsISelectionController::SELECTION_URLSECONDARY: return 8; break;
     524                 :     default:
     525               0 :       return -1; break;
     526                 :     }
     527                 :     /* NOTREACHED */
     528                 :     return 0;
     529                 : }
     530                 : 
     531                 : static SelectionType 
     532               0 : GetSelectionTypeFromIndex(PRInt8 aIndex)
     533                 : {
     534               0 :   switch (aIndex)
     535                 :   {
     536               0 :     case 0: return nsISelectionController::SELECTION_NORMAL; break;
     537               0 :     case 1: return nsISelectionController::SELECTION_SPELLCHECK; break;
     538               0 :     case 2: return nsISelectionController::SELECTION_IME_RAWINPUT; break;
     539               0 :     case 3: return nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT; break;
     540               0 :     case 4: return nsISelectionController::SELECTION_IME_CONVERTEDTEXT; break;
     541               0 :     case 5: return nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT; break;
     542               0 :     case 6: return nsISelectionController::SELECTION_ACCESSIBILITY; break;
     543               0 :     case 7: return nsISelectionController::SELECTION_FIND; break;
     544               0 :     case 8: return nsISelectionController::SELECTION_URLSECONDARY; break;
     545                 :     default:
     546               0 :       return nsISelectionController::SELECTION_NORMAL; break;
     547                 :   }
     548                 :   /* NOTREACHED */
     549                 :   return 0;
     550                 : }
     551                 : 
     552                 : /*
     553                 : The limiter is used specifically for the text areas and textfields
     554                 : In that case it is the DIV tag that is anonymously created for the text
     555                 : areas/fields.  Text nodes and BR nodes fall beneath it.  In the case of a 
     556                 : BR node the limiter will be the parent and the offset will point before or
     557                 : after the BR node.  In the case of the text node the parent content is 
     558                 : the text node itself and the offset will be the exact character position.
     559                 : The offset is not important to check for validity.  Simply look at the 
     560                 : passed in content.  If it equals the limiter then the selection point is valid.
     561                 : If its parent it the limiter then the point is also valid.  In the case of 
     562                 : NO limiter all points are valid since you are in a topmost iframe. (browser
     563                 : or composer)
     564                 : */
     565                 : bool         
     566               0 : IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode)
     567                 : {
     568               0 :   if (!aFrameSel || !aNode)
     569               0 :     return false;
     570                 : 
     571               0 :   nsIContent *limiter = aFrameSel->GetLimiter();
     572               0 :   if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
     573                 :     //if newfocus == the limiter. that's ok. but if not there and not parent bad
     574               0 :     return false; //not in the right content. tLimiter said so
     575                 :   }
     576                 : 
     577               0 :   limiter = aFrameSel->GetAncestorLimiter();
     578               0 :   return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
     579                 : }
     580                 : 
     581                 : 
     582               0 : NS_IMPL_ADDREF(nsSelectionIterator)
     583               0 : NS_IMPL_RELEASE(nsSelectionIterator)
     584                 : 
     585               0 : NS_INTERFACE_MAP_BEGIN(nsSelectionIterator)
     586               0 :   NS_INTERFACE_MAP_ENTRY(nsIEnumerator)
     587               0 :   NS_INTERFACE_MAP_ENTRY(nsIBidirectionalEnumerator)
     588               0 : NS_INTERFACE_MAP_END_AGGREGATED(mDomSelection)
     589                 : 
     590                 : 
     591                 : ///////////BEGIN nsSelectionIterator methods
     592                 : 
     593               0 : nsSelectionIterator::nsSelectionIterator(nsTypedSelection *aList)
     594               0 : :mIndex(0)
     595                 : {
     596               0 :   if (!aList)
     597                 :   {
     598               0 :     NS_NOTREACHED("nsFrameSelection");
     599               0 :     return;
     600                 :   }
     601               0 :   mDomSelection = aList;
     602                 : }
     603                 : 
     604                 : 
     605                 : 
     606               0 : nsSelectionIterator::~nsSelectionIterator()
     607                 : {
     608               0 : }
     609                 : 
     610                 : 
     611                 : 
     612                 : ////////////END nsSelectionIterator methods
     613                 : 
     614                 : ////////////BEGIN nsSelectionIterator methods
     615                 : 
     616                 : 
     617                 : 
     618                 : NS_IMETHODIMP
     619               0 : nsSelectionIterator::Next()
     620                 : {
     621               0 :   mIndex++;
     622               0 :   PRInt32 cnt = mDomSelection->mRanges.Length();
     623               0 :   if (mIndex < cnt)
     624               0 :     return NS_OK;
     625               0 :   return NS_ERROR_FAILURE;
     626                 : }
     627                 : 
     628                 : 
     629                 : 
     630                 : NS_IMETHODIMP
     631               0 : nsSelectionIterator::Prev()
     632                 : {
     633               0 :   mIndex--;
     634               0 :   if (mIndex >= 0 )
     635               0 :     return NS_OK;
     636               0 :   return NS_ERROR_FAILURE;
     637                 : }
     638                 : 
     639                 : 
     640                 : 
     641                 : NS_IMETHODIMP
     642               0 : nsSelectionIterator::First()
     643                 : {
     644               0 :   if (!mDomSelection)
     645               0 :     return NS_ERROR_NULL_POINTER;
     646               0 :   mIndex = 0;
     647               0 :   return NS_OK;
     648                 : }
     649                 : 
     650                 : 
     651                 : 
     652                 : NS_IMETHODIMP
     653               0 : nsSelectionIterator::Last()
     654                 : {
     655               0 :   if (!mDomSelection)
     656               0 :     return NS_ERROR_NULL_POINTER;
     657               0 :   mIndex = mDomSelection->mRanges.Length() - 1;
     658               0 :   return NS_OK;
     659                 : }
     660                 : 
     661                 : 
     662                 : 
     663                 : NS_IMETHODIMP 
     664               0 : nsSelectionIterator::CurrentItem(nsISupports **aItem)
     665                 : {
     666               0 :   *aItem = static_cast<nsIDOMRange*>(CurrentItem());
     667               0 :   if (!*aItem) {
     668               0 :     return NS_ERROR_FAILURE;
     669                 :   }
     670                 : 
     671               0 :   NS_ADDREF(*aItem);
     672               0 :   return NS_OK;
     673                 : }
     674                 : 
     675                 : nsRange*
     676               0 : nsSelectionIterator::CurrentItem()
     677                 : {
     678               0 :   return mDomSelection->mRanges.SafeElementAt(mIndex, sEmptyData).mRange;
     679                 : }
     680                 : 
     681                 : 
     682                 : 
     683                 : NS_IMETHODIMP
     684               0 : nsSelectionIterator::IsDone()
     685                 : {
     686               0 :   PRInt32 cnt = mDomSelection->mRanges.Length();
     687               0 :   if (mIndex >= 0 && mIndex < cnt) {
     688               0 :     return NS_ENUMERATOR_FALSE;
     689                 :   }
     690               0 :   return NS_OK;
     691                 : }
     692                 : 
     693                 : 
     694                 : ////////////END nsSelectionIterator methods
     695                 : 
     696                 : 
     697                 : ////////////BEGIN nsFrameSelection methods
     698                 : 
     699               0 : nsFrameSelection::nsFrameSelection()
     700               0 :   : mDelayedMouseEvent(false, 0, nsnull, nsMouseEvent::eReal)
     701                 : {
     702                 :   PRInt32 i;
     703               0 :   for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
     704               0 :     mDomSelections[i] = new nsTypedSelection(this);
     705               0 :     mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
     706                 :   }
     707               0 :   mBatching = 0;
     708               0 :   mChangesDuringBatching = false;
     709               0 :   mNotifyFrames = true;
     710               0 :   mLimiter = nsnull; //no default limiter.
     711               0 :   mAncestorLimiter = nsnull;
     712                 :   
     713               0 :   mMouseDoubleDownState = false;
     714                 :   
     715               0 :   mHint = HINTLEFT;
     716                 : #ifdef IBMBIDI
     717               0 :   mCaretBidiLevel = BIDI_LEVEL_UNDEFINED;
     718                 : #endif
     719               0 :   mDragSelectingCells = false;
     720               0 :   mSelectingTableCellMode = 0;
     721               0 :   mSelectedCellIndex = 0;
     722                 : 
     723                 :   // Check to see if the autocopy pref is enabled
     724                 :   //   and add the autocopy listener if it is
     725               0 :   if (Preferences::GetBool("clipboard.autocopy")) {
     726               0 :     nsAutoCopyListener *autoCopy = nsAutoCopyListener::GetInstance();
     727                 : 
     728               0 :     if (autoCopy) {
     729                 :       PRInt8 index =
     730               0 :         GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
     731               0 :       if (mDomSelections[index]) {
     732               0 :         autoCopy->Listen(mDomSelections[index]);
     733                 :       }
     734                 :     }
     735                 :   }
     736                 : 
     737               0 :   mDisplaySelection = nsISelectionController::SELECTION_OFF;
     738                 : 
     739               0 :   mDelayedMouseEventValid = false;
     740               0 :   mSelectionChangeReason = nsISelectionListener::NO_REASON;
     741               0 : }
     742                 : 
     743                 : 
     744            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection)
     745               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection)
     746                 :   PRInt32 i;
     747               0 :   for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) {
     748               0 :     tmp->mDomSelections[i] = nsnull;
     749                 :   }
     750                 : 
     751               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCellParent)
     752               0 :   tmp->mSelectingTableCellMode = 0;
     753               0 :   tmp->mDragSelectingCells = false;
     754               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStartSelectedCell)
     755               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEndSelectedCell)
     756               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mAppendStartSelectedCell)
     757               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mUnselectCellOnMouseUp)
     758               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mMaintainRange)
     759               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     760               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameSelection)
     761               0 :   if (tmp->mShell && tmp->mShell->GetDocument() &&
     762                 :       nsCCUncollectableMarker::InGeneration(cb,
     763                 :                                             tmp->mShell->GetDocument()->
     764               0 :                                               GetMarkedCCGeneration())) {
     765               0 :     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
     766                 :   }
     767                 :   PRInt32 i;
     768               0 :   for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) {
     769               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mDomSelections[i],
     770                 :                                                          nsISelection)
     771                 :   }
     772                 : 
     773               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCellParent)
     774               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStartSelectedCell)
     775               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEndSelectedCell)
     776               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mAppendStartSelectedCell)
     777               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mUnselectCellOnMouseUp)
     778               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mMaintainRange, nsIDOMRange)
     779               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     780                 : 
     781               0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameSelection)
     782               0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameSelection)
     783               0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameSelection)
     784               0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     785               0 : NS_INTERFACE_MAP_END
     786                 : 
     787                 : 
     788                 : nsresult
     789               0 : nsFrameSelection::FetchDesiredX(nscoord &aDesiredX) //the x position requested by the Key Handling for up down
     790                 : {
     791               0 :   if (!mShell)
     792                 :   {
     793               0 :     NS_ERROR("fetch desired X failed");
     794               0 :     return NS_ERROR_FAILURE;
     795                 :   }
     796               0 :   if (mDesiredXSet)
     797                 :   {
     798               0 :     aDesiredX = mDesiredX;
     799               0 :     return NS_OK;
     800                 :   }
     801                 : 
     802               0 :   nsRefPtr<nsCaret> caret = mShell->GetCaret();
     803               0 :   if (!caret)
     804               0 :     return NS_ERROR_NULL_POINTER;
     805                 : 
     806               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
     807               0 :   nsresult result = caret->SetCaretDOMSelection(mDomSelections[index]);
     808               0 :   if (NS_FAILED(result))
     809               0 :     return result;
     810                 : 
     811               0 :   nsRect coord;
     812               0 :   nsIFrame* caretFrame = caret->GetGeometry(mDomSelections[index], &coord);
     813               0 :   if (!caretFrame)
     814               0 :     return NS_ERROR_FAILURE;
     815               0 :   nsPoint viewOffset(0, 0);
     816               0 :   nsIView* view = nsnull;
     817               0 :   caretFrame->GetOffsetFromView(viewOffset, &view);
     818               0 :   if (view)
     819               0 :     coord.x += viewOffset.x;
     820                 : 
     821               0 :   aDesiredX = coord.x;
     822               0 :   return NS_OK;
     823                 : }
     824                 : 
     825                 : 
     826                 : 
     827                 : void
     828               0 : nsFrameSelection::InvalidateDesiredX() //do not listen to mDesiredX you must get another.
     829                 : {
     830               0 :   mDesiredXSet = false;
     831               0 : }
     832                 : 
     833                 : 
     834                 : 
     835                 : void
     836               0 : nsFrameSelection::SetDesiredX(nscoord aX) //set the mDesiredX
     837                 : {
     838               0 :   mDesiredX = aX;
     839               0 :   mDesiredXSet = true;
     840               0 : }
     841                 : 
     842                 : nsresult
     843               0 : nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(nsIFrame  *aFrame,
     844                 :                                                         nsPoint&   aPoint,
     845                 :                                                         nsIFrame **aRetFrame,
     846                 :                                                         nsPoint&   aRetPoint)
     847                 : {
     848                 :   //
     849                 :   // The whole point of this method is to return a frame and point that
     850                 :   // that lie within the same valid subtree as the anchor node's frame,
     851                 :   // for use with the method GetContentAndOffsetsFromPoint().
     852                 :   //
     853                 :   // A valid subtree is defined to be one where all the content nodes in
     854                 :   // the tree have a valid parent-child relationship.
     855                 :   //
     856                 :   // If the anchor frame and aFrame are in the same subtree, aFrame will
     857                 :   // be returned in aRetFrame. If they are in different subtrees, we
     858                 :   // return the frame for the root of the subtree.
     859                 :   //
     860                 : 
     861               0 :   if (!aFrame || !aRetFrame)
     862               0 :     return NS_ERROR_NULL_POINTER;
     863                 : 
     864               0 :   *aRetFrame = aFrame;
     865               0 :   aRetPoint  = aPoint;
     866                 : 
     867                 :   //
     868                 :   // Get the frame and content for the selection's anchor point!
     869                 :   //
     870                 : 
     871                 :   nsresult result;
     872               0 :   nsCOMPtr<nsIDOMNode> anchorNode;
     873               0 :   PRInt32 anchorOffset = 0;
     874                 : 
     875               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
     876               0 :   if (!mDomSelections[index])
     877               0 :     return NS_ERROR_NULL_POINTER;
     878                 : 
     879               0 :   result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode));
     880                 : 
     881               0 :   if (NS_FAILED(result))
     882               0 :     return result;
     883                 : 
     884               0 :   if (!anchorNode)
     885               0 :     return NS_OK;
     886                 : 
     887               0 :   result = mDomSelections[index]->GetAnchorOffset(&anchorOffset);
     888                 : 
     889               0 :   if (NS_FAILED(result))
     890               0 :     return result;
     891                 : 
     892               0 :   nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
     893                 : 
     894               0 :   if (!anchorContent)
     895               0 :     return NS_ERROR_FAILURE;
     896                 :   
     897                 :   //
     898                 :   // Now find the root of the subtree containing the anchor's content.
     899                 :   //
     900                 : 
     901               0 :   NS_ENSURE_STATE(mShell);
     902               0 :   nsIContent* anchorRoot = anchorContent->GetSelectionRootContent(mShell);
     903               0 :   NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED);
     904                 : 
     905                 :   //
     906                 :   // Now find the root of the subtree containing aFrame's content.
     907                 :   //
     908                 : 
     909               0 :   nsIContent* content = aFrame->GetContent();
     910                 : 
     911               0 :   if (content)
     912                 :   {
     913               0 :     nsIContent* contentRoot = content->GetSelectionRootContent(mShell);
     914               0 :     NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED);
     915                 : 
     916               0 :     if (anchorRoot == contentRoot)
     917                 :     {
     918                 :       // If the aFrame's content isn't the capturing content, it should be
     919                 :       // a descendant.  At this time, we can return simply.
     920               0 :       nsIContent* capturedContent = nsIPresShell::GetCapturingContent();
     921               0 :       if (capturedContent != content)
     922                 :       {
     923               0 :         return NS_OK;
     924                 :       }
     925                 : 
     926                 :       // Find the frame under the mouse cursor with the root frame.
     927                 :       // At this time, don't use the anchor's frame because it may not have
     928                 :       // fixed positioned frames.
     929               0 :       nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame();
     930               0 :       nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame);
     931                 :       nsIFrame* cursorFrame =
     932               0 :         nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
     933                 : 
     934                 :       // If the mouse cursor in on a frame which is descendant of same
     935                 :       // selection root, we can expand the selection to the frame.
     936               0 :       if (cursorFrame && cursorFrame->PresContext()->PresShell() == mShell)
     937                 :       {
     938               0 :         nsIContent* cursorContent = cursorFrame->GetContent();
     939               0 :         NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE);
     940                 :         nsIContent* cursorContentRoot =
     941               0 :           cursorContent->GetSelectionRootContent(mShell);
     942               0 :         NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED);
     943               0 :         if (cursorContentRoot == anchorRoot)
     944                 :         {
     945               0 :           *aRetFrame = cursorFrame;
     946               0 :           aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame);
     947               0 :           return NS_OK;
     948                 :         }
     949                 :       }
     950                 :       // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse
     951                 :       // cursor is out of the window), we should use the frame of the anchor
     952                 :       // root.
     953                 :     }
     954                 :   }
     955                 : 
     956                 :   //
     957                 :   // When we can't find a frame which is under the mouse cursor and has a same
     958                 :   // selection root as the anchor node's, we should return the selection root
     959                 :   // frame.
     960                 :   //
     961                 : 
     962               0 :   *aRetFrame = anchorRoot->GetPrimaryFrame();
     963                 : 
     964               0 :   if (!*aRetFrame)
     965               0 :     return NS_ERROR_FAILURE;
     966                 : 
     967                 :   //
     968                 :   // Now make sure that aRetPoint is converted to the same coordinate
     969                 :   // system used by aRetFrame.
     970                 :   //
     971                 : 
     972               0 :   aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
     973                 : 
     974               0 :   return NS_OK;
     975                 : }
     976                 : 
     977                 : #ifdef IBMBIDI
     978                 : void
     979               0 : nsFrameSelection::SetCaretBidiLevel(PRUint8 aLevel)
     980                 : {
     981                 :   // If the current level is undefined, we have just inserted new text.
     982                 :   // In this case, we don't want to reset the keyboard language
     983               0 :   bool afterInsert = !!(mCaretBidiLevel & BIDI_LEVEL_UNDEFINED);
     984               0 :   mCaretBidiLevel = aLevel;
     985                 :   
     986               0 :   nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
     987               0 :   if (bidiKeyboard && !afterInsert)
     988               0 :     bidiKeyboard->SetLangFromBidiLevel(aLevel);
     989                 :   return;
     990                 : }
     991                 : 
     992                 : PRUint8
     993               0 : nsFrameSelection::GetCaretBidiLevel() const
     994                 : {
     995               0 :   return mCaretBidiLevel;
     996                 : }
     997                 : 
     998                 : void
     999               0 : nsFrameSelection::UndefineCaretBidiLevel()
    1000                 : {
    1001               0 :   mCaretBidiLevel |= BIDI_LEVEL_UNDEFINED;
    1002               0 : }
    1003                 : #endif
    1004                 : 
    1005                 : 
    1006                 : #ifdef PRINT_RANGE
    1007                 : void printRange(nsRange *aDomRange)
    1008                 : {
    1009                 :   if (!aDomRange)
    1010                 :   {
    1011                 :     printf("NULL nsIDOMRange\n");
    1012                 :   }
    1013                 :   nsINode* startNode = aDomRange->GetStartParent();
    1014                 :   nsINode* endNode = aDomRange->GetEndParent();
    1015                 :   PRInt32 startOffset = aDomRange->StartOffset();
    1016                 :   PRInt32 endOffset = aDomRange->EndOffset();
    1017                 :   
    1018                 :   printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
    1019                 :          (unsigned long)aDomRange,
    1020                 :          (unsigned long)startNode, (long)startOffset,
    1021                 :          (unsigned long)endNode, (long)endOffset);
    1022                 :          
    1023                 : }
    1024                 : #endif /* PRINT_RANGE */
    1025                 : 
    1026                 : static
    1027               0 : nsIAtom *GetTag(nsINode *aNode)
    1028                 : {
    1029               0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
    1030               0 :   if (!content) 
    1031                 :   {
    1032               0 :     NS_NOTREACHED("bad node passed to GetTag()");
    1033               0 :     return nsnull;
    1034                 :   }
    1035                 :   
    1036               0 :   return content->Tag();
    1037                 : }
    1038                 : 
    1039                 : // Returns the parent
    1040                 : nsINode*
    1041               0 : ParentOffset(nsINode *aNode, PRInt32 *aChildOffset)
    1042                 : {
    1043               0 :   if (!aNode || !aChildOffset)
    1044               0 :     return nsnull;
    1045                 : 
    1046               0 :   nsIContent* parent = aNode->GetParent();
    1047               0 :   if (parent)
    1048                 :   {
    1049               0 :     *aChildOffset = parent->IndexOf(aNode);
    1050                 : 
    1051               0 :     return parent;
    1052                 :   }
    1053                 : 
    1054               0 :   return nsnull;
    1055                 : }
    1056                 : 
    1057                 : static nsINode*
    1058               0 : GetCellParent(nsINode *aDomNode)
    1059                 : {
    1060               0 :     if (!aDomNode)
    1061               0 :       return nsnull;
    1062               0 :     nsINode* current = aDomNode;
    1063                 :     // Start with current node and look for a table cell
    1064               0 :     while (current)
    1065                 :     {
    1066               0 :       nsIAtom* tag = GetTag(current);
    1067               0 :       if (tag == nsGkAtoms::td || tag == nsGkAtoms::th)
    1068               0 :         return current;
    1069               0 :       current = current->GetParent();
    1070                 :     }
    1071               0 :     return nsnull;
    1072                 : }
    1073                 : 
    1074                 : 
    1075                 : void
    1076               0 : nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
    1077                 : {
    1078               0 :   mShell = aShell;
    1079               0 :   mMouseDownState = false;
    1080               0 :   mDesiredXSet = false;
    1081               0 :   mLimiter = aLimiter;
    1082                 :   mCaretMovementStyle =
    1083               0 :     Preferences::GetInt("bidi.edit.caret_movement_style", 2);
    1084               0 : }
    1085                 : 
    1086                 : nsresult
    1087               0 : nsFrameSelection::MoveCaret(PRUint32          aKeycode,
    1088                 :                             bool              aContinueSelection,
    1089                 :                             nsSelectionAmount aAmount)
    1090                 : {
    1091                 :   bool visualMovement =
    1092                 :       (aKeycode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE ||
    1093                 :        aKeycode == nsIDOMKeyEvent::DOM_VK_DELETE ||
    1094                 :        aKeycode == nsIDOMKeyEvent::DOM_VK_HOME ||
    1095                 :        aKeycode == nsIDOMKeyEvent::DOM_VK_END) ?
    1096                 :       false : // Delete operations and home/end are always logical
    1097                 :       mCaretMovementStyle == 1 ||
    1098               0 :         (mCaretMovementStyle == 2 && !aContinueSelection);
    1099                 : 
    1100               0 :   return MoveCaret(aKeycode, aContinueSelection, aAmount, visualMovement);
    1101                 : }
    1102                 : 
    1103                 : nsresult
    1104               0 : nsFrameSelection::MoveCaret(PRUint32          aKeycode,
    1105                 :                             bool              aContinueSelection,
    1106                 :                             nsSelectionAmount aAmount,
    1107                 :                             bool              aVisualMovement)
    1108                 : {
    1109               0 :   NS_ENSURE_STATE(mShell);
    1110                 :   // Flush out layout, since we need it to be up to date to do caret
    1111                 :   // positioning.
    1112               0 :   mShell->FlushPendingNotifications(Flush_Layout);
    1113                 : 
    1114               0 :   if (!mShell) {
    1115               0 :     return NS_OK;
    1116                 :   }
    1117                 : 
    1118               0 :   nsPresContext *context = mShell->GetPresContext();
    1119               0 :   if (!context)
    1120               0 :     return NS_ERROR_FAILURE;
    1121                 : 
    1122                 :   bool isCollapsed;
    1123               0 :   nscoord desiredX = 0; //we must keep this around and revalidate it when its just UP/DOWN
    1124                 : 
    1125               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    1126               0 :   nsRefPtr<nsTypedSelection> sel = mDomSelections[index];
    1127               0 :   if (!sel)
    1128               0 :     return NS_ERROR_NULL_POINTER;
    1129                 : 
    1130               0 :   nsresult result = sel->GetIsCollapsed(&isCollapsed);
    1131               0 :   if (NS_FAILED(result))
    1132               0 :     return result;
    1133               0 :   if (aKeycode == nsIDOMKeyEvent::DOM_VK_UP ||
    1134                 :       aKeycode == nsIDOMKeyEvent::DOM_VK_DOWN)
    1135                 :   {
    1136               0 :     result = FetchDesiredX(desiredX);
    1137               0 :     if (NS_FAILED(result))
    1138               0 :       return result;
    1139               0 :     SetDesiredX(desiredX);
    1140                 :   }
    1141                 : 
    1142                 :   PRInt32 caretStyle =
    1143               0 :     Preferences::GetInt("layout.selection.caret_style", 0);
    1144                 : #ifdef XP_MACOSX
    1145                 :   if (caretStyle == 0) {
    1146                 :     caretStyle = 2; // put caret at the selection edge in the |aKeycode| direction
    1147                 :   }
    1148                 : #endif
    1149                 : 
    1150               0 :   if (!isCollapsed && !aContinueSelection && caretStyle == 2) {
    1151               0 :     switch (aKeycode){
    1152                 :       case nsIDOMKeyEvent::DOM_VK_LEFT  :
    1153                 :       case nsIDOMKeyEvent::DOM_VK_UP    :
    1154                 :         {
    1155               0 :           const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
    1156               0 :           if (anchorFocusRange) {
    1157                 :             sel->Collapse(anchorFocusRange->GetStartParent(),
    1158               0 :                           anchorFocusRange->StartOffset());
    1159                 :           }
    1160               0 :           mHint = HINTRIGHT;
    1161               0 :           sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
    1162               0 :           return NS_OK;
    1163                 :         }
    1164                 : 
    1165                 :       case nsIDOMKeyEvent::DOM_VK_RIGHT :
    1166                 :       case nsIDOMKeyEvent::DOM_VK_DOWN  :
    1167                 :         {
    1168               0 :           const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
    1169               0 :           if (anchorFocusRange) {
    1170                 :             sel->Collapse(anchorFocusRange->GetEndParent(),
    1171               0 :                           anchorFocusRange->EndOffset());
    1172                 :           }
    1173               0 :           mHint = HINTLEFT;
    1174               0 :           sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
    1175               0 :           return NS_OK;
    1176                 :         }
    1177                 :     }
    1178                 :   }
    1179                 : 
    1180                 :   nsIFrame *frame;
    1181               0 :   PRInt32 offsetused = 0;
    1182               0 :   result = sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
    1183               0 :                                             aVisualMovement);
    1184                 : 
    1185               0 :   if (NS_FAILED(result) || !frame)
    1186               0 :     return result?result:NS_ERROR_FAILURE;
    1187                 : 
    1188               0 :   nsPeekOffsetStruct pos;
    1189                 :   //set data using mLimiter to stop on scroll views.  If we have a limiter then we stop peeking
    1190                 :   //when we hit scrollable views.  If no limiter then just let it go ahead
    1191                 :   pos.SetData(aAmount, eDirPrevious, offsetused, desiredX, 
    1192               0 :               true, mLimiter != nsnull, true, aVisualMovement);
    1193                 : 
    1194               0 :   nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame);
    1195                 :   
    1196               0 :   HINT tHint(mHint); //temporary variable so we dont set mHint until it is necessary
    1197               0 :   switch (aKeycode){
    1198                 :     case nsIDOMKeyEvent::DOM_VK_RIGHT : 
    1199               0 :         InvalidateDesiredX();
    1200               0 :         pos.mDirection = (baseLevel & 1) ? eDirPrevious : eDirNext;
    1201               0 :       break;
    1202                 :     case nsIDOMKeyEvent::DOM_VK_LEFT :
    1203               0 :         InvalidateDesiredX();
    1204               0 :         pos.mDirection = (baseLevel & 1) ? eDirNext : eDirPrevious;
    1205               0 :       break;
    1206                 :     case nsIDOMKeyEvent::DOM_VK_DELETE :
    1207               0 :         InvalidateDesiredX();
    1208               0 :         pos.mDirection = eDirNext;
    1209               0 :       break;
    1210                 :     case nsIDOMKeyEvent::DOM_VK_BACK_SPACE : 
    1211               0 :         InvalidateDesiredX();
    1212               0 :         pos.mDirection = eDirPrevious;
    1213               0 :       break;
    1214                 :     case nsIDOMKeyEvent::DOM_VK_DOWN : 
    1215               0 :         pos.mAmount = eSelectLine;
    1216               0 :         pos.mDirection = eDirNext;
    1217               0 :       break;
    1218                 :     case nsIDOMKeyEvent::DOM_VK_UP : 
    1219               0 :         pos.mAmount = eSelectLine;
    1220               0 :         pos.mDirection = eDirPrevious;
    1221               0 :       break;
    1222                 :     case nsIDOMKeyEvent::DOM_VK_HOME :
    1223               0 :         InvalidateDesiredX();
    1224               0 :         pos.mAmount = eSelectBeginLine;
    1225               0 :       break;
    1226                 :     case nsIDOMKeyEvent::DOM_VK_END :
    1227               0 :         InvalidateDesiredX();
    1228               0 :         pos.mAmount = eSelectEndLine;
    1229               0 :       break;
    1230               0 :   default :return NS_ERROR_FAILURE;
    1231                 :   }
    1232               0 :   PostReason(nsISelectionListener::KEYPRESS_REASON);
    1233               0 :   if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent)
    1234                 :   {
    1235                 :     nsIFrame *theFrame;
    1236                 :     PRInt32 currentOffset, frameStart, frameEnd;
    1237                 : 
    1238               0 :     if (aAmount >= eSelectCharacter && aAmount <= eSelectWord)
    1239                 :     {
    1240                 :       // For left/right, PeekOffset() sets pos.mResultFrame correctly, but does not set pos.mAttachForward,
    1241                 :       // so determine the hint here based on the result frame and offset:
    1242                 :       // If we're at the end of a text frame, set the hint to HINTLEFT to indicate that we
    1243                 :       // want the caret displayed at the end of this frame, not at the beginning of the next one.
    1244               0 :       theFrame = pos.mResultFrame;
    1245               0 :       theFrame->GetOffsets(frameStart, frameEnd);
    1246               0 :       currentOffset = pos.mContentOffset;
    1247               0 :       if (frameEnd == currentOffset && !(frameStart == 0 && frameEnd == 0))
    1248               0 :         tHint = HINTLEFT;
    1249                 :       else
    1250               0 :         tHint = HINTRIGHT;
    1251                 :     } else {
    1252                 :       // For up/down and home/end, pos.mResultFrame might not be set correctly, or not at all.
    1253                 :       // In these cases, get the frame based on the content and hint returned by PeekOffset().
    1254               0 :       tHint = (HINT)pos.mAttachForward;
    1255                 :       theFrame = GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset,
    1256               0 :                                        tHint, &currentOffset);
    1257               0 :       if (!theFrame)
    1258               0 :         return NS_ERROR_FAILURE;
    1259                 : 
    1260               0 :       theFrame->GetOffsets(frameStart, frameEnd);
    1261                 :     }
    1262                 : 
    1263               0 :     if (context->BidiEnabled())
    1264                 :     {
    1265               0 :       switch (aKeycode) {
    1266                 :         case nsIDOMKeyEvent::DOM_VK_HOME:
    1267                 :         case nsIDOMKeyEvent::DOM_VK_END:
    1268                 :           // set the caret Bidi level to the paragraph embedding level
    1269               0 :           SetCaretBidiLevel(NS_GET_BASE_LEVEL(theFrame));
    1270               0 :           break;
    1271                 : 
    1272                 :         default:
    1273                 :           // If the current position is not a frame boundary, it's enough just to take the Bidi level of the current frame
    1274               0 :           if ((pos.mContentOffset != frameStart && pos.mContentOffset != frameEnd)
    1275                 :               || (eSelectLine == aAmount))
    1276                 :           {
    1277               0 :             SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame));
    1278                 :           }
    1279                 :           else
    1280               0 :             BidiLevelFromMove(mShell, pos.mResultContent, pos.mContentOffset, aKeycode, tHint);
    1281                 :       }
    1282                 :     }
    1283                 :     result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset,
    1284               0 :                        tHint, aContinueSelection, false);
    1285               0 :   } else if (aKeycode == nsIDOMKeyEvent::DOM_VK_RIGHT && !aContinueSelection) {
    1286                 :     // Collapse selection if PeekOffset failed, we either
    1287                 :     //  1. bumped into the BRFrame, bug 207623
    1288                 :     //  2. had select-all in a text input (DIV range), bug 352759.
    1289               0 :     bool isBRFrame = frame->GetType() == nsGkAtoms::brFrame;
    1290               0 :     sel->Collapse(sel->GetFocusNode(), sel->GetFocusOffset());
    1291                 :     // Note: 'frame' might be dead here.
    1292               0 :     if (!isBRFrame) {
    1293               0 :       mHint = HINTLEFT; // We're now at the end of the frame to the left.
    1294                 :     }
    1295               0 :     result = NS_OK;
    1296                 :   }
    1297               0 :   if (NS_SUCCEEDED(result))
    1298                 :   {
    1299               0 :     result = mDomSelections[index]->
    1300               0 :       ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
    1301                 :   }
    1302                 : 
    1303               0 :   return result;
    1304                 : }
    1305                 : 
    1306                 : //END nsFrameSelection methods
    1307                 : 
    1308                 : 
    1309                 : //BEGIN nsFrameSelection methods
    1310                 : 
    1311                 : NS_IMETHODIMP
    1312               0 : nsTypedSelection::ToString(PRUnichar **aReturn)
    1313                 : {
    1314                 :   return ToStringWithFormat("text/plain",
    1315                 :                             nsIDocumentEncoder::SkipInvisibleContent,
    1316               0 :                             0, aReturn);
    1317                 : }
    1318                 : 
    1319                 : 
    1320                 : NS_IMETHODIMP
    1321               0 : nsTypedSelection::ToStringWithFormat(const char * aFormatType, PRUint32 aFlags, 
    1322                 :                                    PRInt32 aWrapCol, PRUnichar **aReturn)
    1323                 : {
    1324               0 :   nsresult rv = NS_OK;
    1325               0 :   if (!aReturn)
    1326               0 :     return NS_ERROR_NULL_POINTER;
    1327                 :   
    1328               0 :   nsCAutoString formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
    1329               0 :   formatType.Append(aFormatType);
    1330                 :   nsCOMPtr<nsIDocumentEncoder> encoder =
    1331               0 :            do_CreateInstance(formatType.get(), &rv);
    1332               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1333                 : 
    1334               0 :   nsCOMPtr<nsIPresShell> shell;
    1335               0 :   rv = GetPresShell(getter_AddRefs(shell));
    1336               0 :   if (NS_FAILED(rv) || !shell) {
    1337               0 :     return NS_ERROR_FAILURE;
    1338                 :   }
    1339                 : 
    1340               0 :   nsIDocument *doc = shell->GetDocument();
    1341               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1342                 : 
    1343               0 :   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
    1344               0 :   NS_ASSERTION(domDoc, "Need a document");
    1345                 : 
    1346                 :   // Flags should always include OutputSelectionOnly if we're coming from here:
    1347               0 :   aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
    1348               0 :   nsAutoString readstring;
    1349               0 :   readstring.AssignASCII(aFormatType);
    1350               0 :   rv = encoder->Init(domDoc, readstring, aFlags);
    1351               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1352                 : 
    1353               0 :   encoder->SetSelection(this);
    1354               0 :   if (aWrapCol != 0)
    1355               0 :     encoder->SetWrapColumn(aWrapCol);
    1356                 : 
    1357               0 :   nsAutoString tmp;
    1358               0 :   rv = encoder->EncodeToString(tmp);
    1359               0 :   *aReturn = ToNewUnicode(tmp);//get the unicode pointer from it. this is temporary
    1360               0 :   return rv;
    1361                 : }
    1362                 : 
    1363                 : NS_IMETHODIMP
    1364               0 : nsTypedSelection::SetInterlinePosition(bool aHintRight)
    1365                 : {
    1366               0 :   if (!mFrameSelection)
    1367               0 :     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
    1368                 :   nsFrameSelection::HINT hint;
    1369               0 :   if (aHintRight)
    1370               0 :     hint = nsFrameSelection::HINTRIGHT;
    1371                 :   else
    1372               0 :     hint = nsFrameSelection::HINTLEFT;
    1373               0 :   mFrameSelection->SetHint(hint);
    1374                 :   
    1375               0 :   return NS_OK;
    1376                 : }
    1377                 : 
    1378                 : NS_IMETHODIMP
    1379               0 : nsTypedSelection::GetInterlinePosition(bool *aHintRight)
    1380                 : {
    1381               0 :   if (!mFrameSelection)
    1382               0 :     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
    1383               0 :   *aHintRight = (mFrameSelection->GetHint() == nsFrameSelection::HINTRIGHT);
    1384               0 :   return NS_OK;
    1385                 : }
    1386                 : 
    1387                 : nsPrevNextBidiLevels
    1388               0 : nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
    1389                 :                                         PRUint32    aContentOffset,
    1390                 :                                         bool        aJumpLines) const
    1391                 : {
    1392               0 :   return GetPrevNextBidiLevels(aNode, aContentOffset, mHint, aJumpLines);
    1393                 : }
    1394                 : 
    1395                 : nsPrevNextBidiLevels
    1396               0 : nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
    1397                 :                                         PRUint32    aContentOffset,
    1398                 :                                         HINT        aHint,
    1399                 :                                         bool        aJumpLines) const
    1400                 : {
    1401                 :   // Get the level of the frames on each side
    1402                 :   nsIFrame    *currentFrame;
    1403                 :   PRInt32     currentOffset;
    1404                 :   PRInt32     frameStart, frameEnd;
    1405                 :   nsDirection direction;
    1406                 :   
    1407                 :   nsPrevNextBidiLevels levels;
    1408               0 :   levels.SetData(nsnull, nsnull, 0, 0);
    1409                 : 
    1410                 :   currentFrame = GetFrameForNodeOffset(aNode, aContentOffset,
    1411               0 :                                        aHint, &currentOffset);
    1412               0 :   if (!currentFrame)
    1413               0 :     return levels;
    1414                 : 
    1415               0 :   currentFrame->GetOffsets(frameStart, frameEnd);
    1416                 : 
    1417               0 :   if (0 == frameStart && 0 == frameEnd)
    1418               0 :     direction = eDirPrevious;
    1419               0 :   else if (frameStart == currentOffset)
    1420               0 :     direction = eDirPrevious;
    1421               0 :   else if (frameEnd == currentOffset)
    1422               0 :     direction = eDirNext;
    1423                 :   else {
    1424                 :     // we are neither at the beginning nor at the end of the frame, so we have no worries
    1425                 :     levels.SetData(currentFrame, currentFrame,
    1426               0 :                    NS_GET_EMBEDDING_LEVEL(currentFrame),
    1427               0 :                    NS_GET_EMBEDDING_LEVEL(currentFrame));
    1428               0 :     return levels;
    1429                 :   }
    1430                 : 
    1431                 :   nsIFrame *newFrame;
    1432                 :   PRInt32 offset;
    1433                 :   bool jumpedLine;
    1434                 :   nsresult rv = currentFrame->GetFrameFromDirection(direction, false,
    1435                 :                                                     aJumpLines, true,
    1436               0 :                                                     &newFrame, &offset, &jumpedLine);
    1437               0 :   if (NS_FAILED(rv))
    1438               0 :     newFrame = nsnull;
    1439                 : 
    1440               0 :   PRUint8 baseLevel = NS_GET_BASE_LEVEL(currentFrame);
    1441               0 :   PRUint8 currentLevel = NS_GET_EMBEDDING_LEVEL(currentFrame);
    1442               0 :   PRUint8 newLevel = newFrame ? NS_GET_EMBEDDING_LEVEL(newFrame) : baseLevel;
    1443                 :   
    1444                 :   // If not jumping lines, disregard br frames, since they might be positioned incorrectly.
    1445                 :   // XXX This could be removed once bug 339786 is fixed.
    1446               0 :   if (!aJumpLines) {
    1447               0 :     if (currentFrame->GetType() == nsGkAtoms::brFrame) {
    1448               0 :       currentFrame = nsnull;
    1449               0 :       currentLevel = baseLevel;
    1450                 :     }
    1451               0 :     if (newFrame && newFrame->GetType() == nsGkAtoms::brFrame) {
    1452               0 :       newFrame = nsnull;
    1453               0 :       newLevel = baseLevel;
    1454                 :     }
    1455                 :   }
    1456                 :   
    1457               0 :   if (direction == eDirNext)
    1458               0 :     levels.SetData(currentFrame, newFrame, currentLevel, newLevel);
    1459                 :   else
    1460               0 :     levels.SetData(newFrame, currentFrame, newLevel, currentLevel);
    1461                 : 
    1462               0 :   return levels;
    1463                 : }
    1464                 : 
    1465                 : nsresult
    1466               0 : nsFrameSelection::GetFrameFromLevel(nsIFrame    *aFrameIn,
    1467                 :                                     nsDirection  aDirection,
    1468                 :                                     PRUint8      aBidiLevel,
    1469                 :                                     nsIFrame   **aFrameOut) const
    1470                 : {
    1471               0 :   NS_ENSURE_STATE(mShell);
    1472               0 :   PRUint8 foundLevel = 0;
    1473               0 :   nsIFrame *foundFrame = aFrameIn;
    1474                 : 
    1475               0 :   nsCOMPtr<nsIFrameEnumerator> frameTraversal;
    1476                 :   nsresult result;
    1477               0 :   nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
    1478               0 :   if (NS_FAILED(result))
    1479               0 :       return result;
    1480                 : 
    1481               0 :   result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
    1482                 :                                    mShell->GetPresContext(), aFrameIn,
    1483                 :                                    eLeaf,
    1484                 :                                    false, // aVisual
    1485                 :                                    false, // aLockInScrollView
    1486                 :                                    false     // aFollowOOFs
    1487               0 :                                    );
    1488               0 :   if (NS_FAILED(result))
    1489               0 :     return result;
    1490                 : 
    1491               0 :   do {
    1492               0 :     *aFrameOut = foundFrame;
    1493               0 :     if (aDirection == eDirNext)
    1494               0 :       frameTraversal->Next();
    1495                 :     else 
    1496               0 :       frameTraversal->Prev();
    1497                 : 
    1498               0 :     foundFrame = frameTraversal->CurrentItem();
    1499               0 :     if (!foundFrame)
    1500               0 :       return NS_ERROR_FAILURE;
    1501               0 :     foundLevel = NS_GET_EMBEDDING_LEVEL(foundFrame);
    1502                 : 
    1503                 :   } while (foundLevel > aBidiLevel);
    1504                 : 
    1505               0 :   return NS_OK;
    1506                 : }
    1507                 : 
    1508                 : 
    1509                 : nsresult
    1510               0 : nsFrameSelection::MaintainSelection(nsSelectionAmount aAmount)
    1511                 : {
    1512               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    1513               0 :   if (!mDomSelections[index])
    1514               0 :     return NS_ERROR_NULL_POINTER;
    1515                 : 
    1516               0 :   mMaintainedAmount = aAmount;
    1517                 : 
    1518                 :   const nsRange* anchorFocusRange =
    1519               0 :     mDomSelections[index]->GetAnchorFocusRange();
    1520               0 :   if (anchorFocusRange) {
    1521               0 :     return anchorFocusRange->CloneRange(getter_AddRefs(mMaintainRange));
    1522                 :   }
    1523                 : 
    1524               0 :   mMaintainRange = nsnull;
    1525               0 :   return NS_OK;
    1526                 : }
    1527                 : 
    1528                 : 
    1529                 : /** After moving the caret, its Bidi level is set according to the following rules:
    1530                 :  *
    1531                 :  *  After moving over a character with left/right arrow, set to the Bidi level of the last moved over character.
    1532                 :  *  After Home and End, set to the paragraph embedding level.
    1533                 :  *  After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters.
    1534                 :  *  After mouse click, set to the level of the current frame.
    1535                 :  *
    1536                 :  *  The following two methods use GetPrevNextBidiLevels to determine the new Bidi level.
    1537                 :  *  BidiLevelFromMove is called when the caret is moved in response to a keyboard event
    1538                 :  *
    1539                 :  * @param aPresShell is the presentation shell
    1540                 :  * @param aNode is the content node
    1541                 :  * @param aContentOffset is the new caret position, as an offset into aNode
    1542                 :  * @param aKeycode is the keyboard event that moved the caret to the new position
    1543                 :  * @param aHint is the hint indicating in what logical direction the caret moved
    1544                 :  */
    1545               0 : void nsFrameSelection::BidiLevelFromMove(nsIPresShell* aPresShell,
    1546                 :                                          nsIContent   *aNode,
    1547                 :                                          PRUint32      aContentOffset,
    1548                 :                                          PRUint32      aKeycode,
    1549                 :                                          HINT          aHint)
    1550                 : {
    1551               0 :   switch (aKeycode) {
    1552                 : 
    1553                 :     // Right and Left: the new cursor Bidi level is the level of the character moved over
    1554                 :     case nsIDOMKeyEvent::DOM_VK_RIGHT:
    1555                 :     case nsIDOMKeyEvent::DOM_VK_LEFT:
    1556                 :     {
    1557                 :       nsPrevNextBidiLevels levels = GetPrevNextBidiLevels(aNode, aContentOffset,
    1558               0 :                                                           aHint, false);
    1559                 : 
    1560               0 :       if (HINTLEFT == aHint)
    1561               0 :         SetCaretBidiLevel(levels.mLevelBefore);
    1562                 :       else
    1563               0 :         SetCaretBidiLevel(levels.mLevelAfter);
    1564               0 :       break;
    1565                 :     }
    1566                 :       /*
    1567                 :     // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters      
    1568                 :     case nsIDOMKeyEvent::DOM_VK_UP:
    1569                 :     case nsIDOMKeyEvent::DOM_VK_DOWN:
    1570                 :       GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
    1571                 :       aPresShell->SetCaretBidiLevel(NS_MIN(firstLevel, secondLevel));
    1572                 :       break;
    1573                 :       */
    1574                 : 
    1575                 :     default:
    1576               0 :       UndefineCaretBidiLevel();
    1577                 :   }
    1578               0 : }
    1579                 : 
    1580                 : /**
    1581                 :  * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse
    1582                 :  *
    1583                 :  * @param aNode is the content node
    1584                 :  * @param aContentOffset is the new caret position, as an offset into aNode
    1585                 :  */
    1586               0 : void nsFrameSelection::BidiLevelFromClick(nsIContent *aNode,
    1587                 :                                           PRUint32    aContentOffset)
    1588                 : {
    1589               0 :   nsIFrame* clickInFrame=nsnull;
    1590                 :   PRInt32 OffsetNotUsed;
    1591                 : 
    1592               0 :   clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &OffsetNotUsed);
    1593               0 :   if (!clickInFrame)
    1594               0 :     return;
    1595                 : 
    1596               0 :   SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(clickInFrame));
    1597                 : }
    1598                 : 
    1599                 : 
    1600                 : bool
    1601               0 : nsFrameSelection::AdjustForMaintainedSelection(nsIContent *aContent,
    1602                 :                                                PRInt32     aOffset)
    1603                 : {
    1604               0 :   if (!mMaintainRange)
    1605               0 :     return false;
    1606                 : 
    1607               0 :   if (!aContent) {
    1608               0 :     return false;
    1609                 :   }
    1610                 : 
    1611               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    1612               0 :   if (!mDomSelections[index])
    1613               0 :     return false;
    1614                 : 
    1615               0 :   nsINode* rangeStartNode = mMaintainRange->GetStartParent();
    1616               0 :   nsINode* rangeEndNode = mMaintainRange->GetEndParent();
    1617               0 :   PRInt32 rangeStartOffset = mMaintainRange->StartOffset();
    1618               0 :   PRInt32 rangeEndOffset = mMaintainRange->EndOffset();
    1619                 : 
    1620                 :   PRInt32 relToStart =
    1621                 :     nsContentUtils::ComparePoints(rangeStartNode, rangeStartOffset,
    1622               0 :                                   aContent, aOffset);
    1623                 :   PRInt32 relToEnd =
    1624                 :     nsContentUtils::ComparePoints(rangeEndNode, rangeEndOffset,
    1625               0 :                                   aContent, aOffset);
    1626                 : 
    1627                 :   // If aContent/aOffset is inside the maintained selection, or if it is on the
    1628                 :   // "anchor" side of the maintained selection, we need to do something.
    1629               0 :   if ((relToStart < 0 && relToEnd > 0) ||
    1630                 :       (relToStart > 0 &&
    1631               0 :        mDomSelections[index]->GetDirection() == eDirNext) ||
    1632                 :       (relToEnd < 0 &&
    1633               0 :        mDomSelections[index]->GetDirection() == eDirPrevious)) {
    1634                 :     // Set the current range to the maintained range. 
    1635               0 :     mDomSelections[index]->ReplaceAnchorFocusRange(mMaintainRange);
    1636               0 :     if (relToStart < 0 && relToEnd > 0) {
    1637                 :       // We're inside the maintained selection, just keep it selected.
    1638               0 :       return true;
    1639                 :     }
    1640                 :     // Reverse the direction of the selection so that the anchor will be on the 
    1641                 :     // far side of the maintained selection, relative to aContent/aOffset.
    1642               0 :     mDomSelections[index]->SetDirection(relToStart > 0 ? eDirPrevious : eDirNext);
    1643                 :   }
    1644               0 :   return false;
    1645                 : }
    1646                 : 
    1647                 : 
    1648                 : nsresult
    1649               0 : nsFrameSelection::HandleClick(nsIContent *aNewFocus,
    1650                 :                               PRUint32    aContentOffset,
    1651                 :                               PRUint32    aContentEndOffset,
    1652                 :                               bool        aContinueSelection, 
    1653                 :                               bool        aMultipleSelection,
    1654                 :                               bool        aHint) 
    1655                 : {
    1656               0 :   if (!aNewFocus)
    1657               0 :     return NS_ERROR_INVALID_ARG;
    1658                 : 
    1659               0 :   InvalidateDesiredX();
    1660                 : 
    1661               0 :   if (!aContinueSelection) {
    1662               0 :     mMaintainRange = nsnull;
    1663               0 :     if (!IsValidSelectionPoint(this, aNewFocus)) {
    1664               0 :       mAncestorLimiter = nsnull;
    1665                 :     }
    1666                 :   }
    1667                 : 
    1668                 :   // Don't take focus when dragging off of a table
    1669               0 :   if (!mDragSelectingCells)
    1670                 :   {
    1671               0 :     BidiLevelFromClick(aNewFocus, aContentOffset);
    1672               0 :     PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
    1673               0 :     if (aContinueSelection &&
    1674               0 :         AdjustForMaintainedSelection(aNewFocus, aContentOffset))
    1675               0 :       return NS_OK; //shift clicked to maintained selection. rejected.
    1676                 : 
    1677                 :     return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, HINT(aHint),
    1678               0 :                      aContinueSelection, aMultipleSelection);
    1679                 :   }
    1680                 :   
    1681               0 :   return NS_OK;
    1682                 : }
    1683                 : 
    1684                 : void
    1685               0 : nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint)
    1686                 : {
    1687               0 :   if (!aFrame || !mShell)
    1688               0 :     return;
    1689                 : 
    1690                 :   nsresult result;
    1691               0 :   nsIFrame *newFrame = 0;
    1692               0 :   nsPoint   newPoint;
    1693                 : 
    1694               0 :   result = ConstrainFrameAndPointToAnchorSubtree(aFrame, aPoint, &newFrame, newPoint);
    1695               0 :   if (NS_FAILED(result))
    1696               0 :     return;
    1697               0 :   if (!newFrame)
    1698               0 :     return;
    1699                 : 
    1700                 :   nsIFrame::ContentOffsets offsets =
    1701               0 :       newFrame->GetContentOffsetsFromPoint(newPoint);
    1702               0 :   if (!offsets.content)
    1703                 :     return;
    1704                 : 
    1705               0 :   if (newFrame->IsSelected() &&
    1706               0 :       AdjustForMaintainedSelection(offsets.content, offsets.offset))
    1707                 :     return;
    1708                 : 
    1709                 :   // Adjust offsets according to maintained amount
    1710               0 :   if (mMaintainRange && 
    1711               0 :       mMaintainedAmount != eSelectNoAmount) {    
    1712                 :     
    1713               0 :     nsINode* rangenode = mMaintainRange->GetStartParent();
    1714               0 :     PRInt32 rangeOffset = mMaintainRange->StartOffset();
    1715                 :     PRInt32 relativePosition =
    1716                 :       nsContentUtils::ComparePoints(rangenode, rangeOffset,
    1717               0 :                                     offsets.content, offsets.offset);
    1718                 : 
    1719               0 :     nsDirection direction = relativePosition > 0 ? eDirPrevious : eDirNext;
    1720               0 :     nsSelectionAmount amount = mMaintainedAmount;
    1721               0 :     if (amount == eSelectBeginLine && direction == eDirNext)
    1722               0 :       amount = eSelectEndLine;
    1723                 : 
    1724                 :     PRInt32 offset;
    1725               0 :     nsIFrame* frame = GetFrameForNodeOffset(offsets.content, offsets.offset, HINTRIGHT, &offset);
    1726                 : 
    1727               0 :     if (frame && amount == eSelectWord && direction == eDirPrevious) {
    1728                 :       // To avoid selecting the previous word when at start of word,
    1729                 :       // first move one character forward.
    1730               0 :       nsPeekOffsetStruct charPos;
    1731                 :       charPos.SetData(eSelectCharacter, eDirNext, offset, 0,
    1732               0 :                       false, mLimiter != nsnull, false, false);
    1733               0 :       if (NS_SUCCEEDED(frame->PeekOffset(&charPos))) {
    1734               0 :         frame = charPos.mResultFrame;
    1735               0 :         offset = charPos.mContentOffset;
    1736                 :       }
    1737                 :     }
    1738                 : 
    1739               0 :     nsPeekOffsetStruct pos;
    1740                 :     pos.SetData(amount, direction, offset, 0,
    1741               0 :                 false, mLimiter != nsnull, false, false);
    1742                 : 
    1743               0 :     if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos)) && pos.mResultContent) {
    1744               0 :       offsets.content = pos.mResultContent;
    1745               0 :       offsets.offset = pos.mContentOffset;
    1746                 :     }
    1747                 :   }
    1748                 :   
    1749                 :   HandleClick(offsets.content, offsets.offset, offsets.offset,
    1750               0 :               true, false, offsets.associateWithNext);
    1751                 : }
    1752                 : 
    1753                 : nsresult
    1754               0 : nsFrameSelection::StartAutoScrollTimer(nsIFrame *aFrame,
    1755                 :                                        nsPoint   aPoint,
    1756                 :                                        PRUint32  aDelay)
    1757                 : {
    1758               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    1759               0 :   if (!mDomSelections[index])
    1760               0 :     return NS_ERROR_NULL_POINTER;
    1761                 : 
    1762               0 :   return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay);
    1763                 : }
    1764                 : 
    1765                 : void
    1766               0 : nsFrameSelection::StopAutoScrollTimer()
    1767                 : {
    1768               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    1769               0 :   if (!mDomSelections[index])
    1770               0 :     return;
    1771                 : 
    1772               0 :   mDomSelections[index]->StopAutoScrollTimer();
    1773                 : }
    1774                 : 
    1775                 : /**
    1776                 : hard to go from nodes to frames, easy the other way!
    1777                 :  */
    1778                 : nsresult
    1779               0 : nsFrameSelection::TakeFocus(nsIContent *aNewFocus,
    1780                 :                             PRUint32    aContentOffset,
    1781                 :                             PRUint32    aContentEndOffset,
    1782                 :                             HINT        aHint,
    1783                 :                             bool        aContinueSelection,
    1784                 :                             bool        aMultipleSelection)
    1785                 : {
    1786               0 :   if (!aNewFocus)
    1787               0 :     return NS_ERROR_NULL_POINTER;
    1788                 : 
    1789               0 :   NS_ENSURE_STATE(mShell);
    1790                 : 
    1791               0 :   if (!IsValidSelectionPoint(this,aNewFocus))
    1792               0 :     return NS_ERROR_FAILURE;
    1793                 : 
    1794                 :   // Clear all table selection data
    1795               0 :   mSelectingTableCellMode = 0;
    1796               0 :   mDragSelectingCells = false;
    1797               0 :   mStartSelectedCell = nsnull;
    1798               0 :   mEndSelectedCell = nsnull;
    1799               0 :   mAppendStartSelectedCell = nsnull;
    1800                 : 
    1801                 :   //HACKHACKHACK
    1802               0 :   if (!aNewFocus->GetParent())
    1803               0 :     return NS_ERROR_FAILURE;
    1804                 :   //END HACKHACKHACK /checking for root frames/content
    1805                 : 
    1806               0 :   mHint = aHint;
    1807                 :   
    1808               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    1809               0 :   if (!mDomSelections[index])
    1810               0 :     return NS_ERROR_NULL_POINTER;
    1811                 : 
    1812                 :   //traverse through document and unselect crap here
    1813               0 :   if (!aContinueSelection) {//single click? setting cursor down
    1814               0 :     PRUint32 batching = mBatching;//hack to use the collapse code.
    1815               0 :     bool changes = mChangesDuringBatching;
    1816               0 :     mBatching = 1;
    1817                 : 
    1818               0 :     if (aMultipleSelection) {
    1819                 :       // Remove existing collapsed ranges as there's no point in having 
    1820                 :       // non-anchor/focus collapsed ranges.
    1821               0 :       mDomSelections[index]->RemoveCollapsedRanges();
    1822                 : 
    1823               0 :       nsRefPtr<nsRange> newRange = new nsRange();
    1824                 : 
    1825               0 :       newRange->SetStart(aNewFocus, aContentOffset);
    1826               0 :       newRange->SetEnd(aNewFocus, aContentOffset);
    1827               0 :       mDomSelections[index]->AddRange(newRange);
    1828               0 :       mBatching = batching;
    1829               0 :       mChangesDuringBatching = changes;
    1830                 :     }
    1831                 :     else
    1832                 :     {
    1833               0 :       bool oldDesiredXSet = mDesiredXSet; //need to keep old desired X if it was set.
    1834               0 :       mDomSelections[index]->Collapse(aNewFocus, aContentOffset);
    1835               0 :       mDesiredXSet = oldDesiredXSet; //now reset desired X back.
    1836               0 :       mBatching = batching;
    1837               0 :       mChangesDuringBatching = changes;
    1838                 :     }
    1839               0 :     if (aContentEndOffset != aContentOffset)
    1840               0 :       mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);
    1841                 : 
    1842                 :     //find out if we are inside a table. if so, find out which one and which cell
    1843                 :     //once we do that, the next time we get a takefocus, check the parent tree. 
    1844                 :     //if we are no longer inside same table ,cell then switch to table selection mode.
    1845                 :     // BUT only do this in an editor
    1846                 : 
    1847               0 :     NS_ENSURE_STATE(mShell);
    1848               0 :     PRInt16 displaySelection = mShell->GetSelectionFlags();
    1849                 : 
    1850                 :     // Editor has DISPLAY_ALL selection type
    1851               0 :     if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
    1852                 :     {
    1853               0 :       mCellParent = GetCellParent(aNewFocus);
    1854                 : #ifdef DEBUG_TABLE_SELECTION
    1855                 :       if (mCellParent)
    1856                 :         printf(" * TakeFocus - Collapsing into new cell\n");
    1857                 : #endif
    1858                 :     }
    1859                 :   }
    1860                 :   else {
    1861                 :     // Now update the range list:
    1862               0 :     if (aContinueSelection && aNewFocus)
    1863                 :     {
    1864                 :       PRInt32 offset;
    1865               0 :       nsINode *cellparent = GetCellParent(aNewFocus);
    1866               0 :       if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode
    1867                 :       {
    1868                 : #ifdef DEBUG_TABLE_SELECTION
    1869                 : printf(" * TakeFocus - moving into new cell\n");
    1870                 : #endif
    1871               0 :         nsMouseEvent event(false, 0, nsnull, nsMouseEvent::eReal);
    1872                 : 
    1873                 :         // Start selecting in the cell we were in before
    1874               0 :         nsINode* parent = ParentOffset(mCellParent, &offset);
    1875               0 :         if (parent)
    1876                 :           HandleTableSelection(parent, offset,
    1877               0 :                                nsISelectionPrivate::TABLESELECTION_CELL, &event);
    1878                 : 
    1879                 :         // Find the parent of this new cell and extend selection to it
    1880               0 :         parent = ParentOffset(cellparent, &offset);
    1881                 : 
    1882                 :         // XXXX We need to REALLY get the current key shift state
    1883                 :         //  (we'd need to add event listener -- let's not bother for now)
    1884               0 :         event.isShift = false; //aContinueSelection;
    1885               0 :         if (parent)
    1886                 :         {
    1887               0 :           mCellParent = cellparent;
    1888                 :           // Continue selection into next cell
    1889                 :           HandleTableSelection(parent, offset,
    1890               0 :                                nsISelectionPrivate::TABLESELECTION_CELL, &event);
    1891                 :         }
    1892                 :       }
    1893                 :       else
    1894                 :       {
    1895                 :         // XXXX Problem: Shift+click in browser is appending text selection to selected table!!!
    1896                 :         //   is this the place to erase seleced cells ?????
    1897               0 :         if (mDomSelections[index]->GetDirection() == eDirNext && aContentEndOffset > aContentOffset) //didn't go far enough 
    1898                 :         {
    1899               0 :           mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);//this will only redraw the diff 
    1900                 :         }
    1901                 :         else
    1902               0 :           mDomSelections[index]->Extend(aNewFocus, aContentOffset);
    1903                 :       }
    1904                 :     }
    1905                 :   }
    1906                 : 
    1907                 :   // Don't notify selection listeners if batching is on:
    1908               0 :   if (GetBatching())
    1909               0 :     return NS_OK;
    1910               0 :   return NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
    1911                 : }
    1912                 : 
    1913                 : 
    1914                 : SelectionDetails*
    1915               0 : nsFrameSelection::LookUpSelection(nsIContent *aContent,
    1916                 :                                   PRInt32 aContentOffset,
    1917                 :                                   PRInt32 aContentLength,
    1918                 :                                   bool aSlowCheck) const
    1919                 : {
    1920               0 :   if (!aContent || !mShell)
    1921               0 :     return nsnull;
    1922                 : 
    1923               0 :   SelectionDetails* details = nsnull;
    1924                 : 
    1925               0 :   for (PRInt32 j = 0; j < nsISelectionController::NUM_SELECTIONTYPES; j++) {
    1926               0 :     if (mDomSelections[j]) {
    1927               0 :       mDomSelections[j]->LookUpSelection(aContent, aContentOffset,
    1928               0 :           aContentLength, &details, (SelectionType)(1<<j), aSlowCheck);
    1929                 :     }
    1930                 :   }
    1931                 : 
    1932               0 :   return details;
    1933                 : }
    1934                 : 
    1935                 : void
    1936               0 : nsFrameSelection::SetMouseDownState(bool aState)
    1937                 : {
    1938               0 :   if (mMouseDownState == aState)
    1939               0 :     return;
    1940                 : 
    1941               0 :   mMouseDownState = aState;
    1942                 :     
    1943               0 :   if (!mMouseDownState)
    1944                 :   {
    1945               0 :     mDragSelectingCells = false;
    1946               0 :     PostReason(nsISelectionListener::MOUSEUP_REASON);
    1947               0 :     NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); //notify that reason is mouse up please.
    1948                 :   }
    1949                 : }
    1950                 : 
    1951                 : nsISelection*
    1952               0 : nsFrameSelection::GetSelection(SelectionType aType) const
    1953                 : {
    1954               0 :   PRInt8 index = GetIndexFromSelectionType(aType);
    1955               0 :   if (index < 0)
    1956               0 :     return nsnull;
    1957                 : 
    1958               0 :   return static_cast<nsISelection*>(mDomSelections[index]);
    1959                 : }
    1960                 : 
    1961                 : nsresult
    1962               0 : nsFrameSelection::ScrollSelectionIntoView(SelectionType   aType,
    1963                 :                                           SelectionRegion aRegion,
    1964                 :                                           PRInt16         aFlags) const
    1965                 : {
    1966               0 :   PRInt8 index = GetIndexFromSelectionType(aType);
    1967               0 :   if (index < 0)
    1968               0 :     return NS_ERROR_INVALID_ARG;
    1969                 : 
    1970               0 :   if (!mDomSelections[index])
    1971               0 :     return NS_ERROR_NULL_POINTER;
    1972                 : 
    1973               0 :   PRInt16 verticalScroll = PRInt16(NS_PRESSHELL_SCROLL_ANYWHERE);
    1974               0 :   PRInt32 flags = nsTypedSelection::SCROLL_DO_FLUSH;
    1975               0 :   if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) {
    1976               0 :     flags |= nsTypedSelection::SCROLL_SYNCHRONOUS;
    1977               0 :   } else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) {
    1978               0 :     flags |= nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY;
    1979                 :   }
    1980               0 :   if (aFlags & nsISelectionController::SCROLL_CENTER_VERTICALLY) {
    1981               0 :     verticalScroll = PRInt16(NS_PRESSHELL_SCROLL_CENTER);
    1982                 :   }
    1983                 : 
    1984                 :   // After ScrollSelectionIntoView(), the pending notifications might be
    1985                 :   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
    1986               0 :   return mDomSelections[index]->ScrollIntoView(aRegion,
    1987                 :                                                verticalScroll,
    1988                 :                                                PRInt16(NS_PRESSHELL_SCROLL_ANYWHERE),
    1989               0 :                                                flags);
    1990                 : }
    1991                 : 
    1992                 : nsresult
    1993               0 : nsFrameSelection::RepaintSelection(SelectionType aType) const
    1994                 : {
    1995               0 :   PRInt8 index = GetIndexFromSelectionType(aType);
    1996               0 :   if (index < 0)
    1997               0 :     return NS_ERROR_INVALID_ARG;
    1998               0 :   if (!mDomSelections[index])
    1999               0 :     return NS_ERROR_NULL_POINTER;
    2000               0 :   NS_ENSURE_STATE(mShell);
    2001               0 :   return mDomSelections[index]->Repaint(mShell->GetPresContext());
    2002                 : }
    2003                 :  
    2004                 : nsIFrame*
    2005               0 : nsFrameSelection::GetFrameForNodeOffset(nsIContent *aNode,
    2006                 :                                         PRInt32     aOffset,
    2007                 :                                         HINT        aHint,
    2008                 :                                         PRInt32    *aReturnOffset) const
    2009                 : {
    2010               0 :   if (!aNode || !aReturnOffset || !mShell)
    2011               0 :     return nsnull;
    2012                 : 
    2013               0 :   if (aOffset < 0)
    2014               0 :     return nsnull;
    2015                 : 
    2016               0 :   *aReturnOffset = aOffset;
    2017                 : 
    2018               0 :   nsCOMPtr<nsIContent> theNode = aNode;
    2019                 : 
    2020               0 :   if (aNode->IsElement())
    2021                 :   {
    2022               0 :     PRInt32 childIndex  = 0;
    2023               0 :     PRInt32 numChildren = theNode->GetChildCount();
    2024                 : 
    2025               0 :     if (aHint == HINTLEFT)
    2026                 :     {
    2027               0 :       if (aOffset > 0)
    2028               0 :         childIndex = aOffset - 1;
    2029                 :       else
    2030               0 :         childIndex = aOffset;
    2031                 :     }
    2032                 :     else // HINTRIGHT
    2033                 :     {
    2034               0 :       if (aOffset >= numChildren)
    2035                 :       {
    2036               0 :         if (numChildren > 0)
    2037               0 :           childIndex = numChildren - 1;
    2038                 :         else
    2039               0 :           childIndex = 0;
    2040                 :       }
    2041                 :       else
    2042               0 :         childIndex = aOffset;
    2043                 :     }
    2044                 :     
    2045               0 :     if (childIndex > 0 || numChildren > 0) {
    2046               0 :       nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex);
    2047                 : 
    2048               0 :       if (!childNode)
    2049               0 :         return nsnull;
    2050                 : 
    2051               0 :       theNode = childNode;
    2052                 :     }
    2053                 : 
    2054                 : #ifdef DONT_DO_THIS_YET
    2055                 :     // XXX: We can't use this code yet because the hinting
    2056                 :     //      can cause us to attach to the wrong line frame.
    2057                 : 
    2058                 :     // Now that we have the child node, check if it too
    2059                 :     // can contain children. If so, call this method again!
    2060                 : 
    2061                 :     if (theNode->IsElement())
    2062                 :     {
    2063                 :       PRInt32 newOffset = 0;
    2064                 : 
    2065                 :       if (aOffset > childIndex)
    2066                 :       {
    2067                 :         numChildren = theNode->GetChildCount();
    2068                 : 
    2069                 :         newOffset = numChildren;
    2070                 :       }
    2071                 : 
    2072                 :       return GetFrameForNodeOffset(theNode, newOffset, aHint, aReturnOffset);
    2073                 :     }
    2074                 :     else
    2075                 : #endif // DONT_DO_THIS_YET
    2076                 :     {
    2077                 :       // Check to see if theNode is a text node. If it is, translate
    2078                 :       // aOffset into an offset into the text node.
    2079                 : 
    2080               0 :       nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode);
    2081                 : 
    2082               0 :       if (textNode)
    2083                 :       {
    2084               0 :         if (theNode->GetPrimaryFrame())
    2085                 :         {
    2086               0 :           if (aOffset > childIndex)
    2087                 :           {
    2088               0 :             PRUint32 textLength = 0;
    2089                 : 
    2090               0 :             nsresult rv = textNode->GetLength(&textLength);
    2091               0 :             if (NS_FAILED(rv))
    2092               0 :               return nsnull;
    2093                 : 
    2094               0 :             *aReturnOffset = (PRInt32)textLength;
    2095                 :           }
    2096                 :           else
    2097               0 :             *aReturnOffset = 0;
    2098                 :         }
    2099                 :         else
    2100                 :         {
    2101                 :           // If we're at a collapsed whitespace content node (which
    2102                 :           // does not have a primary frame), just use the original node
    2103                 :           // to get the frame on which we should put the caret.
    2104               0 :           theNode = aNode;
    2105                 :         }
    2106                 :       }
    2107                 :     }
    2108                 :   }
    2109                 :   
    2110               0 :   nsIFrame* returnFrame = theNode->GetPrimaryFrame();
    2111               0 :   if (!returnFrame)
    2112               0 :     return nsnull;
    2113                 : 
    2114                 :   // find the child frame containing the offset we want
    2115                 :   returnFrame->GetChildFrameContainingOffset(*aReturnOffset, aHint,
    2116               0 :                                              &aOffset, &returnFrame);
    2117               0 :   return returnFrame;
    2118                 : }
    2119                 : 
    2120                 : void
    2121               0 : nsFrameSelection::CommonPageMove(bool aForward,
    2122                 :                                  bool aExtend,
    2123                 :                                  nsIScrollableFrame* aScrollableFrame)
    2124                 : {
    2125                 :   // expected behavior for PageMove is to scroll AND move the caret
    2126                 :   // and remain relative position of the caret in view. see Bug 4302.
    2127                 : 
    2128                 :   //get the frame from the scrollable view
    2129                 : 
    2130               0 :   nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
    2131               0 :   if (!scrolledFrame)
    2132               0 :     return;
    2133                 : 
    2134                 :   // find out where the caret is.
    2135                 :   // we should know mDesiredX value of nsFrameSelection, but I havent seen that behavior in other windows applications yet.
    2136               0 :   nsISelection* domSel = GetSelection(nsISelectionController::SELECTION_NORMAL);
    2137               0 :   if (!domSel) 
    2138               0 :     return;
    2139                 : 
    2140               0 :   nsRefPtr<nsCaret> caret = mShell->GetCaret();
    2141                 : 
    2142               0 :   nsRect caretPos;
    2143               0 :   nsIFrame* caretFrame = caret->GetGeometry(domSel, &caretPos);
    2144               0 :   if (!caretFrame) 
    2145                 :     return;
    2146                 :   
    2147                 :   //need to adjust caret jump by percentage scroll
    2148               0 :   nsSize scrollDelta = aScrollableFrame->GetPageScrollAmount();
    2149                 : 
    2150               0 :   if (aForward)
    2151               0 :     caretPos.y += scrollDelta.height;
    2152                 :   else
    2153               0 :     caretPos.y -= scrollDelta.height;
    2154                 : 
    2155               0 :   caretPos += caretFrame->GetOffsetTo(scrolledFrame);
    2156                 :     
    2157                 :   // get a content at desired location
    2158               0 :   nsPoint desiredPoint;
    2159               0 :   desiredPoint.x = caretPos.x;
    2160               0 :   desiredPoint.y = caretPos.y + caretPos.height/2;
    2161                 :   nsIFrame::ContentOffsets offsets =
    2162               0 :       scrolledFrame->GetContentOffsetsFromPoint(desiredPoint);
    2163                 : 
    2164               0 :   if (!offsets.content)
    2165                 :     return;
    2166                 : 
    2167                 :   // scroll one page
    2168                 :   aScrollableFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
    2169                 :                              nsIScrollableFrame::PAGES,
    2170               0 :                              nsIScrollableFrame::SMOOTH);
    2171                 : 
    2172                 :   // place the caret
    2173                 :   HandleClick(offsets.content, offsets.offset,
    2174               0 :               offsets.offset, aExtend, false, true);
    2175                 : }
    2176                 : 
    2177                 : nsresult
    2178               0 : nsFrameSelection::CharacterMove(bool aForward, bool aExtend)
    2179                 : {
    2180               0 :   if (aForward)
    2181               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT, aExtend, eSelectCluster);
    2182                 :   else
    2183               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT, aExtend, eSelectCluster);
    2184                 : }
    2185                 : 
    2186                 : nsresult
    2187               0 : nsFrameSelection::CharacterExtendForDelete()
    2188                 : {
    2189               0 :   return MoveCaret(nsIDOMKeyEvent::DOM_VK_DELETE, true, eSelectCluster);
    2190                 : }
    2191                 : 
    2192                 : nsresult
    2193               0 : nsFrameSelection::CharacterExtendForBackspace()
    2194                 : {
    2195               0 :   return MoveCaret(nsIDOMKeyEvent::DOM_VK_BACK_SPACE, true, eSelectCharacter);
    2196                 : }
    2197                 : 
    2198                 : nsresult
    2199               0 : nsFrameSelection::WordMove(bool aForward, bool aExtend)
    2200                 : {
    2201               0 :   if (aForward)
    2202               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectWord);
    2203                 :   else
    2204               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectWord);
    2205                 : }
    2206                 : 
    2207                 : nsresult
    2208               0 : nsFrameSelection::WordExtendForDelete(bool aForward)
    2209                 : {
    2210               0 :   if (aForward)
    2211               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_DELETE, true, eSelectWord);
    2212                 :   else
    2213               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_BACK_SPACE, true, eSelectWord);
    2214                 : }
    2215                 : 
    2216                 : nsresult
    2217               0 : nsFrameSelection::LineMove(bool aForward, bool aExtend)
    2218                 : {
    2219               0 :   if (aForward)
    2220               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_DOWN,aExtend,eSelectLine);
    2221                 :   else
    2222               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_UP,aExtend,eSelectLine);
    2223                 : }
    2224                 : 
    2225                 : nsresult
    2226               0 : nsFrameSelection::IntraLineMove(bool aForward, bool aExtend)
    2227                 : {
    2228               0 :   if (aForward)
    2229               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_END,aExtend,eSelectLine);
    2230                 :   else
    2231               0 :     return MoveCaret(nsIDOMKeyEvent::DOM_VK_HOME,aExtend,eSelectLine);
    2232                 : }
    2233                 : 
    2234                 : nsresult
    2235               0 : nsFrameSelection::SelectAll()
    2236                 : {
    2237               0 :   nsCOMPtr<nsIContent> rootContent;
    2238               0 :   if (mLimiter)
    2239                 :   {
    2240               0 :     rootContent = mLimiter;//addrefit
    2241                 :   }
    2242               0 :   else if (mAncestorLimiter) {
    2243               0 :     rootContent = mAncestorLimiter;
    2244                 :   }
    2245                 :   else
    2246                 :   {
    2247               0 :     NS_ENSURE_STATE(mShell);
    2248               0 :     nsIDocument *doc = mShell->GetDocument();
    2249               0 :     if (!doc)
    2250               0 :       return NS_ERROR_FAILURE;
    2251               0 :     rootContent = doc->GetRootElement();
    2252               0 :     if (!rootContent)
    2253               0 :       return NS_ERROR_FAILURE;
    2254                 :   }
    2255               0 :   PRInt32 numChildren = rootContent->GetChildCount();
    2256               0 :   PostReason(nsISelectionListener::NO_REASON);
    2257               0 :   return TakeFocus(rootContent, 0, numChildren, HINTLEFT, false, false);
    2258                 : }
    2259                 : 
    2260                 : //////////END FRAMESELECTION
    2261                 : 
    2262                 : void
    2263               0 : nsFrameSelection::StartBatchChanges()
    2264                 : {
    2265               0 :   mBatching++;
    2266               0 : }
    2267                 : 
    2268                 : void
    2269               0 : nsFrameSelection::EndBatchChanges()
    2270                 : {
    2271               0 :   mBatching--;
    2272               0 :   NS_ASSERTION(mBatching >=0,"Bad mBatching");
    2273               0 :   if (mBatching == 0 && mChangesDuringBatching){
    2274               0 :     mChangesDuringBatching = false;
    2275               0 :     NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
    2276                 :   }
    2277               0 : }
    2278                 : 
    2279                 : 
    2280                 : nsresult
    2281               0 : nsFrameSelection::NotifySelectionListeners(SelectionType aType)
    2282                 : {
    2283               0 :   PRInt8 index = GetIndexFromSelectionType(aType);
    2284               0 :   if (index >=0 && mDomSelections[index])
    2285                 :   {
    2286               0 :     return mDomSelections[index]->NotifySelectionListeners();
    2287                 :   }
    2288               0 :   return NS_ERROR_FAILURE;
    2289                 : }
    2290                 : 
    2291                 : // Start of Table Selection methods
    2292                 : 
    2293               0 : static bool IsCell(nsIContent *aContent)
    2294                 : {
    2295               0 :   return ((aContent->Tag() == nsGkAtoms::td ||
    2296               0 :            aContent->Tag() == nsGkAtoms::th) &&
    2297               0 :           aContent->IsHTML());
    2298                 : }
    2299                 : 
    2300                 : nsITableCellLayout* 
    2301               0 : nsFrameSelection::GetCellLayout(nsIContent *aCellContent) const
    2302                 : {
    2303               0 :   NS_ENSURE_TRUE(mShell, nsnull);
    2304                 :   nsITableCellLayout *cellLayoutObject =
    2305               0 :     do_QueryFrame(aCellContent->GetPrimaryFrame());
    2306               0 :   return cellLayoutObject;
    2307                 : }
    2308                 : 
    2309                 : nsITableLayout* 
    2310               0 : nsFrameSelection::GetTableLayout(nsIContent *aTableContent) const
    2311                 : {
    2312               0 :   NS_ENSURE_TRUE(mShell, nsnull);
    2313                 :   nsITableLayout *tableLayoutObject =
    2314               0 :     do_QueryFrame(aTableContent->GetPrimaryFrame());
    2315               0 :   return tableLayoutObject;
    2316                 : }
    2317                 : 
    2318                 : nsresult
    2319               0 : nsFrameSelection::ClearNormalSelection()
    2320                 : {
    2321               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    2322               0 :   if (!mDomSelections[index])
    2323               0 :     return NS_ERROR_NULL_POINTER;
    2324                 : 
    2325               0 :   return mDomSelections[index]->RemoveAllRanges();
    2326                 : }
    2327                 : 
    2328                 : static nsIContent*
    2329               0 : GetFirstSelectedContent(nsRange* aRange)
    2330                 : {
    2331               0 :   if (!aRange) {
    2332               0 :     return nsnull;
    2333                 :   }
    2334                 : 
    2335               0 :   NS_PRECONDITION(aRange->GetStartParent(), "Must have start parent!");
    2336               0 :   NS_PRECONDITION(aRange->GetStartParent()->IsElement(),
    2337                 :                   "Unexpected parent");
    2338                 : 
    2339               0 :   return aRange->GetStartParent()->GetChildAt(aRange->StartOffset());
    2340                 : }
    2341                 : 
    2342                 : // Table selection support.
    2343                 : // TODO: Separate table methods into a separate nsITableSelection interface
    2344                 : nsresult
    2345               0 : nsFrameSelection::HandleTableSelection(nsINode *aParentContent,
    2346                 :                                        PRInt32 aContentOffset,
    2347                 :                                        PRInt32 aTarget,
    2348                 :                                        nsMouseEvent *aMouseEvent)
    2349                 : {
    2350               0 :   NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER);
    2351               0 :   NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER);
    2352                 : 
    2353               0 :   if (mMouseDownState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE))
    2354                 :   {
    2355                 :     // We were selecting cells and user drags mouse in table border or inbetween cells,
    2356                 :     //  just do nothing
    2357               0 :       return NS_OK;
    2358                 :   }
    2359                 : 
    2360               0 :   nsresult result = NS_OK;
    2361                 : 
    2362               0 :   nsIContent *childContent = aParentContent->GetChildAt(aContentOffset);
    2363                 : 
    2364                 :   // When doing table selection, always set the direction to next so
    2365                 :   // we can be sure that anchorNode's offset always points to the
    2366                 :   // selected cell
    2367               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    2368               0 :   if (!mDomSelections[index])
    2369               0 :     return NS_ERROR_NULL_POINTER;
    2370                 : 
    2371               0 :   mDomSelections[index]->SetDirection(eDirNext);
    2372                 : 
    2373                 :   // Stack-class to wrap all table selection changes in 
    2374                 :   //  BeginBatchChanges() / EndBatchChanges()
    2375               0 :   nsSelectionBatcher selectionBatcher(mDomSelections[index]);
    2376                 : 
    2377                 :   PRInt32 startRowIndex, startColIndex, curRowIndex, curColIndex;
    2378               0 :   if (mMouseDownState && mDragSelectingCells)
    2379                 :   {
    2380                 :     // We are drag-selecting
    2381               0 :     if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE)
    2382                 :     {
    2383                 :       // If dragging in the same cell as last event, do nothing
    2384               0 :       if (mEndSelectedCell == childContent)
    2385               0 :         return NS_OK;
    2386                 : 
    2387                 : #ifdef DEBUG_TABLE_SELECTION
    2388                 : printf(" mStartSelectedCell = %x, mEndSelectedCell = %x, childContent = %x \n", mStartSelectedCell, mEndSelectedCell, childContent);
    2389                 : #endif
    2390                 :       // aTarget can be any "cell mode",
    2391                 :       //  so we can easily drag-select rows and columns 
    2392                 :       // Once we are in row or column mode,
    2393                 :       //  we can drift into any cell to stay in that mode
    2394                 :       //  even if aTarget = TABLESELECTION_CELL
    2395                 : 
    2396               0 :       if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW ||
    2397                 :           mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN)
    2398                 :       {
    2399               0 :         if (mEndSelectedCell)
    2400                 :         {
    2401                 :           // Also check if cell is in same row/col
    2402               0 :           result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
    2403               0 :           if (NS_FAILED(result)) return result;
    2404               0 :           result = GetCellIndexes(childContent, curRowIndex, curColIndex);
    2405               0 :           if (NS_FAILED(result)) return result;
    2406                 :         
    2407                 : #ifdef DEBUG_TABLE_SELECTION
    2408                 : printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex);
    2409                 : #endif
    2410               0 :           if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
    2411                 :               (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex)) 
    2412               0 :             return NS_OK;
    2413                 :         }
    2414                 : #ifdef DEBUG_TABLE_SELECTION
    2415                 : printf(" Dragged into a new column or row\n");
    2416                 : #endif
    2417                 :         // Continue dragging row or column selection
    2418               0 :         return SelectRowOrColumn(childContent, mSelectingTableCellMode);
    2419                 :       }
    2420               0 :       else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL)
    2421                 :       {
    2422                 : #ifdef DEBUG_TABLE_SELECTION
    2423                 : printf("HandleTableSelection: Dragged into a new cell\n");
    2424                 : #endif
    2425                 :         // Trick for quick selection of rows and columns
    2426                 :         // Hold down shift, then start selecting in one direction
    2427                 :         // If next cell dragged into is in same row, select entire row,
    2428                 :         //   if next cell is in same column, select entire column
    2429               0 :         if (mStartSelectedCell && aMouseEvent->isShift)
    2430                 :         {
    2431               0 :           result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
    2432               0 :           if (NS_FAILED(result)) return result;
    2433               0 :           result = GetCellIndexes(childContent, curRowIndex, curColIndex);
    2434               0 :           if (NS_FAILED(result)) return result;
    2435                 :           
    2436               0 :           if (startRowIndex == curRowIndex || 
    2437                 :               startColIndex == curColIndex)
    2438                 :           {
    2439                 :             // Force new selection block
    2440               0 :             mStartSelectedCell = nsnull;
    2441               0 :             mDomSelections[index]->RemoveAllRanges();
    2442                 : 
    2443               0 :             if (startRowIndex == curRowIndex)
    2444               0 :               mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW;
    2445                 :             else
    2446               0 :               mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN;
    2447                 : 
    2448               0 :             return SelectRowOrColumn(childContent, mSelectingTableCellMode);
    2449                 :           }
    2450                 :         }
    2451                 :         
    2452                 :         // Reselect block of cells to new end location
    2453               0 :         return SelectBlockOfCells(mStartSelectedCell, childContent);
    2454                 :       }
    2455                 :     }
    2456                 :     // Do nothing if dragging in table, but outside a cell
    2457               0 :     return NS_OK;
    2458                 :   }
    2459                 :   else 
    2460                 :   {
    2461                 :     // Not dragging  -- mouse event is down or up
    2462               0 :     if (mMouseDownState)
    2463                 :     {
    2464                 : #ifdef DEBUG_TABLE_SELECTION
    2465                 : printf("HandleTableSelection: Mouse down event\n");
    2466                 : #endif
    2467                 :       // Clear cell we stored in mouse-down
    2468               0 :       mUnselectCellOnMouseUp = nsnull;
    2469                 :       
    2470               0 :       if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL)
    2471                 :       {
    2472               0 :         bool isSelected = false;
    2473                 : 
    2474                 :         // Check if we have other selected cells
    2475                 :         nsIContent* previousCellNode =
    2476               0 :           GetFirstSelectedContent(GetFirstCellRange());
    2477               0 :         if (previousCellNode)
    2478                 :         {
    2479                 :           // We have at least 1 other selected cell
    2480                 : 
    2481                 :           // Check if new cell is already selected
    2482               0 :           nsIFrame  *cellFrame = childContent->GetPrimaryFrame();
    2483               0 :           if (!cellFrame) return NS_ERROR_NULL_POINTER;
    2484               0 :           isSelected = cellFrame->IsSelected();
    2485                 :         }
    2486                 :         else
    2487                 :         {
    2488                 :           // No cells selected -- remove non-cell selection
    2489               0 :           mDomSelections[index]->RemoveAllRanges();
    2490                 :         }
    2491               0 :         mDragSelectingCells = true;    // Signal to start drag-cell-selection
    2492               0 :         mSelectingTableCellMode = aTarget;
    2493                 :         // Set start for new drag-selection block (not appended)
    2494               0 :         mStartSelectedCell = childContent;
    2495                 :         // The initial block end is same as the start
    2496               0 :         mEndSelectedCell = childContent;
    2497                 :         
    2498               0 :         if (isSelected)
    2499                 :         {
    2500                 :           // Remember this cell to (possibly) unselect it on mouseup
    2501               0 :           mUnselectCellOnMouseUp = childContent;
    2502                 : #ifdef DEBUG_TABLE_SELECTION
    2503                 : printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
    2504                 : #endif
    2505                 :         }
    2506                 :         else
    2507                 :         {
    2508                 :           // Select an unselected cell
    2509                 :           // but first remove existing selection if not in same table
    2510               0 :           if (previousCellNode &&
    2511               0 :               !IsInSameTable(previousCellNode, childContent))
    2512                 :           {
    2513               0 :             mDomSelections[index]->RemoveAllRanges();
    2514                 :             // Reset selection mode that is cleared in RemoveAllRanges
    2515               0 :             mSelectingTableCellMode = aTarget;
    2516                 :           }
    2517                 : 
    2518               0 :           return SelectCellElement(childContent);
    2519                 :         }
    2520                 : 
    2521               0 :         return NS_OK;
    2522                 :       }
    2523               0 :       else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE)
    2524                 :       {
    2525                 :         //TODO: We currently select entire table when clicked between cells,
    2526                 :         //  should we restrict to only around border?
    2527                 :         //  *** How do we get location data for cell and click?
    2528               0 :         mDragSelectingCells = false;
    2529               0 :         mStartSelectedCell = nsnull;
    2530               0 :         mEndSelectedCell = nsnull;
    2531                 : 
    2532                 :         // Remove existing selection and select the table
    2533               0 :         mDomSelections[index]->RemoveAllRanges();
    2534               0 :         return CreateAndAddRange(aParentContent, aContentOffset);
    2535                 :       }
    2536               0 :       else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
    2537                 :       {
    2538                 : #ifdef DEBUG_TABLE_SELECTION
    2539                 : printf("aTarget == %d\n", aTarget);
    2540                 : #endif
    2541                 : 
    2542                 :         // Start drag-selecting mode so multiple rows/cols can be selected
    2543                 :         // Note: Currently, nsFrame::GetDataForTableSelection
    2544                 :         //       will never call us for row or column selection on mouse down
    2545               0 :         mDragSelectingCells = true;
    2546                 :       
    2547                 :         // Force new selection block
    2548               0 :         mStartSelectedCell = nsnull;
    2549               0 :         mDomSelections[index]->RemoveAllRanges();
    2550                 :         // Always do this AFTER RemoveAllRanges
    2551               0 :         mSelectingTableCellMode = aTarget;
    2552               0 :         return SelectRowOrColumn(childContent, aTarget);
    2553                 :       }
    2554                 :     }
    2555                 :     else
    2556                 :     {
    2557                 : #ifdef DEBUG_TABLE_SELECTION
    2558                 : printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%d\n", mDragSelectingCells, mStartSelectedCell);
    2559                 : #endif
    2560                 :       // First check if we are extending a block selection
    2561                 :       PRInt32 rangeCount;
    2562               0 :       result = mDomSelections[index]->GetRangeCount(&rangeCount);
    2563               0 :       if (NS_FAILED(result)) 
    2564               0 :         return result;
    2565                 : 
    2566               0 :       if (rangeCount > 0 && aMouseEvent->isShift && 
    2567               0 :           mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
    2568                 :       {
    2569                 :         // Shift key is down: append a block selection
    2570               0 :         mDragSelectingCells = false;
    2571               0 :         return SelectBlockOfCells(mAppendStartSelectedCell, childContent);
    2572                 :       }
    2573                 : 
    2574               0 :       if (mDragSelectingCells)
    2575               0 :         mAppendStartSelectedCell = mStartSelectedCell;
    2576                 :         
    2577               0 :       mDragSelectingCells = false;
    2578               0 :       mStartSelectedCell = nsnull;
    2579               0 :       mEndSelectedCell = nsnull;
    2580                 : 
    2581                 :       // Any other mouseup actions require that Ctrl or Cmd key is pressed
    2582                 :       //  else stop table selection mode
    2583               0 :       bool doMouseUpAction = false;
    2584                 : #ifdef XP_MACOSX
    2585                 :       doMouseUpAction = aMouseEvent->isMeta;
    2586                 : #else
    2587               0 :       doMouseUpAction = aMouseEvent->isControl;
    2588                 : #endif
    2589               0 :       if (!doMouseUpAction)
    2590                 :       {
    2591                 : #ifdef DEBUG_TABLE_SELECTION
    2592                 : printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%d\n", mAppendStartSelectedCell);
    2593                 : #endif
    2594               0 :         return NS_OK;
    2595                 :       }
    2596                 :       // Unselect a cell only if it wasn't
    2597                 :       //  just selected on mousedown
    2598               0 :       if( childContent == mUnselectCellOnMouseUp)
    2599                 :       {
    2600                 :         // Scan ranges to find the cell to unselect (the selection range to remove)
    2601                 :         // XXXbz it's really weird that this lives outside the loop, so once we
    2602                 :         // find one we keep looking at it even if we find no more cells...
    2603               0 :         nsINode* previousCellParent = nsnull;
    2604                 : #ifdef DEBUG_TABLE_SELECTION
    2605                 : printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
    2606                 : #endif
    2607               0 :         for( PRInt32 i = 0; i < rangeCount; i++)
    2608                 :         {
    2609                 :           // Strong reference, because sometimes we want to remove
    2610                 :           // this range, and then we might be the only owner.
    2611               0 :           nsRefPtr<nsRange> range = mDomSelections[index]->GetRangeAt(i);
    2612               0 :           if (!range) return NS_ERROR_NULL_POINTER;
    2613                 : 
    2614               0 :           nsINode* parent = range->GetStartParent();
    2615               0 :           if (!parent) return NS_ERROR_NULL_POINTER;
    2616                 : 
    2617               0 :           PRInt32 offset = range->StartOffset();
    2618                 :           // Be sure previous selection is a table cell
    2619               0 :           nsIContent* child = parent->GetChildAt(offset);
    2620               0 :           if (child && IsCell(child))
    2621               0 :             previousCellParent = parent;
    2622                 : 
    2623                 :           // We're done if we didn't find parent of a previously-selected cell
    2624               0 :           if (!previousCellParent) break;
    2625                 :         
    2626               0 :           if (previousCellParent == aParentContent && offset == aContentOffset)
    2627                 :           {
    2628                 :             // Cell is already selected
    2629               0 :             if (rangeCount == 1)
    2630                 :             {
    2631                 : #ifdef DEBUG_TABLE_SELECTION
    2632                 : printf("HandleTableSelection: Unselecting single selected cell\n");
    2633                 : #endif
    2634                 :               // This was the only cell selected.
    2635                 :               // Collapse to "normal" selection inside the cell
    2636               0 :               mStartSelectedCell = nsnull;
    2637               0 :               mEndSelectedCell = nsnull;
    2638               0 :               mAppendStartSelectedCell = nsnull;
    2639                 :               //TODO: We need a "Collapse to just before deepest child" routine
    2640                 :               // Even better, should we collapse to just after the LAST deepest child
    2641                 :               //  (i.e., at the end of the cell's contents)?
    2642               0 :               return mDomSelections[index]->Collapse(childContent, 0);
    2643                 :             }
    2644                 : #ifdef DEBUG_TABLE_SELECTION
    2645                 : printf("HandleTableSelection: Removing cell from multi-cell selection\n");
    2646                 : #endif
    2647                 :             // Unselecting the start of previous block 
    2648                 :             // XXX What do we use now!
    2649               0 :             if (childContent == mAppendStartSelectedCell)
    2650               0 :                mAppendStartSelectedCell = nsnull;
    2651                 : 
    2652                 :             // Deselect cell by removing its range from selection
    2653               0 :             return mDomSelections[index]->RemoveRange(range);
    2654                 :           }
    2655                 :         }
    2656               0 :         mUnselectCellOnMouseUp = nsnull;
    2657                 :       }
    2658                 :     }
    2659                 :   }
    2660               0 :   return result;
    2661                 : }
    2662                 : 
    2663                 : nsresult
    2664               0 : nsFrameSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell)
    2665                 : {
    2666               0 :   NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER);
    2667               0 :   NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER);
    2668               0 :   mEndSelectedCell = aEndCell;
    2669                 : 
    2670               0 :   nsCOMPtr<nsIContent> startCell;
    2671               0 :   nsresult result = NS_OK;
    2672                 : 
    2673                 :   // If new end cell is in a different table, do nothing
    2674               0 :   nsIContent* table = IsInSameTable(aStartCell, aEndCell);
    2675               0 :   if (!table) {
    2676               0 :     return NS_OK;
    2677                 :   }
    2678                 : 
    2679                 :   // Get starting and ending cells' location in the cellmap
    2680                 :   PRInt32 startRowIndex, startColIndex, endRowIndex, endColIndex;
    2681               0 :   result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
    2682               0 :   if(NS_FAILED(result)) return result;
    2683               0 :   result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
    2684               0 :   if(NS_FAILED(result)) return result;
    2685                 : 
    2686               0 :   if (mDragSelectingCells)
    2687                 :   {
    2688                 :     // Drag selecting: remove selected cells outside of new block limits
    2689                 :     UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex,
    2690               0 :                   true);
    2691                 :   }
    2692                 : 
    2693                 :   // Note that we select block in the direction of user's mouse dragging,
    2694                 :   //  which means start cell may be after the end cell in either row or column
    2695                 :   return AddCellsToSelection(table, startRowIndex, startColIndex,
    2696               0 :                              endRowIndex, endColIndex);
    2697                 : }
    2698                 : 
    2699                 : nsresult
    2700               0 : nsFrameSelection::UnselectCells(nsIContent *aTableContent,
    2701                 :                                 PRInt32 aStartRowIndex,
    2702                 :                                 PRInt32 aStartColumnIndex,
    2703                 :                                 PRInt32 aEndRowIndex,
    2704                 :                                 PRInt32 aEndColumnIndex,
    2705                 :                                 bool aRemoveOutsideOfCellRange)
    2706                 : {
    2707                 :   PRInt8 index =
    2708               0 :     GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    2709               0 :   if (!mDomSelections[index])
    2710               0 :     return NS_ERROR_NULL_POINTER;
    2711                 : 
    2712               0 :   nsITableLayout *tableLayout = GetTableLayout(aTableContent);
    2713               0 :   if (!tableLayout)
    2714               0 :     return NS_ERROR_FAILURE;
    2715                 : 
    2716               0 :   PRInt32 minRowIndex = NS_MIN(aStartRowIndex, aEndRowIndex);
    2717               0 :   PRInt32 maxRowIndex = NS_MAX(aStartRowIndex, aEndRowIndex);
    2718               0 :   PRInt32 minColIndex = NS_MIN(aStartColumnIndex, aEndColumnIndex);
    2719               0 :   PRInt32 maxColIndex = NS_MAX(aStartColumnIndex, aEndColumnIndex);
    2720                 : 
    2721                 :   // Strong reference because we sometimes remove the range
    2722               0 :   nsRefPtr<nsRange> range = GetFirstCellRange();
    2723               0 :   nsIContent* cellNode = GetFirstSelectedContent(range);
    2724               0 :   NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
    2725                 : 
    2726                 :   PRInt32 curRowIndex, curColIndex;
    2727               0 :   while (cellNode)
    2728                 :   {
    2729               0 :     nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
    2730               0 :     if (NS_FAILED(result))
    2731               0 :       return result;
    2732                 : 
    2733                 : #ifdef DEBUG_TABLE_SELECTION
    2734                 :     if (!range)
    2735                 :       printf("RemoveCellsToSelection -- range is null\n");
    2736                 : #endif
    2737                 : 
    2738               0 :     if (range) {
    2739               0 :       if (aRemoveOutsideOfCellRange) {
    2740               0 :         if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
    2741                 :             curColIndex < minColIndex || curColIndex > maxColIndex) {
    2742                 : 
    2743               0 :           mDomSelections[index]->RemoveRange(range);
    2744                 :           // Since we've removed the range, decrement pointer to next range
    2745               0 :           mSelectedCellIndex--;
    2746                 :         }
    2747                 : 
    2748                 :       } else {
    2749                 :         // Remove cell from selection if it belongs to the given cells range or
    2750                 :         // it is spanned onto the cells range.
    2751               0 :         nsCOMPtr<nsIDOMElement> cellElement;
    2752                 :         PRInt32 origRowIndex, origColIndex, rowSpan, colSpan,
    2753                 :           actualRowSpan, actualColSpan;
    2754                 :         bool isSelected;
    2755                 : 
    2756                 :         result = tableLayout->GetCellDataAt(curRowIndex, curColIndex,
    2757               0 :                                             *getter_AddRefs(cellElement),
    2758                 :                                             origRowIndex, origColIndex,
    2759                 :                                             rowSpan, colSpan, 
    2760                 :                                             actualRowSpan, actualColSpan,
    2761               0 :                                             isSelected);
    2762               0 :         if (NS_FAILED(result))
    2763               0 :           return result;
    2764                 : 
    2765               0 :         if (origRowIndex <= maxRowIndex &&
    2766                 :             origRowIndex + actualRowSpan - 1 >= minRowIndex &&
    2767                 :             origColIndex <= maxColIndex &&
    2768                 :             origColIndex + actualColSpan - 1 >= minColIndex) {
    2769                 : 
    2770               0 :           mDomSelections[index]->RemoveRange(range);
    2771                 :           // Since we've removed the range, decrement pointer to next range
    2772               0 :           mSelectedCellIndex--;
    2773                 :         }
    2774                 :       }
    2775                 :     }
    2776                 : 
    2777               0 :     range = GetNextCellRange();
    2778               0 :     cellNode = GetFirstSelectedContent(range);
    2779               0 :     NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
    2780                 :   }
    2781                 : 
    2782               0 :   return NS_OK;
    2783                 : }
    2784                 : 
    2785                 : nsresult
    2786               0 : nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
    2787                 :                                       PRInt32 aStartRowIndex,
    2788                 :                                       PRInt32 aStartColumnIndex,
    2789                 :                                       PRInt32 aEndRowIndex,
    2790                 :                                       PRInt32 aEndColumnIndex)
    2791                 : {
    2792               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    2793               0 :   if (!mDomSelections[index])
    2794               0 :     return NS_ERROR_NULL_POINTER;
    2795                 : 
    2796                 :   // Get TableLayout interface to access cell data based on cellmap location
    2797                 :   // frames are not ref counted, so don't use an nsCOMPtr
    2798               0 :   nsITableLayout *tableLayoutObject = GetTableLayout(aTableContent);
    2799               0 :   if (!tableLayoutObject) // Check that |table| is a table.
    2800               0 :     return NS_ERROR_FAILURE;
    2801                 : 
    2802               0 :   nsCOMPtr<nsIDOMElement> cellElement;
    2803                 :   PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan,
    2804                 :     curRowIndex, curColIndex;
    2805                 :   bool isSelected;
    2806               0 :   nsresult result = NS_OK;
    2807                 : 
    2808               0 :   PRInt32 row = aStartRowIndex;
    2809               0 :   while(true)
    2810                 :   {
    2811               0 :     PRInt32 col = aStartColumnIndex;
    2812               0 :     while(true)
    2813                 :     {
    2814               0 :       result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
    2815                 :                                                 curRowIndex, curColIndex, rowSpan, colSpan, 
    2816               0 :                                                 actualRowSpan, actualColSpan, isSelected);
    2817               0 :       if (NS_FAILED(result)) return result;
    2818                 : 
    2819               0 :       NS_ASSERTION(actualColSpan, "!actualColSpan is 0!");
    2820                 : 
    2821                 :       // Skip cells that are spanned from previous locations or are already selected
    2822               0 :       if (!isSelected && cellElement && row == curRowIndex && col == curColIndex)
    2823                 :       {
    2824               0 :         nsCOMPtr<nsIContent> cellContent = do_QueryInterface(cellElement);
    2825               0 :         result = SelectCellElement(cellContent);
    2826               0 :         if (NS_FAILED(result)) return result;
    2827                 :       }
    2828                 :       // Done when we reach end column
    2829               0 :       if (col == aEndColumnIndex) break;
    2830                 : 
    2831               0 :       if (aStartColumnIndex < aEndColumnIndex)
    2832               0 :         col ++;
    2833                 :       else
    2834               0 :         col--;
    2835                 :     };
    2836               0 :     if (row == aEndRowIndex) break;
    2837                 : 
    2838               0 :     if (aStartRowIndex < aEndRowIndex)
    2839               0 :       row++;
    2840                 :     else
    2841               0 :       row--;
    2842                 :   };
    2843               0 :   return result;
    2844                 : }
    2845                 : 
    2846                 : nsresult
    2847               0 : nsFrameSelection::RemoveCellsFromSelection(nsIContent *aTable,
    2848                 :                                            PRInt32 aStartRowIndex,
    2849                 :                                            PRInt32 aStartColumnIndex,
    2850                 :                                            PRInt32 aEndRowIndex,
    2851                 :                                            PRInt32 aEndColumnIndex)
    2852                 : {
    2853                 :   return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
    2854               0 :                        aEndRowIndex, aEndColumnIndex, false);
    2855                 : }
    2856                 : 
    2857                 : nsresult
    2858               0 : nsFrameSelection::RestrictCellsToSelection(nsIContent *aTable,
    2859                 :                                            PRInt32 aStartRowIndex,
    2860                 :                                            PRInt32 aStartColumnIndex,
    2861                 :                                            PRInt32 aEndRowIndex,
    2862                 :                                            PRInt32 aEndColumnIndex)
    2863                 : {
    2864                 :   return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
    2865               0 :                        aEndRowIndex, aEndColumnIndex, true);
    2866                 : }
    2867                 : 
    2868                 : nsresult
    2869               0 : nsFrameSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
    2870                 : {
    2871               0 :   if (!aCellContent) return NS_ERROR_NULL_POINTER;
    2872                 : 
    2873               0 :   nsIContent* table = GetParentTable(aCellContent);
    2874               0 :   if (!table) return NS_ERROR_NULL_POINTER;
    2875                 : 
    2876                 :   // Get table and cell layout interfaces to access 
    2877                 :   //   cell data based on cellmap location
    2878                 :   // Frames are not ref counted, so don't use an nsCOMPtr
    2879               0 :   nsITableLayout *tableLayout = GetTableLayout(table);
    2880               0 :   if (!tableLayout) return NS_ERROR_FAILURE;
    2881               0 :   nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
    2882               0 :   if (!cellLayout) return NS_ERROR_FAILURE;
    2883                 : 
    2884                 :   // Get location of target cell:      
    2885                 :   PRInt32 rowIndex, colIndex, curRowIndex, curColIndex;
    2886               0 :   nsresult result = cellLayout->GetCellIndexes(rowIndex, colIndex);
    2887               0 :   if (NS_FAILED(result)) return result;
    2888                 : 
    2889                 :   // Be sure we start at proper beginning
    2890                 :   // (This allows us to select row or col given ANY cell!)
    2891               0 :   if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
    2892               0 :     colIndex = 0;
    2893               0 :   if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
    2894               0 :     rowIndex = 0;
    2895                 : 
    2896               0 :   nsCOMPtr<nsIDOMElement> cellElement;
    2897               0 :   nsCOMPtr<nsIContent> firstCell;
    2898               0 :   nsCOMPtr<nsIDOMElement> lastCell;
    2899                 :   PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
    2900                 :   bool isSelected;
    2901                 : 
    2902               0 :   do {
    2903                 :     // Loop through all cells in column or row to find first and last
    2904               0 :     result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
    2905                 :                                         curRowIndex, curColIndex, rowSpan, colSpan, 
    2906               0 :                                         actualRowSpan, actualColSpan, isSelected);
    2907               0 :     if (NS_FAILED(result)) return result;
    2908               0 :     if (cellElement)
    2909                 :     {
    2910               0 :       NS_ASSERTION(actualRowSpan > 0 && actualColSpan> 0, "SelectRowOrColumn: Bad rowspan or colspan\n");
    2911               0 :       if (!firstCell)
    2912               0 :         firstCell = do_QueryInterface(cellElement);
    2913                 : 
    2914               0 :       lastCell = cellElement;
    2915                 : 
    2916                 :       // Move to next cell in cellmap, skipping spanned locations
    2917               0 :       if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
    2918               0 :         colIndex += actualColSpan;
    2919                 :       else
    2920               0 :         rowIndex += actualRowSpan;
    2921                 :     }
    2922                 :   }
    2923               0 :   while (cellElement);
    2924                 : 
    2925                 :   // Use SelectBlockOfCells:
    2926                 :   // This will replace existing selection,
    2927                 :   //  but allow unselecting by dragging out of selected region
    2928               0 :   if (firstCell && lastCell)
    2929                 :   {
    2930               0 :     if (!mStartSelectedCell)
    2931                 :     {
    2932                 :       // We are starting a new block, so select the first cell
    2933               0 :       result = SelectCellElement(firstCell);
    2934               0 :       if (NS_FAILED(result)) return result;
    2935               0 :       mStartSelectedCell = firstCell;
    2936                 :     }
    2937               0 :     nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
    2938               0 :     result = SelectBlockOfCells(mStartSelectedCell, lastCellContent);
    2939                 : 
    2940                 :     // This gets set to the cell at end of row/col, 
    2941                 :     //   but we need it to be the cell under cursor
    2942               0 :     mEndSelectedCell = aCellContent;
    2943               0 :     return result;
    2944                 :   }
    2945                 : 
    2946                 : #if 0
    2947                 : // This is a more efficient strategy that appends row to current selection,
    2948                 : //  but doesn't allow dragging OFF of an existing selection to unselect!
    2949                 :   do {
    2950                 :     // Loop through all cells in column or row
    2951                 :     result = tableLayout->GetCellDataAt(rowIndex, colIndex,
    2952                 :                                         getter_AddRefs(cellElement),
    2953                 :                                         curRowIndex, curColIndex,
    2954                 :                                         rowSpan, colSpan,
    2955                 :                                         actualRowSpan, actualColSpan,
    2956                 :                                         isSelected);
    2957                 :     if (NS_FAILED(result)) return result;
    2958                 :     // We're done when cell is not found
    2959                 :     if (!cellElement) break;
    2960                 : 
    2961                 : 
    2962                 :     // Check spans else we infinitely loop
    2963                 :     NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
    2964                 :     NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
    2965                 :     
    2966                 :     // Skip cells that are already selected or span from outside our region
    2967                 :     if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
    2968                 :     {
    2969                 :       result = SelectCellElement(cellElement);
    2970                 :       if (NS_FAILED(result)) return result;
    2971                 :     }
    2972                 :     // Move to next row or column in cellmap, skipping spanned locations
    2973                 :     if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
    2974                 :       colIndex += actualColSpan;
    2975                 :     else
    2976                 :       rowIndex += actualRowSpan;
    2977                 :   }
    2978                 :   while (cellElement);
    2979                 : #endif
    2980                 : 
    2981               0 :   return NS_OK;
    2982                 : }
    2983                 : 
    2984                 : nsIContent*
    2985               0 : nsFrameSelection::GetFirstCellNodeInRange(nsRange *aRange) const
    2986                 : {
    2987               0 :   if (!aRange) return nsnull;
    2988                 : 
    2989               0 :   nsINode* startParent = aRange->GetStartParent();
    2990               0 :   if (!startParent)
    2991               0 :     return nsnull;
    2992                 : 
    2993               0 :   PRInt32 offset = aRange->StartOffset();
    2994                 : 
    2995               0 :   nsIContent* childContent = startParent->GetChildAt(offset);
    2996               0 :   if (!childContent)
    2997               0 :     return nsnull;
    2998                 :   // Don't return node if not a cell
    2999               0 :   if (!IsCell(childContent))
    3000               0 :     return nsnull;
    3001                 : 
    3002               0 :   return childContent;
    3003                 : }
    3004                 : 
    3005                 : nsRange*
    3006               0 : nsFrameSelection::GetFirstCellRange()
    3007                 : {
    3008               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    3009               0 :   if (!mDomSelections[index])
    3010               0 :     return nsnull;
    3011                 : 
    3012               0 :   nsRange* firstRange = mDomSelections[index]->GetRangeAt(0);
    3013               0 :   if (!GetFirstCellNodeInRange(firstRange)) {
    3014               0 :     return nsnull;
    3015                 :   }
    3016                 : 
    3017                 :   // Setup for next cell
    3018               0 :   mSelectedCellIndex = 1;
    3019                 : 
    3020               0 :   return firstRange;
    3021                 : }
    3022                 : 
    3023                 : nsRange*
    3024               0 : nsFrameSelection::GetNextCellRange()
    3025                 : {
    3026               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    3027               0 :   if (!mDomSelections[index])
    3028               0 :     return nsnull;
    3029                 : 
    3030               0 :   nsRange* range = mDomSelections[index]->GetRangeAt(mSelectedCellIndex);
    3031                 : 
    3032                 :   // Get first node in next range of selection - test if it's a cell
    3033               0 :   if (!GetFirstCellNodeInRange(range)) {
    3034               0 :     return nsnull;
    3035                 :   }
    3036                 : 
    3037                 :   // Setup for next cell
    3038               0 :   mSelectedCellIndex++;
    3039                 : 
    3040               0 :   return range;
    3041                 : }
    3042                 : 
    3043                 : nsresult
    3044               0 : nsFrameSelection::GetCellIndexes(nsIContent *aCell,
    3045                 :                                  PRInt32    &aRowIndex,
    3046                 :                                  PRInt32    &aColIndex)
    3047                 : {
    3048               0 :   if (!aCell) return NS_ERROR_NULL_POINTER;
    3049                 : 
    3050               0 :   aColIndex=0; // initialize out params
    3051               0 :   aRowIndex=0;
    3052                 : 
    3053               0 :   nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
    3054               0 :   if (!cellLayoutObject)  return NS_ERROR_FAILURE;
    3055               0 :   return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
    3056                 : }
    3057                 : 
    3058                 : nsIContent*
    3059               0 : nsFrameSelection::IsInSameTable(nsIContent  *aContent1,
    3060                 :                                 nsIContent  *aContent2) const
    3061                 : {
    3062               0 :   if (!aContent1 || !aContent2) return nsnull;
    3063                 :   
    3064               0 :   nsIContent* tableNode1 = GetParentTable(aContent1);
    3065               0 :   nsIContent* tableNode2 = GetParentTable(aContent2);
    3066                 : 
    3067                 :   // Must be in the same table.  Note that we want to return false for
    3068                 :   // the test if both tables are null.
    3069               0 :   return (tableNode1 == tableNode2) ? tableNode1 : nsnull;
    3070                 : }
    3071                 : 
    3072                 : nsIContent*
    3073               0 : nsFrameSelection::GetParentTable(nsIContent *aCell) const
    3074                 : {
    3075               0 :   if (!aCell) {
    3076               0 :     return nsnull;
    3077                 :   }
    3078                 : 
    3079               0 :   for (nsIContent* parent = aCell->GetParent(); parent;
    3080               0 :        parent = parent->GetParent()) {
    3081               0 :     if (parent->Tag() == nsGkAtoms::table &&
    3082               0 :         parent->IsHTML()) {
    3083               0 :       return parent;
    3084                 :     }
    3085                 :   }
    3086                 : 
    3087               0 :   return nsnull;
    3088                 : }
    3089                 : 
    3090                 : nsresult
    3091               0 : nsFrameSelection::SelectCellElement(nsIContent *aCellElement)
    3092                 : {
    3093               0 :   nsIContent *parent = aCellElement->GetParent();
    3094                 : 
    3095                 :   // Get child offset
    3096               0 :   PRInt32 offset = parent->IndexOf(aCellElement);
    3097                 : 
    3098               0 :   return CreateAndAddRange(parent, offset);
    3099                 : }
    3100                 : 
    3101                 : nsresult
    3102               0 : nsTypedSelection::getTableCellLocationFromRange(nsRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol)
    3103                 : {
    3104               0 :   if (!aRange || !aSelectionType || !aRow || !aCol)
    3105               0 :     return NS_ERROR_NULL_POINTER;
    3106                 : 
    3107               0 :   *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
    3108               0 :   *aRow = 0;
    3109               0 :   *aCol = 0;
    3110                 : 
    3111                 :   // Must have access to frame selection to get cell info
    3112               0 :   if (!mFrameSelection) return NS_OK;
    3113                 : 
    3114               0 :   nsresult result = GetTableSelectionType(aRange, aSelectionType);
    3115               0 :   if (NS_FAILED(result)) return result;
    3116                 :   
    3117                 :   // Don't fail if range does not point to a single table cell,
    3118                 :   //  let aSelectionType tell user if we don't have a cell
    3119               0 :   if (*aSelectionType  != nsISelectionPrivate::TABLESELECTION_CELL)
    3120               0 :     return NS_OK;
    3121                 : 
    3122                 :   // Get the child content (the cell) pointed to by starting node of range
    3123                 :   // We do minimal checking since GetTableSelectionType assures
    3124                 :   //   us that this really is a table cell
    3125               0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
    3126               0 :   if (!content)
    3127               0 :     return NS_ERROR_FAILURE;
    3128                 : 
    3129               0 :   nsIContent *child = content->GetChildAt(aRange->StartOffset());
    3130               0 :   if (!child)
    3131               0 :     return NS_ERROR_FAILURE;
    3132                 : 
    3133                 :   //Note: This is a non-ref-counted pointer to the frame
    3134               0 :   nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
    3135               0 :   if (NS_FAILED(result))
    3136               0 :     return result;
    3137               0 :   if (!cellLayout)
    3138               0 :     return NS_ERROR_FAILURE;
    3139                 : 
    3140               0 :   return cellLayout->GetCellIndexes(*aRow, *aCol);
    3141                 : }
    3142                 : 
    3143                 : nsresult
    3144               0 : nsTypedSelection::addTableCellRange(nsRange *aRange, bool *aDidAddRange,
    3145                 :                                     PRInt32 *aOutIndex)
    3146                 : {  
    3147               0 :   if (!aDidAddRange || !aOutIndex)
    3148               0 :     return NS_ERROR_NULL_POINTER;
    3149                 : 
    3150               0 :   *aDidAddRange = false;
    3151               0 :   *aOutIndex = -1;
    3152                 : 
    3153               0 :   if (!mFrameSelection)
    3154               0 :     return NS_OK;
    3155                 : 
    3156               0 :   if (!aRange)
    3157               0 :     return NS_ERROR_NULL_POINTER;
    3158                 : 
    3159                 :   nsresult result;
    3160                 : 
    3161                 :   // Get if we are adding a cell selection and the row, col of cell if we are
    3162                 :   PRInt32 newRow, newCol, tableMode;
    3163               0 :   result = getTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
    3164               0 :   if (NS_FAILED(result)) return result;
    3165                 :   
    3166                 :   // If not adding a cell range, we are done here
    3167               0 :   if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
    3168                 :   {
    3169               0 :     mFrameSelection->mSelectingTableCellMode = tableMode;
    3170                 :     // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
    3171               0 :     return NS_OK;
    3172                 :   }
    3173                 :   
    3174                 :   // Set frame selection mode only if not already set to a table mode
    3175                 :   //  so we don't lose the select row and column flags (not detected by getTableCellLocation)
    3176               0 :   if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE)
    3177               0 :     mFrameSelection->mSelectingTableCellMode = tableMode;
    3178                 : 
    3179               0 :   *aDidAddRange = true;
    3180               0 :   return AddItem(aRange, aOutIndex);
    3181                 : }
    3182                 : 
    3183                 : //TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
    3184                 : nsresult
    3185               0 : nsTypedSelection::GetTableSelectionType(nsIDOMRange* aDOMRange,
    3186                 :                                         PRInt32* aTableSelectionType)
    3187                 : {
    3188               0 :   if (!aDOMRange || !aTableSelectionType)
    3189               0 :     return NS_ERROR_NULL_POINTER;
    3190               0 :   nsRange* range = static_cast<nsRange*>(aDOMRange);
    3191                 :   
    3192               0 :   *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
    3193                 :  
    3194                 :   // Must have access to frame selection to get cell info
    3195               0 :   if(!mFrameSelection) return NS_OK;
    3196                 : 
    3197               0 :   nsINode* startNode = range->GetStartParent();
    3198               0 :   if (!startNode) return NS_ERROR_FAILURE;
    3199                 :   
    3200               0 :   nsINode* endNode = range->GetEndParent();
    3201               0 :   if (!endNode) return NS_ERROR_FAILURE;
    3202                 : 
    3203                 :   // Not a single selected node
    3204               0 :   if (startNode != endNode) return NS_OK;
    3205                 : 
    3206               0 :   PRInt32 startOffset = range->StartOffset();
    3207               0 :   PRInt32 endOffset = range->EndOffset();
    3208                 : 
    3209                 :   // Not a single selected node
    3210               0 :   if ((endOffset - startOffset) != 1)
    3211               0 :     return NS_OK;
    3212                 : 
    3213               0 :   nsIContent* startContent = static_cast<nsIContent*>(startNode);
    3214               0 :   if (!(startNode->IsElement() && startContent->IsHTML())) {
    3215                 :     // Implies a check for being an element; if we ever make this work
    3216                 :     // for non-HTML, need to keep checking for elements.
    3217               0 :     return NS_OK;
    3218                 :   }
    3219                 : 
    3220               0 :   nsIAtom *tag = startContent->Tag();
    3221                 : 
    3222               0 :   if (tag == nsGkAtoms::tr)
    3223                 :   {
    3224               0 :     *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
    3225                 :   }
    3226                 :   else //check to see if we are selecting a table or row (column and all cells not done yet)
    3227                 :   {
    3228               0 :     nsIContent *child = startNode->GetChildAt(startOffset);
    3229               0 :     if (!child)
    3230               0 :       return NS_ERROR_FAILURE;
    3231                 : 
    3232               0 :     tag = child->Tag();
    3233                 : 
    3234               0 :     if (tag == nsGkAtoms::table)
    3235               0 :       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
    3236               0 :     else if (tag == nsGkAtoms::tr)
    3237               0 :       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
    3238                 :   }
    3239                 : 
    3240               0 :   return NS_OK;
    3241                 : }
    3242                 : 
    3243                 : nsresult
    3244               0 : nsFrameSelection::CreateAndAddRange(nsINode *aParentNode, PRInt32 aOffset)
    3245                 : {
    3246               0 :   if (!aParentNode) return NS_ERROR_NULL_POINTER;
    3247                 : 
    3248               0 :   nsRefPtr<nsRange> range = new nsRange();
    3249                 : 
    3250                 :   // Set range around child at given offset
    3251               0 :   nsresult result = range->SetStart(aParentNode, aOffset);
    3252               0 :   if (NS_FAILED(result)) return result;
    3253               0 :   result = range->SetEnd(aParentNode, aOffset+1);
    3254               0 :   if (NS_FAILED(result)) return result;
    3255                 :   
    3256               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    3257               0 :   if (!mDomSelections[index])
    3258               0 :     return NS_ERROR_NULL_POINTER;
    3259                 : 
    3260               0 :   return mDomSelections[index]->AddRange(range);
    3261                 : }
    3262                 : 
    3263                 : // End of Table Selection
    3264                 : 
    3265                 : void
    3266               0 : nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter)
    3267                 : {
    3268               0 :   if (mAncestorLimiter != aLimiter) {
    3269               0 :     mAncestorLimiter = aLimiter;
    3270                 :     PRInt8 index =
    3271               0 :       GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    3272               0 :     if (!mDomSelections[index])
    3273               0 :       return;
    3274                 : 
    3275               0 :     if (!IsValidSelectionPoint(this, mDomSelections[index]->GetFocusNode())) {
    3276               0 :       ClearNormalSelection();
    3277               0 :       if (mAncestorLimiter) {
    3278               0 :         PostReason(nsISelectionListener::NO_REASON);
    3279               0 :         TakeFocus(mAncestorLimiter, 0, 0, HINTLEFT, false, false);
    3280                 :       }
    3281                 :     }
    3282                 :   }
    3283                 : }
    3284                 : 
    3285                 : //END nsFrameSelection methods
    3286                 : 
    3287                 : 
    3288                 : //BEGIN nsISelection interface implementations
    3289                 : 
    3290                 : 
    3291                 : 
    3292                 : nsresult
    3293               0 : nsFrameSelection::DeleteFromDocument()
    3294                 : {
    3295                 :   nsresult res;
    3296                 : 
    3297                 :   // If we're already collapsed, then we do nothing (bug 719503).
    3298                 :   bool isCollapsed;
    3299               0 :   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
    3300               0 :   if (!mDomSelections[index])
    3301               0 :     return NS_ERROR_NULL_POINTER;
    3302                 : 
    3303               0 :   mDomSelections[index]->GetIsCollapsed( &isCollapsed);
    3304               0 :   if (isCollapsed)
    3305                 :   {
    3306               0 :     return NS_OK;
    3307                 :   }
    3308                 : 
    3309                 :   // Get an iterator
    3310               0 :   nsSelectionIterator iter(mDomSelections[index]);
    3311               0 :   res = iter.First();
    3312               0 :   if (NS_FAILED(res))
    3313               0 :     return res;
    3314                 : 
    3315               0 :   while (iter.IsDone())
    3316                 :   {
    3317               0 :     nsRefPtr<nsRange> range = iter.CurrentItem();
    3318               0 :     res = range->DeleteContents();
    3319               0 :     if (NS_FAILED(res))
    3320               0 :       return res;
    3321               0 :     iter.Next();
    3322                 :   }
    3323                 : 
    3324                 :   // Collapse to the new location.
    3325                 :   // If we deleted one character, then we move back one element.
    3326                 :   // FIXME  We don't know how to do this past frame boundaries yet.
    3327               0 :   if (isCollapsed)
    3328               0 :     mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->GetAnchorOffset()-1);
    3329               0 :   else if (mDomSelections[index]->GetAnchorOffset() > 0)
    3330               0 :     mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->GetAnchorOffset());
    3331                 : #ifdef DEBUG
    3332                 :   else
    3333               0 :     printf("Don't know how to set selection back past frame boundary\n");
    3334                 : #endif
    3335                 : 
    3336               0 :   return NS_OK;
    3337                 : }
    3338                 : 
    3339                 : void
    3340               0 : nsFrameSelection::SetDelayedCaretData(nsMouseEvent *aMouseEvent)
    3341                 : {
    3342               0 :   if (aMouseEvent)
    3343                 :   {
    3344               0 :     mDelayedMouseEventValid = true;
    3345               0 :     mDelayedMouseEvent      = *aMouseEvent;
    3346                 : 
    3347                 :     // Don't cache the widget.  We don't need it and it could go away.
    3348               0 :     mDelayedMouseEvent.widget = nsnull;
    3349                 :   }
    3350                 :   else
    3351               0 :     mDelayedMouseEventValid = false;
    3352               0 : }
    3353                 : 
    3354                 : nsMouseEvent*
    3355               0 : nsFrameSelection::GetDelayedCaretData()
    3356                 : {
    3357               0 :   if (mDelayedMouseEventValid)
    3358               0 :     return &mDelayedMouseEvent;
    3359                 :   
    3360               0 :   return nsnull;
    3361                 : }
    3362                 : 
    3363                 : void
    3364               0 : nsFrameSelection::DisconnectFromPresShell()
    3365                 : {
    3366               0 :   StopAutoScrollTimer();
    3367               0 :   for (PRInt32 i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; i++) {
    3368               0 :     mDomSelections[i]->Clear(nsnull);
    3369                 :   }
    3370               0 :   mShell = nsnull;
    3371               0 : }
    3372                 : 
    3373                 : //END nsISelection interface implementations
    3374                 : 
    3375                 : #if 0
    3376                 : #pragma mark -
    3377                 : #endif
    3378                 : 
    3379                 : // nsTypedSelection implementation
    3380                 : 
    3381                 : // note: this can return a nil anchor node
    3382                 : 
    3383               0 : nsTypedSelection::nsTypedSelection()
    3384                 :   : mCachedOffsetForFrame(nsnull)
    3385                 :   , mDirection(eDirNext)
    3386               0 :   , mType(nsISelectionController::SELECTION_NORMAL)
    3387                 : {
    3388               0 : }
    3389                 : 
    3390               0 : nsTypedSelection::nsTypedSelection(nsFrameSelection *aList)
    3391                 :   : mFrameSelection(aList)
    3392                 :   , mCachedOffsetForFrame(nsnull)
    3393                 :   , mDirection(eDirNext)
    3394               0 :   , mType(nsISelectionController::SELECTION_NORMAL)
    3395                 : {
    3396               0 : }
    3397                 : 
    3398               0 : nsTypedSelection::~nsTypedSelection()
    3399                 : {
    3400               0 :   setAnchorFocusRange(-1);
    3401                 : 
    3402               0 :   PRUint32 count = mRanges.Length();
    3403               0 :   for (PRUint32 i = 0; i < count; ++i) {
    3404               0 :     mRanges[i].mRange->SetInSelection(false);
    3405                 :   }
    3406                 : 
    3407               0 :   if (mAutoScrollTimer) {
    3408               0 :     mAutoScrollTimer->Stop();
    3409               0 :     mAutoScrollTimer = nsnull;
    3410                 :   }
    3411                 : 
    3412               0 :   mScrollEvent.Revoke();
    3413                 : 
    3414               0 :   if (mCachedOffsetForFrame) {
    3415               0 :     delete mCachedOffsetForFrame;
    3416               0 :     mCachedOffsetForFrame = nsnull;
    3417                 :   }
    3418               0 : }
    3419                 : 
    3420                 : 
    3421            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsTypedSelection)
    3422               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTypedSelection)
    3423                 :   // Unlink the selection listeners *before* we do RemoveAllRanges since
    3424                 :   // we don't want to notify the listeners during JS GC (they could be
    3425                 :   // in JS!).
    3426               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mSelectionListeners)
    3427               0 :   tmp->RemoveAllRanges();
    3428               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFrameSelection)
    3429               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    3430               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTypedSelection)
    3431                 :   {
    3432               0 :     PRUint32 i, count = tmp->mRanges.Length();
    3433               0 :     for (i = 0; i < count; ++i) {
    3434               0 :       NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRanges[i].mRange, nsIDOMRange)
    3435                 :     }
    3436                 :   }
    3437               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mAnchorFocusRange, nsIDOMRange)
    3438               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFrameSelection)
    3439               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mSelectionListeners)
    3440               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    3441                 : 
    3442                 : DOMCI_DATA(Selection, nsTypedSelection)
    3443                 : 
    3444                 : // QueryInterface implementation for nsTypedSelection
    3445               0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTypedSelection)
    3446               0 :   NS_INTERFACE_MAP_ENTRY(nsISelection)
    3447               0 :   NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
    3448               0 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    3449               0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
    3450               0 :   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Selection)
    3451               0 : NS_INTERFACE_MAP_END
    3452                 : 
    3453               0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTypedSelection)
    3454               0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTypedSelection)
    3455                 : 
    3456                 : NS_IMETHODIMP
    3457               0 : nsTypedSelection::SetPresShell(nsIPresShell *aPresShell)
    3458                 : {
    3459               0 :   mPresShellWeak = do_GetWeakReference(aPresShell);
    3460               0 :   return NS_OK;
    3461                 : }
    3462                 : 
    3463                 : 
    3464                 : 
    3465                 : NS_IMETHODIMP
    3466               0 : nsTypedSelection::GetAnchorNode(nsIDOMNode** aAnchorNode)
    3467                 : {
    3468               0 :   nsINode* anchorNode = GetAnchorNode();
    3469               0 :   if (anchorNode) {
    3470               0 :     return CallQueryInterface(anchorNode, aAnchorNode);
    3471                 :   }
    3472                 : 
    3473               0 :   *aAnchorNode = nsnull;
    3474               0 :   return NS_OK;
    3475                 : }
    3476                 : 
    3477                 : nsINode*
    3478               0 : nsTypedSelection::GetAnchorNode()
    3479                 : {
    3480               0 :   if (!mAnchorFocusRange)
    3481               0 :     return nsnull;
    3482                 :    
    3483               0 :   if (GetDirection() == eDirNext) {
    3484               0 :     return mAnchorFocusRange->GetStartParent();
    3485                 :   }
    3486                 : 
    3487               0 :   return mAnchorFocusRange->GetEndParent();
    3488                 : }
    3489                 : 
    3490                 : NS_IMETHODIMP
    3491               0 : nsTypedSelection::GetAnchorOffset(PRInt32* aAnchorOffset)
    3492                 : {
    3493               0 :   *aAnchorOffset = GetAnchorOffset();
    3494               0 :   return NS_OK;
    3495                 : }
    3496                 : 
    3497                 : // note: this can return a nil focus node
    3498                 : NS_IMETHODIMP
    3499               0 : nsTypedSelection::GetFocusNode(nsIDOMNode** aFocusNode)
    3500                 : {
    3501               0 :   nsINode* focusNode = GetFocusNode();
    3502               0 :   if (focusNode) {
    3503               0 :     return CallQueryInterface(focusNode, aFocusNode);
    3504                 :   }
    3505                 : 
    3506               0 :   *aFocusNode = nsnull;
    3507               0 :   return NS_OK;
    3508                 : }
    3509                 : 
    3510                 : nsINode*
    3511               0 : nsTypedSelection::GetFocusNode()
    3512                 : {
    3513               0 :   if (!mAnchorFocusRange)
    3514               0 :     return nsnull;
    3515                 : 
    3516               0 :   if (GetDirection() == eDirNext){
    3517               0 :     return mAnchorFocusRange->GetEndParent();
    3518                 :   }
    3519                 : 
    3520               0 :   return mAnchorFocusRange->GetStartParent();
    3521                 : }
    3522                 : 
    3523               0 : NS_IMETHODIMP nsTypedSelection::GetFocusOffset(PRInt32* aFocusOffset)
    3524                 : {
    3525               0 :   *aFocusOffset = GetFocusOffset();
    3526               0 :   return NS_OK;
    3527                 : }
    3528                 : 
    3529               0 : void nsTypedSelection::setAnchorFocusRange(PRInt32 indx)
    3530                 : {
    3531               0 :   if (indx >= (PRInt32)mRanges.Length())
    3532               0 :     return;
    3533               0 :   if (indx < 0) //release all
    3534                 :   {
    3535               0 :     mAnchorFocusRange = nsnull;
    3536                 :   }
    3537                 :   else{
    3538               0 :     mAnchorFocusRange = mRanges[indx].mRange;
    3539                 :   }
    3540                 : }
    3541                 : 
    3542                 : PRInt32
    3543               0 : nsTypedSelection::GetAnchorOffset()
    3544                 : {
    3545               0 :   if (!mAnchorFocusRange)
    3546               0 :     return 0;
    3547                 : 
    3548               0 :   if (GetDirection() == eDirNext){
    3549               0 :     return mAnchorFocusRange->StartOffset();
    3550                 :   }
    3551                 : 
    3552               0 :   return mAnchorFocusRange->EndOffset();
    3553                 : }
    3554                 : 
    3555                 : PRInt32
    3556               0 : nsTypedSelection::GetFocusOffset()
    3557                 : {
    3558               0 :   if (!mAnchorFocusRange)
    3559               0 :     return 0;
    3560                 : 
    3561               0 :   if (GetDirection() == eDirNext){
    3562               0 :     return mAnchorFocusRange->EndOffset();
    3563                 :   }
    3564                 : 
    3565               0 :   return mAnchorFocusRange->StartOffset();
    3566                 : }
    3567                 : 
    3568                 : static nsresult
    3569               0 : CompareToRangeStart(nsINode* aCompareNode, PRInt32 aCompareOffset,
    3570                 :                     nsRange* aRange, PRInt32* aCmp)
    3571                 : {
    3572               0 :   nsINode* start = aRange->GetStartParent();
    3573               0 :   NS_ENSURE_STATE(aCompareNode && start);
    3574                 :   // If the nodes that we're comparing are not in the same document,
    3575                 :   // assume that aCompareNode will fall at the end of the ranges.
    3576               0 :   if (aCompareNode->GetCurrentDoc() != start->GetCurrentDoc() ||
    3577               0 :       !start->GetCurrentDoc()) {
    3578               0 :     *aCmp = 1;
    3579                 :   } else {
    3580                 :     *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
    3581               0 :                                           start, aRange->StartOffset());
    3582                 :   }
    3583               0 :   return NS_OK;
    3584                 : }
    3585                 : 
    3586                 : static nsresult
    3587               0 : CompareToRangeEnd(nsINode* aCompareNode, PRInt32 aCompareOffset,
    3588                 :                   nsRange* aRange, PRInt32* aCmp)
    3589                 : {
    3590               0 :   nsINode* end = aRange->GetEndParent();
    3591               0 :   NS_ENSURE_STATE(aCompareNode && end);
    3592                 :   // If the nodes that we're comparing are not in the same document,
    3593                 :   // assume that aCompareNode will fall at the end of the ranges.
    3594               0 :   if (aCompareNode->GetCurrentDoc() != end->GetCurrentDoc() ||
    3595               0 :       !end->GetCurrentDoc()) {
    3596               0 :     *aCmp = 1;
    3597                 :   } else {
    3598                 :     *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
    3599               0 :                                           end, aRange->EndOffset());
    3600                 :   }
    3601               0 :   return NS_OK;
    3602                 : }
    3603                 : 
    3604                 : // nsTypedSelection::FindInsertionPoint
    3605                 : //
    3606                 : //    Binary searches the given sorted array of ranges for the insertion point
    3607                 : //    for the given node/offset. The given comparator is used, and the index
    3608                 : //    where the point should appear in the array is placed in *aInsertionPoint.
    3609                 : //
    3610                 : //    If there is an item in the array equal to the input point, we will return
    3611                 : //    the index of this item.
    3612                 : 
    3613                 : nsresult
    3614               0 : nsTypedSelection::FindInsertionPoint(
    3615                 :     nsTArray<RangeData>* aElementArray,
    3616                 :     nsINode* aPointNode, PRInt32 aPointOffset,
    3617                 :     nsresult (*aComparator)(nsINode*,PRInt32,nsRange*,PRInt32*),
    3618                 :     PRInt32* aPoint)
    3619                 : {
    3620               0 :   *aPoint = 0;
    3621               0 :   PRInt32 beginSearch = 0;
    3622               0 :   PRInt32 endSearch = aElementArray->Length(); // one beyond what to check
    3623               0 :   while (endSearch - beginSearch > 0) {
    3624               0 :     PRInt32 center = (endSearch - beginSearch) / 2 + beginSearch;
    3625                 : 
    3626               0 :     nsRange* range = (*aElementArray)[center].mRange;
    3627                 : 
    3628                 :     PRInt32 cmp;
    3629               0 :     nsresult rv = aComparator(aPointNode, aPointOffset, range, &cmp);
    3630               0 :     NS_ENSURE_SUCCESS(rv, rv);
    3631                 : 
    3632               0 :     if (cmp < 0) {        // point < cur
    3633               0 :       endSearch = center;
    3634               0 :     } else if (cmp > 0) { // point > cur
    3635               0 :       beginSearch = center + 1;
    3636                 :     } else {              // found match, done
    3637               0 :       beginSearch = center;
    3638               0 :       break;
    3639                 :     }
    3640                 :   }
    3641               0 :   *aPoint = beginSearch;
    3642               0 :   return NS_OK;
    3643                 : }
    3644                 : 
    3645                 : // nsTypedSelection::SubtractRange
    3646                 : //
    3647                 : //    A helper function that subtracts aSubtract from aRange, and adds
    3648                 : //    1 or 2 RangeData objects representing the remaining non-overlapping
    3649                 : //    difference to aOutput. It is assumed that the caller has checked that
    3650                 : //    aRange and aSubtract do indeed overlap
    3651                 : 
    3652                 : nsresult
    3653               0 : nsTypedSelection::SubtractRange(RangeData* aRange, nsRange* aSubtract,
    3654                 :                                 nsTArray<RangeData>* aOutput)
    3655                 : {
    3656               0 :   nsRange* range = aRange->mRange;
    3657                 : 
    3658                 :   // First we want to compare to the range start
    3659                 :   PRInt32 cmp;
    3660                 :   nsresult rv = CompareToRangeStart(range->GetStartParent(),
    3661                 :                                     range->StartOffset(),
    3662               0 :                                     aSubtract, &cmp);
    3663               0 :   NS_ENSURE_SUCCESS(rv, rv);
    3664                 : 
    3665                 :   // Also, make a comparison to the range end
    3666                 :   PRInt32 cmp2;
    3667                 :   rv = CompareToRangeEnd(range->GetEndParent(),
    3668                 :                          range->EndOffset(),
    3669               0 :                          aSubtract, &cmp2);
    3670               0 :   NS_ENSURE_SUCCESS(rv, rv);
    3671                 : 
    3672                 :   // If the existing range left overlaps the new range (aSubtract) then
    3673                 :   // cmp < 0, and cmp2 < 0
    3674                 :   // If it right overlaps the new range then cmp > 0 and cmp2 > 0
    3675                 :   // If it fully contains the new range, then cmp < 0 and cmp2 > 0
    3676                 : 
    3677               0 :   if (cmp2 > 0) {
    3678                 :     // We need to add a new RangeData to the output, running from
    3679                 :     // the end of aSubtract to the end of range
    3680               0 :     nsRange* postOverlap = new nsRange();
    3681                 : 
    3682                 :     rv =
    3683               0 :       postOverlap->SetStart(aSubtract->GetEndParent(), aSubtract->EndOffset());
    3684               0 :     NS_ENSURE_SUCCESS(rv, rv);
    3685                 :     rv =
    3686               0 :      postOverlap->SetEnd(range->GetEndParent(), range->EndOffset());
    3687               0 :     NS_ENSURE_SUCCESS(rv, rv);
    3688               0 :     if (!postOverlap->Collapsed()) {
    3689               0 :       if (!aOutput->InsertElementAt(0, RangeData(postOverlap)))
    3690               0 :         return NS_ERROR_OUT_OF_MEMORY;
    3691               0 :       (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
    3692                 :     }
    3693                 :   }
    3694                 : 
    3695               0 :   if (cmp < 0) {
    3696                 :     // We need to add a new RangeData to the output, running from
    3697                 :     // the start of the range to the start of aSubtract
    3698               0 :     nsRange* preOverlap = new nsRange();
    3699                 : 
    3700                 :     nsresult rv =
    3701               0 :      preOverlap->SetStart(range->GetStartParent(), range->StartOffset());
    3702               0 :     NS_ENSURE_SUCCESS(rv, rv);
    3703                 :     rv =
    3704               0 :      preOverlap->SetEnd(aSubtract->GetStartParent(), aSubtract->StartOffset());
    3705               0 :     NS_ENSURE_SUCCESS(rv, rv);
    3706                 :     
    3707               0 :     if (!preOverlap->Collapsed()) {
    3708               0 :       if (!aOutput->InsertElementAt(0, RangeData(preOverlap)))
    3709               0 :         return NS_ERROR_OUT_OF_MEMORY;
    3710               0 :       (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
    3711                 :     }
    3712                 :   }
    3713                 : 
    3714               0 :   return NS_OK;
    3715                 : }
    3716                 : 
    3717                 : nsresult
    3718               0 : nsTypedSelection::AddItem(nsRange *aItem, PRInt32 *aOutIndex)
    3719                 : {
    3720               0 :   if (!aItem)
    3721               0 :     return NS_ERROR_NULL_POINTER;
    3722               0 :   if (!aItem->IsPositioned())
    3723               0 :     return NS_ERROR_UNEXPECTED;
    3724               0 :   if (aOutIndex)
    3725               0 :     *aOutIndex = -1;
    3726                 : 
    3727                 :   // a common case is that we have no ranges yet
    3728               0 :   if (mRanges.Length() == 0) {
    3729               0 :     if (!mRanges.AppendElement(RangeData(aItem)))
    3730               0 :       return NS_ERROR_OUT_OF_MEMORY;
    3731               0 :     aItem->SetInSelection(true);
    3732                 : 
    3733               0 :     if (aOutIndex)
    3734               0 :       *aOutIndex = 0;
    3735               0 :     return NS_OK;
    3736                 :   }
    3737                 : 
    3738                 :   PRInt32 startIndex, endIndex;
    3739                 :   GetIndicesForInterval(aItem->GetStartParent(), aItem->StartOffset(),
    3740                 :                         aItem->GetEndParent(), aItem->EndOffset(),
    3741               0 :                         false, &startIndex, &endIndex);
    3742                 : 
    3743               0 :   if (endIndex == -1) {
    3744                 :     // All ranges start after the given range. We can insert our range at
    3745                 :     // position 0, knowing there are no overlaps (handled below)
    3746               0 :     startIndex = 0;
    3747               0 :     endIndex = 0;
    3748               0 :   } else if (startIndex == -1) {
    3749                 :     // All ranges end before the given range. We can insert our range at
    3750                 :     // the end of the array, knowing there are no overlaps (handled below)
    3751               0 :     startIndex = mRanges.Length();
    3752               0 :     endIndex = startIndex;
    3753                 :   }
    3754                 : 
    3755                 :   // If the range is already contained in mRanges, silently succeed
    3756                 :   bool sameRange = EqualsRangeAtPoint(aItem->GetStartParent(),
    3757                 :                                         aItem->StartOffset(),
    3758                 :                                         aItem->GetEndParent(),
    3759               0 :                                         aItem->EndOffset(), startIndex);
    3760               0 :   if (sameRange) {
    3761               0 :     if (aOutIndex)
    3762               0 :       *aOutIndex = startIndex;
    3763               0 :     return NS_OK;
    3764                 :   }
    3765                 : 
    3766               0 :   if (startIndex == endIndex) {
    3767                 :     // The new range doesn't overlap any existing ranges
    3768               0 :     if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
    3769               0 :       return NS_ERROR_OUT_OF_MEMORY;
    3770               0 :     aItem->SetInSelection(true);
    3771               0 :     if (aOutIndex)
    3772               0 :       *aOutIndex = startIndex;
    3773               0 :     return NS_OK;
    3774                 :   }
    3775                 : 
    3776                 :   // We now know that at least 1 existing range overlaps with the range that
    3777                 :   // we are trying to add. In fact, the only ranges of interest are those at
    3778                 :   // the two end points, startIndex and endIndex - 1 (which may point to the
    3779                 :   // same range) as these may partially overlap the new range. Any ranges
    3780                 :   // between these indices are fully overlapped by the new range, and so can be
    3781                 :   // removed
    3782               0 :   nsTArray<RangeData> overlaps;
    3783               0 :   if (!overlaps.InsertElementAt(0, mRanges[startIndex]))
    3784               0 :     return NS_ERROR_OUT_OF_MEMORY;
    3785                 : 
    3786               0 :   if (endIndex - 1 != startIndex) {
    3787               0 :     if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
    3788               0 :       return NS_ERROR_OUT_OF_MEMORY;
    3789                 :   }
    3790                 : 
    3791                 :   // Remove all the overlapping ranges
    3792               0 :   for (PRInt32 i = startIndex; i < endIndex; ++i) {
    3793               0 :     mRanges[i].mRange->SetInSelection(false);
    3794                 :   }
    3795               0 :   mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
    3796                 : 
    3797               0 :   nsTArray<RangeData> temp;
    3798               0 :   for (PRInt32 i = overlaps.Length() - 1; i >= 0; i--) {
    3799               0 :     nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
    3800               0 :     NS_ENSURE_SUCCESS(rv, rv);
    3801                 :   }
    3802                 : 
    3803                 :   // Insert the new element into our "leftovers" array
    3804                 :   PRInt32 insertionPoint;
    3805                 :   nsresult rv = FindInsertionPoint(&temp, aItem->GetStartParent(),
    3806                 :                                    aItem->StartOffset(),
    3807                 :                                    CompareToRangeStart,
    3808               0 :                                    &insertionPoint);
    3809               0 :   NS_ENSURE_SUCCESS(rv, rv);
    3810                 : 
    3811               0 :   if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
    3812               0 :     return NS_ERROR_OUT_OF_MEMORY;
    3813                 : 
    3814                 :   // Merge the leftovers back in to mRanges
    3815               0 :   if (!mRanges.InsertElementsAt(startIndex, temp))
    3816               0 :     return NS_ERROR_OUT_OF_MEMORY;
    3817                 : 
    3818               0 :   for (PRUint32 i = 0; i < temp.Length(); ++i) {
    3819               0 :     temp[i].mRange->SetInSelection(true);
    3820                 :   }
    3821                 : 
    3822               0 :   *aOutIndex = startIndex + insertionPoint;
    3823               0 :   return NS_OK;
    3824                 : }
    3825                 : 
    3826                 : nsresult
    3827               0 : nsTypedSelection::RemoveItem(nsRange *aItem)
    3828                 : {
    3829               0 :   if (!aItem)
    3830               0 :     return NS_ERROR_NULL_POINTER;
    3831                 : 
    3832                 :   // Find the range's index & remove it. We could use FindInsertionPoint to
    3833                 :   // get O(log n) time, but that requires many expensive DOM comparisons.
    3834                 :   // For even several thousand items, this is probably faster because the
    3835                 :   // comparisons are so fast.
    3836               0 :   PRInt32 idx = -1;
    3837                 :   PRUint32 i;
    3838               0 :   for (i = 0; i < mRanges.Length(); i ++) {
    3839               0 :     if (mRanges[i].mRange == aItem) {
    3840               0 :       idx = (PRInt32)i;
    3841               0 :       break;
    3842                 :     }
    3843                 :   }
    3844               0 :   if (idx < 0)
    3845               0 :     return NS_ERROR_INVALID_ARG;
    3846                 : 
    3847               0 :   mRanges.RemoveElementAt(idx);
    3848               0 :   aItem->SetInSelection(false);
    3849               0 :   return NS_OK;
    3850                 : }
    3851                 : 
    3852                 : nsresult
    3853               0 : nsTypedSelection::RemoveCollapsedRanges()
    3854                 : {
    3855               0 :   PRUint32 i = 0;
    3856               0 :   while (i < mRanges.Length()) {
    3857               0 :     if (mRanges[i].mRange->Collapsed()) {
    3858               0 :       nsresult rv = RemoveItem(mRanges[i].mRange);
    3859               0 :       NS_ENSURE_SUCCESS(rv, rv);
    3860                 :     } else {
    3861               0 :       ++i;
    3862                 :     }
    3863                 :   }
    3864               0 :   return NS_OK;
    3865                 : }
    3866                 : 
    3867                 : nsresult
    3868               0 : nsTypedSelection::Clear(nsPresContext* aPresContext)
    3869                 : {
    3870               0 :   setAnchorFocusRange(-1);
    3871                 : 
    3872               0 :   for (PRUint32 i = 0; i < mRanges.Length(); ++i) {
    3873               0 :     mRanges[i].mRange->SetInSelection(false);
    3874               0 :     selectFrames(aPresContext, mRanges[i].mRange, false);
    3875                 :   }
    3876               0 :   mRanges.Clear();
    3877                 : 
    3878                 :   // Reset direction so for more dependable table selection range handling
    3879               0 :   SetDirection(eDirNext);
    3880                 : 
    3881                 :   // If this was an ATTENTION selection, change it back to normal now
    3882               0 :   if (mFrameSelection &&
    3883               0 :       mFrameSelection->GetDisplaySelection() ==
    3884               0 :       nsISelectionController::SELECTION_ATTENTION) {
    3885               0 :     mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
    3886                 :   }
    3887                 : 
    3888               0 :   return NS_OK;
    3889                 : }
    3890                 : 
    3891                 : NS_IMETHODIMP
    3892               0 : nsTypedSelection::GetType(PRInt16 *aType)
    3893                 : {
    3894               0 :   NS_ENSURE_ARG_POINTER(aType);
    3895               0 :   *aType = mType;
    3896                 : 
    3897               0 :   return NS_OK;
    3898                 : }
    3899                 : 
    3900                 : // RangeMatches*Point
    3901                 : //
    3902                 : //    Compares the range beginning or ending point, and returns true if it
    3903                 : //    exactly matches the given DOM point.
    3904                 : 
    3905                 : static inline bool
    3906               0 : RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, PRInt32 aOffset)
    3907                 : {
    3908               0 :   return aRange->GetStartParent() == aNode && aRange->StartOffset() == aOffset;
    3909                 : }
    3910                 : 
    3911                 : static inline bool
    3912               0 : RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, PRInt32 aOffset)
    3913                 : {
    3914               0 :   return aRange->GetEndParent() == aNode && aRange->EndOffset() == aOffset;
    3915                 : }
    3916                 : 
    3917                 : // nsTypedSelection::EqualsRangeAtPoint
    3918                 : //
    3919                 : //    Utility method for checking equivalence of two ranges.
    3920                 : 
    3921                 : bool
    3922               0 : nsTypedSelection::EqualsRangeAtPoint(
    3923                 :     nsINode* aBeginNode, PRInt32 aBeginOffset,
    3924                 :     nsINode* aEndNode, PRInt32 aEndOffset,
    3925                 :     PRInt32 aRangeIndex)
    3926                 : {
    3927               0 :   if (aRangeIndex >=0 && aRangeIndex < (PRInt32) mRanges.Length()) {
    3928               0 :     nsRange* range = mRanges[aRangeIndex].mRange;
    3929               0 :     if (RangeMatchesBeginPoint(range, aBeginNode, aBeginOffset) &&
    3930               0 :         RangeMatchesEndPoint(range, aEndNode, aEndOffset))
    3931               0 :       return true;
    3932                 :   }
    3933               0 :   return false;
    3934                 : }
    3935                 : 
    3936                 : // nsTypedSelection::GetRangesForInterval
    3937                 : //
    3938                 : //    XPCOM wrapper for the nsTArray version
    3939                 : 
    3940                 : NS_IMETHODIMP
    3941               0 : nsTypedSelection::GetRangesForInterval(nsIDOMNode* aBeginNode, PRInt32 aBeginOffset,
    3942                 :                                        nsIDOMNode* aEndNode, PRInt32 aEndOffset,
    3943                 :                                        bool aAllowAdjacent,
    3944                 :                                        PRUint32 *aResultCount,
    3945                 :                                        nsIDOMRange ***aResults)
    3946                 : {
    3947               0 :   if (!aBeginNode || ! aEndNode || ! aResultCount || ! aResults)
    3948               0 :     return NS_ERROR_NULL_POINTER;
    3949                 : 
    3950               0 :   *aResultCount = 0;
    3951               0 :   *aResults = nsnull;
    3952                 :   
    3953               0 :   nsCOMPtr<nsINode> beginNode = do_QueryInterface(aBeginNode);
    3954               0 :   nsCOMPtr<nsINode> endNode = do_QueryInterface(aEndNode);
    3955                 : 
    3956               0 :   nsTArray<nsRange*> results;
    3957                 :   nsresult rv = GetRangesForIntervalArray(beginNode, aBeginOffset,
    3958                 :                                           endNode, aEndOffset,
    3959               0 :                                           aAllowAdjacent, &results);
    3960               0 :   NS_ENSURE_SUCCESS(rv, rv);
    3961               0 :   *aResultCount = results.Length();
    3962               0 :   if (*aResultCount == 0) {
    3963               0 :     return NS_OK;
    3964                 :   }
    3965                 : 
    3966                 :   *aResults = static_cast<nsIDOMRange**>
    3967               0 :                          (nsMemory::Alloc(sizeof(nsIDOMRange*) * *aResultCount));
    3968               0 :   NS_ENSURE_TRUE(*aResults, NS_ERROR_OUT_OF_MEMORY);
    3969                 : 
    3970               0 :   for (PRUint32 i = 0; i < *aResultCount; i++)
    3971               0 :     NS_ADDREF((*aResults)[i] = results[i]);
    3972               0 :   return NS_OK;
    3973                 : }
    3974                 : 
    3975                 : // nsTypedSelection::GetRangesForIntervalArray
    3976                 : //
    3977                 : //    Fills a nsTArray with the ranges overlapping the range specified by
    3978                 : //    the given endpoints. Ranges in the selection exactly adjacent to the
    3979                 : //    input range are not returned unless aAllowAdjacent is set.
    3980                 : //
    3981                 : //    For example, if the following ranges were in the selection
    3982                 : //    (assume everything is within the same node)
    3983                 : //
    3984                 : //    Start Offset: 0 2 7 9
    3985                 : //      End Offset: 2 5 9 10
    3986                 : //
    3987                 : //    and passed aBeginOffset of 2 and aEndOffset of 9, then with
    3988                 : //    aAllowAdjacent set, all the ranges should be returned. If
    3989                 : //    aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
    3990                 : //    should be returned
    3991                 : //
    3992                 : //    Now that overlapping ranges are disallowed, there can be a maximum of
    3993                 : //    2 adjacent ranges
    3994                 : 
    3995                 : nsresult
    3996               0 : nsTypedSelection::GetRangesForIntervalArray(nsINode* aBeginNode, PRInt32 aBeginOffset,
    3997                 :                                             nsINode* aEndNode, PRInt32 aEndOffset,
    3998                 :                                             bool aAllowAdjacent,
    3999                 :                                             nsTArray<nsRange*>* aRanges)
    4000                 : {
    4001               0 :   aRanges->Clear();
    4002                 :   PRInt32 startIndex, endIndex;
    4003                 :   GetIndicesForInterval(aBeginNode, aBeginOffset, aEndNode, aEndOffset,
    4004               0 :                         aAllowAdjacent, &startIndex, &endIndex);
    4005               0 :   if (startIndex == -1 || endIndex == -1)
    4006               0 :     return NS_OK;
    4007                 : 
    4008               0 :   for (PRInt32 i = startIndex; i < endIndex; i++) {
    4009               0 :     if (!aRanges->AppendElement(mRanges[i].mRange))
    4010               0 :       return NS_ERROR_OUT_OF_MEMORY;
    4011                 :   }
    4012                 : 
    4013               0 :   return NS_OK;
    4014                 : }
    4015                 : 
    4016                 : // nsTypedSelection::GetIndicesForInterval
    4017                 : //
    4018                 : //    Works on the same principle as GetRangesForIntervalArray above, however
    4019                 : //    instead this returns the indices into mRanges between which the
    4020                 : //    overlapping ranges lie.
    4021                 : 
    4022                 : void
    4023               0 : nsTypedSelection::GetIndicesForInterval(nsINode* aBeginNode,
    4024                 :                                         PRInt32 aBeginOffset,
    4025                 :                                         nsINode* aEndNode, PRInt32 aEndOffset,
    4026                 :                                         bool aAllowAdjacent,
    4027                 :                                         PRInt32 *aStartIndex,
    4028                 :                                         PRInt32 *aEndIndex)
    4029                 : {
    4030                 :   PRInt32 startIndex;
    4031                 :   PRInt32 endIndex;
    4032                 : 
    4033               0 :   if (!aStartIndex)
    4034               0 :     aStartIndex = &startIndex;
    4035               0 :   if (!aEndIndex)
    4036               0 :     aEndIndex = &endIndex;
    4037                 : 
    4038               0 :   *aStartIndex = -1;
    4039               0 :   *aEndIndex = -1;
    4040                 : 
    4041               0 :   if (mRanges.Length() == 0)
    4042               0 :     return;
    4043                 : 
    4044                 :   bool intervalIsCollapsed = aBeginNode == aEndNode &&
    4045               0 :     aBeginOffset == aEndOffset;
    4046                 : 
    4047                 :   // Ranges that end before the given interval and begin after the given
    4048                 :   // interval can be discarded
    4049                 :   PRInt32 endsBeforeIndex;
    4050               0 :   if (NS_FAILED(FindInsertionPoint(&mRanges, aEndNode, aEndOffset,
    4051                 :                                    &CompareToRangeStart,
    4052                 :                                    &endsBeforeIndex))) {
    4053               0 :     return;
    4054                 :   }
    4055                 : 
    4056               0 :   if (endsBeforeIndex == 0) {
    4057               0 :     nsRange* endRange = mRanges[endsBeforeIndex].mRange;
    4058                 : 
    4059                 :     // If the interval is strictly before the range at index 0, we can optimize
    4060                 :     // by returning now - all ranges start after the given interval
    4061               0 :     if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
    4062               0 :       return;
    4063                 : 
    4064                 :     // We now know that the start point of mRanges[0].mRange equals the end of
    4065                 :     // the interval. Thus, when aAllowadjacent is true, the caller is always
    4066                 :     // interested in this range. However, when excluding adjacencies, we must
    4067                 :     // remember to include the range when both it and the given interval are
    4068                 :     // collapsed to the same point
    4069               0 :     if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed))
    4070               0 :       return;
    4071                 :   }
    4072               0 :   *aEndIndex = endsBeforeIndex;
    4073                 : 
    4074                 :   PRInt32 beginsAfterIndex;
    4075               0 :   if (NS_FAILED(FindInsertionPoint(&mRanges, aBeginNode, aBeginOffset,
    4076                 :                                    &CompareToRangeEnd,
    4077                 :                                    &beginsAfterIndex))) {
    4078               0 :     return;
    4079                 :   }
    4080               0 :   if (beginsAfterIndex == (PRInt32) mRanges.Length())
    4081               0 :     return; // optimization: all ranges are strictly before us
    4082                 : 
    4083               0 :   if (aAllowAdjacent) {
    4084                 :     // At this point, one of the following holds:
    4085                 :     //   endsBeforeIndex == mRanges.Length(),
    4086                 :     //   endsBeforeIndex points to a range whose start point does not equal the
    4087                 :     //     given interval's start point
    4088                 :     //   endsBeforeIndex points to a range whose start point equals the given
    4089                 :     //     interval's start point
    4090                 :     // In the final case, there can be two such ranges, a collapsed range, and
    4091                 :     // an adjacent range (they will appear in mRanges in that order). For this
    4092                 :     // final case, we need to increment endsBeforeIndex, until one of the
    4093                 :     // first two possibilites hold
    4094               0 :     while (endsBeforeIndex < (PRInt32) mRanges.Length()) {
    4095               0 :       nsRange* endRange = mRanges[endsBeforeIndex].mRange;
    4096               0 :       if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
    4097               0 :         break;
    4098               0 :       endsBeforeIndex++;
    4099                 :     }
    4100                 : 
    4101                 :     // Likewise, one of the following holds:
    4102                 :     //   beginsAfterIndex == 0,
    4103                 :     //   beginsAfterIndex points to a range whose end point does not equal
    4104                 :     //     the given interval's end point
    4105                 :     //   beginsOnOrAfter points to a range whose end point equals the given
    4106                 :     //     interval's end point
    4107                 :     // In the final case, there can be two such ranges, an adjacent range, and
    4108                 :     // a collapsed range (they will appear in mRanges in that order). For this
    4109                 :     // final case, we only need to take action if both those ranges exist, and
    4110                 :     // we are pointing to the collapsed range - we need to point to the
    4111                 :     // adjacent range
    4112               0 :     nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
    4113               0 :     if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
    4114               0 :         RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) {
    4115               0 :       beginRange = mRanges[beginsAfterIndex - 1].mRange;
    4116               0 :       if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset))
    4117               0 :         beginsAfterIndex--;
    4118                 :     }
    4119                 :   } else {
    4120                 :     // See above for the possibilities at this point. The only case where we
    4121                 :     // need to take action is when the range at beginsAfterIndex ends on
    4122                 :     // the given interval's start point, but that range isn't collapsed (a
    4123                 :     // collapsed range should be included in the returned results).
    4124               0 :     nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
    4125               0 :     if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset) &&
    4126               0 :         !beginRange->Collapsed())
    4127               0 :       beginsAfterIndex++;
    4128                 : 
    4129                 :     // Again, see above for the meaning of endsBeforeIndex at this point.
    4130                 :     // In particular, endsBeforeIndex may point to a collaped range which
    4131                 :     // represents the point at the end of the interval - this range should be
    4132                 :     // included
    4133               0 :     if (endsBeforeIndex < (PRInt32) mRanges.Length()) {
    4134               0 :       nsRange* endRange = mRanges[endsBeforeIndex].mRange;
    4135               0 :       if (RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset) &&
    4136               0 :           endRange->Collapsed())
    4137               0 :         endsBeforeIndex++;
    4138                 :      }
    4139                 :   }
    4140                 : 
    4141               0 :   *aStartIndex = beginsAfterIndex;
    4142               0 :   *aEndIndex = endsBeforeIndex;
    4143               0 :   return;
    4144                 : }
    4145                 : 
    4146                 : //utility method to get the primary frame of node or use the offset to get frame of child node
    4147                 : 
    4148                 : #if 0
    4149                 : NS_IMETHODIMP
    4150                 : nsTypedSelection::GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, bool aIsEndNode, nsIFrame **aReturnFrame)
    4151                 : {
    4152                 :   if (!aNode || !aReturnFrame || !mFrameSelection)
    4153                 :     return NS_ERROR_NULL_POINTER;
    4154                 :   
    4155                 :   if (aOffset < 0)
    4156                 :     return NS_ERROR_FAILURE;
    4157                 : 
    4158                 :   *aReturnFrame = 0;
    4159                 :   
    4160                 :   nsresult  result = NS_OK;
    4161                 :   
    4162                 :   nsCOMPtr<nsIDOMNode> node = aNode;
    4163                 : 
    4164                 :   if (!node)
    4165                 :     return NS_ERROR_NULL_POINTER;
    4166                 :   
    4167                 :   nsCOMPtr<nsIContent> content = do_QueryInterface(node, &result);
    4168                 : 
    4169                 :   if (NS_FAILED(result))
    4170                 :     return result;
    4171                 : 
    4172                 :   if (!content)
    4173                 :     return NS_ERROR_NULL_POINTER;
    4174                 :   
    4175                 :   if (content->IsElement())
    4176                 :   {
    4177                 :     if (aIsEndNode)
    4178                 :       aOffset--;
    4179                 : 
    4180                 :     if (aOffset >= 0)
    4181                 :     {
    4182                 :       nsIContent *child = content->GetChildAt(aOffset);
    4183                 :       if (!child) //out of bounds?
    4184                 :         return NS_ERROR_FAILURE;
    4185                 : 
    4186                 :       content = child; // releases the focusnode
    4187                 :     }
    4188                 :   }
    4189                 :   *aReturnFrame = content->GetPrimaryFrame();
    4190                 :   return NS_OK;
    4191                 : }
    4192                 : #endif
    4193                 : 
    4194                 : 
    4195                 : NS_IMETHODIMP
    4196               0 : nsTypedSelection::GetPrimaryFrameForAnchorNode(nsIFrame **aReturnFrame)
    4197                 : {
    4198               0 :   if (!aReturnFrame)
    4199               0 :     return NS_ERROR_NULL_POINTER;
    4200                 :   
    4201               0 :   PRInt32 frameOffset = 0;
    4202               0 :   *aReturnFrame = 0;
    4203               0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode());
    4204               0 :   if (content && mFrameSelection)
    4205                 :   {
    4206               0 :     *aReturnFrame = mFrameSelection->
    4207                 :       GetFrameForNodeOffset(content, GetAnchorOffset(),
    4208               0 :                             mFrameSelection->GetHint(), &frameOffset);
    4209               0 :     if (*aReturnFrame)
    4210               0 :       return NS_OK;
    4211                 :   }
    4212               0 :   return NS_ERROR_FAILURE;
    4213                 : }
    4214                 : 
    4215                 : NS_IMETHODIMP
    4216               0 : nsTypedSelection::GetPrimaryFrameForFocusNode(nsIFrame **aReturnFrame, PRInt32 *aOffsetUsed,
    4217                 :                                               bool aVisual)
    4218                 : {
    4219               0 :   if (!aReturnFrame)
    4220               0 :     return NS_ERROR_NULL_POINTER;
    4221                 :   
    4222               0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(GetFocusNode());
    4223               0 :   if (!content || !mFrameSelection)
    4224               0 :     return NS_ERROR_FAILURE;
    4225                 :   
    4226               0 :   nsIPresShell *presShell = mFrameSelection->GetShell();
    4227                 : 
    4228               0 :   PRInt32 frameOffset = 0;
    4229               0 :   *aReturnFrame = 0;
    4230               0 :   if (!aOffsetUsed)
    4231               0 :     aOffsetUsed = &frameOffset;
    4232                 :     
    4233               0 :   nsFrameSelection::HINT hint = mFrameSelection->GetHint();
    4234                 : 
    4235               0 :   if (aVisual) {
    4236               0 :     nsRefPtr<nsCaret> caret = presShell->GetCaret();
    4237               0 :     if (!caret)
    4238               0 :       return NS_ERROR_FAILURE;
    4239                 :     
    4240               0 :     PRUint8 caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
    4241                 : 
    4242                 :     return caret->GetCaretFrameForNodeOffset(content, GetFocusOffset(),
    4243               0 :       hint, caretBidiLevel, aReturnFrame, aOffsetUsed);
    4244                 :   }
    4245                 :   
    4246               0 :   *aReturnFrame = mFrameSelection->
    4247                 :     GetFrameForNodeOffset(content, GetFocusOffset(),
    4248               0 :                           hint, aOffsetUsed);
    4249               0 :   if (!*aReturnFrame)
    4250               0 :     return NS_ERROR_FAILURE;
    4251                 : 
    4252               0 :   return NS_OK;
    4253                 : }
    4254                 : 
    4255                 : //select all content children of aContent
    4256                 : nsresult
    4257               0 : nsTypedSelection::SelectAllFramesForContent(nsIContentIterator *aInnerIter,
    4258                 :                                   nsIContent *aContent,
    4259                 :                                   bool aSelected)
    4260                 : {
    4261               0 :   nsresult result = aInnerIter->Init(aContent);
    4262                 :   nsIFrame *frame;
    4263               0 :   if (NS_SUCCEEDED(result))
    4264                 :   {
    4265                 :     // First select frame of content passed in
    4266               0 :     frame = aContent->GetPrimaryFrame();
    4267               0 :     if (frame && frame->GetType() == nsGkAtoms::textFrame) {
    4268               0 :       nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
    4269               0 :       textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(), aSelected, mType);
    4270                 :     }
    4271                 :     // Now iterated through the child frames and set them
    4272               0 :     while (!aInnerIter->IsDone()) {
    4273                 :       nsCOMPtr<nsIContent> innercontent =
    4274               0 :         do_QueryInterface(aInnerIter->GetCurrentNode());
    4275                 : 
    4276               0 :       frame = innercontent->GetPrimaryFrame();
    4277               0 :       if (frame) {
    4278               0 :         if (frame->GetType() == nsGkAtoms::textFrame) {
    4279               0 :           nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
    4280               0 :           textFrame->SetSelectedRange(0, innercontent->GetText()->GetLength(), aSelected, mType);
    4281                 :         } else {
    4282               0 :           frame->InvalidateFrameSubtree();  // frame continuations?
    4283                 :         }
    4284                 :       }
    4285                 : 
    4286               0 :       aInnerIter->Next();
    4287                 :     }
    4288                 : 
    4289               0 :     return NS_OK;
    4290                 :   }
    4291                 : 
    4292               0 :   return NS_ERROR_FAILURE;
    4293                 : }
    4294                 : 
    4295                 : //the idea of this helper method is to select, deselect "top to bottom" traversing through the frames
    4296                 : nsresult
    4297               0 : nsTypedSelection::selectFrames(nsPresContext* aPresContext, nsRange* aRange, bool aFlags)
    4298                 : {
    4299               0 :   if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
    4300               0 :     return NS_OK; // nothing to do
    4301                 :   }
    4302               0 :   if (!aRange) {
    4303               0 :     return NS_ERROR_NULL_POINTER;
    4304                 :   }
    4305                 : 
    4306               0 :   if (mFrameSelection->GetTableCellSelection()) {
    4307               0 :     nsINode* node = aRange->GetCommonAncestor();
    4308               0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(node);
    4309               0 :     nsIFrame* frame = content ? content->GetPrimaryFrame()
    4310               0 :                               : aPresContext->FrameManager()->GetRootFrame();
    4311               0 :     if (frame) {
    4312               0 :       frame->InvalidateFrameSubtree();
    4313                 :     }
    4314               0 :     return NS_OK;
    4315                 :   }
    4316                 : 
    4317                 :   nsresult result;
    4318                 :   nsCOMPtr<nsIContentIterator> iter = do_CreateInstance(
    4319                 :                                               kCSubtreeIteratorCID,
    4320               0 :                                               &result);
    4321               0 :   if (NS_FAILED(result))
    4322               0 :     return result;
    4323                 : 
    4324                 :   nsCOMPtr<nsIContentIterator> inneriter = do_CreateInstance(
    4325                 :                                               kCContentIteratorCID,
    4326               0 :                                               &result);
    4327                 : 
    4328               0 :   if ((NS_SUCCEEDED(result)) && iter) {
    4329               0 :     result = iter->Init(aRange);
    4330                 : 
    4331                 :     // loop through the content iterator for each content node
    4332                 :     // for each text node, call SetSelected on it:
    4333               0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
    4334                 : 
    4335                 :     // we must call first one explicitly
    4336               0 :     if (!content)
    4337               0 :       return NS_ERROR_UNEXPECTED;
    4338                 : 
    4339               0 :     if (content->IsNodeOfType(nsINode::eTEXT)) {
    4340               0 :       nsIFrame* frame = content->GetPrimaryFrame();
    4341                 :       // The frame could be an SVG text frame, in which case we'll ignore
    4342                 :       // it.
    4343               0 :       if (frame && frame->GetType() == nsGkAtoms::textFrame)
    4344                 :       {
    4345               0 :         nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
    4346               0 :         PRUint32 startOffset = aRange->StartOffset();
    4347                 :         PRUint32 endOffset;
    4348               0 :         if (aRange->GetEndParent() == content) {
    4349               0 :           endOffset = aRange->EndOffset();
    4350                 :         } else {
    4351               0 :           endOffset = content->GetText()->GetLength();
    4352                 :         }
    4353               0 :         textFrame->SetSelectedRange(startOffset, endOffset, aFlags, mType);
    4354                 :       }
    4355                 :     }
    4356                 : 
    4357               0 :     iter->First();
    4358               0 :     while (!iter->IsDone()) {
    4359               0 :       content = do_QueryInterface(iter->GetCurrentNode());
    4360               0 :       SelectAllFramesForContent(inneriter, content, aFlags);
    4361               0 :       iter->Next();
    4362                 :     }
    4363                 : 
    4364                 :     //we must now do the last one  if it is not the same as the first
    4365               0 :     if (aRange->GetEndParent() != aRange->GetStartParent())
    4366                 :     {
    4367               0 :       content = do_QueryInterface(aRange->GetEndParent(), &result);
    4368               0 :       if (NS_FAILED(result) || !content)
    4369               0 :         return result;
    4370                 : 
    4371               0 :       if (content->IsNodeOfType(nsINode::eTEXT))
    4372                 :       {
    4373               0 :         nsIFrame* frame = content->GetPrimaryFrame();
    4374                 :         // The frame could be an SVG text frame, in which case we'll
    4375                 :         // ignore it.
    4376               0 :         if (frame && frame->GetType() == nsGkAtoms::textFrame)
    4377                 :         {
    4378               0 :           nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
    4379               0 :           textFrame->SetSelectedRange(0, aRange->EndOffset(), aFlags, mType);
    4380                 :         }
    4381                 :       }
    4382                 :     }
    4383                 :   }
    4384               0 :   return result;
    4385                 : }
    4386                 : 
    4387                 : 
    4388                 : // nsTypedSelection::LookUpSelection
    4389                 : //
    4390                 : //    This function is called when a node wants to know where the selection is
    4391                 : //    over itself.
    4392                 : //
    4393                 : //    Usually, this is called when we already know there is a selection over
    4394                 : //    the node in question, and we only need to find the boundaries of it on
    4395                 : //    that node. This is when slowCheck is false--a strict test is not needed.
    4396                 : //    Other times, the caller has no idea, and wants us to test everything,
    4397                 : //    so we are supposed to determine whether there is a selection over the
    4398                 : //    node at all.
    4399                 : //
    4400                 : //    A previous version of this code used this flag to do less work when
    4401                 : //    inclusion was already known (slowCheck=false). However, our tree
    4402                 : //    structure allows us to quickly determine ranges overlapping the node,
    4403                 : //    so we just ignore the slowCheck flag and do the full test every time.
    4404                 : //
    4405                 : //    PERFORMANCE: a common case is that we are doing a fast check with exactly
    4406                 : //    one range in the selection. In this case, this function is slower than
    4407                 : //    brute force because of the overhead of checking the tree. We can optimize
    4408                 : //    this case to make it faster by doing the same thing the previous version
    4409                 : //    of this function did in the case of 1 range. This would also mean that
    4410                 : //    the aSlowCheck flag would have meaning again.
    4411                 : 
    4412                 : NS_IMETHODIMP
    4413               0 : nsTypedSelection::LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset,
    4414                 :                                   PRInt32 aContentLength,
    4415                 :                                   SelectionDetails **aReturnDetails,
    4416                 :                                   SelectionType aType, bool aSlowCheck)
    4417                 : {
    4418                 :   nsresult rv;
    4419               0 :   if (!aContent || ! aReturnDetails)
    4420               0 :     return NS_ERROR_NULL_POINTER;
    4421                 : 
    4422                 :   // it is common to have no ranges, to optimize that
    4423               0 :   if (mRanges.Length() == 0)
    4424               0 :     return NS_OK;
    4425                 : 
    4426               0 :   nsTArray<nsRange*> overlappingRanges;
    4427                 :   rv = GetRangesForIntervalArray(aContent, aContentOffset,
    4428                 :                                  aContent, aContentOffset + aContentLength,
    4429                 :                                  false,
    4430               0 :                                  &overlappingRanges);
    4431               0 :   NS_ENSURE_SUCCESS(rv, rv);
    4432               0 :   if (overlappingRanges.Length() == 0)
    4433               0 :     return NS_OK;
    4434                 : 
    4435               0 :   for (PRUint32 i = 0; i < overlappingRanges.Length(); i++) {
    4436               0 :     nsRange* range = overlappingRanges[i];
    4437               0 :     nsINode* startNode = range->GetStartParent();
    4438               0 :     nsINode* endNode = range->GetEndParent();
    4439               0 :     PRInt32 startOffset = range->StartOffset();
    4440               0 :     PRInt32 endOffset = range->EndOffset();
    4441                 : 
    4442               0 :     PRInt32 start = -1, end = -1;
    4443               0 :     if (startNode == aContent && endNode == aContent) {
    4444               0 :       if (startOffset < (aContentOffset + aContentLength)  &&
    4445                 :           endOffset > aContentOffset) {
    4446                 :         // this range is totally inside the requested content range
    4447               0 :         start = NS_MAX(0, startOffset - aContentOffset);
    4448               0 :         end = NS_MIN(aContentLength, endOffset - aContentOffset);
    4449                 :       }
    4450                 :       // otherwise, range is inside the requested node, but does not intersect
    4451                 :       // the requested content range, so ignore it
    4452               0 :     } else if (startNode == aContent) {
    4453               0 :       if (startOffset < (aContentOffset + aContentLength)) {
    4454                 :         // the beginning of the range is inside the requested node, but the
    4455                 :         // end is outside, select everything from there to the end
    4456               0 :         start = NS_MAX(0, startOffset - aContentOffset);
    4457               0 :         end = aContentLength;
    4458                 :       }
    4459               0 :     } else if (endNode == aContent) {
    4460               0 :       if (endOffset > aContentOffset) {
    4461                 :         // the end of the range is inside the requested node, but the beginning
    4462                 :         // is outside, select everything from the beginning to there
    4463               0 :         start = 0;
    4464               0 :         end = NS_MIN(aContentLength, endOffset - aContentOffset);
    4465                 :       }
    4466                 :     } else {
    4467                 :       // this range does not begin or end in the requested node, but since
    4468                 :       // GetRangesForInterval returned this range, we know it overlaps.
    4469                 :       // Therefore, this node is enclosed in the range, and we select all
    4470                 :       // of it.
    4471               0 :       start = 0;
    4472               0 :       end = aContentLength;
    4473                 :     }
    4474               0 :     if (start < 0)
    4475               0 :       continue; // the ranges do not overlap the input range
    4476                 : 
    4477               0 :     SelectionDetails* details = new SelectionDetails;
    4478                 : 
    4479               0 :     details->mNext = *aReturnDetails;
    4480               0 :     details->mStart = start;
    4481               0 :     details->mEnd = end;
    4482               0 :     details->mType = aType;
    4483               0 :     RangeData *rd = FindRangeData(range);
    4484               0 :     if (rd) {
    4485               0 :       details->mTextRangeStyle = rd->mTextRangeStyle;
    4486                 :     }
    4487               0 :     *aReturnDetails = details;
    4488                 :   }
    4489               0 :   return NS_OK;
    4490                 : }
    4491                 : 
    4492                 : NS_IMETHODIMP
    4493               0 : nsTypedSelection::Repaint(nsPresContext* aPresContext)
    4494                 : {
    4495               0 :   PRInt32 arrCount = (PRInt32)mRanges.Length();
    4496                 : 
    4497               0 :   if (arrCount < 1)
    4498               0 :     return NS_OK;
    4499                 : 
    4500                 :   PRInt32 i;
    4501                 :   
    4502               0 :   for (i = 0; i < arrCount; i++)
    4503                 :   {
    4504               0 :     nsresult rv = selectFrames(aPresContext, mRanges[i].mRange, true);
    4505                 : 
    4506               0 :     if (NS_FAILED(rv)) {
    4507               0 :       return rv;
    4508                 :     }
    4509                 :   }
    4510                 : 
    4511               0 :   return NS_OK;
    4512                 : }
    4513                 : 
    4514                 : NS_IMETHODIMP
    4515               0 : nsTypedSelection::GetCanCacheFrameOffset(bool *aCanCacheFrameOffset)
    4516                 : { 
    4517               0 :   NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
    4518                 : 
    4519               0 :   if (mCachedOffsetForFrame)
    4520               0 :     *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
    4521                 :   else
    4522               0 :     *aCanCacheFrameOffset = false;
    4523                 : 
    4524               0 :   return NS_OK;
    4525                 : }
    4526                 : 
    4527                 : NS_IMETHODIMP    
    4528               0 : nsTypedSelection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset)
    4529                 : {
    4530               0 :   if (!mCachedOffsetForFrame) {
    4531               0 :     mCachedOffsetForFrame = new CachedOffsetForFrame;
    4532                 :   }
    4533                 : 
    4534               0 :   mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
    4535                 : 
    4536                 :   // clean up cached frame when turn off cache
    4537                 :   // fix bug 207936
    4538               0 :   if (!aCanCacheFrameOffset) {
    4539               0 :     mCachedOffsetForFrame->mLastCaretFrame = nsnull;
    4540                 :   }
    4541                 : 
    4542               0 :   return NS_OK;
    4543                 : }
    4544                 : 
    4545                 : NS_IMETHODIMP    
    4546               0 : nsTypedSelection::GetCachedFrameOffset(nsIFrame *aFrame, PRInt32 inOffset, nsPoint& aPoint)
    4547                 : {
    4548               0 :   if (!mCachedOffsetForFrame) {
    4549               0 :     mCachedOffsetForFrame = new CachedOffsetForFrame;
    4550                 :   }
    4551                 : 
    4552               0 :   nsresult rv = NS_OK;
    4553               0 :   if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
    4554                 :       mCachedOffsetForFrame->mLastCaretFrame &&
    4555                 :       (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
    4556                 :       (inOffset == mCachedOffsetForFrame->mLastContentOffset))
    4557                 :   {
    4558                 :      // get cached frame offset
    4559               0 :      aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
    4560                 :   } 
    4561                 :   else
    4562                 :   {
    4563                 :      // Recalculate frame offset and cache it. Don't cache a frame offset if
    4564                 :      // GetPointFromOffset fails, though.
    4565               0 :      rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
    4566               0 :      if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
    4567               0 :        mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
    4568               0 :        mCachedOffsetForFrame->mLastCaretFrame = aFrame;
    4569               0 :        mCachedOffsetForFrame->mLastContentOffset = inOffset; 
    4570                 :      }
    4571                 :   }
    4572                 : 
    4573               0 :   return rv;
    4574                 : }
    4575                 : 
    4576                 : NS_IMETHODIMP
    4577               0 : nsTypedSelection::GetFrameSelection(nsFrameSelection **aFrameSelection) {
    4578               0 :   NS_ENSURE_ARG_POINTER(aFrameSelection);
    4579               0 :   *aFrameSelection = mFrameSelection;
    4580               0 :   NS_IF_ADDREF(*aFrameSelection);
    4581               0 :   return NS_OK;
    4582                 : }
    4583                 : 
    4584                 : NS_IMETHODIMP
    4585               0 : nsTypedSelection::SetAncestorLimiter(nsIContent *aContent)
    4586                 : {
    4587               0 :   if (mFrameSelection)
    4588               0 :     mFrameSelection->SetAncestorLimiter(aContent);
    4589               0 :   return NS_OK;
    4590                 : }
    4591                 : 
    4592                 : RangeData*
    4593               0 : nsTypedSelection::FindRangeData(nsIDOMRange* aRange)
    4594                 : {
    4595               0 :   NS_ENSURE_TRUE(aRange, nsnull);
    4596               0 :   for (PRUint32 i = 0; i < mRanges.Length(); i++) {
    4597               0 :     if (mRanges[i].mRange == aRange)
    4598               0 :       return &mRanges[i];
    4599                 :   }
    4600               0 :   return nsnull;
    4601                 : }
    4602                 : 
    4603                 : NS_IMETHODIMP
    4604               0 : nsTypedSelection::SetTextRangeStyle(nsIDOMRange *aRange,
    4605                 :                                     const nsTextRangeStyle &aTextRangeStyle)
    4606                 : {
    4607               0 :   NS_ENSURE_ARG_POINTER(aRange);
    4608               0 :   RangeData *rd = FindRangeData(aRange);
    4609               0 :   if (rd) {
    4610               0 :     rd->mTextRangeStyle = aTextRangeStyle;
    4611                 :   }
    4612               0 :   return NS_OK;
    4613                 : }
    4614                 : 
    4615                 : nsresult
    4616               0 : nsTypedSelection::StartAutoScrollTimer(nsIFrame *aFrame,
    4617                 :                                        nsPoint& aPoint,
    4618                 :                                        PRUint32 aDelay)
    4619                 : {
    4620               0 :   NS_PRECONDITION(aFrame, "Need a frame");
    4621                 : 
    4622                 :   nsresult result;
    4623               0 :   if (!mFrameSelection)
    4624               0 :     return NS_OK;//nothing to do
    4625                 : 
    4626               0 :   if (!mAutoScrollTimer)
    4627                 :   {
    4628               0 :     mAutoScrollTimer = new nsAutoScrollTimer();
    4629                 : 
    4630               0 :     result = mAutoScrollTimer->Init(mFrameSelection, this);
    4631                 : 
    4632               0 :     if (NS_FAILED(result))
    4633               0 :       return result;
    4634                 :   }
    4635                 : 
    4636               0 :   result = mAutoScrollTimer->SetDelay(aDelay);
    4637                 : 
    4638               0 :   if (NS_FAILED(result))
    4639               0 :     return result;
    4640                 : 
    4641               0 :   return DoAutoScroll(aFrame, aPoint);
    4642                 : }
    4643                 : 
    4644                 : nsresult
    4645               0 : nsTypedSelection::StopAutoScrollTimer()
    4646                 : {
    4647               0 :   if (mAutoScrollTimer)
    4648               0 :     return mAutoScrollTimer->Stop();
    4649                 : 
    4650               0 :   return NS_OK; 
    4651                 : }
    4652                 : 
    4653                 : nsresult
    4654               0 : nsTypedSelection::DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint)
    4655                 : {
    4656               0 :   NS_PRECONDITION(aFrame, "Need a frame");
    4657                 : 
    4658               0 :   if (mAutoScrollTimer)
    4659               0 :     (void)mAutoScrollTimer->Stop();
    4660                 : 
    4661               0 :   nsPresContext* presContext = aFrame->PresContext();
    4662               0 :   nsRootPresContext* rootPC = presContext->GetRootPresContext();
    4663               0 :   if (!rootPC)
    4664               0 :     return NS_OK;
    4665               0 :   nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
    4666                 :   // Get the point relative to the root most frame because the scroll we are
    4667                 :   // about to do will change the coordinates of aFrame.
    4668               0 :   nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
    4669                 : 
    4670               0 :   bool didScroll = presContext->PresShell()->
    4671               0 :     ScrollFrameRectIntoView(aFrame, nsRect(aPoint, nsSize(1,1)),
    4672                 :                             NS_PRESSHELL_SCROLL_ANYWHERE,
    4673               0 :                             NS_PRESSHELL_SCROLL_ANYWHERE, 0);
    4674                 : 
    4675                 :   //
    4676                 :   // Start the AutoScroll timer if necessary.
    4677                 :   //
    4678                 : 
    4679               0 :   if (didScroll && mAutoScrollTimer)
    4680                 :   {
    4681                 :     nsPoint presContextPoint = globalPoint -
    4682               0 :       presContext->PresShell()->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
    4683               0 :     mAutoScrollTimer->Start(presContext, presContextPoint);
    4684                 :   }
    4685                 : 
    4686               0 :   return NS_OK;
    4687                 : }
    4688                 : 
    4689                 : NS_IMETHODIMP
    4690               0 : nsTypedSelection::GetEnumerator(nsIEnumerator **aIterator)
    4691                 : {
    4692               0 :   NS_ADDREF(*aIterator = new nsSelectionIterator(this));
    4693               0 :   return NS_OK;
    4694                 : }
    4695                 : 
    4696                 : 
    4697                 : 
    4698                 : /** RemoveAllRanges zeroes the selection
    4699                 :  */
    4700                 : NS_IMETHODIMP
    4701               0 : nsTypedSelection::RemoveAllRanges()
    4702                 : {
    4703               0 :   if (!mFrameSelection)
    4704               0 :     return NS_OK;//nothing to do
    4705               0 :   nsRefPtr<nsPresContext>  presContext;
    4706               0 :   GetPresContext(getter_AddRefs(presContext));
    4707                 : 
    4708                 : 
    4709               0 :   nsresult  result = Clear(presContext);
    4710               0 :   if (NS_FAILED(result))
    4711               0 :     return result;
    4712                 :   
    4713                 :   // Turn off signal for table selection
    4714               0 :   mFrameSelection->ClearTableCellSelection();
    4715                 : 
    4716               0 :   return mFrameSelection->NotifySelectionListeners(GetType());
    4717                 :   // Also need to notify the frames!
    4718                 :   // PresShell::CharacterDataChanged should do that on DocumentChanged
    4719                 : }
    4720                 : 
    4721                 : /** AddRange adds the specified range to the selection
    4722                 :  *  @param aRange is the range to be added
    4723                 :  */
    4724                 : NS_IMETHODIMP
    4725               0 : nsTypedSelection::AddRange(nsIDOMRange* aDOMRange)
    4726                 : {
    4727               0 :   if (!aDOMRange) {
    4728               0 :     return NS_ERROR_NULL_POINTER;
    4729                 :   }
    4730               0 :   nsRange* range = static_cast<nsRange*>(aDOMRange);
    4731                 : 
    4732                 :   // This inserts a table cell range in proper document order
    4733                 :   // and returns NS_OK if range doesn't contain just one table cell
    4734                 :   bool didAddRange;
    4735                 :   PRInt32 rangeIndex;
    4736               0 :   nsresult result = addTableCellRange(range, &didAddRange, &rangeIndex);
    4737               0 :   if (NS_FAILED(result)) return result;
    4738                 : 
    4739               0 :   if (!didAddRange)
    4740                 :   {
    4741               0 :     result = AddItem(range, &rangeIndex);
    4742               0 :     if (NS_FAILED(result)) return result;
    4743                 :   }
    4744                 : 
    4745               0 :   NS_ASSERTION(rangeIndex >= 0, "Range index not returned");
    4746               0 :   setAnchorFocusRange(rangeIndex);
    4747                 :   
    4748                 :   // Make sure the caret appears on the next line, if at a newline
    4749               0 :   if (mType == nsISelectionController::SELECTION_NORMAL)
    4750               0 :     SetInterlinePosition(true);
    4751                 : 
    4752               0 :   nsRefPtr<nsPresContext>  presContext;
    4753               0 :   GetPresContext(getter_AddRefs(presContext));
    4754               0 :   selectFrames(presContext, range, true);
    4755                 : 
    4756               0 :   if (!mFrameSelection)
    4757               0 :     return NS_OK;//nothing to do
    4758                 : 
    4759               0 :   return mFrameSelection->NotifySelectionListeners(GetType());
    4760                 : }
    4761                 : 
    4762                 : // nsTypedSelection::RemoveRange
    4763                 : //
    4764                 : //    Removes the given range from the selection. The tricky part is updating
    4765                 : //    the flags on the frames that indicate whether they have a selection or
    4766                 : //    not. There could be several selection ranges on the frame, and clearing
    4767                 : //    the bit would cause the selection to not be drawn, even when there is
    4768                 : //    another range on the frame (bug 346185).
    4769                 : //
    4770                 : //    We therefore find any ranges that intersect the same nodes as the range
    4771                 : //    being removed, and cause them to set the selected bits back on their
    4772                 : //    selected frames after we've cleared the bit from ours.
    4773                 : 
    4774                 : nsresult
    4775               0 : nsTypedSelection::RemoveRange(nsIDOMRange* aDOMRange)
    4776                 : {
    4777               0 :   if (!aDOMRange) {
    4778               0 :     return NS_ERROR_INVALID_ARG;
    4779                 :   }
    4780               0 :   nsRefPtr<nsRange> range = static_cast<nsRange*>(aDOMRange);
    4781                 :   
    4782               0 :   nsresult rv = RemoveItem(range);
    4783               0 :   if (NS_FAILED(rv))
    4784               0 :     return rv;
    4785                 : 
    4786               0 :   nsINode* beginNode = range->GetStartParent();
    4787               0 :   nsINode* endNode = range->GetEndParent();
    4788                 : 
    4789               0 :   if (!beginNode || !endNode) {
    4790                 :     // Detached range; nothing else to do here.
    4791               0 :     return NS_OK;
    4792                 :   }
    4793                 :   
    4794                 :   // find out the length of the end node, so we can select all of it
    4795                 :   PRInt32 beginOffset, endOffset;
    4796               0 :   if (endNode->IsNodeOfType(nsINode::eTEXT)) {
    4797                 :     // Get the length of the text. We can't just use the offset because
    4798                 :     // another range could be touching this text node but not intersect our
    4799                 :     // range.
    4800               0 :     beginOffset = 0;
    4801               0 :     endOffset = static_cast<nsIContent*>(endNode)->TextLength();
    4802                 :   } else {
    4803                 :     // For non-text nodes, the given offsets should be sufficient.
    4804               0 :     beginOffset = range->StartOffset();
    4805               0 :     endOffset = range->EndOffset();
    4806                 :   }
    4807                 : 
    4808                 :   // clear the selected bit from the removed range's frames
    4809               0 :   nsRefPtr<nsPresContext>  presContext;
    4810               0 :   GetPresContext(getter_AddRefs(presContext));
    4811               0 :   selectFrames(presContext, range, false);
    4812                 : 
    4813                 :   // add back the selected bit for each range touching our nodes
    4814               0 :   nsTArray<nsRange*> affectedRanges;
    4815                 :   rv = GetRangesForIntervalArray(beginNode, beginOffset,
    4816                 :                                  endNode, endOffset,
    4817               0 :                                  true, &affectedRanges);
    4818               0 :   NS_ENSURE_SUCCESS(rv, rv);
    4819               0 :   for (PRUint32 i = 0; i < affectedRanges.Length(); i++) {
    4820               0 :     selectFrames(presContext, affectedRanges[i], true);
    4821                 :   }
    4822                 : 
    4823               0 :   PRInt32 cnt = mRanges.Length();
    4824               0 :   if (range == mAnchorFocusRange) {
    4825                 :     // Reset anchor to LAST range or clear it if there are no ranges.
    4826               0 :     setAnchorFocusRange(cnt - 1);
    4827                 : 
    4828                 :     // When the selection is user-created it makes sense to scroll the range
    4829                 :     // into view. The spell-check selection, however, is created and destroyed
    4830                 :     // in the background. We don't want to scroll in this case or the view
    4831                 :     // might appear to be moving randomly (bug 337871).
    4832               0 :     if (mType != nsISelectionController::SELECTION_SPELLCHECK && cnt > 0)
    4833               0 :       ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
    4834                 :   }
    4835                 : 
    4836               0 :   if (!mFrameSelection)
    4837               0 :     return NS_OK;//nothing to do
    4838               0 :   return mFrameSelection->NotifySelectionListeners(GetType());
    4839                 : }
    4840                 : 
    4841                 : 
    4842                 : 
    4843                 : /*
    4844                 :  * Collapse sets the whole selection to be one point.
    4845                 :  */
    4846                 : NS_IMETHODIMP
    4847               0 : nsTypedSelection::Collapse(nsIDOMNode* aParentNode, PRInt32 aOffset)
    4848                 : {
    4849               0 :   nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
    4850               0 :   return Collapse(parentNode, aOffset);
    4851                 : }
    4852                 : 
    4853                 : NS_IMETHODIMP
    4854               0 : nsTypedSelection::CollapseNative(nsINode* aParentNode, PRInt32 aOffset)
    4855                 : {
    4856               0 :   return Collapse(aParentNode, aOffset);
    4857                 : }
    4858                 : 
    4859                 : nsresult
    4860               0 : nsTypedSelection::Collapse(nsINode* aParentNode, PRInt32 aOffset)
    4861                 : {
    4862               0 :   if (!aParentNode)
    4863               0 :     return NS_ERROR_INVALID_ARG;
    4864               0 :   if (!mFrameSelection)
    4865               0 :     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
    4866                 : 
    4867               0 :   nsCOMPtr<nsINode> kungfuDeathGrip = aParentNode;
    4868                 : 
    4869               0 :   mFrameSelection->InvalidateDesiredX();
    4870               0 :   if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
    4871               0 :     return NS_ERROR_FAILURE;
    4872                 :   nsresult result;
    4873                 :   // Delete all of the current ranges
    4874               0 :   nsRefPtr<nsPresContext>  presContext;
    4875               0 :   GetPresContext(getter_AddRefs(presContext));
    4876               0 :   Clear(presContext);
    4877                 : 
    4878                 :   // Turn off signal for table selection
    4879               0 :   mFrameSelection->ClearTableCellSelection();
    4880                 : 
    4881               0 :   nsRefPtr<nsRange> range = new nsRange();
    4882               0 :   result = range->SetEnd(aParentNode, aOffset);
    4883               0 :   if (NS_FAILED(result))
    4884               0 :     return result;
    4885               0 :   result = range->SetStart(aParentNode, aOffset);
    4886               0 :   if (NS_FAILED(result))
    4887               0 :     return result;
    4888                 : 
    4889                 : #ifdef DEBUG_SELECTION
    4890                 :   if (aParentNode) {
    4891                 :     nsCOMPtr<nsIContent> content = do_QueryInterface(aParentNode);
    4892                 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(aParentNode);
    4893                 :     printf ("Sel. Collapse to %p %s %d\n", aParentNode,
    4894                 :             content ? nsAtomCString(content->Tag()).get()
    4895                 :                     : (doc ? "DOCUMENT" : "???"),
    4896                 :             aOffset);
    4897                 :   }
    4898                 : #endif
    4899                 : 
    4900               0 :   result = AddItem(range);
    4901               0 :   if (NS_FAILED(result))
    4902               0 :     return result;
    4903               0 :   setAnchorFocusRange(0);
    4904               0 :   selectFrames(presContext, range, true);
    4905               0 :   return mFrameSelection->NotifySelectionListeners(GetType());
    4906                 : }
    4907                 : 
    4908                 : /*
    4909                 :  * Sets the whole selection to be one point
    4910                 :  * at the start of the current selection
    4911                 :  */
    4912                 : NS_IMETHODIMP
    4913               0 : nsTypedSelection::CollapseToStart()
    4914                 : {
    4915                 :   PRInt32 cnt;
    4916               0 :   nsresult rv = GetRangeCount(&cnt);
    4917               0 :   if (NS_FAILED(rv) || cnt <= 0)
    4918               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    4919                 : 
    4920                 :   // Get the first range
    4921               0 :   nsRange* firstRange = mRanges[0].mRange;
    4922               0 :   if (!firstRange)
    4923               0 :     return NS_ERROR_FAILURE;
    4924                 : 
    4925               0 :   return Collapse(firstRange->GetStartParent(), firstRange->StartOffset());
    4926                 : }
    4927                 : 
    4928                 : /*
    4929                 :  * Sets the whole selection to be one point
    4930                 :  * at the end of the current selection
    4931                 :  */
    4932                 : NS_IMETHODIMP
    4933               0 : nsTypedSelection::CollapseToEnd()
    4934                 : {
    4935                 :   PRInt32 cnt;
    4936               0 :   nsresult rv = GetRangeCount(&cnt);
    4937               0 :   if (NS_FAILED(rv) || cnt <= 0)
    4938               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    4939                 : 
    4940                 :   // Get the last range
    4941               0 :   nsRange* lastRange = mRanges[cnt - 1].mRange;
    4942               0 :   if (!lastRange)
    4943               0 :     return NS_ERROR_FAILURE;
    4944                 : 
    4945               0 :   return Collapse(lastRange->GetEndParent(), lastRange->EndOffset());
    4946                 : }
    4947                 : 
    4948                 : /*
    4949                 :  * IsCollapsed -- is the whole selection just one point, or unset?
    4950                 :  */
    4951                 : NS_IMETHODIMP
    4952               0 : nsTypedSelection::GetIsCollapsed(bool* aIsCollapsed)
    4953                 : {
    4954               0 :   if (!aIsCollapsed)
    4955               0 :     return NS_ERROR_NULL_POINTER;
    4956                 : 
    4957               0 :   PRInt32 cnt = (PRInt32)mRanges.Length();;
    4958               0 :   if (cnt == 0)
    4959                 :   {
    4960               0 :     *aIsCollapsed = true;
    4961               0 :     return NS_OK;
    4962                 :   }
    4963                 : 
    4964               0 :   if (cnt != 1)
    4965                 :   {
    4966               0 :     *aIsCollapsed = false;
    4967               0 :     return NS_OK;
    4968                 :   }
    4969                 : 
    4970               0 :   *aIsCollapsed = mRanges[0].mRange->Collapsed();
    4971               0 :   return NS_OK;
    4972                 : }
    4973                 : 
    4974                 : NS_IMETHODIMP
    4975               0 : nsTypedSelection::GetRangeCount(PRInt32* aRangeCount)
    4976                 : {
    4977               0 :   *aRangeCount = (PRInt32)mRanges.Length();
    4978                 : 
    4979               0 :   return NS_OK;
    4980                 : }
    4981                 : 
    4982                 : NS_IMETHODIMP
    4983               0 : nsTypedSelection::GetRangeAt(PRInt32 aIndex, nsIDOMRange** aReturn)
    4984                 : {
    4985               0 :   *aReturn = mRanges.SafeElementAt(aIndex, sEmptyData).mRange;
    4986               0 :   if (!*aReturn) {
    4987               0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    4988                 :   }
    4989                 : 
    4990               0 :   NS_ADDREF(*aReturn);
    4991                 : 
    4992               0 :   return NS_OK;
    4993                 : }
    4994                 : 
    4995                 : nsRange*
    4996               0 : nsTypedSelection::GetRangeAt(PRInt32 aIndex)
    4997                 : {
    4998               0 :   return mRanges.SafeElementAt(aIndex, sEmptyData).mRange;
    4999                 : }
    5000                 : 
    5001                 : /*
    5002                 : utility function
    5003                 : */
    5004                 : nsresult
    5005               0 : nsTypedSelection::CopyRangeToAnchorFocus(nsRange *aRange)
    5006                 : {
    5007                 :   // XXXbz could we just clone into mAnchorFocusRange, or do consumers
    5008                 :   // expect that pointer to not change across this call?
    5009               0 :   NS_ENSURE_STATE(mAnchorFocusRange);
    5010                 :   
    5011               0 :   nsINode* startNode = aRange->GetStartParent();
    5012               0 :   nsINode* endNode = aRange->GetEndParent();
    5013               0 :   PRInt32 startOffset = aRange->StartOffset();
    5014               0 :   PRInt32 endOffset = aRange->EndOffset();;
    5015               0 :   if (NS_FAILED(mAnchorFocusRange->SetStart(startNode,startOffset)))
    5016                 :   {
    5017                 :     // XXXbz what is this doing exactly?
    5018               0 :     if (NS_FAILED(mAnchorFocusRange->SetEnd(endNode,endOffset)))
    5019               0 :       return NS_ERROR_FAILURE;//???
    5020               0 :     if (NS_FAILED(mAnchorFocusRange->SetStart(startNode,startOffset)))
    5021               0 :       return NS_ERROR_FAILURE;//???
    5022                 :   }
    5023               0 :   else if (NS_FAILED(mAnchorFocusRange->SetEnd(endNode,endOffset)))
    5024               0 :     return NS_ERROR_FAILURE;//???
    5025               0 :   return NS_OK;
    5026                 : }
    5027                 : 
    5028                 : void
    5029               0 : nsTypedSelection::ReplaceAnchorFocusRange(nsRange *aRange)
    5030                 : {
    5031               0 :   nsRefPtr<nsPresContext> presContext;
    5032               0 :   GetPresContext(getter_AddRefs(presContext));
    5033               0 :   if (presContext) {
    5034               0 :     selectFrames(presContext, mAnchorFocusRange, false);
    5035               0 :     CopyRangeToAnchorFocus(aRange);
    5036               0 :     selectFrames(presContext, mAnchorFocusRange, true);
    5037                 :   }
    5038               0 : }
    5039                 : 
    5040                 : /*
    5041                 : Notes which might come in handy for extend:
    5042                 : 
    5043                 : We can tell the direction of the selection by asking for the anchors selection
    5044                 : if the begin is less than the end then we know the selection is to the "right".
    5045                 : else it is a backwards selection.
    5046                 : a = anchor
    5047                 : 1 = old cursor
    5048                 : 2 = new cursor
    5049                 : 
    5050                 :   if (a <= 1 && 1 <=2)    a,1,2  or (a1,2)
    5051                 :   if (a < 2 && 1 > 2)     a,2,1
    5052                 :   if (1 < a && a <2)      1,a,2
    5053                 :   if (a > 2 && 2 >1)      1,2,a
    5054                 :   if (2 < a && a <1)      2,a,1
    5055                 :   if (a > 1 && 1 >2)      2,1,a
    5056                 : then execute
    5057                 : a  1  2 select from 1 to 2
    5058                 : a  2  1 deselect from 2 to 1
    5059                 : 1  a  2 deselect from 1 to a select from a to 2
    5060                 : 1  2  a deselect from 1 to 2
    5061                 : 2  1  a = continue selection from 2 to 1
    5062                 : */
    5063                 : 
    5064                 : 
    5065                 : /*
    5066                 :  * Extend extends the selection away from the anchor.
    5067                 :  * We don't need to know the direction, because we always change the focus.
    5068                 :  */
    5069                 : NS_IMETHODIMP
    5070               0 : nsTypedSelection::Extend(nsIDOMNode* aParentNode, PRInt32 aOffset)
    5071                 : {
    5072               0 :   nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
    5073               0 :   return Extend(parentNode, aOffset);
    5074                 : }
    5075                 : 
    5076                 : nsresult
    5077               0 : nsTypedSelection::Extend(nsINode* aParentNode, PRInt32 aOffset)
    5078                 : {
    5079               0 :   if (!aParentNode)
    5080               0 :     return NS_ERROR_INVALID_ARG;
    5081                 : 
    5082                 :   // First, find the range containing the old focus point:
    5083               0 :   if (!mAnchorFocusRange)
    5084               0 :     return NS_ERROR_NOT_INITIALIZED;
    5085                 : 
    5086               0 :   if (!mFrameSelection)
    5087               0 :     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
    5088                 : 
    5089                 :   nsresult res;
    5090               0 :   if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
    5091               0 :     return NS_ERROR_FAILURE;
    5092                 : 
    5093                 :   //mFrameSelection->InvalidateDesiredX();
    5094                 : 
    5095               0 :   nsINode* anchorNode = GetAnchorNode();
    5096               0 :   nsINode* focusNode = GetFocusNode();
    5097               0 :   PRInt32 anchorOffset = GetAnchorOffset();
    5098               0 :   PRInt32 focusOffset = GetFocusOffset();
    5099                 : 
    5100               0 :   if (focusNode == aParentNode && focusOffset == aOffset)
    5101               0 :     return NS_OK; //same node nothing to do!
    5102                 : 
    5103               0 :   nsRefPtr<nsRange> range;
    5104               0 :   res = mAnchorFocusRange->CloneRange(getter_AddRefs(range));
    5105               0 :   if (NS_FAILED(res))
    5106               0 :     return res;
    5107                 :   //range = mAnchorFocusRange;
    5108                 : 
    5109               0 :   nsINode* startNode = range->GetStartParent();
    5110               0 :   nsINode* endNode = range->GetEndParent();
    5111               0 :   PRInt32 startOffset = range->StartOffset();
    5112               0 :   PRInt32 endOffset = range->EndOffset();;
    5113                 : 
    5114               0 :   nsDirection dir = GetDirection();
    5115                 : 
    5116                 :   //compare anchor to old cursor.
    5117                 : 
    5118               0 :   if (NS_FAILED(res))
    5119               0 :     return res;
    5120                 :   // We pass |disconnected| to the following ComparePoints calls in order
    5121                 :   // to avoid assertions, and there is no special handling required, since
    5122                 :   // ComparePoints returns 1 in the disconnected case.
    5123               0 :   bool disconnected = false;
    5124                 :   PRInt32 result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
    5125                 :                                                   focusNode, focusOffset,
    5126               0 :                                                   &disconnected);
    5127                 :   //compare old cursor to new cursor
    5128                 :   PRInt32 result2 = nsContentUtils::ComparePoints(focusNode, focusOffset,
    5129                 :                                                   aParentNode, aOffset,
    5130               0 :                                                   &disconnected);
    5131                 :   //compare anchor to new cursor
    5132                 :   PRInt32 result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
    5133                 :                                                   aParentNode, aOffset,
    5134               0 :                                                   &disconnected);
    5135                 : 
    5136               0 :   if (result2 == 0) //not selecting anywhere
    5137               0 :     return NS_OK;
    5138                 : 
    5139               0 :   nsRefPtr<nsPresContext>  presContext;
    5140               0 :   GetPresContext(getter_AddRefs(presContext));
    5141               0 :   nsRefPtr<nsRange> difRange = new nsRange();
    5142               0 :   if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2  a,1,2
    5143                 :     //select from 1 to 2 unless they are collapsed
    5144               0 :     res = range->SetEnd(aParentNode, aOffset);
    5145               0 :     if (NS_FAILED(res))
    5146               0 :       return res;
    5147               0 :     dir = eDirNext;
    5148               0 :     res = difRange->SetEnd(range->GetEndParent(), range->EndOffset());
    5149               0 :     res |= difRange->SetStart(focusNode, focusOffset);
    5150               0 :     if (NS_FAILED(res))
    5151               0 :       return res;
    5152               0 :     selectFrames(presContext, difRange , true);
    5153               0 :     res = CopyRangeToAnchorFocus(range);
    5154               0 :     if (NS_FAILED(res))
    5155               0 :       return res;
    5156                 :   }
    5157               0 :   else if (result1 == 0 && result3 > 0){//2, a1
    5158                 :     //select from 2 to 1a
    5159               0 :     dir = eDirPrevious;
    5160               0 :     res = range->SetStart(aParentNode, aOffset);
    5161               0 :     if (NS_FAILED(res))
    5162               0 :       return res;
    5163               0 :     selectFrames(presContext, range, true);
    5164               0 :     res = CopyRangeToAnchorFocus(range);
    5165               0 :     if (NS_FAILED(res))
    5166               0 :       return res;
    5167                 :   }
    5168               0 :   else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
    5169                 :     //deselect from 2 to 1
    5170               0 :     res = difRange->SetEnd(focusNode, focusOffset);
    5171               0 :     res |= difRange->SetStart(aParentNode, aOffset);
    5172               0 :     if (NS_FAILED(res))
    5173               0 :       return res;
    5174                 : 
    5175               0 :     res = range->SetEnd(aParentNode, aOffset);
    5176               0 :     if (NS_FAILED(res))
    5177               0 :       return res;
    5178               0 :     res = CopyRangeToAnchorFocus(range);
    5179               0 :     if (NS_FAILED(res))
    5180               0 :       return res;
    5181               0 :     selectFrames(presContext, difRange, false); // deselect now
    5182               0 :     difRange->SetEnd(range->GetEndParent(), range->EndOffset());
    5183               0 :     selectFrames(presContext, difRange, true); // must reselect last node maybe more
    5184                 :   }
    5185               0 :   else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
    5186               0 :     if (GetDirection() == eDirPrevious){
    5187               0 :       res = range->SetStart(endNode, endOffset);
    5188               0 :       if (NS_FAILED(res))
    5189               0 :         return res;
    5190                 :     }
    5191               0 :     dir = eDirNext;
    5192               0 :     res = range->SetEnd(aParentNode, aOffset);
    5193               0 :     if (NS_FAILED(res))
    5194               0 :       return res;
    5195               0 :     if (focusNode != anchorNode || focusOffset != anchorOffset) {//if collapsed diff dont do anything
    5196               0 :       res = difRange->SetStart(focusNode, focusOffset);
    5197               0 :       res |= difRange->SetEnd(anchorNode, anchorOffset);
    5198               0 :       if (NS_FAILED(res))
    5199               0 :         return res;
    5200               0 :       res = CopyRangeToAnchorFocus(range);
    5201               0 :       if (NS_FAILED(res))
    5202               0 :         return res;
    5203                 :       //deselect from 1 to a
    5204               0 :       selectFrames(presContext, difRange , false);
    5205                 :     }
    5206                 :     else
    5207                 :     {
    5208               0 :       res = CopyRangeToAnchorFocus(range);
    5209               0 :       if (NS_FAILED(res))
    5210               0 :         return res;
    5211                 :     }
    5212                 :     //select from a to 2
    5213               0 :     selectFrames(presContext, range , true);
    5214                 :   }
    5215               0 :   else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
    5216                 :     //deselect from 1 to 2
    5217               0 :     res = difRange->SetEnd(aParentNode, aOffset);
    5218               0 :     res |= difRange->SetStart(focusNode, focusOffset);
    5219               0 :     if (NS_FAILED(res))
    5220               0 :       return res;
    5221               0 :     dir = eDirPrevious;
    5222               0 :     res = range->SetStart(aParentNode, aOffset);
    5223               0 :     if (NS_FAILED(res))
    5224               0 :       return res;
    5225                 : 
    5226               0 :     res = CopyRangeToAnchorFocus(range);
    5227               0 :     if (NS_FAILED(res))
    5228               0 :       return res;
    5229               0 :     selectFrames(presContext, difRange , false);
    5230               0 :     difRange->SetStart(range->GetStartParent(), range->StartOffset());
    5231               0 :     selectFrames(presContext, difRange, true);//must reselect last node
    5232                 :   }
    5233               0 :   else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
    5234               0 :     if (GetDirection() == eDirNext){
    5235               0 :       range->SetEnd(startNode, startOffset);
    5236                 :     }
    5237               0 :     dir = eDirPrevious;
    5238               0 :     res = range->SetStart(aParentNode, aOffset);
    5239               0 :     if (NS_FAILED(res))
    5240               0 :       return res;
    5241                 :     //deselect from a to 1
    5242               0 :     if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything
    5243               0 :       res = difRange->SetStart(anchorNode, anchorOffset);
    5244               0 :       res |= difRange->SetEnd(focusNode, focusOffset);
    5245               0 :       res |= CopyRangeToAnchorFocus(range);
    5246               0 :       if (NS_FAILED(res))
    5247               0 :         return res;
    5248               0 :       selectFrames(presContext, difRange, false);
    5249                 :     }
    5250                 :     else
    5251                 :     {
    5252               0 :       res = CopyRangeToAnchorFocus(range);
    5253               0 :       if (NS_FAILED(res))
    5254               0 :         return res;
    5255                 :     }
    5256                 :     //select from 2 to a
    5257               0 :     selectFrames(presContext, range , true);
    5258                 :   }
    5259               0 :   else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
    5260                 :     //select from 2 to 1
    5261               0 :     res = range->SetStart(aParentNode, aOffset);
    5262               0 :     if (NS_FAILED(res))
    5263               0 :       return res;
    5264               0 :     dir = eDirPrevious;
    5265               0 :     res = difRange->SetEnd(focusNode, focusOffset);
    5266               0 :     res |= difRange->SetStart(range->GetStartParent(), range->StartOffset());
    5267               0 :     if (NS_FAILED(res))
    5268               0 :       return res;
    5269                 : 
    5270               0 :     selectFrames(presContext, difRange, true);
    5271               0 :     res = CopyRangeToAnchorFocus(range);
    5272               0 :     if (NS_FAILED(res))
    5273               0 :       return res;
    5274                 :   }
    5275                 : 
    5276                 :   DEBUG_OUT_RANGE(range);
    5277                 : #ifdef DEBUG_SELECTION
    5278                 :   if (eDirNext == mDirection)
    5279                 :     printf("    direction = 1  LEFT TO RIGHT\n");
    5280                 :   else
    5281                 :     printf("    direction = 0  RIGHT TO LEFT\n");
    5282                 : #endif
    5283               0 :   SetDirection(dir);
    5284                 : #ifdef DEBUG_SELECTION
    5285                 :   if (aParentNode)
    5286                 :   {
    5287                 :     nsCOMPtr<nsIContent>content;
    5288                 :     content = do_QueryInterface(aParentNode);
    5289                 : 
    5290                 :     printf ("Sel. Extend to %p %s %d\n", content.get(),
    5291                 :             nsAtomCString(content->Tag()).get(), aOffset);
    5292                 :   }
    5293                 :   else {
    5294                 :     printf ("Sel. Extend set to null parent.\n");
    5295                 :   }
    5296                 : #endif
    5297               0 :   return mFrameSelection->NotifySelectionListeners(GetType());
    5298                 : }
    5299                 : 
    5300                 : static nsresult
    5301               0 : GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
    5302                 : {
    5303               0 :   NS_ASSERTION((aChild && aParent), "bad args");
    5304               0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
    5305               0 :   nsCOMPtr<nsIContent> cChild = do_QueryInterface(aChild);
    5306                 : 
    5307               0 :   if (!cChild || !content)
    5308               0 :     return NS_ERROR_NULL_POINTER;
    5309                 : 
    5310               0 :   aOffset = content->IndexOf(cChild);
    5311                 : 
    5312               0 :   return NS_OK;
    5313                 : }
    5314                 : 
    5315                 : NS_IMETHODIMP
    5316               0 : nsTypedSelection::SelectAllChildren(nsIDOMNode* aParentNode)
    5317                 : {
    5318               0 :   NS_ENSURE_ARG_POINTER(aParentNode);
    5319                 :   
    5320               0 :   if (mFrameSelection) 
    5321                 :   {
    5322               0 :     mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
    5323                 :   }
    5324               0 :   nsresult result = Collapse(aParentNode, 0);
    5325               0 :   if (NS_SUCCEEDED(result))
    5326                 :   {
    5327               0 :     nsCOMPtr<nsIDOMNode>lastChild;
    5328               0 :     result = aParentNode->GetLastChild(getter_AddRefs(lastChild));
    5329               0 :     if ((NS_SUCCEEDED(result)) && lastChild)
    5330                 :     {
    5331               0 :       PRInt32 numBodyChildren=0;
    5332               0 :       GetChildOffset(lastChild, aParentNode, numBodyChildren);
    5333               0 :       if (mFrameSelection) 
    5334                 :       {
    5335               0 :         mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
    5336                 :       }
    5337               0 :       result = Extend(aParentNode, numBodyChildren+1);
    5338                 :     }
    5339                 :   }
    5340               0 :   return result;
    5341                 : }
    5342                 : 
    5343                 : NS_IMETHODIMP
    5344               0 : nsTypedSelection::ContainsNode(nsIDOMNode* aNode, bool aAllowPartial,
    5345                 :                                bool* aYes)
    5346                 : {
    5347                 :   nsresult rv;
    5348               0 :   if (!aYes)
    5349               0 :     return NS_ERROR_NULL_POINTER;
    5350               0 :   *aYes = false;
    5351                 : 
    5352               0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
    5353               0 :   if (mRanges.Length() == 0 || !node)
    5354               0 :     return NS_OK;
    5355                 : 
    5356                 :   // XXXbz this duplicates the GetNodeLength code in nsRange.cpp
    5357                 :   PRUint32 nodeLength;
    5358               0 :   bool isData = node->IsNodeOfType(nsINode::eDATA_NODE);
    5359               0 :   if (isData) {
    5360               0 :     nodeLength = static_cast<nsIContent*>(node.get())->TextLength();
    5361                 :   } else {
    5362               0 :     nodeLength = node->GetChildCount();
    5363                 :   }
    5364                 : 
    5365               0 :   nsTArray<nsRange*> overlappingRanges;
    5366                 :   rv = GetRangesForIntervalArray(node, 0, node, nodeLength,
    5367               0 :                                  false, &overlappingRanges);
    5368               0 :   NS_ENSURE_SUCCESS(rv, rv);
    5369               0 :   if (overlappingRanges.Length() == 0)
    5370               0 :     return NS_OK; // no ranges overlap
    5371                 :   
    5372                 :   // if the caller said partial intersections are OK, we're done
    5373               0 :   if (aAllowPartial) {
    5374               0 :     *aYes = true;
    5375               0 :     return NS_OK;
    5376                 :   }
    5377                 : 
    5378                 :   // text nodes always count as inside
    5379               0 :   if (isData) {
    5380               0 :     *aYes = true;
    5381               0 :     return NS_OK;
    5382                 :   }
    5383                 : 
    5384                 :   // The caller wants to know if the node is entirely within the given range,
    5385                 :   // so we have to check all intersecting ranges.
    5386               0 :   for (PRUint32 i = 0; i < overlappingRanges.Length(); i++) {
    5387                 :     bool nodeStartsBeforeRange, nodeEndsAfterRange;
    5388               0 :     if (NS_SUCCEEDED(nsRange::CompareNodeToRange(node, overlappingRanges[i],
    5389                 :                                                  &nodeStartsBeforeRange,
    5390                 :                                                  &nodeEndsAfterRange))) {
    5391               0 :       if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
    5392               0 :         *aYes = true;
    5393               0 :         return NS_OK;
    5394                 :       }
    5395                 :     }
    5396                 :   }
    5397               0 :   return NS_OK;
    5398                 : }
    5399                 : 
    5400                 : 
    5401                 : nsresult
    5402               0 : nsTypedSelection::GetPresContext(nsPresContext **aPresContext)
    5403                 : {
    5404               0 :   if (!mFrameSelection)
    5405               0 :     return NS_ERROR_FAILURE;//nothing to do
    5406               0 :   nsIPresShell *shell = mFrameSelection->GetShell();
    5407                 : 
    5408               0 :   if (!shell)
    5409               0 :     return NS_ERROR_NULL_POINTER;
    5410                 : 
    5411               0 :   NS_IF_ADDREF(*aPresContext = shell->GetPresContext());
    5412               0 :   return NS_OK;
    5413                 : }
    5414                 : 
    5415                 : nsresult
    5416               0 : nsTypedSelection::GetPresShell(nsIPresShell **aPresShell)
    5417                 : {
    5418               0 :   if (mPresShellWeak)
    5419                 :   {
    5420               0 :     nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShellWeak);
    5421               0 :     if (presShell)
    5422               0 :       NS_ADDREF(*aPresShell = presShell);
    5423               0 :     return NS_OK;
    5424                 :   }
    5425               0 :   nsresult rv = NS_OK;
    5426               0 :   if (!mFrameSelection)
    5427               0 :     return NS_ERROR_FAILURE;//nothing to do
    5428                 : 
    5429               0 :   nsIPresShell *shell = mFrameSelection->GetShell();
    5430                 : 
    5431               0 :   mPresShellWeak = do_GetWeakReference(shell);    // the presshell owns us, so no addref
    5432               0 :   if (mPresShellWeak)
    5433               0 :     NS_ADDREF(*aPresShell = shell);
    5434               0 :   return rv;
    5435                 : }
    5436                 : 
    5437                 : nsIFrame *
    5438               0 : nsTypedSelection::GetSelectionAnchorGeometry(SelectionRegion aRegion,
    5439                 :                                              nsRect *aRect)
    5440                 : {
    5441               0 :   if (!mFrameSelection)
    5442               0 :     return nsnull;  // nothing to do
    5443                 : 
    5444               0 :   NS_ENSURE_TRUE(aRect, nsnull);
    5445                 : 
    5446               0 :   aRect->SetRect(0, 0, 0, 0);
    5447                 : 
    5448               0 :   switch (aRegion) {
    5449                 :     case nsISelectionController::SELECTION_ANCHOR_REGION:
    5450                 :     case nsISelectionController::SELECTION_FOCUS_REGION:
    5451               0 :       return GetSelectionEndPointGeometry(aRegion, aRect);
    5452                 :       break;
    5453                 :     case nsISelectionController::SELECTION_WHOLE_SELECTION:
    5454                 :       break;
    5455                 :     default:
    5456               0 :       return nsnull;
    5457                 :   }
    5458                 : 
    5459               0 :   NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION,
    5460                 :     "should only be SELECTION_WHOLE_SELECTION here");
    5461                 : 
    5462               0 :   nsRect anchorRect;
    5463                 :   nsIFrame* anchorFrame = GetSelectionEndPointGeometry(
    5464               0 :     nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect);
    5465               0 :   if (!anchorFrame)
    5466               0 :     return nsnull;
    5467                 : 
    5468               0 :   nsRect focusRect;
    5469                 :   nsIFrame* focusFrame = GetSelectionEndPointGeometry(
    5470               0 :     nsISelectionController::SELECTION_FOCUS_REGION, &focusRect);
    5471               0 :   if (!focusFrame)
    5472               0 :     return nsnull;
    5473                 : 
    5474               0 :   NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(),
    5475                 :     "points of selection in different documents?");
    5476                 :   // make focusRect relative to anchorFrame
    5477               0 :   focusRect += focusFrame->GetOffsetTo(anchorFrame);
    5478                 : 
    5479               0 :   aRect->UnionRectEdges(anchorRect, focusRect);
    5480               0 :   return anchorFrame;
    5481                 : }
    5482                 : 
    5483                 : nsIFrame *
    5484               0 : nsTypedSelection::GetSelectionEndPointGeometry(SelectionRegion aRegion,
    5485                 :                                                nsRect *aRect)
    5486                 : {
    5487               0 :   if (!mFrameSelection)
    5488               0 :     return nsnull;  // nothing to do
    5489                 : 
    5490               0 :   NS_ENSURE_TRUE(aRect, nsnull);
    5491                 : 
    5492               0 :   aRect->SetRect(0, 0, 0, 0);
    5493                 : 
    5494               0 :   nsINode    *node       = nsnull;
    5495               0 :   PRInt32     nodeOffset = 0;
    5496               0 :   nsIFrame   *frame      = nsnull;
    5497                 : 
    5498               0 :   switch (aRegion) {
    5499                 :     case nsISelectionController::SELECTION_ANCHOR_REGION:
    5500               0 :       node       = GetAnchorNode();
    5501               0 :       nodeOffset = GetAnchorOffset();
    5502               0 :       break;
    5503                 :     case nsISelectionController::SELECTION_FOCUS_REGION:
    5504               0 :       node       = GetFocusNode();
    5505               0 :       nodeOffset = GetFocusOffset();
    5506               0 :       break;
    5507                 :     default:
    5508               0 :       return nsnull;
    5509                 :   }
    5510                 : 
    5511               0 :   if (!node)
    5512               0 :     return nsnull;
    5513                 : 
    5514               0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(node);
    5515               0 :   NS_ENSURE_TRUE(content.get(), nsnull);
    5516               0 :   PRInt32 frameOffset = 0;
    5517               0 :   frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset,
    5518                 :                                                  mFrameSelection->GetHint(),
    5519               0 :                                                  &frameOffset);
    5520               0 :   if (!frame)
    5521               0 :     return nsnull;
    5522                 : 
    5523                 :   // Figure out what node type we have, then get the
    5524                 :   // appropriate rect for it's nodeOffset.
    5525               0 :   bool isText = node->IsNodeOfType(nsINode::eTEXT);
    5526                 : 
    5527               0 :   nsPoint pt(0, 0);
    5528               0 :   if (isText) {
    5529               0 :     nsIFrame* childFrame = nsnull;
    5530               0 :     frameOffset = 0;
    5531                 :     nsresult rv =
    5532                 :       frame->GetChildFrameContainingOffset(nodeOffset,
    5533               0 :                                            mFrameSelection->GetHint(),
    5534               0 :                                            &frameOffset, &childFrame);
    5535               0 :     if (NS_FAILED(rv))
    5536               0 :       return nsnull;
    5537               0 :     if (!childFrame)
    5538               0 :       return nsnull;
    5539                 : 
    5540               0 :     frame = childFrame;
    5541                 : 
    5542                 :     // Get the x coordinate of the offset into the text frame.
    5543               0 :     rv = GetCachedFrameOffset(frame, nodeOffset, pt);
    5544               0 :     if (NS_FAILED(rv))
    5545               0 :       return nsnull;
    5546                 :   }
    5547                 : 
    5548                 :   // Return the rect relative to the frame, with zero width.
    5549               0 :   if (isText) {
    5550               0 :     aRect->x = pt.x;
    5551               0 :   } else if (mFrameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
    5552                 :     // It's the frame's right edge we're interested in.
    5553               0 :     aRect->x = frame->GetRect().width;
    5554                 :   }
    5555               0 :   aRect->height = frame->GetRect().height;
    5556                 : 
    5557               0 :   return frame;
    5558                 : }
    5559                 : 
    5560                 : NS_IMETHODIMP
    5561               0 : nsTypedSelection::ScrollSelectionIntoViewEvent::Run()
    5562                 : {
    5563               0 :   if (!mTypedSelection)
    5564               0 :     return NS_OK;  // event revoked
    5565                 : 
    5566                 :   PRInt32 flags = nsTypedSelection::SCROLL_DO_FLUSH |
    5567               0 :                   nsTypedSelection::SCROLL_SYNCHRONOUS;
    5568               0 :   if (mFirstAncestorOnly) {
    5569               0 :     flags |= nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY;
    5570                 :   }
    5571                 : 
    5572               0 :   mTypedSelection->mScrollEvent.Forget();
    5573                 :   mTypedSelection->ScrollIntoView(mRegion, mVerticalScroll,
    5574               0 :                                   mHorizontalScroll, flags);
    5575               0 :   return NS_OK;
    5576                 : }
    5577                 : 
    5578                 : nsresult
    5579               0 : nsTypedSelection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion,
    5580                 :                                                    bool aFirstAncestorOnly,
    5581                 :                                                    PRInt16 aVPercent,
    5582                 :                                                    PRInt16 aHPercent)
    5583                 : {
    5584                 :   // If we've already posted an event, revoke it and place a new one at the
    5585                 :   // end of the queue to make sure that any new pending reflow events are
    5586                 :   // processed before we scroll. This will insure that we scroll to the
    5587                 :   // correct place on screen.
    5588               0 :   mScrollEvent.Revoke();
    5589                 : 
    5590                 :   nsRefPtr<ScrollSelectionIntoViewEvent> ev =
    5591                 :       new ScrollSelectionIntoViewEvent(this, aRegion, aVPercent, aHPercent,
    5592               0 :                                        aFirstAncestorOnly);
    5593               0 :   nsresult rv = NS_DispatchToCurrentThread(ev);
    5594               0 :   NS_ENSURE_SUCCESS(rv, rv);
    5595                 : 
    5596               0 :   mScrollEvent = ev;
    5597               0 :   return NS_OK;
    5598                 : }
    5599                 : 
    5600                 : NS_IMETHODIMP
    5601               0 : nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, bool aIsSynchronous,
    5602                 :                                  PRInt16 aVPercent, PRInt16 aHPercent)
    5603                 : {
    5604                 :   return ScrollIntoView(aRegion, aVPercent, aHPercent,
    5605               0 :                         aIsSynchronous ? nsTypedSelection::SCROLL_SYNCHRONOUS : 0);
    5606                 : }
    5607                 : 
    5608                 : nsresult
    5609               0 : nsTypedSelection::ScrollIntoView(SelectionRegion aRegion,
    5610                 :                                  PRInt16 aVPercent, PRInt16 aHPercent,
    5611                 :                                  PRInt32 aFlags)
    5612                 : {
    5613                 :   nsresult result;
    5614               0 :   if (!mFrameSelection)
    5615               0 :     return NS_OK;//nothing to do
    5616                 : 
    5617               0 :   if (mFrameSelection->GetBatching())
    5618               0 :     return NS_OK;
    5619                 : 
    5620               0 :   if (!(aFlags & nsTypedSelection::SCROLL_SYNCHRONOUS))
    5621                 :     return PostScrollSelectionIntoViewEvent(aRegion,
    5622                 :       !!(aFlags & nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY),
    5623               0 :       aVPercent, aHPercent);
    5624                 : 
    5625                 :   //
    5626                 :   // Shut the caret off before scrolling to avoid
    5627                 :   // leaving caret turds on the screen!
    5628                 :   //
    5629               0 :   nsCOMPtr<nsIPresShell> presShell;
    5630               0 :   result = GetPresShell(getter_AddRefs(presShell));
    5631               0 :   if (NS_FAILED(result) || !presShell)
    5632               0 :     return result;
    5633               0 :   nsRefPtr<nsCaret> caret = presShell->GetCaret();
    5634               0 :   if (caret)
    5635                 :   {
    5636                 :     // Now that text frame character offsets are always valid (though not
    5637                 :     // necessarily correct), the worst that will happen if we don't flush here
    5638                 :     // is that some callers might scroll to the wrong place.  Those should
    5639                 :     // either manually flush if they're in a safe position for it or use the
    5640                 :     // async version of this method.
    5641               0 :     if (aFlags & nsTypedSelection::SCROLL_DO_FLUSH) {
    5642               0 :       presShell->FlushPendingNotifications(Flush_Layout);
    5643                 : 
    5644                 :       // Reget the presshell, since it might have gone away.
    5645               0 :       result = GetPresShell(getter_AddRefs(presShell));
    5646               0 :       if (NS_FAILED(result) || !presShell)
    5647               0 :         return result;
    5648                 :     }
    5649                 : 
    5650               0 :     StCaretHider  caretHider(caret);      // stack-based class hides and shows the caret
    5651                 : 
    5652                 :     //
    5653                 :     // Scroll the selection region into view.
    5654                 :     //
    5655                 : 
    5656               0 :     nsRect rect;
    5657               0 :     nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
    5658               0 :     if (!frame)
    5659               0 :       return NS_ERROR_FAILURE;
    5660                 : 
    5661               0 :     presShell->ScrollFrameRectIntoView(frame, rect, aVPercent, aHPercent,
    5662               0 :       (aFlags & nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY) ? nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY: 0);
    5663               0 :     return NS_OK;
    5664                 :   }
    5665               0 :   return result;
    5666                 : }
    5667                 : 
    5668                 : 
    5669                 : 
    5670                 : NS_IMETHODIMP
    5671               0 : nsTypedSelection::AddSelectionListener(nsISelectionListener* aNewListener)
    5672                 : {
    5673               0 :   if (!aNewListener)
    5674               0 :     return NS_ERROR_NULL_POINTER;
    5675               0 :   return mSelectionListeners.AppendObject(aNewListener) ? NS_OK : NS_ERROR_FAILURE;      // addrefs
    5676                 : }
    5677                 : 
    5678                 : 
    5679                 : 
    5680                 : NS_IMETHODIMP
    5681               0 : nsTypedSelection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
    5682                 : {
    5683               0 :   if (!aListenerToRemove )
    5684               0 :     return NS_ERROR_NULL_POINTER;
    5685               0 :   return mSelectionListeners.RemoveObject(aListenerToRemove) ? NS_OK : NS_ERROR_FAILURE; // releases
    5686                 : }
    5687                 : 
    5688                 : 
    5689                 : nsresult
    5690               0 : nsTypedSelection::NotifySelectionListeners()
    5691                 : {
    5692               0 :   if (!mFrameSelection)
    5693               0 :     return NS_OK;//nothing to do
    5694                 :  
    5695               0 :   if (mFrameSelection->GetBatching()) {
    5696               0 :     mFrameSelection->SetDirty();
    5697               0 :     return NS_OK;
    5698                 :   }
    5699               0 :   nsCOMArray<nsISelectionListener> selectionListeners(mSelectionListeners);
    5700               0 :   PRInt32 cnt = selectionListeners.Count();
    5701               0 :   if (cnt != mSelectionListeners.Count()) {
    5702               0 :     return NS_ERROR_OUT_OF_MEMORY;  // nsCOMArray is fallible
    5703                 :   }
    5704               0 :   nsCOMPtr<nsIDOMDocument> domdoc;
    5705               0 :   nsCOMPtr<nsIPresShell> shell;
    5706               0 :   nsresult rv = GetPresShell(getter_AddRefs(shell));
    5707               0 :   if (NS_SUCCEEDED(rv) && shell)
    5708               0 :     domdoc = do_QueryInterface(shell->GetDocument());
    5709               0 :   short reason = mFrameSelection->PopReason();
    5710               0 :   for (PRInt32 i = 0; i < cnt; i++) {
    5711               0 :     selectionListeners[i]->NotifySelectionChanged(domdoc, this, reason);
    5712                 :   }
    5713               0 :   return NS_OK;
    5714                 : }
    5715                 : 
    5716                 : NS_IMETHODIMP
    5717               0 : nsTypedSelection::StartBatchChanges()
    5718                 : {
    5719               0 :   if (mFrameSelection)
    5720               0 :     mFrameSelection->StartBatchChanges();
    5721                 : 
    5722               0 :   return NS_OK;
    5723                 : }
    5724                 : 
    5725                 : 
    5726                 : 
    5727                 : NS_IMETHODIMP
    5728               0 : nsTypedSelection::EndBatchChanges()
    5729                 : {
    5730               0 :   if (mFrameSelection)
    5731               0 :     mFrameSelection->EndBatchChanges();
    5732                 : 
    5733               0 :   return NS_OK;
    5734                 : }
    5735                 : 
    5736                 : 
    5737                 : 
    5738                 : NS_IMETHODIMP
    5739               0 : nsTypedSelection::DeleteFromDocument()
    5740                 : {
    5741               0 :   if (!mFrameSelection)
    5742               0 :     return NS_OK;//nothing to do
    5743               0 :   return mFrameSelection->DeleteFromDocument();
    5744                 : }
    5745                 : 
    5746                 : NS_IMETHODIMP
    5747               0 : nsTypedSelection::Modify(const nsAString& aAlter, const nsAString& aDirection,
    5748                 :                          const nsAString& aGranularity)
    5749                 : {
    5750                 :   // Silently exit if there's no selection or no focus node.
    5751               0 :   if (!mFrameSelection || !GetAnchorFocusRange() || !GetFocusNode()) {
    5752               0 :     return NS_OK;
    5753                 :   }
    5754                 : 
    5755               0 :   if (!aAlter.LowerCaseEqualsLiteral("move") &&
    5756               0 :       !aAlter.LowerCaseEqualsLiteral("extend")) {
    5757               0 :     return NS_ERROR_INVALID_ARG;
    5758                 :   }
    5759                 : 
    5760               0 :   if (!aDirection.LowerCaseEqualsLiteral("forward") &&
    5761               0 :       !aDirection.LowerCaseEqualsLiteral("backward") &&
    5762               0 :       !aDirection.LowerCaseEqualsLiteral("left") &&
    5763               0 :       !aDirection.LowerCaseEqualsLiteral("right")) {
    5764               0 :     return NS_ERROR_INVALID_ARG;
    5765                 :   }
    5766                 : 
    5767                 :   // Line moves are always visual.
    5768               0 :   bool visual  = aDirection.LowerCaseEqualsLiteral("left") ||
    5769               0 :                    aDirection.LowerCaseEqualsLiteral("right") ||
    5770               0 :                    aGranularity.LowerCaseEqualsLiteral("line");
    5771                 : 
    5772               0 :   bool forward = aDirection.LowerCaseEqualsLiteral("forward") ||
    5773               0 :                    aDirection.LowerCaseEqualsLiteral("right");
    5774                 : 
    5775               0 :   bool extend  = aAlter.LowerCaseEqualsLiteral("extend");
    5776                 : 
    5777                 :   // The PRUint32 casts below prevent an enum mismatch warning.
    5778                 :   nsSelectionAmount amount;
    5779                 :   PRUint32 keycode;
    5780               0 :   if (aGranularity.LowerCaseEqualsLiteral("character")) {
    5781               0 :     amount = eSelectCluster;
    5782                 :     keycode = forward ? (PRUint32) nsIDOMKeyEvent::DOM_VK_RIGHT :
    5783               0 :                         (PRUint32) nsIDOMKeyEvent::DOM_VK_LEFT;
    5784                 :   }
    5785               0 :   else if (aGranularity.LowerCaseEqualsLiteral("word")) {
    5786               0 :     amount = eSelectWordNoSpace;
    5787                 :     keycode = forward ? (PRUint32) nsIDOMKeyEvent::DOM_VK_RIGHT :
    5788               0 :                         (PRUint32) nsIDOMKeyEvent::DOM_VK_LEFT;
    5789                 :   }
    5790               0 :   else if (aGranularity.LowerCaseEqualsLiteral("line")) {
    5791               0 :     amount = eSelectLine;
    5792                 :     keycode = forward ? (PRUint32) nsIDOMKeyEvent::DOM_VK_DOWN :
    5793               0 :                         (PRUint32) nsIDOMKeyEvent::DOM_VK_UP;
    5794                 :   }
    5795               0 :   else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) {
    5796               0 :     amount = eSelectLine;
    5797                 :     keycode = forward ? (PRUint32) nsIDOMKeyEvent::DOM_VK_END :
    5798               0 :                         (PRUint32) nsIDOMKeyEvent::DOM_VK_HOME;
    5799                 :   }
    5800               0 :   else if (aGranularity.LowerCaseEqualsLiteral("sentence") ||
    5801               0 :            aGranularity.LowerCaseEqualsLiteral("sentenceboundary") ||
    5802               0 :            aGranularity.LowerCaseEqualsLiteral("paragraph") ||
    5803               0 :            aGranularity.LowerCaseEqualsLiteral("paragraphboundary") ||
    5804               0 :            aGranularity.LowerCaseEqualsLiteral("documentboundary")) {
    5805               0 :     return NS_ERROR_NOT_IMPLEMENTED;
    5806                 :   }
    5807                 :   else {
    5808               0 :     return NS_ERROR_INVALID_ARG;
    5809                 :   }
    5810                 : 
    5811                 :   // If the anchor doesn't equal the focus and we try to move without first
    5812                 :   // collapsing the selection, MoveCaret will collapse the selection and quit.
    5813                 :   // To avoid this, we need to collapse the selection first.
    5814               0 :   nsresult rv = NS_OK;
    5815               0 :   if (!extend) {
    5816               0 :     nsINode* focusNode = GetFocusNode();
    5817                 :     // We should have checked earlier that there was a focus node.
    5818               0 :     NS_ENSURE_TRUE(focusNode, NS_ERROR_UNEXPECTED);
    5819               0 :     PRInt32 focusOffset = GetFocusOffset();
    5820               0 :     Collapse(focusNode, focusOffset);
    5821                 :   }
    5822                 : 
    5823                 :   // If the base level of the focused frame is odd, we may have to swap the
    5824                 :   // direction of the keycode.
    5825                 :   nsIFrame *frame;
    5826                 :   PRInt32 offset;
    5827               0 :   rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual);
    5828               0 :   if (NS_SUCCEEDED(rv) && frame) {
    5829               0 :     nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame);
    5830                 : 
    5831               0 :     if (baseLevel & 1) {
    5832               0 :       if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_RIGHT) {
    5833               0 :         keycode = nsIDOMKeyEvent::DOM_VK_LEFT;
    5834                 :       }
    5835               0 :       else if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_LEFT) {
    5836               0 :         keycode = nsIDOMKeyEvent::DOM_VK_RIGHT;
    5837                 :       }
    5838               0 :       else if (visual && keycode == nsIDOMKeyEvent::DOM_VK_HOME) {
    5839               0 :         keycode = nsIDOMKeyEvent::DOM_VK_END;
    5840                 :       }
    5841               0 :       else if (visual && keycode == nsIDOMKeyEvent::DOM_VK_END) {
    5842               0 :         keycode = nsIDOMKeyEvent::DOM_VK_HOME;
    5843                 :       }
    5844                 :     }
    5845                 :   }
    5846                 : 
    5847                 :   // MoveCaret will return an error if it can't move in the specified
    5848                 :   // direction, but we just ignore this error unless it's a line move, in which
    5849                 :   // case we call nsISelectionController::CompleteMove to move the cursor to
    5850                 :   // the beginning/end of the line.
    5851               0 :   rv = mFrameSelection->MoveCaret(keycode, extend, amount, visual);
    5852                 : 
    5853               0 :   if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) {
    5854                 :     nsCOMPtr<nsISelectionController> shell =
    5855               0 :       do_QueryInterface(mFrameSelection->GetShell());
    5856               0 :     if (!shell)
    5857               0 :       return NS_OK;
    5858               0 :     shell->CompleteMove(forward, extend);
    5859                 :   }
    5860               0 :   return NS_OK;
    5861                 : }
    5862                 : 
    5863                 : /** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction
    5864                 :  *  @param aLangRTL is true if the new language is right-to-left or false if the new language is left-to-right
    5865                 :  */
    5866                 : NS_IMETHODIMP
    5867               0 : nsTypedSelection::SelectionLanguageChange(bool aLangRTL)
    5868                 : {
    5869               0 :   if (!mFrameSelection)
    5870               0 :     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
    5871                 :   nsresult result;
    5872               0 :   nsIFrame *focusFrame = 0;
    5873                 : 
    5874               0 :   result = GetPrimaryFrameForFocusNode(&focusFrame, nsnull, false);
    5875               0 :   if (NS_FAILED(result)) {
    5876               0 :     return result;
    5877                 :   }
    5878               0 :   if (!focusFrame) {
    5879               0 :     return NS_ERROR_FAILURE;
    5880                 :   }
    5881                 : 
    5882                 :   PRInt32 frameStart, frameEnd;
    5883               0 :   focusFrame->GetOffsets(frameStart, frameEnd);
    5884               0 :   nsRefPtr<nsPresContext> context;
    5885                 :   PRUint8 levelBefore, levelAfter;
    5886               0 :   result = GetPresContext(getter_AddRefs(context));
    5887               0 :   if (NS_FAILED(result) || !context)
    5888               0 :     return result?result:NS_ERROR_FAILURE;
    5889                 : 
    5890               0 :   PRUint8 level = NS_GET_EMBEDDING_LEVEL(focusFrame);
    5891               0 :   PRInt32 focusOffset = GetFocusOffset();
    5892               0 :   if ((focusOffset != frameStart) && (focusOffset != frameEnd))
    5893                 :     // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
    5894                 :     //  is equal to the frame level
    5895               0 :     levelBefore = levelAfter = level;
    5896                 :   else {
    5897                 :     // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
    5898                 :     //  before and after the cursor
    5899               0 :     nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
    5900                 :     /*
    5901                 :     nsFrameSelection::HINT hint;
    5902                 : 
    5903                 :     if ((focusOffset == frameStart && level)        // beginning of an RTL frame
    5904                 :         || (focusOffset == frameEnd && !level)) {   // end of an LTR frame
    5905                 :       hint = nsFrameSelection::HINTRIGHT;
    5906                 :     }
    5907                 :     else {                                          // end of an RTL frame or beginning of an LTR frame
    5908                 :       hint = nsFrameSelection::HINTLEFT;
    5909                 :     }
    5910                 :     mFrameSelection->SetHint(hint);
    5911                 :     */
    5912               0 :     nsPrevNextBidiLevels levels = mFrameSelection->
    5913               0 :       GetPrevNextBidiLevels(focusContent, focusOffset, false);
    5914                 :       
    5915               0 :     levelBefore = levels.mLevelBefore;
    5916               0 :     levelAfter = levels.mLevelAfter;
    5917                 :   }
    5918                 : 
    5919               0 :   if ((levelBefore & 1) == (levelAfter & 1)) {
    5920                 :     // if cursor is between two characters with the same orientation, changing the keyboard language
    5921                 :     //  must toggle the cursor level between the level of the character with the lowest level
    5922                 :     //  (if the new language corresponds to the orientation of that character) and this level plus 1
    5923                 :     //  (if the new language corresponds to the opposite orientation)
    5924               0 :     if ((level != levelBefore) && (level != levelAfter))
    5925               0 :       level = NS_MIN(levelBefore, levelAfter);
    5926               0 :     if ((level & 1) == aLangRTL)
    5927               0 :       mFrameSelection->SetCaretBidiLevel(level);
    5928                 :     else
    5929               0 :       mFrameSelection->SetCaretBidiLevel(level + 1);
    5930                 :   }
    5931                 :   else {
    5932                 :     // if cursor is between characters with opposite orientations, changing the keyboard language must change
    5933                 :     //  the cursor level to that of the adjacent character with the orientation corresponding to the new language.
    5934               0 :     if ((levelBefore & 1) == aLangRTL)
    5935               0 :       mFrameSelection->SetCaretBidiLevel(levelBefore);
    5936                 :     else
    5937               0 :       mFrameSelection->SetCaretBidiLevel(levelAfter);
    5938                 :   }
    5939                 :   
    5940                 :   // The caret might have moved, so invalidate the desired X position
    5941                 :   // for future usages of up-arrow or down-arrow
    5942               0 :   mFrameSelection->InvalidateDesiredX();
    5943                 :   
    5944               0 :   return NS_OK;
    5945                 : }
    5946                 : 
    5947                 : NS_IMETHODIMP_(nsDirection)
    5948               0 : nsTypedSelection::GetSelectionDirection() {
    5949               0 :   return mDirection;
    5950                 : }
    5951                 : 
    5952                 : NS_IMETHODIMP_(void)
    5953               0 : nsTypedSelection::SetSelectionDirection(nsDirection aDirection) {
    5954               0 :   mDirection = aDirection;
    5955               0 : }
    5956                 : 
    5957                 : 
    5958                 : // nsAutoCopyListener
    5959                 : 
    5960                 : nsAutoCopyListener* nsAutoCopyListener::sInstance = nsnull;
    5961                 : 
    5962               0 : NS_IMPL_ISUPPORTS1(nsAutoCopyListener, nsISelectionListener)
    5963                 : 
    5964                 : /*
    5965                 :  * What we do now:
    5966                 :  * On every selection change, we copy to the clipboard anew, creating a
    5967                 :  * HTML buffer, a transferable, an nsISupportsString and
    5968                 :  * a huge mess every time.  This is basically what nsPresShell::DoCopy does
    5969                 :  * to move the selection into the clipboard for Edit->Copy.
    5970                 :  * 
    5971                 :  * What we should do, to make our end of the deal faster:
    5972                 :  * Create a singleton transferable with our own magic converter.  When selection
    5973                 :  * changes (use a quick cache to detect ``real'' changes), we put the new
    5974                 :  * nsISelection in the transferable.  Our magic converter will take care of
    5975                 :  * transferable->whatever-other-format when the time comes to actually
    5976                 :  * hand over the clipboard contents.
    5977                 :  *
    5978                 :  * Other issues:
    5979                 :  * - which X clipboard should we populate?
    5980                 :  * - should we use a different one than Edit->Copy, so that inadvertant
    5981                 :  *   selections (or simple clicks, which currently cause a selection
    5982                 :  *   notification, regardless of if they're in the document which currently has
    5983                 :  *   selection!) don't lose the contents of the ``application''?  Or should we
    5984                 :  *   just put some intelligence in the ``is this a real selection?'' code to
    5985                 :  *   protect our selection against clicks in other documents that don't create
    5986                 :  *   selections?
    5987                 :  * - maybe we should just never clear the X clipboard?  That would make this 
    5988                 :  *   problem just go away, which is very tempting.
    5989                 :  */
    5990                 : 
    5991                 : NS_IMETHODIMP
    5992               0 : nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc,
    5993                 :                                            nsISelection *aSel, PRInt16 aReason)
    5994                 : {
    5995               0 :   if (!(aReason & nsISelectionListener::MOUSEUP_REASON   || 
    5996                 :         aReason & nsISelectionListener::SELECTALL_REASON ||
    5997               0 :         aReason & nsISelectionListener::KEYPRESS_REASON))
    5998               0 :     return NS_OK; //dont care if we are still dragging
    5999                 : 
    6000                 :   bool collapsed;
    6001               0 :   if (!aDoc || !aSel ||
    6002               0 :       NS_FAILED(aSel->GetIsCollapsed(&collapsed)) || collapsed) {
    6003                 : #ifdef DEBUG_CLIPBOARD
    6004                 :     fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n");
    6005                 : #endif
    6006                 :     /* clear X clipboard? */
    6007               0 :     return NS_OK;
    6008                 :   }
    6009                 : 
    6010               0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
    6011               0 :   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
    6012                 : 
    6013                 :   // call the copy code
    6014               0 :   return nsCopySupport::HTMLCopy(aSel, doc, nsIClipboard::kSelectionClipboard);
    6015            4392 : }

Generated by: LCOV version 1.7