LCOV - app.info - layout/generic/nsSelection.cpp

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-04-21 | 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            1419 : {
         165            1396 :   RangeData(nsRange* aRange)
         166            1396 :     : mRange(aRange)
         167            1396 :   {}
         168                 :
         169                 :   nsRefPtr<nsRange> mRange;
         170                 :   nsTextRangeStyle mTextRangeStyle;
         171                 : };
         172                 :
         173            1396 : 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            1396 :   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            1396 : 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            1396 : 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            4188 : }

Generated by: LCOV version 1.7