layout/generic/nsSelection.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * Implementation of selection: nsISelection,nsISelectionPrivate and nsFrameSelection
     9  */
    11 #include "mozilla/dom/Selection.h"
    13 #include "mozilla/Attributes.h"
    14 #include "mozilla/EventStates.h"
    16 #include "nsCOMPtr.h"
    17 #include "nsString.h"
    18 #include "nsFrameSelection.h"
    19 #include "nsISelectionListener.h"
    20 #include "nsContentCID.h"
    21 #include "nsIContent.h"
    22 #include "nsIDOMNode.h"
    23 #include "nsRange.h"
    24 #include "nsCOMArray.h"
    25 #include "nsIDOMKeyEvent.h"
    26 #include "nsITableCellLayout.h"
    27 #include "nsTArray.h"
    28 #include "nsTableOuterFrame.h"
    29 #include "nsTableCellFrame.h"
    30 #include "nsIScrollableFrame.h"
    31 #include "nsCCUncollectableMarker.h"
    32 #include "nsIContentIterator.h"
    33 #include "nsIDocumentEncoder.h"
    34 #include "nsTextFragment.h"
    35 #include <algorithm>
    37 #include "nsGkAtoms.h"
    38 #include "nsIFrameTraversal.h"
    39 #include "nsLayoutUtils.h"
    40 #include "nsLayoutCID.h"
    41 #include "nsBidiPresUtils.h"
    42 static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
    43 #include "nsTextFrame.h"
    45 #include "nsIDOMText.h"
    47 #include "nsContentUtils.h"
    48 #include "nsThreadUtils.h"
    49 #include "mozilla/Preferences.h"
    50 #include "nsDOMClassInfoID.h"
    52 //included for desired x position;
    53 #include "nsPresContext.h"
    54 #include "nsIPresShell.h"
    55 #include "nsCaret.h"
    57 #include "mozilla/MouseEvents.h"
    58 #include "mozilla/TextEvents.h"
    60 #include "nsITimer.h"
    61 #include "nsFrameManager.h"
    62 // notifications
    63 #include "nsIDOMDocument.h"
    64 #include "nsIDocument.h"
    66 #include "nsISelectionController.h"//for the enums
    67 #include "nsAutoCopyListener.h"
    68 #include "nsCopySupport.h"
    69 #include "nsIClipboard.h"
    70 #include "nsIFrameInlines.h"
    72 #include "nsIBidiKeyboard.h"
    74 #include "nsError.h"
    75 #include "mozilla/dom/Element.h"
    76 #include "mozilla/dom/ShadowRoot.h"
    77 #include "mozilla/ErrorResult.h"
    78 #include "mozilla/dom/SelectionBinding.h"
    80 using namespace mozilla;
    81 using namespace mozilla::dom;
    83 //#define DEBUG_TABLE 1
    85 static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
    87 static nsIAtom *GetTag(nsINode *aNode);
    88 // returns the parent
    89 static nsINode* ParentOffset(nsINode *aNode, int32_t *aChildOffset);
    90 static nsINode* GetCellParent(nsINode *aDomNode);
    92 #ifdef PRINT_RANGE
    93 static void printRange(nsRange *aDomRange);
    94 #define DEBUG_OUT_RANGE(x)  printRange(x)
    95 #else
    96 #define DEBUG_OUT_RANGE(x)  
    97 #endif // PRINT_RANGE
   101 //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
   102 //#define DEBUG_NAVIGATION
   105 //#define DEBUG_TABLE_SELECTION 1
   107 struct CachedOffsetForFrame {
   108   CachedOffsetForFrame()
   109   : mCachedFrameOffset(0, 0) // nsPoint ctor
   110   , mLastCaretFrame(nullptr)
   111   , mLastContentOffset(0)
   112   , mCanCacheFrameOffset(false)
   113   {}
   115   nsPoint      mCachedFrameOffset;      // cached frame offset
   116   nsIFrame*    mLastCaretFrame;         // store the frame the caret was last drawn in.
   117   int32_t      mLastContentOffset;      // store last content offset
   118   bool mCanCacheFrameOffset;    // cached frame offset is valid?
   119 };
   121 // Stack-class to turn on/off selection batching for table selection
   122 class MOZ_STACK_CLASS nsSelectionBatcher MOZ_FINAL
   123 {
   124 private:
   125   nsCOMPtr<nsISelectionPrivate> mSelection;
   126 public:
   127   nsSelectionBatcher(nsISelectionPrivate *aSelection) : mSelection(aSelection)
   128   {
   129     if (mSelection) mSelection->StartBatchChanges();
   130   }
   131   ~nsSelectionBatcher() 
   132   { 
   133     if (mSelection) mSelection->EndBatchChanges();
   134   }
   135 };
   137 class nsAutoScrollTimer : public nsITimerCallback
   138 {
   139 public:
   141   NS_DECL_ISUPPORTS
   143   nsAutoScrollTimer()
   144   : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30)
   145   {
   146   }
   148   virtual ~nsAutoScrollTimer()
   149   {
   150    if (mTimer)
   151        mTimer->Cancel();
   152   }
   154   // aPoint is relative to aPresContext's root frame
   155   nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
   156   {
   157     mPoint = aPoint;
   159     // Store the presentation context. The timer will be
   160     // stopped by the selection if the prescontext is destroyed.
   161     mPresContext = aPresContext;
   163     mContent = nsIPresShell::GetCapturingContent();
   165     if (!mTimer)
   166     {
   167       nsresult result;
   168       mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
   170       if (NS_FAILED(result))
   171         return result;
   172     }
   174     return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
   175   }
   177   nsresult Stop()
   178   {
   179     if (mTimer)
   180     {
   181       mTimer->Cancel();
   182       mTimer = 0;
   183     }
   185     mContent = nullptr;
   186     return NS_OK;
   187   }
   189   nsresult Init(nsFrameSelection* aFrameSelection, Selection* aSelection)
   190   {
   191     mFrameSelection = aFrameSelection;
   192     mSelection = aSelection;
   193     return NS_OK;
   194   }
   196   nsresult SetDelay(uint32_t aDelay)
   197   {
   198     mDelay = aDelay;
   199     return NS_OK;
   200   }
   202   NS_IMETHOD Notify(nsITimer *timer) MOZ_OVERRIDE
   203   {
   204     if (mSelection && mPresContext)
   205     {
   206       nsWeakFrame frame =
   207         mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
   208       if (!frame)
   209         return NS_OK;
   210       mContent = nullptr;
   212       nsPoint pt = mPoint -
   213         frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
   214       mFrameSelection->HandleDrag(frame, pt);
   215       if (!frame.IsAlive())
   216         return NS_OK;
   218       NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
   219       mSelection->DoAutoScroll(frame, pt);
   220     }
   221     return NS_OK;
   222   }
   223 private:
   224   nsFrameSelection *mFrameSelection;
   225   Selection* mSelection;
   226   nsPresContext *mPresContext;
   227   // relative to mPresContext's root frame
   228   nsPoint mPoint;
   229   nsCOMPtr<nsITimer> mTimer;
   230   nsCOMPtr<nsIContent> mContent;
   231   uint32_t mDelay;
   232 };
   234 NS_IMPL_ISUPPORTS(nsAutoScrollTimer, nsITimerCallback)
   236 nsresult NS_NewDomSelection(nsISelection **aDomSelection)
   237 {
   238   Selection* rlist = new Selection;
   239   *aDomSelection = (nsISelection *)rlist;
   240   NS_ADDREF(rlist);
   241   return NS_OK;
   242 }
   244 static int8_t
   245 GetIndexFromSelectionType(SelectionType aType)
   246 {
   247     switch (aType)
   248     {
   249     case nsISelectionController::SELECTION_NORMAL: return 0; break;
   250     case nsISelectionController::SELECTION_SPELLCHECK: return 1; break;
   251     case nsISelectionController::SELECTION_IME_RAWINPUT: return 2; break;
   252     case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT: return 3; break;
   253     case nsISelectionController::SELECTION_IME_CONVERTEDTEXT: return 4; break;
   254     case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: return 5; break;
   255     case nsISelectionController::SELECTION_ACCESSIBILITY: return 6; break;
   256     case nsISelectionController::SELECTION_FIND: return 7; break;
   257     case nsISelectionController::SELECTION_URLSECONDARY: return 8; break;
   258     default:
   259       return -1; break;
   260     }
   261     /* NOTREACHED */
   262     return 0;
   263 }
   265 static SelectionType 
   266 GetSelectionTypeFromIndex(int8_t aIndex)
   267 {
   268   switch (aIndex)
   269   {
   270     case 0: return nsISelectionController::SELECTION_NORMAL; break;
   271     case 1: return nsISelectionController::SELECTION_SPELLCHECK; break;
   272     case 2: return nsISelectionController::SELECTION_IME_RAWINPUT; break;
   273     case 3: return nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT; break;
   274     case 4: return nsISelectionController::SELECTION_IME_CONVERTEDTEXT; break;
   275     case 5: return nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT; break;
   276     case 6: return nsISelectionController::SELECTION_ACCESSIBILITY; break;
   277     case 7: return nsISelectionController::SELECTION_FIND; break;
   278     case 8: return nsISelectionController::SELECTION_URLSECONDARY; break;
   279     default:
   280       return nsISelectionController::SELECTION_NORMAL; break;
   281   }
   282   /* NOTREACHED */
   283   return 0;
   284 }
   286 /*
   287 The limiter is used specifically for the text areas and textfields
   288 In that case it is the DIV tag that is anonymously created for the text
   289 areas/fields.  Text nodes and BR nodes fall beneath it.  In the case of a 
   290 BR node the limiter will be the parent and the offset will point before or
   291 after the BR node.  In the case of the text node the parent content is 
   292 the text node itself and the offset will be the exact character position.
   293 The offset is not important to check for validity.  Simply look at the 
   294 passed in content.  If it equals the limiter then the selection point is valid.
   295 If its parent it the limiter then the point is also valid.  In the case of 
   296 NO limiter all points are valid since you are in a topmost iframe. (browser
   297 or composer)
   298 */
   299 bool         
   300 IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode)
   301 {
   302   if (!aFrameSel || !aNode)
   303     return false;
   305   nsIContent *limiter = aFrameSel->GetLimiter();
   306   if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
   307     //if newfocus == the limiter. that's ok. but if not there and not parent bad
   308     return false; //not in the right content. tLimiter said so
   309   }
   311   limiter = aFrameSel->GetAncestorLimiter();
   312   return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
   313 }
   316 ////////////BEGIN nsFrameSelection methods
   318 nsFrameSelection::nsFrameSelection()
   319 {
   320   int32_t i;
   321   for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
   322     mDomSelections[i] = new Selection(this);
   323     mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
   324   }
   325   mBatching = 0;
   326   mChangesDuringBatching = false;
   327   mNotifyFrames = true;
   329   mMouseDoubleDownState = false;
   331   mHint = HINTLEFT;
   332   mCaretBidiLevel = BIDI_LEVEL_UNDEFINED;
   333   mDragSelectingCells = false;
   334   mSelectingTableCellMode = 0;
   335   mSelectedCellIndex = 0;
   337   // Check to see if the autocopy pref is enabled
   338   //   and add the autocopy listener if it is
   339   if (Preferences::GetBool("clipboard.autocopy")) {
   340     nsAutoCopyListener *autoCopy = nsAutoCopyListener::GetInstance();
   342     if (autoCopy) {
   343       int8_t index =
   344         GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
   345       if (mDomSelections[index]) {
   346         autoCopy->Listen(mDomSelections[index]);
   347       }
   348     }
   349   }
   351   mDisplaySelection = nsISelectionController::SELECTION_OFF;
   352   mSelectionChangeReason = nsISelectionListener::NO_REASON;
   354   mDelayedMouseEventValid = false;
   355   // These values are not used since they are only valid when
   356   // mDelayedMouseEventValid is true, and setting mDelayedMouseEventValid
   357   //alwaysoverrides these values.
   358   mDelayedMouseEventIsShift = false;
   359   mDelayedMouseEventClickCount = 0;
   360 }
   363 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection)
   365 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection)
   366   int32_t i;
   367   for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) {
   368     tmp->mDomSelections[i] = nullptr;
   369   }
   371   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCellParent)
   372   tmp->mSelectingTableCellMode = 0;
   373   tmp->mDragSelectingCells = false;
   374   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartSelectedCell)
   375   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSelectedCell)
   376   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAppendStartSelectedCell)
   377   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnselectCellOnMouseUp)
   378   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaintainRange)
   379   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLimiter)
   380   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAncestorLimiter)
   381 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   382 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameSelection)
   383   if (tmp->mShell && tmp->mShell->GetDocument() &&
   384       nsCCUncollectableMarker::InGeneration(cb,
   385                                             tmp->mShell->GetDocument()->
   386                                               GetMarkedCCGeneration())) {
   387     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   388   }
   389   int32_t i;
   390   for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) {
   391     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDomSelections[i])
   392   }
   394   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellParent)
   395   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartSelectedCell)
   396   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSelectedCell)
   397   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAppendStartSelectedCell)
   398   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnselectCellOnMouseUp)
   399   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMaintainRange)
   400   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLimiter)
   401   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAncestorLimiter)
   402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   404 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsFrameSelection, AddRef)
   405 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsFrameSelection, Release)
   408 nsresult
   409 nsFrameSelection::FetchDesiredX(nscoord &aDesiredX) //the x position requested by the Key Handling for up down
   410 {
   411   if (!mShell)
   412   {
   413     NS_ERROR("fetch desired X failed");
   414     return NS_ERROR_FAILURE;
   415   }
   416   if (mDesiredXSet)
   417   {
   418     aDesiredX = mDesiredX;
   419     return NS_OK;
   420   }
   422   nsRefPtr<nsCaret> caret = mShell->GetCaret();
   423   if (!caret)
   424     return NS_ERROR_NULL_POINTER;
   426   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
   427   nsresult result = caret->SetCaretDOMSelection(mDomSelections[index]);
   428   if (NS_FAILED(result))
   429     return result;
   431   nsRect coord;
   432   nsIFrame* caretFrame = caret->GetGeometry(mDomSelections[index], &coord);
   433   if (!caretFrame)
   434     return NS_ERROR_FAILURE;
   435   nsPoint viewOffset(0, 0);
   436   nsView* view = nullptr;
   437   caretFrame->GetOffsetFromView(viewOffset, &view);
   438   if (view)
   439     coord.x += viewOffset.x;
   441   aDesiredX = coord.x;
   442   return NS_OK;
   443 }
   447 void
   448 nsFrameSelection::InvalidateDesiredX() //do not listen to mDesiredX you must get another.
   449 {
   450   mDesiredXSet = false;
   451 }
   455 void
   456 nsFrameSelection::SetDesiredX(nscoord aX) //set the mDesiredX
   457 {
   458   mDesiredX = aX;
   459   mDesiredXSet = true;
   460 }
   462 nsresult
   463 nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(nsIFrame  *aFrame,
   464                                                         nsPoint&   aPoint,
   465                                                         nsIFrame **aRetFrame,
   466                                                         nsPoint&   aRetPoint)
   467 {
   468   //
   469   // The whole point of this method is to return a frame and point that
   470   // that lie within the same valid subtree as the anchor node's frame,
   471   // for use with the method GetContentAndOffsetsFromPoint().
   472   //
   473   // A valid subtree is defined to be one where all the content nodes in
   474   // the tree have a valid parent-child relationship.
   475   //
   476   // If the anchor frame and aFrame are in the same subtree, aFrame will
   477   // be returned in aRetFrame. If they are in different subtrees, we
   478   // return the frame for the root of the subtree.
   479   //
   481   if (!aFrame || !aRetFrame)
   482     return NS_ERROR_NULL_POINTER;
   484   *aRetFrame = aFrame;
   485   aRetPoint  = aPoint;
   487   //
   488   // Get the frame and content for the selection's anchor point!
   489   //
   491   nsresult result;
   492   nsCOMPtr<nsIDOMNode> anchorNode;
   493   int32_t anchorOffset = 0;
   495   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
   496   if (!mDomSelections[index])
   497     return NS_ERROR_NULL_POINTER;
   499   result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode));
   501   if (NS_FAILED(result))
   502     return result;
   504   if (!anchorNode)
   505     return NS_OK;
   507   result = mDomSelections[index]->GetAnchorOffset(&anchorOffset);
   509   if (NS_FAILED(result))
   510     return result;
   512   nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
   514   if (!anchorContent)
   515     return NS_ERROR_FAILURE;
   517   //
   518   // Now find the root of the subtree containing the anchor's content.
   519   //
   521   NS_ENSURE_STATE(mShell);
   522   nsIContent* anchorRoot = anchorContent->GetSelectionRootContent(mShell);
   523   NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED);
   525   //
   526   // Now find the root of the subtree containing aFrame's content.
   527   //
   529   nsIContent* content = aFrame->GetContent();
   531   if (content)
   532   {
   533     nsIContent* contentRoot = content->GetSelectionRootContent(mShell);
   534     NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED);
   536     if (anchorRoot == contentRoot)
   537     {
   538       // If the aFrame's content isn't the capturing content, it should be
   539       // a descendant.  At this time, we can return simply.
   540       nsIContent* capturedContent = nsIPresShell::GetCapturingContent();
   541       if (capturedContent != content)
   542       {
   543         return NS_OK;
   544       }
   546       // Find the frame under the mouse cursor with the root frame.
   547       // At this time, don't use the anchor's frame because it may not have
   548       // fixed positioned frames.
   549       nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame();
   550       nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame);
   551       nsIFrame* cursorFrame =
   552         nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
   554       // If the mouse cursor in on a frame which is descendant of same
   555       // selection root, we can expand the selection to the frame.
   556       if (cursorFrame && cursorFrame->PresContext()->PresShell() == mShell)
   557       {
   558         nsIContent* cursorContent = cursorFrame->GetContent();
   559         NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE);
   560         nsIContent* cursorContentRoot =
   561           cursorContent->GetSelectionRootContent(mShell);
   562         NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED);
   563         if (cursorContentRoot == anchorRoot)
   564         {
   565           *aRetFrame = cursorFrame;
   566           aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame);
   567           return NS_OK;
   568         }
   569       }
   570       // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse
   571       // cursor is out of the window), we should use the frame of the anchor
   572       // root.
   573     }
   574   }
   576   //
   577   // When we can't find a frame which is under the mouse cursor and has a same
   578   // selection root as the anchor node's, we should return the selection root
   579   // frame.
   580   //
   582   *aRetFrame = anchorRoot->GetPrimaryFrame();
   584   if (!*aRetFrame)
   585     return NS_ERROR_FAILURE;
   587   //
   588   // Now make sure that aRetPoint is converted to the same coordinate
   589   // system used by aRetFrame.
   590   //
   592   aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
   594   return NS_OK;
   595 }
   597 void
   598 nsFrameSelection::SetCaretBidiLevel(uint8_t aLevel)
   599 {
   600   // If the current level is undefined, we have just inserted new text.
   601   // In this case, we don't want to reset the keyboard language
   602   mCaretBidiLevel = aLevel;
   603   return;
   604 }
   606 uint8_t
   607 nsFrameSelection::GetCaretBidiLevel() const
   608 {
   609   return mCaretBidiLevel;
   610 }
   612 void
   613 nsFrameSelection::UndefineCaretBidiLevel()
   614 {
   615   mCaretBidiLevel |= BIDI_LEVEL_UNDEFINED;
   616 }
   618 #ifdef PRINT_RANGE
   619 void printRange(nsRange *aDomRange)
   620 {
   621   if (!aDomRange)
   622   {
   623     printf("NULL nsIDOMRange\n");
   624   }
   625   nsINode* startNode = aDomRange->GetStartParent();
   626   nsINode* endNode = aDomRange->GetEndParent();
   627   int32_t startOffset = aDomRange->StartOffset();
   628   int32_t endOffset = aDomRange->EndOffset();
   630   printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
   631          (unsigned long)aDomRange,
   632          (unsigned long)startNode, (long)startOffset,
   633          (unsigned long)endNode, (long)endOffset);
   635 }
   636 #endif /* PRINT_RANGE */
   638 static
   639 nsIAtom *GetTag(nsINode *aNode)
   640 {
   641   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
   642   if (!content) 
   643   {
   644     NS_NOTREACHED("bad node passed to GetTag()");
   645     return nullptr;
   646   }
   648   return content->Tag();
   649 }
   651 // Returns the parent
   652 nsINode*
   653 ParentOffset(nsINode *aNode, int32_t *aChildOffset)
   654 {
   655   if (!aNode || !aChildOffset)
   656     return nullptr;
   658   nsIContent* parent = aNode->GetParent();
   659   if (parent)
   660   {
   661     *aChildOffset = parent->IndexOf(aNode);
   663     return parent;
   664   }
   666   return nullptr;
   667 }
   669 static nsINode*
   670 GetCellParent(nsINode *aDomNode)
   671 {
   672     if (!aDomNode)
   673       return nullptr;
   674     nsINode* current = aDomNode;
   675     // Start with current node and look for a table cell
   676     while (current)
   677     {
   678       nsIAtom* tag = GetTag(current);
   679       if (tag == nsGkAtoms::td || tag == nsGkAtoms::th)
   680         return current;
   681       current = current->GetParent();
   682     }
   683     return nullptr;
   684 }
   687 void
   688 nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
   689 {
   690   mShell = aShell;
   691   mMouseDownState = false;
   692   mDesiredXSet = false;
   693   mLimiter = aLimiter;
   694   mCaretMovementStyle =
   695     Preferences::GetInt("bidi.edit.caret_movement_style", 2);
   696 }
   698 nsresult
   699 nsFrameSelection::MoveCaret(uint32_t          aKeycode,
   700                             bool              aContinueSelection,
   701                             nsSelectionAmount aAmount)
   702 {
   703   bool visualMovement =
   704       (aKeycode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE ||
   705        aKeycode == nsIDOMKeyEvent::DOM_VK_DELETE ||
   706        aKeycode == nsIDOMKeyEvent::DOM_VK_HOME ||
   707        aKeycode == nsIDOMKeyEvent::DOM_VK_END) ?
   708       false : // Delete operations and home/end are always logical
   709       mCaretMovementStyle == 1 ||
   710         (mCaretMovementStyle == 2 && !aContinueSelection);
   712   return MoveCaret(aKeycode, aContinueSelection, aAmount, visualMovement);
   713 }
   715 nsresult
   716 nsFrameSelection::MoveCaret(uint32_t          aKeycode,
   717                             bool              aContinueSelection,
   718                             nsSelectionAmount aAmount,
   719                             bool              aVisualMovement)
   720 {
   721   NS_ENSURE_STATE(mShell);
   722   // Flush out layout, since we need it to be up to date to do caret
   723   // positioning.
   724   mShell->FlushPendingNotifications(Flush_Layout);
   726   if (!mShell) {
   727     return NS_OK;
   728   }
   730   nsPresContext *context = mShell->GetPresContext();
   731   if (!context)
   732     return NS_ERROR_FAILURE;
   734   bool isCollapsed;
   735   nscoord desiredX = 0; //we must keep this around and revalidate it when its just UP/DOWN
   737   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
   738   nsRefPtr<Selection> sel = mDomSelections[index];
   739   if (!sel)
   740     return NS_ERROR_NULL_POINTER;
   742   int32_t scrollFlags = 0;
   743   nsINode* focusNode = sel->GetFocusNode();
   744   if (focusNode &&
   745       (focusNode->IsEditable() ||
   746        (focusNode->IsElement() &&
   747         focusNode->AsElement()->State().
   748           HasState(NS_EVENT_STATE_MOZ_READWRITE)))) {
   749     // If caret moves in editor, it should cause scrolling even if it's in
   750     // overflow: hidden;.
   751     scrollFlags |= Selection::SCROLL_OVERFLOW_HIDDEN;
   752   }
   754   nsresult result = sel->GetIsCollapsed(&isCollapsed);
   755   if (NS_FAILED(result))
   756     return result;
   757   if (aKeycode == nsIDOMKeyEvent::DOM_VK_UP ||
   758       aKeycode == nsIDOMKeyEvent::DOM_VK_DOWN)
   759   {
   760     result = FetchDesiredX(desiredX);
   761     if (NS_FAILED(result))
   762       return result;
   763     SetDesiredX(desiredX);
   764   }
   766   int32_t caretStyle = Preferences::GetInt("layout.selection.caret_style", 0);
   767   if (caretStyle == 0
   768 #ifdef XP_WIN
   769       && aKeycode != nsIDOMKeyEvent::DOM_VK_UP
   770       && aKeycode != nsIDOMKeyEvent::DOM_VK_DOWN
   771 #endif
   772      ) {
   773     // Put caret at the selection edge in the |aKeycode| direction.
   774     caretStyle = 2;
   775   }
   777   if (!isCollapsed && !aContinueSelection && caretStyle == 2) {
   778     switch (aKeycode){
   779       case nsIDOMKeyEvent::DOM_VK_LEFT  :
   780       case nsIDOMKeyEvent::DOM_VK_UP    :
   781         {
   782           const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
   783           if (anchorFocusRange) {
   784             PostReason(nsISelectionListener::COLLAPSETOSTART_REASON);
   785             sel->Collapse(anchorFocusRange->GetStartParent(),
   786                           anchorFocusRange->StartOffset());
   787           }
   788           mHint = HINTRIGHT;
   789           sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
   790                               nsIPresShell::ScrollAxis(),
   791                               nsIPresShell::ScrollAxis(), scrollFlags);
   792           return NS_OK;
   793         }
   795       case nsIDOMKeyEvent::DOM_VK_RIGHT :
   796       case nsIDOMKeyEvent::DOM_VK_DOWN  :
   797         {
   798           const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
   799           if (anchorFocusRange) {
   800             PostReason(nsISelectionListener::COLLAPSETOEND_REASON);
   801             sel->Collapse(anchorFocusRange->GetEndParent(),
   802                           anchorFocusRange->EndOffset());
   803           }
   804           mHint = HINTLEFT;
   805           sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
   806                               nsIPresShell::ScrollAxis(),
   807                               nsIPresShell::ScrollAxis(), scrollFlags);
   808           return NS_OK;
   809         }
   810     }
   811   }
   813   nsIFrame *frame;
   814   int32_t offsetused = 0;
   815   result = sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
   816                                             aVisualMovement);
   818   if (NS_FAILED(result) || !frame)
   819     return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
   821   //set data using mLimiter to stop on scroll views.  If we have a limiter then we stop peeking
   822   //when we hit scrollable views.  If no limiter then just let it go ahead
   823   nsPeekOffsetStruct pos(aAmount, eDirPrevious, offsetused, desiredX,
   824                          true, mLimiter != nullptr, true, aVisualMovement);
   826   nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame);
   828   HINT tHint(mHint); //temporary variable so we dont set mHint until it is necessary
   829   switch (aKeycode){
   830     case nsIDOMKeyEvent::DOM_VK_RIGHT : 
   831         InvalidateDesiredX();
   832         pos.mDirection = (baseLevel & 1) ? eDirPrevious : eDirNext;
   833       break;
   834     case nsIDOMKeyEvent::DOM_VK_LEFT :
   835         InvalidateDesiredX();
   836         pos.mDirection = (baseLevel & 1) ? eDirNext : eDirPrevious;
   837       break;
   838     case nsIDOMKeyEvent::DOM_VK_DELETE :
   839         InvalidateDesiredX();
   840         pos.mDirection = eDirNext;
   841       break;
   842     case nsIDOMKeyEvent::DOM_VK_BACK_SPACE : 
   843         InvalidateDesiredX();
   844         pos.mDirection = eDirPrevious;
   845       break;
   846     case nsIDOMKeyEvent::DOM_VK_DOWN : 
   847         pos.mAmount = eSelectLine;
   848         pos.mDirection = eDirNext;
   849       break;
   850     case nsIDOMKeyEvent::DOM_VK_UP : 
   851         pos.mAmount = eSelectLine;
   852         pos.mDirection = eDirPrevious;
   853       break;
   854     case nsIDOMKeyEvent::DOM_VK_HOME :
   855         InvalidateDesiredX();
   856         pos.mAmount = eSelectBeginLine;
   857       break;
   858     case nsIDOMKeyEvent::DOM_VK_END :
   859         InvalidateDesiredX();
   860         pos.mAmount = eSelectEndLine;
   861       break;
   862   default :return NS_ERROR_FAILURE;
   863   }
   864   PostReason(nsISelectionListener::KEYPRESS_REASON);
   865   if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent)
   866   {
   867     nsIFrame *theFrame;
   868     int32_t currentOffset, frameStart, frameEnd;
   870     if (aAmount >= eSelectCharacter && aAmount <= eSelectWord)
   871     {
   872       // For left/right, PeekOffset() sets pos.mResultFrame correctly, but does not set pos.mAttachForward,
   873       // so determine the hint here based on the result frame and offset:
   874       // If we're at the end of a text frame, set the hint to HINTLEFT to indicate that we
   875       // want the caret displayed at the end of this frame, not at the beginning of the next one.
   876       theFrame = pos.mResultFrame;
   877       theFrame->GetOffsets(frameStart, frameEnd);
   878       currentOffset = pos.mContentOffset;
   879       if (frameEnd == currentOffset && !(frameStart == 0 && frameEnd == 0))
   880         tHint = HINTLEFT;
   881       else
   882         tHint = HINTRIGHT;
   883     } else {
   884       // For up/down and home/end, pos.mResultFrame might not be set correctly, or not at all.
   885       // In these cases, get the frame based on the content and hint returned by PeekOffset().
   886       tHint = (HINT)pos.mAttachForward;
   887       theFrame = GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset,
   888                                        tHint, &currentOffset);
   889       if (!theFrame)
   890         return NS_ERROR_FAILURE;
   892       theFrame->GetOffsets(frameStart, frameEnd);
   893     }
   895     if (context->BidiEnabled())
   896     {
   897       switch (aKeycode) {
   898         case nsIDOMKeyEvent::DOM_VK_HOME:
   899         case nsIDOMKeyEvent::DOM_VK_END:
   900           // set the caret Bidi level to the paragraph embedding level
   901           SetCaretBidiLevel(NS_GET_BASE_LEVEL(theFrame));
   902           break;
   904         default:
   905           // If the current position is not a frame boundary, it's enough just to take the Bidi level of the current frame
   906           if ((pos.mContentOffset != frameStart && pos.mContentOffset != frameEnd)
   907               || (eSelectLine == aAmount))
   908           {
   909             SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame));
   910           }
   911           else
   912             BidiLevelFromMove(mShell, pos.mResultContent, pos.mContentOffset, aKeycode, tHint);
   913       }
   914     }
   915     result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset,
   916                        tHint, aContinueSelection, false);
   917   } else if (aKeycode == nsIDOMKeyEvent::DOM_VK_RIGHT && !aContinueSelection) {
   918     // Collapse selection if PeekOffset failed, we either
   919     //  1. bumped into the BRFrame, bug 207623
   920     //  2. had select-all in a text input (DIV range), bug 352759.
   921     bool isBRFrame = frame->GetType() == nsGkAtoms::brFrame;
   922     sel->Collapse(sel->GetFocusNode(), sel->FocusOffset());
   923     // Note: 'frame' might be dead here.
   924     if (!isBRFrame) {
   925       mHint = HINTLEFT; // We're now at the end of the frame to the left.
   926     }
   927     result = NS_OK;
   928   }
   929   if (NS_SUCCEEDED(result))
   930   {
   931     result = mDomSelections[index]->
   932       ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
   933                      nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
   934                      scrollFlags);
   935   }
   937   return result;
   938 }
   940 //END nsFrameSelection methods
   943 //BEGIN nsFrameSelection methods
   945 NS_IMETHODIMP
   946 Selection::ToString(nsAString& aReturn)
   947 {
   948   // We need Flush_Style here to make sure frames have been created for
   949   // the selected content.  Use mFrameSelection->GetShell() which returns
   950   // null if the Selection has been disconnected (the shell is Destroyed).
   951   nsCOMPtr<nsIPresShell> shell =
   952     mFrameSelection ? mFrameSelection->GetShell() : nullptr;
   953   if (!shell) {
   954     aReturn.Truncate();
   955     return NS_OK;
   956   }
   957   shell->FlushPendingNotifications(Flush_Style);
   959   return ToStringWithFormat("text/plain",
   960                             nsIDocumentEncoder::SkipInvisibleContent,
   961                             0, aReturn);
   962 }
   964 void
   965 Selection::Stringify(nsAString& aResult)
   966 {
   967   // Eat the error code
   968   ToString(aResult);
   969 }
   971 NS_IMETHODIMP
   972 Selection::ToStringWithFormat(const char* aFormatType, uint32_t aFlags,
   973                               int32_t aWrapCol, nsAString& aReturn)
   974 {
   975   ErrorResult result;
   976   NS_ConvertUTF8toUTF16 format(aFormatType);
   977   ToStringWithFormat(format, aFlags, aWrapCol, aReturn, result);
   978   if (result.Failed()) {
   979     return result.ErrorCode();
   980   }
   981   return NS_OK;
   982 }
   984 void
   985 Selection::ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags,
   986                               int32_t aWrapCol, nsAString& aReturn,
   987                               ErrorResult& aRv)
   988 {
   989   nsresult rv = NS_OK;
   990   NS_ConvertUTF8toUTF16 formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
   991   formatType.Append(aFormatType);
   992   nsCOMPtr<nsIDocumentEncoder> encoder =
   993            do_CreateInstance(NS_ConvertUTF16toUTF8(formatType).get(), &rv);
   994   if (NS_FAILED(rv)) {
   995     aRv.Throw(rv);
   996     return;
   997   }
   999   nsIPresShell* shell = GetPresShell();
  1000   if (!shell) {
  1001     aRv.Throw(NS_ERROR_FAILURE);
  1002     return;
  1005   nsIDocument *doc = shell->GetDocument();
  1007   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
  1008   NS_ASSERTION(domDoc, "Need a document");
  1010   // Flags should always include OutputSelectionOnly if we're coming from here:
  1011   aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
  1012   nsAutoString readstring;
  1013   readstring.Assign(aFormatType);
  1014   rv = encoder->Init(domDoc, readstring, aFlags);
  1015   if (NS_FAILED(rv)) {
  1016     aRv.Throw(rv);
  1017     return;
  1020   encoder->SetSelection(this);
  1021   if (aWrapCol != 0)
  1022     encoder->SetWrapColumn(aWrapCol);
  1024   rv = encoder->EncodeToString(aReturn);
  1025   if (NS_FAILED(rv)) {
  1026     aRv.Throw(rv);
  1030 NS_IMETHODIMP
  1031 Selection::SetInterlinePosition(bool aHintRight)
  1033   ErrorResult result;
  1034   SetInterlinePosition(aHintRight, result);
  1035   if (result.Failed()) {
  1036     return result.ErrorCode();
  1038   return NS_OK;
  1041 void
  1042 Selection::SetInterlinePosition(bool aHintRight, ErrorResult& aRv)
  1044   if (!mFrameSelection) {
  1045     aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
  1046     return;
  1048   nsFrameSelection::HINT hint;
  1049   if (aHintRight)
  1050     hint = nsFrameSelection::HINTRIGHT;
  1051   else
  1052     hint = nsFrameSelection::HINTLEFT;
  1053   mFrameSelection->SetHint(hint);
  1056 NS_IMETHODIMP
  1057 Selection::GetInterlinePosition(bool* aHintRight)
  1059   ErrorResult result;
  1060   *aHintRight = GetInterlinePosition(result);
  1061   if (result.Failed()) {
  1062     return result.ErrorCode();
  1064   return NS_OK;
  1067 bool
  1068 Selection::GetInterlinePosition(ErrorResult& aRv)
  1070   if (!mFrameSelection) {
  1071     aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
  1072     return false;
  1074   return (mFrameSelection->GetHint() == nsFrameSelection::HINTRIGHT);
  1077 nsPrevNextBidiLevels
  1078 nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
  1079                                         uint32_t    aContentOffset,
  1080                                         bool        aJumpLines) const
  1082   return GetPrevNextBidiLevels(aNode, aContentOffset, mHint, aJumpLines);
  1085 nsPrevNextBidiLevels
  1086 nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
  1087                                         uint32_t    aContentOffset,
  1088                                         HINT        aHint,
  1089                                         bool        aJumpLines) const
  1091   // Get the level of the frames on each side
  1092   nsIFrame    *currentFrame;
  1093   int32_t     currentOffset;
  1094   int32_t     frameStart, frameEnd;
  1095   nsDirection direction;
  1097   nsPrevNextBidiLevels levels;
  1098   levels.SetData(nullptr, nullptr, 0, 0);
  1100   currentFrame = GetFrameForNodeOffset(aNode, aContentOffset,
  1101                                        aHint, &currentOffset);
  1102   if (!currentFrame)
  1103     return levels;
  1105   currentFrame->GetOffsets(frameStart, frameEnd);
  1107   if (0 == frameStart && 0 == frameEnd)
  1108     direction = eDirPrevious;
  1109   else if (frameStart == currentOffset)
  1110     direction = eDirPrevious;
  1111   else if (frameEnd == currentOffset)
  1112     direction = eDirNext;
  1113   else {
  1114     // we are neither at the beginning nor at the end of the frame, so we have no worries
  1115     levels.SetData(currentFrame, currentFrame,
  1116                    NS_GET_EMBEDDING_LEVEL(currentFrame),
  1117                    NS_GET_EMBEDDING_LEVEL(currentFrame));
  1118     return levels;
  1121   nsIFrame *newFrame;
  1122   int32_t offset;
  1123   bool jumpedLine;
  1124   nsresult rv = currentFrame->GetFrameFromDirection(direction, false,
  1125                                                     aJumpLines, true,
  1126                                                     &newFrame, &offset, &jumpedLine);
  1127   if (NS_FAILED(rv))
  1128     newFrame = nullptr;
  1130   uint8_t baseLevel = NS_GET_BASE_LEVEL(currentFrame);
  1131   uint8_t currentLevel = NS_GET_EMBEDDING_LEVEL(currentFrame);
  1132   uint8_t newLevel = newFrame ? NS_GET_EMBEDDING_LEVEL(newFrame) : baseLevel;
  1134   // If not jumping lines, disregard br frames, since they might be positioned incorrectly.
  1135   // XXX This could be removed once bug 339786 is fixed.
  1136   if (!aJumpLines) {
  1137     if (currentFrame->GetType() == nsGkAtoms::brFrame) {
  1138       currentFrame = nullptr;
  1139       currentLevel = baseLevel;
  1141     if (newFrame && newFrame->GetType() == nsGkAtoms::brFrame) {
  1142       newFrame = nullptr;
  1143       newLevel = baseLevel;
  1147   if (direction == eDirNext)
  1148     levels.SetData(currentFrame, newFrame, currentLevel, newLevel);
  1149   else
  1150     levels.SetData(newFrame, currentFrame, newLevel, currentLevel);
  1152   return levels;
  1155 nsresult
  1156 nsFrameSelection::GetFrameFromLevel(nsIFrame    *aFrameIn,
  1157                                     nsDirection  aDirection,
  1158                                     uint8_t      aBidiLevel,
  1159                                     nsIFrame   **aFrameOut) const
  1161   NS_ENSURE_STATE(mShell);
  1162   uint8_t foundLevel = 0;
  1163   nsIFrame *foundFrame = aFrameIn;
  1165   nsCOMPtr<nsIFrameEnumerator> frameTraversal;
  1166   nsresult result;
  1167   nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
  1168   if (NS_FAILED(result))
  1169       return result;
  1171   result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
  1172                                    mShell->GetPresContext(), aFrameIn,
  1173                                    eLeaf,
  1174                                    false, // aVisual
  1175                                    false, // aLockInScrollView
  1176                                    false     // aFollowOOFs
  1177                                    );
  1178   if (NS_FAILED(result))
  1179     return result;
  1181   do {
  1182     *aFrameOut = foundFrame;
  1183     if (aDirection == eDirNext)
  1184       frameTraversal->Next();
  1185     else
  1186       frameTraversal->Prev();
  1188     foundFrame = frameTraversal->CurrentItem();
  1189     if (!foundFrame)
  1190       return NS_ERROR_FAILURE;
  1191     foundLevel = NS_GET_EMBEDDING_LEVEL(foundFrame);
  1193   } while (foundLevel > aBidiLevel);
  1195   return NS_OK;
  1199 nsresult
  1200 nsFrameSelection::MaintainSelection(nsSelectionAmount aAmount)
  1202   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  1203   if (!mDomSelections[index])
  1204     return NS_ERROR_NULL_POINTER;
  1206   mMaintainedAmount = aAmount;
  1208   const nsRange* anchorFocusRange =
  1209     mDomSelections[index]->GetAnchorFocusRange();
  1210   if (anchorFocusRange) {
  1211     mMaintainRange = anchorFocusRange->CloneRange();
  1212     return NS_OK;
  1215   mMaintainRange = nullptr;
  1216   return NS_OK;
  1220 /** After moving the caret, its Bidi level is set according to the following rules:
  1222  *  After moving over a character with left/right arrow, set to the Bidi level of the last moved over character.
  1223  *  After Home and End, set to the paragraph embedding level.
  1224  *  After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters.
  1225  *  After mouse click, set to the level of the current frame.
  1227  *  The following two methods use GetPrevNextBidiLevels to determine the new Bidi level.
  1228  *  BidiLevelFromMove is called when the caret is moved in response to a keyboard event
  1230  * @param aPresShell is the presentation shell
  1231  * @param aNode is the content node
  1232  * @param aContentOffset is the new caret position, as an offset into aNode
  1233  * @param aKeycode is the keyboard event that moved the caret to the new position
  1234  * @param aHint is the hint indicating in what logical direction the caret moved
  1235  */
  1236 void nsFrameSelection::BidiLevelFromMove(nsIPresShell* aPresShell,
  1237                                          nsIContent   *aNode,
  1238                                          uint32_t      aContentOffset,
  1239                                          uint32_t      aKeycode,
  1240                                          HINT          aHint)
  1242   switch (aKeycode) {
  1244     // Right and Left: the new cursor Bidi level is the level of the character moved over
  1245     case nsIDOMKeyEvent::DOM_VK_RIGHT:
  1246     case nsIDOMKeyEvent::DOM_VK_LEFT:
  1248       nsPrevNextBidiLevels levels = GetPrevNextBidiLevels(aNode, aContentOffset,
  1249                                                           aHint, false);
  1251       if (HINTLEFT == aHint)
  1252         SetCaretBidiLevel(levels.mLevelBefore);
  1253       else
  1254         SetCaretBidiLevel(levels.mLevelAfter);
  1255       break;
  1257       /*
  1258     // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters      
  1259     case nsIDOMKeyEvent::DOM_VK_UP:
  1260     case nsIDOMKeyEvent::DOM_VK_DOWN:
  1261       GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
  1262       aPresShell->SetCaretBidiLevel(std::min(firstLevel, secondLevel));
  1263       break;
  1264       */
  1266     default:
  1267       UndefineCaretBidiLevel();
  1271 /**
  1272  * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse
  1274  * @param aNode is the content node
  1275  * @param aContentOffset is the new caret position, as an offset into aNode
  1276  */
  1277 void nsFrameSelection::BidiLevelFromClick(nsIContent *aNode,
  1278                                           uint32_t    aContentOffset)
  1280   nsIFrame* clickInFrame=nullptr;
  1281   int32_t OffsetNotUsed;
  1283   clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &OffsetNotUsed);
  1284   if (!clickInFrame)
  1285     return;
  1287   SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(clickInFrame));
  1291 bool
  1292 nsFrameSelection::AdjustForMaintainedSelection(nsIContent *aContent,
  1293                                                int32_t     aOffset)
  1295   if (!mMaintainRange)
  1296     return false;
  1298   if (!aContent) {
  1299     return false;
  1302   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  1303   if (!mDomSelections[index])
  1304     return false;
  1306   nsINode* rangeStartNode = mMaintainRange->GetStartParent();
  1307   nsINode* rangeEndNode = mMaintainRange->GetEndParent();
  1308   int32_t rangeStartOffset = mMaintainRange->StartOffset();
  1309   int32_t rangeEndOffset = mMaintainRange->EndOffset();
  1311   int32_t relToStart =
  1312     nsContentUtils::ComparePoints(rangeStartNode, rangeStartOffset,
  1313                                   aContent, aOffset);
  1314   int32_t relToEnd =
  1315     nsContentUtils::ComparePoints(rangeEndNode, rangeEndOffset,
  1316                                   aContent, aOffset);
  1318   // If aContent/aOffset is inside the maintained selection, or if it is on the
  1319   // "anchor" side of the maintained selection, we need to do something.
  1320   if ((relToStart < 0 && relToEnd > 0) ||
  1321       (relToStart > 0 &&
  1322        mDomSelections[index]->GetDirection() == eDirNext) ||
  1323       (relToEnd < 0 &&
  1324        mDomSelections[index]->GetDirection() == eDirPrevious)) {
  1325     // Set the current range to the maintained range. 
  1326     mDomSelections[index]->ReplaceAnchorFocusRange(mMaintainRange);
  1327     if (relToStart < 0 && relToEnd > 0) {
  1328       // We're inside the maintained selection, just keep it selected.
  1329       return true;
  1331     // Reverse the direction of the selection so that the anchor will be on the 
  1332     // far side of the maintained selection, relative to aContent/aOffset.
  1333     mDomSelections[index]->SetDirection(relToStart > 0 ? eDirPrevious : eDirNext);
  1335   return false;
  1339 nsresult
  1340 nsFrameSelection::HandleClick(nsIContent *aNewFocus,
  1341                               uint32_t    aContentOffset,
  1342                               uint32_t    aContentEndOffset,
  1343                               bool        aContinueSelection, 
  1344                               bool        aMultipleSelection,
  1345                               bool        aHint) 
  1347   if (!aNewFocus)
  1348     return NS_ERROR_INVALID_ARG;
  1350   InvalidateDesiredX();
  1352   if (!aContinueSelection) {
  1353     mMaintainRange = nullptr;
  1354     if (!IsValidSelectionPoint(this, aNewFocus)) {
  1355       mAncestorLimiter = nullptr;
  1359   // Don't take focus when dragging off of a table
  1360   if (!mDragSelectingCells)
  1362     BidiLevelFromClick(aNewFocus, aContentOffset);
  1363     PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
  1364     if (aContinueSelection &&
  1365         AdjustForMaintainedSelection(aNewFocus, aContentOffset))
  1366       return NS_OK; //shift clicked to maintained selection. rejected.
  1368     return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, HINT(aHint),
  1369                      aContinueSelection, aMultipleSelection);
  1372   return NS_OK;
  1375 void
  1376 nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint)
  1378   if (!aFrame || !mShell)
  1379     return;
  1381   nsresult result;
  1382   nsIFrame *newFrame = 0;
  1383   nsPoint   newPoint;
  1385   result = ConstrainFrameAndPointToAnchorSubtree(aFrame, aPoint, &newFrame, newPoint);
  1386   if (NS_FAILED(result))
  1387     return;
  1388   if (!newFrame)
  1389     return;
  1391   nsIFrame::ContentOffsets offsets =
  1392       newFrame->GetContentOffsetsFromPoint(newPoint);
  1393   if (!offsets.content)
  1394     return;
  1396   if (newFrame->IsSelected() &&
  1397       AdjustForMaintainedSelection(offsets.content, offsets.offset))
  1398     return;
  1400   // Adjust offsets according to maintained amount
  1401   if (mMaintainRange && 
  1402       mMaintainedAmount != eSelectNoAmount) {    
  1404     nsINode* rangenode = mMaintainRange->GetStartParent();
  1405     int32_t rangeOffset = mMaintainRange->StartOffset();
  1406     int32_t relativePosition =
  1407       nsContentUtils::ComparePoints(rangenode, rangeOffset,
  1408                                     offsets.content, offsets.offset);
  1410     nsDirection direction = relativePosition > 0 ? eDirPrevious : eDirNext;
  1411     nsSelectionAmount amount = mMaintainedAmount;
  1412     if (amount == eSelectBeginLine && direction == eDirNext)
  1413       amount = eSelectEndLine;
  1415     int32_t offset;
  1416     nsIFrame* frame = GetFrameForNodeOffset(offsets.content, offsets.offset, HINTRIGHT, &offset);
  1418     if (frame && amount == eSelectWord && direction == eDirPrevious) {
  1419       // To avoid selecting the previous word when at start of word,
  1420       // first move one character forward.
  1421       nsPeekOffsetStruct charPos(eSelectCharacter, eDirNext, offset, 0,
  1422                                  false, mLimiter != nullptr, false, false);
  1423       if (NS_SUCCEEDED(frame->PeekOffset(&charPos))) {
  1424         frame = charPos.mResultFrame;
  1425         offset = charPos.mContentOffset;
  1429     nsPeekOffsetStruct pos(amount, direction, offset, 0,
  1430                            false, mLimiter != nullptr, false, false);
  1432     if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos)) && pos.mResultContent) {
  1433       offsets.content = pos.mResultContent;
  1434       offsets.offset = pos.mContentOffset;
  1438   HandleClick(offsets.content, offsets.offset, offsets.offset,
  1439               true, false, offsets.associateWithNext);
  1442 nsresult
  1443 nsFrameSelection::StartAutoScrollTimer(nsIFrame *aFrame,
  1444                                        nsPoint   aPoint,
  1445                                        uint32_t  aDelay)
  1447   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  1448   if (!mDomSelections[index])
  1449     return NS_ERROR_NULL_POINTER;
  1451   return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay);
  1454 void
  1455 nsFrameSelection::StopAutoScrollTimer()
  1457   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  1458   if (!mDomSelections[index])
  1459     return;
  1461   mDomSelections[index]->StopAutoScrollTimer();
  1464 /**
  1465 hard to go from nodes to frames, easy the other way!
  1466  */
  1467 nsresult
  1468 nsFrameSelection::TakeFocus(nsIContent *aNewFocus,
  1469                             uint32_t    aContentOffset,
  1470                             uint32_t    aContentEndOffset,
  1471                             HINT        aHint,
  1472                             bool        aContinueSelection,
  1473                             bool        aMultipleSelection)
  1475   if (!aNewFocus)
  1476     return NS_ERROR_NULL_POINTER;
  1478   NS_ENSURE_STATE(mShell);
  1480   if (!IsValidSelectionPoint(this,aNewFocus))
  1481     return NS_ERROR_FAILURE;
  1483   // Clear all table selection data
  1484   mSelectingTableCellMode = 0;
  1485   mDragSelectingCells = false;
  1486   mStartSelectedCell = nullptr;
  1487   mEndSelectedCell = nullptr;
  1488   mAppendStartSelectedCell = nullptr;
  1489   mHint = aHint;
  1491   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  1492   if (!mDomSelections[index])
  1493     return NS_ERROR_NULL_POINTER;
  1495   //traverse through document and unselect crap here
  1496   if (!aContinueSelection) {//single click? setting cursor down
  1497     uint32_t batching = mBatching;//hack to use the collapse code.
  1498     bool changes = mChangesDuringBatching;
  1499     mBatching = 1;
  1501     if (aMultipleSelection) {
  1502       // Remove existing collapsed ranges as there's no point in having 
  1503       // non-anchor/focus collapsed ranges.
  1504       mDomSelections[index]->RemoveCollapsedRanges();
  1506       nsRefPtr<nsRange> newRange = new nsRange(aNewFocus);
  1508       newRange->SetStart(aNewFocus, aContentOffset);
  1509       newRange->SetEnd(aNewFocus, aContentOffset);
  1510       mDomSelections[index]->AddRange(newRange);
  1511       mBatching = batching;
  1512       mChangesDuringBatching = changes;
  1514     else
  1516       bool oldDesiredXSet = mDesiredXSet; //need to keep old desired X if it was set.
  1517       mDomSelections[index]->Collapse(aNewFocus, aContentOffset);
  1518       mDesiredXSet = oldDesiredXSet; //now reset desired X back.
  1519       mBatching = batching;
  1520       mChangesDuringBatching = changes;
  1522     if (aContentEndOffset != aContentOffset)
  1523       mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);
  1525     //find out if we are inside a table. if so, find out which one and which cell
  1526     //once we do that, the next time we get a takefocus, check the parent tree. 
  1527     //if we are no longer inside same table ,cell then switch to table selection mode.
  1528     // BUT only do this in an editor
  1530     NS_ENSURE_STATE(mShell);
  1531     int16_t displaySelection = mShell->GetSelectionFlags();
  1533     // Editor has DISPLAY_ALL selection type
  1534     if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
  1536       mCellParent = GetCellParent(aNewFocus);
  1537 #ifdef DEBUG_TABLE_SELECTION
  1538       if (mCellParent)
  1539         printf(" * TakeFocus - Collapsing into new cell\n");
  1540 #endif
  1543   else {
  1544     // Now update the range list:
  1545     if (aContinueSelection && aNewFocus)
  1547       int32_t offset;
  1548       nsINode *cellparent = GetCellParent(aNewFocus);
  1549       if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode
  1551 #ifdef DEBUG_TABLE_SELECTION
  1552 printf(" * TakeFocus - moving into new cell\n");
  1553 #endif
  1554         WidgetMouseEvent event(false, 0, nullptr, WidgetMouseEvent::eReal);
  1556         // Start selecting in the cell we were in before
  1557         nsINode* parent = ParentOffset(mCellParent, &offset);
  1558         if (parent)
  1559           HandleTableSelection(parent, offset,
  1560                                nsISelectionPrivate::TABLESELECTION_CELL, &event);
  1562         // Find the parent of this new cell and extend selection to it
  1563         parent = ParentOffset(cellparent, &offset);
  1565         // XXXX We need to REALLY get the current key shift state
  1566         //  (we'd need to add event listener -- let's not bother for now)
  1567         event.modifiers &= ~MODIFIER_SHIFT; //aContinueSelection;
  1568         if (parent)
  1570           mCellParent = cellparent;
  1571           // Continue selection into next cell
  1572           HandleTableSelection(parent, offset,
  1573                                nsISelectionPrivate::TABLESELECTION_CELL, &event);
  1576       else
  1578         // XXXX Problem: Shift+click in browser is appending text selection to selected table!!!
  1579         //   is this the place to erase seleced cells ?????
  1580         if (mDomSelections[index]->GetDirection() == eDirNext && aContentEndOffset > aContentOffset) //didn't go far enough 
  1582           mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);//this will only redraw the diff 
  1584         else
  1585           mDomSelections[index]->Extend(aNewFocus, aContentOffset);
  1590   // Don't notify selection listeners if batching is on:
  1591   if (GetBatching())
  1592     return NS_OK;
  1593   return NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
  1597 SelectionDetails*
  1598 nsFrameSelection::LookUpSelection(nsIContent *aContent,
  1599                                   int32_t aContentOffset,
  1600                                   int32_t aContentLength,
  1601                                   bool aSlowCheck) const
  1603   if (!aContent || !mShell)
  1604     return nullptr;
  1606   SelectionDetails* details = nullptr;
  1608   for (int32_t j = 0; j < nsISelectionController::NUM_SELECTIONTYPES; j++) {
  1609     if (mDomSelections[j]) {
  1610       mDomSelections[j]->LookUpSelection(aContent, aContentOffset,
  1611           aContentLength, &details, (SelectionType)(1<<j), aSlowCheck);
  1615   return details;
  1618 void
  1619 nsFrameSelection::SetMouseDownState(bool aState)
  1621   if (mMouseDownState == aState)
  1622     return;
  1624   mMouseDownState = aState;
  1626   if (!mMouseDownState)
  1628     mDragSelectingCells = false;
  1629     PostReason(nsISelectionListener::MOUSEUP_REASON);
  1630     NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); //notify that reason is mouse up please.
  1634 Selection*
  1635 nsFrameSelection::GetSelection(SelectionType aType) const
  1637   int8_t index = GetIndexFromSelectionType(aType);
  1638   if (index < 0)
  1639     return nullptr;
  1641   return mDomSelections[index];
  1644 nsresult
  1645 nsFrameSelection::ScrollSelectionIntoView(SelectionType   aType,
  1646                                           SelectionRegion aRegion,
  1647                                           int16_t         aFlags) const
  1649   int8_t index = GetIndexFromSelectionType(aType);
  1650   if (index < 0)
  1651     return NS_ERROR_INVALID_ARG;
  1653   if (!mDomSelections[index])
  1654     return NS_ERROR_NULL_POINTER;
  1656   nsIPresShell::ScrollAxis verticalScroll = nsIPresShell::ScrollAxis();
  1657   int32_t flags = Selection::SCROLL_DO_FLUSH;
  1658   if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) {
  1659     flags |= Selection::SCROLL_SYNCHRONOUS;
  1660   } else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) {
  1661     flags |= Selection::SCROLL_FIRST_ANCESTOR_ONLY;
  1663   if (aFlags & nsISelectionController::SCROLL_OVERFLOW_HIDDEN) {
  1664     flags |= Selection::SCROLL_OVERFLOW_HIDDEN;
  1666   if (aFlags & nsISelectionController::SCROLL_CENTER_VERTICALLY) {
  1667     verticalScroll = nsIPresShell::ScrollAxis(
  1668       nsIPresShell::SCROLL_CENTER, nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE);
  1671   // After ScrollSelectionIntoView(), the pending notifications might be
  1672   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
  1673   return mDomSelections[index]->ScrollIntoView(aRegion,
  1674                                                verticalScroll,
  1675                                                nsIPresShell::ScrollAxis(),
  1676                                                flags);
  1679 nsresult
  1680 nsFrameSelection::RepaintSelection(SelectionType aType) const
  1682   int8_t index = GetIndexFromSelectionType(aType);
  1683   if (index < 0)
  1684     return NS_ERROR_INVALID_ARG;
  1685   if (!mDomSelections[index])
  1686     return NS_ERROR_NULL_POINTER;
  1687   NS_ENSURE_STATE(mShell);
  1688   return mDomSelections[index]->Repaint(mShell->GetPresContext());
  1691 nsIFrame*
  1692 nsFrameSelection::GetFrameForNodeOffset(nsIContent *aNode,
  1693                                         int32_t     aOffset,
  1694                                         HINT        aHint,
  1695                                         int32_t    *aReturnOffset) const
  1697   if (!aNode || !aReturnOffset || !mShell)
  1698     return nullptr;
  1700   if (aOffset < 0)
  1701     return nullptr;
  1703   *aReturnOffset = aOffset;
  1705   nsCOMPtr<nsIContent> theNode = aNode;
  1707   if (aNode->IsElement())
  1709     int32_t childIndex  = 0;
  1710     int32_t numChildren = theNode->GetChildCount();
  1712     if (aHint == HINTLEFT)
  1714       if (aOffset > 0)
  1715         childIndex = aOffset - 1;
  1716       else
  1717         childIndex = aOffset;
  1719     else // HINTRIGHT
  1721       if (aOffset >= numChildren)
  1723         if (numChildren > 0)
  1724           childIndex = numChildren - 1;
  1725         else
  1726           childIndex = 0;
  1728       else
  1729         childIndex = aOffset;
  1732     if (childIndex > 0 || numChildren > 0) {
  1733       nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex);
  1735       if (!childNode)
  1736         return nullptr;
  1738       theNode = childNode;
  1741 #ifdef DONT_DO_THIS_YET
  1742     // XXX: We can't use this code yet because the hinting
  1743     //      can cause us to attach to the wrong line frame.
  1745     // Now that we have the child node, check if it too
  1746     // can contain children. If so, call this method again!
  1748     if (theNode->IsElement())
  1750       int32_t newOffset = 0;
  1752       if (aOffset > childIndex)
  1754         numChildren = theNode->GetChildCount();
  1756         newOffset = numChildren;
  1759       return GetFrameForNodeOffset(theNode, newOffset, aHint, aReturnOffset);
  1761     else
  1762 #endif // DONT_DO_THIS_YET
  1764       // Check to see if theNode is a text node. If it is, translate
  1765       // aOffset into an offset into the text node.
  1767       nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode);
  1769       if (textNode)
  1771         if (theNode->GetPrimaryFrame())
  1773           if (aOffset > childIndex)
  1775             uint32_t textLength = 0;
  1777             nsresult rv = textNode->GetLength(&textLength);
  1778             if (NS_FAILED(rv))
  1779               return nullptr;
  1781             *aReturnOffset = (int32_t)textLength;
  1783           else
  1784             *aReturnOffset = 0;
  1786         else
  1788           // If we're at a collapsed whitespace content node (which
  1789           // does not have a primary frame), just use the original node
  1790           // to get the frame on which we should put the caret.
  1791           theNode = aNode;
  1797   // If the node is a ShadowRoot, the frame needs to be adjusted,
  1798   // because a ShadowRoot does not get a frame. Its children are rendered
  1799   // as children of the host.
  1800   mozilla::dom::ShadowRoot* shadowRoot =
  1801     mozilla::dom::ShadowRoot::FromNode(theNode);
  1802   if (shadowRoot) {
  1803     theNode = shadowRoot->GetHost();
  1806   nsIFrame* returnFrame = theNode->GetPrimaryFrame();
  1807   if (!returnFrame)
  1808     return nullptr;
  1810   // find the child frame containing the offset we want
  1811   returnFrame->GetChildFrameContainingOffset(*aReturnOffset, aHint == HINTRIGHT,
  1812                                              &aOffset, &returnFrame);
  1813   return returnFrame;
  1816 void
  1817 nsFrameSelection::CommonPageMove(bool aForward,
  1818                                  bool aExtend,
  1819                                  nsIScrollableFrame* aScrollableFrame)
  1821   // expected behavior for PageMove is to scroll AND move the caret
  1822   // and remain relative position of the caret in view. see Bug 4302.
  1824   //get the frame from the scrollable view
  1826   nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
  1827   if (!scrolledFrame)
  1828     return;
  1830   // find out where the caret is.
  1831   // we should know mDesiredX value of nsFrameSelection, but I havent seen that behavior in other windows applications yet.
  1832   nsISelection* domSel = GetSelection(nsISelectionController::SELECTION_NORMAL);
  1833   if (!domSel) 
  1834     return;
  1836   nsRefPtr<nsCaret> caret = mShell->GetCaret();
  1838   nsRect caretPos;
  1839   nsIFrame* caretFrame = caret->GetGeometry(domSel, &caretPos);
  1840   if (!caretFrame) 
  1841     return;
  1843   //need to adjust caret jump by percentage scroll
  1844   nsSize scrollDelta = aScrollableFrame->GetPageScrollAmount();
  1846   if (aForward)
  1847     caretPos.y += scrollDelta.height;
  1848   else
  1849     caretPos.y -= scrollDelta.height;
  1851   caretPos += caretFrame->GetOffsetTo(scrolledFrame);
  1853   // get a content at desired location
  1854   nsPoint desiredPoint;
  1855   desiredPoint.x = caretPos.x;
  1856   desiredPoint.y = caretPos.y + caretPos.height/2;
  1857   nsIFrame::ContentOffsets offsets =
  1858       scrolledFrame->GetContentOffsetsFromPoint(desiredPoint);
  1860   if (!offsets.content)
  1861     return;
  1863   // scroll one page
  1864   aScrollableFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
  1865                              nsIScrollableFrame::PAGES,
  1866                              nsIScrollableFrame::SMOOTH);
  1868   // place the caret
  1869   HandleClick(offsets.content, offsets.offset,
  1870               offsets.offset, aExtend, false, true);
  1873 nsresult
  1874 nsFrameSelection::CharacterMove(bool aForward, bool aExtend)
  1876   if (aForward)
  1877     return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT, aExtend, eSelectCluster);
  1878   else
  1879     return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT, aExtend, eSelectCluster);
  1882 nsresult
  1883 nsFrameSelection::CharacterExtendForDelete()
  1885   return MoveCaret(nsIDOMKeyEvent::DOM_VK_DELETE, true, eSelectCluster);
  1888 nsresult
  1889 nsFrameSelection::CharacterExtendForBackspace()
  1891   return MoveCaret(nsIDOMKeyEvent::DOM_VK_BACK_SPACE, true, eSelectCharacter);
  1894 nsresult
  1895 nsFrameSelection::WordMove(bool aForward, bool aExtend)
  1897   if (aForward)
  1898     return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectWord);
  1899   else
  1900     return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectWord);
  1903 nsresult
  1904 nsFrameSelection::WordExtendForDelete(bool aForward)
  1906   if (aForward)
  1907     return MoveCaret(nsIDOMKeyEvent::DOM_VK_DELETE, true, eSelectWord);
  1908   else
  1909     return MoveCaret(nsIDOMKeyEvent::DOM_VK_BACK_SPACE, true, eSelectWord);
  1912 nsresult
  1913 nsFrameSelection::LineMove(bool aForward, bool aExtend)
  1915   if (aForward)
  1916     return MoveCaret(nsIDOMKeyEvent::DOM_VK_DOWN,aExtend,eSelectLine);
  1917   else
  1918     return MoveCaret(nsIDOMKeyEvent::DOM_VK_UP,aExtend,eSelectLine);
  1921 nsresult
  1922 nsFrameSelection::IntraLineMove(bool aForward, bool aExtend)
  1924   if (aForward)
  1925     return MoveCaret(nsIDOMKeyEvent::DOM_VK_END,aExtend,eSelectLine);
  1926   else
  1927     return MoveCaret(nsIDOMKeyEvent::DOM_VK_HOME,aExtend,eSelectLine);
  1930 nsresult
  1931 nsFrameSelection::SelectAll()
  1933   nsCOMPtr<nsIContent> rootContent;
  1934   if (mLimiter)
  1936     rootContent = mLimiter;//addrefit
  1938   else if (mAncestorLimiter) {
  1939     rootContent = mAncestorLimiter;
  1941   else
  1943     NS_ENSURE_STATE(mShell);
  1944     nsIDocument *doc = mShell->GetDocument();
  1945     if (!doc)
  1946       return NS_ERROR_FAILURE;
  1947     rootContent = doc->GetRootElement();
  1948     if (!rootContent)
  1949       return NS_ERROR_FAILURE;
  1951   int32_t numChildren = rootContent->GetChildCount();
  1952   PostReason(nsISelectionListener::NO_REASON);
  1953   return TakeFocus(rootContent, 0, numChildren, HINTLEFT, false, false);
  1956 //////////END FRAMESELECTION
  1958 void
  1959 nsFrameSelection::StartBatchChanges()
  1961   mBatching++;
  1964 void
  1965 nsFrameSelection::EndBatchChanges()
  1967   mBatching--;
  1968   NS_ASSERTION(mBatching >=0,"Bad mBatching");
  1969   if (mBatching == 0 && mChangesDuringBatching){
  1970     mChangesDuringBatching = false;
  1971     NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
  1976 nsresult
  1977 nsFrameSelection::NotifySelectionListeners(SelectionType aType)
  1979   int8_t index = GetIndexFromSelectionType(aType);
  1980   if (index >=0 && mDomSelections[index])
  1982     return mDomSelections[index]->NotifySelectionListeners();
  1984   return NS_ERROR_FAILURE;
  1987 // Start of Table Selection methods
  1989 static bool IsCell(nsIContent *aContent)
  1991   return ((aContent->Tag() == nsGkAtoms::td ||
  1992            aContent->Tag() == nsGkAtoms::th) &&
  1993           aContent->IsHTML());
  1996 nsITableCellLayout* 
  1997 nsFrameSelection::GetCellLayout(nsIContent *aCellContent) const
  1999   NS_ENSURE_TRUE(mShell, nullptr);
  2000   nsITableCellLayout *cellLayoutObject =
  2001     do_QueryFrame(aCellContent->GetPrimaryFrame());
  2002   return cellLayoutObject;
  2005 nsresult
  2006 nsFrameSelection::ClearNormalSelection()
  2008   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  2009   if (!mDomSelections[index])
  2010     return NS_ERROR_NULL_POINTER;
  2012   return mDomSelections[index]->RemoveAllRanges();
  2015 static nsIContent*
  2016 GetFirstSelectedContent(nsRange* aRange)
  2018   if (!aRange) {
  2019     return nullptr;
  2022   NS_PRECONDITION(aRange->GetStartParent(), "Must have start parent!");
  2023   NS_PRECONDITION(aRange->GetStartParent()->IsElement(),
  2024                   "Unexpected parent");
  2026   return aRange->GetStartParent()->GetChildAt(aRange->StartOffset());
  2029 // Table selection support.
  2030 // TODO: Separate table methods into a separate nsITableSelection interface
  2031 nsresult
  2032 nsFrameSelection::HandleTableSelection(nsINode* aParentContent,
  2033                                        int32_t aContentOffset,
  2034                                        int32_t aTarget,
  2035                                        WidgetMouseEvent* aMouseEvent)
  2037   NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER);
  2038   NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER);
  2040   if (mMouseDownState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE))
  2042     // We were selecting cells and user drags mouse in table border or inbetween cells,
  2043     //  just do nothing
  2044       return NS_OK;
  2047   nsresult result = NS_OK;
  2049   nsIContent *childContent = aParentContent->GetChildAt(aContentOffset);
  2051   // When doing table selection, always set the direction to next so
  2052   // we can be sure that anchorNode's offset always points to the
  2053   // selected cell
  2054   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  2055   if (!mDomSelections[index])
  2056     return NS_ERROR_NULL_POINTER;
  2058   mDomSelections[index]->SetDirection(eDirNext);
  2060   // Stack-class to wrap all table selection changes in 
  2061   //  BeginBatchChanges() / EndBatchChanges()
  2062   nsSelectionBatcher selectionBatcher(mDomSelections[index]);
  2064   int32_t startRowIndex, startColIndex, curRowIndex, curColIndex;
  2065   if (mMouseDownState && mDragSelectingCells)
  2067     // We are drag-selecting
  2068     if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE)
  2070       // If dragging in the same cell as last event, do nothing
  2071       if (mEndSelectedCell == childContent)
  2072         return NS_OK;
  2074 #ifdef DEBUG_TABLE_SELECTION
  2075 printf(" mStartSelectedCell = %x, mEndSelectedCell = %x, childContent = %x \n", mStartSelectedCell, mEndSelectedCell, childContent);
  2076 #endif
  2077       // aTarget can be any "cell mode",
  2078       //  so we can easily drag-select rows and columns 
  2079       // Once we are in row or column mode,
  2080       //  we can drift into any cell to stay in that mode
  2081       //  even if aTarget = TABLESELECTION_CELL
  2083       if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW ||
  2084           mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN)
  2086         if (mEndSelectedCell)
  2088           // Also check if cell is in same row/col
  2089           result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
  2090           if (NS_FAILED(result)) return result;
  2091           result = GetCellIndexes(childContent, curRowIndex, curColIndex);
  2092           if (NS_FAILED(result)) return result;
  2094 #ifdef DEBUG_TABLE_SELECTION
  2095 printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex);
  2096 #endif
  2097           if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
  2098               (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex)) 
  2099             return NS_OK;
  2101 #ifdef DEBUG_TABLE_SELECTION
  2102 printf(" Dragged into a new column or row\n");
  2103 #endif
  2104         // Continue dragging row or column selection
  2105         return SelectRowOrColumn(childContent, mSelectingTableCellMode);
  2107       else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL)
  2109 #ifdef DEBUG_TABLE_SELECTION
  2110 printf("HandleTableSelection: Dragged into a new cell\n");
  2111 #endif
  2112         // Trick for quick selection of rows and columns
  2113         // Hold down shift, then start selecting in one direction
  2114         // If next cell dragged into is in same row, select entire row,
  2115         //   if next cell is in same column, select entire column
  2116         if (mStartSelectedCell && aMouseEvent->IsShift())
  2118           result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
  2119           if (NS_FAILED(result)) return result;
  2120           result = GetCellIndexes(childContent, curRowIndex, curColIndex);
  2121           if (NS_FAILED(result)) return result;
  2123           if (startRowIndex == curRowIndex || 
  2124               startColIndex == curColIndex)
  2126             // Force new selection block
  2127             mStartSelectedCell = nullptr;
  2128             mDomSelections[index]->RemoveAllRanges();
  2130             if (startRowIndex == curRowIndex)
  2131               mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW;
  2132             else
  2133               mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN;
  2135             return SelectRowOrColumn(childContent, mSelectingTableCellMode);
  2139         // Reselect block of cells to new end location
  2140         return SelectBlockOfCells(mStartSelectedCell, childContent);
  2143     // Do nothing if dragging in table, but outside a cell
  2144     return NS_OK;
  2146   else 
  2148     // Not dragging  -- mouse event is down or up
  2149     if (mMouseDownState)
  2151 #ifdef DEBUG_TABLE_SELECTION
  2152 printf("HandleTableSelection: Mouse down event\n");
  2153 #endif
  2154       // Clear cell we stored in mouse-down
  2155       mUnselectCellOnMouseUp = nullptr;
  2157       if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL)
  2159         bool isSelected = false;
  2161         // Check if we have other selected cells
  2162         nsIContent* previousCellNode =
  2163           GetFirstSelectedContent(GetFirstCellRange());
  2164         if (previousCellNode)
  2166           // We have at least 1 other selected cell
  2168           // Check if new cell is already selected
  2169           nsIFrame  *cellFrame = childContent->GetPrimaryFrame();
  2170           if (!cellFrame) return NS_ERROR_NULL_POINTER;
  2171           isSelected = cellFrame->IsSelected();
  2173         else
  2175           // No cells selected -- remove non-cell selection
  2176           mDomSelections[index]->RemoveAllRanges();
  2178         mDragSelectingCells = true;    // Signal to start drag-cell-selection
  2179         mSelectingTableCellMode = aTarget;
  2180         // Set start for new drag-selection block (not appended)
  2181         mStartSelectedCell = childContent;
  2182         // The initial block end is same as the start
  2183         mEndSelectedCell = childContent;
  2185         if (isSelected)
  2187           // Remember this cell to (possibly) unselect it on mouseup
  2188           mUnselectCellOnMouseUp = childContent;
  2189 #ifdef DEBUG_TABLE_SELECTION
  2190 printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
  2191 #endif
  2193         else
  2195           // Select an unselected cell
  2196           // but first remove existing selection if not in same table
  2197           if (previousCellNode &&
  2198               !IsInSameTable(previousCellNode, childContent))
  2200             mDomSelections[index]->RemoveAllRanges();
  2201             // Reset selection mode that is cleared in RemoveAllRanges
  2202             mSelectingTableCellMode = aTarget;
  2205           return SelectCellElement(childContent);
  2208         return NS_OK;
  2210       else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE)
  2212         //TODO: We currently select entire table when clicked between cells,
  2213         //  should we restrict to only around border?
  2214         //  *** How do we get location data for cell and click?
  2215         mDragSelectingCells = false;
  2216         mStartSelectedCell = nullptr;
  2217         mEndSelectedCell = nullptr;
  2219         // Remove existing selection and select the table
  2220         mDomSelections[index]->RemoveAllRanges();
  2221         return CreateAndAddRange(aParentContent, aContentOffset);
  2223       else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
  2225 #ifdef DEBUG_TABLE_SELECTION
  2226 printf("aTarget == %d\n", aTarget);
  2227 #endif
  2229         // Start drag-selecting mode so multiple rows/cols can be selected
  2230         // Note: Currently, nsFrame::GetDataForTableSelection
  2231         //       will never call us for row or column selection on mouse down
  2232         mDragSelectingCells = true;
  2234         // Force new selection block
  2235         mStartSelectedCell = nullptr;
  2236         mDomSelections[index]->RemoveAllRanges();
  2237         // Always do this AFTER RemoveAllRanges
  2238         mSelectingTableCellMode = aTarget;
  2239         return SelectRowOrColumn(childContent, aTarget);
  2242     else
  2244 #ifdef DEBUG_TABLE_SELECTION
  2245 printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%d\n", mDragSelectingCells, mStartSelectedCell);
  2246 #endif
  2247       // First check if we are extending a block selection
  2248       int32_t rangeCount;
  2249       result = mDomSelections[index]->GetRangeCount(&rangeCount);
  2250       if (NS_FAILED(result)) 
  2251         return result;
  2253       if (rangeCount > 0 && aMouseEvent->IsShift() && 
  2254           mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
  2256         // Shift key is down: append a block selection
  2257         mDragSelectingCells = false;
  2258         return SelectBlockOfCells(mAppendStartSelectedCell, childContent);
  2261       if (mDragSelectingCells)
  2262         mAppendStartSelectedCell = mStartSelectedCell;
  2264       mDragSelectingCells = false;
  2265       mStartSelectedCell = nullptr;
  2266       mEndSelectedCell = nullptr;
  2268       // Any other mouseup actions require that Ctrl or Cmd key is pressed
  2269       //  else stop table selection mode
  2270       bool doMouseUpAction = false;
  2271 #ifdef XP_MACOSX
  2272       doMouseUpAction = aMouseEvent->IsMeta();
  2273 #else
  2274       doMouseUpAction = aMouseEvent->IsControl();
  2275 #endif
  2276       if (!doMouseUpAction)
  2278 #ifdef DEBUG_TABLE_SELECTION
  2279 printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%d\n", mAppendStartSelectedCell);
  2280 #endif
  2281         return NS_OK;
  2283       // Unselect a cell only if it wasn't
  2284       //  just selected on mousedown
  2285       if( childContent == mUnselectCellOnMouseUp)
  2287         // Scan ranges to find the cell to unselect (the selection range to remove)
  2288         // XXXbz it's really weird that this lives outside the loop, so once we
  2289         // find one we keep looking at it even if we find no more cells...
  2290         nsINode* previousCellParent = nullptr;
  2291 #ifdef DEBUG_TABLE_SELECTION
  2292 printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
  2293 #endif
  2294         for( int32_t i = 0; i < rangeCount; i++)
  2296           // Strong reference, because sometimes we want to remove
  2297           // this range, and then we might be the only owner.
  2298           nsRefPtr<nsRange> range = mDomSelections[index]->GetRangeAt(i);
  2299           if (!range) return NS_ERROR_NULL_POINTER;
  2301           nsINode* parent = range->GetStartParent();
  2302           if (!parent) return NS_ERROR_NULL_POINTER;
  2304           int32_t offset = range->StartOffset();
  2305           // Be sure previous selection is a table cell
  2306           nsIContent* child = parent->GetChildAt(offset);
  2307           if (child && IsCell(child))
  2308             previousCellParent = parent;
  2310           // We're done if we didn't find parent of a previously-selected cell
  2311           if (!previousCellParent) break;
  2313           if (previousCellParent == aParentContent && offset == aContentOffset)
  2315             // Cell is already selected
  2316             if (rangeCount == 1)
  2318 #ifdef DEBUG_TABLE_SELECTION
  2319 printf("HandleTableSelection: Unselecting single selected cell\n");
  2320 #endif
  2321               // This was the only cell selected.
  2322               // Collapse to "normal" selection inside the cell
  2323               mStartSelectedCell = nullptr;
  2324               mEndSelectedCell = nullptr;
  2325               mAppendStartSelectedCell = nullptr;
  2326               //TODO: We need a "Collapse to just before deepest child" routine
  2327               // Even better, should we collapse to just after the LAST deepest child
  2328               //  (i.e., at the end of the cell's contents)?
  2329               return mDomSelections[index]->Collapse(childContent, 0);
  2331 #ifdef DEBUG_TABLE_SELECTION
  2332 printf("HandleTableSelection: Removing cell from multi-cell selection\n");
  2333 #endif
  2334             // Unselecting the start of previous block 
  2335             // XXX What do we use now!
  2336             if (childContent == mAppendStartSelectedCell)
  2337                mAppendStartSelectedCell = nullptr;
  2339             // Deselect cell by removing its range from selection
  2340             return mDomSelections[index]->RemoveRange(range);
  2343         mUnselectCellOnMouseUp = nullptr;
  2347   return result;
  2350 nsresult
  2351 nsFrameSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell)
  2353   NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER);
  2354   NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER);
  2355   mEndSelectedCell = aEndCell;
  2357   nsCOMPtr<nsIContent> startCell;
  2358   nsresult result = NS_OK;
  2360   // If new end cell is in a different table, do nothing
  2361   nsIContent* table = IsInSameTable(aStartCell, aEndCell);
  2362   if (!table) {
  2363     return NS_OK;
  2366   // Get starting and ending cells' location in the cellmap
  2367   int32_t startRowIndex, startColIndex, endRowIndex, endColIndex;
  2368   result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
  2369   if(NS_FAILED(result)) return result;
  2370   result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
  2371   if(NS_FAILED(result)) return result;
  2373   if (mDragSelectingCells)
  2375     // Drag selecting: remove selected cells outside of new block limits
  2376     UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex,
  2377                   true);
  2380   // Note that we select block in the direction of user's mouse dragging,
  2381   //  which means start cell may be after the end cell in either row or column
  2382   return AddCellsToSelection(table, startRowIndex, startColIndex,
  2383                              endRowIndex, endColIndex);
  2386 nsresult
  2387 nsFrameSelection::UnselectCells(nsIContent *aTableContent,
  2388                                 int32_t aStartRowIndex,
  2389                                 int32_t aStartColumnIndex,
  2390                                 int32_t aEndRowIndex,
  2391                                 int32_t aEndColumnIndex,
  2392                                 bool aRemoveOutsideOfCellRange)
  2394   int8_t index =
  2395     GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  2396   if (!mDomSelections[index])
  2397     return NS_ERROR_NULL_POINTER;
  2399   nsTableOuterFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
  2400   if (!tableFrame)
  2401     return NS_ERROR_FAILURE;
  2403   int32_t minRowIndex = std::min(aStartRowIndex, aEndRowIndex);
  2404   int32_t maxRowIndex = std::max(aStartRowIndex, aEndRowIndex);
  2405   int32_t minColIndex = std::min(aStartColumnIndex, aEndColumnIndex);
  2406   int32_t maxColIndex = std::max(aStartColumnIndex, aEndColumnIndex);
  2408   // Strong reference because we sometimes remove the range
  2409   nsRefPtr<nsRange> range = GetFirstCellRange();
  2410   nsIContent* cellNode = GetFirstSelectedContent(range);
  2411   NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
  2413   int32_t curRowIndex, curColIndex;
  2414   while (cellNode)
  2416     nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
  2417     if (NS_FAILED(result))
  2418       return result;
  2420 #ifdef DEBUG_TABLE_SELECTION
  2421     if (!range)
  2422       printf("RemoveCellsToSelection -- range is null\n");
  2423 #endif
  2425     if (range) {
  2426       if (aRemoveOutsideOfCellRange) {
  2427         if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
  2428             curColIndex < minColIndex || curColIndex > maxColIndex) {
  2430           mDomSelections[index]->RemoveRange(range);
  2431           // Since we've removed the range, decrement pointer to next range
  2432           mSelectedCellIndex--;
  2435       } else {
  2436         // Remove cell from selection if it belongs to the given cells range or
  2437         // it is spanned onto the cells range.
  2438         nsTableCellFrame* cellFrame =
  2439           tableFrame->GetCellFrameAt(curRowIndex, curColIndex);
  2441         int32_t origRowIndex, origColIndex;
  2442         cellFrame->GetRowIndex(origRowIndex);
  2443         cellFrame->GetColIndex(origColIndex);
  2444         uint32_t actualRowSpan =
  2445           tableFrame->GetEffectiveRowSpanAt(origRowIndex, origColIndex);
  2446         uint32_t actualColSpan =
  2447           tableFrame->GetEffectiveColSpanAt(curRowIndex, curColIndex);
  2448         if (origRowIndex <= maxRowIndex && maxRowIndex >= 0 &&
  2449             origRowIndex + actualRowSpan - 1 >= static_cast<uint32_t>(minRowIndex) &&
  2450             origColIndex <= maxColIndex && maxColIndex >= 0 &&
  2451             origColIndex + actualColSpan - 1 >= static_cast<uint32_t>(minColIndex)) {
  2453           mDomSelections[index]->RemoveRange(range);
  2454           // Since we've removed the range, decrement pointer to next range
  2455           mSelectedCellIndex--;
  2460     range = GetNextCellRange();
  2461     cellNode = GetFirstSelectedContent(range);
  2462     NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
  2465   return NS_OK;
  2468 nsresult
  2469 nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
  2470                                       int32_t aStartRowIndex,
  2471                                       int32_t aStartColumnIndex,
  2472                                       int32_t aEndRowIndex,
  2473                                       int32_t aEndColumnIndex)
  2475   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  2476   if (!mDomSelections[index])
  2477     return NS_ERROR_NULL_POINTER;
  2479   nsTableOuterFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
  2480   if (!tableFrame) // Check that |table| is a table.
  2481     return NS_ERROR_FAILURE;
  2483   nsresult result = NS_OK;
  2484   int32_t row = aStartRowIndex;
  2485   while(true)
  2487     int32_t col = aStartColumnIndex;
  2488     while(true)
  2490       nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(row, col);
  2492       // Skip cells that are spanned from previous locations or are already selected
  2493       if (cellFrame) {
  2494         int32_t origRow, origCol;
  2495         cellFrame->GetRowIndex(origRow);
  2496         cellFrame->GetColIndex(origCol);
  2497         if (origRow == row && origCol == col && !cellFrame->IsSelected()) {
  2498           result = SelectCellElement(cellFrame->GetContent());
  2499           if (NS_FAILED(result)) return result;
  2502       // Done when we reach end column
  2503       if (col == aEndColumnIndex) break;
  2505       if (aStartColumnIndex < aEndColumnIndex)
  2506         col ++;
  2507       else
  2508         col--;
  2509     };
  2510     if (row == aEndRowIndex) break;
  2512     if (aStartRowIndex < aEndRowIndex)
  2513       row++;
  2514     else
  2515       row--;
  2516   };
  2517   return result;
  2520 nsresult
  2521 nsFrameSelection::RemoveCellsFromSelection(nsIContent *aTable,
  2522                                            int32_t aStartRowIndex,
  2523                                            int32_t aStartColumnIndex,
  2524                                            int32_t aEndRowIndex,
  2525                                            int32_t aEndColumnIndex)
  2527   return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
  2528                        aEndRowIndex, aEndColumnIndex, false);
  2531 nsresult
  2532 nsFrameSelection::RestrictCellsToSelection(nsIContent *aTable,
  2533                                            int32_t aStartRowIndex,
  2534                                            int32_t aStartColumnIndex,
  2535                                            int32_t aEndRowIndex,
  2536                                            int32_t aEndColumnIndex)
  2538   return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
  2539                        aEndRowIndex, aEndColumnIndex, true);
  2542 nsresult
  2543 nsFrameSelection::SelectRowOrColumn(nsIContent *aCellContent, uint32_t aTarget)
  2545   if (!aCellContent) return NS_ERROR_NULL_POINTER;
  2547   nsIContent* table = GetParentTable(aCellContent);
  2548   if (!table) return NS_ERROR_NULL_POINTER;
  2550   // Get table and cell layout interfaces to access 
  2551   //   cell data based on cellmap location
  2552   // Frames are not ref counted, so don't use an nsCOMPtr
  2553   nsTableOuterFrame* tableFrame = do_QueryFrame(table->GetPrimaryFrame());
  2554   if (!tableFrame) return NS_ERROR_FAILURE;
  2555   nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
  2556   if (!cellLayout) return NS_ERROR_FAILURE;
  2558   // Get location of target cell:      
  2559   int32_t rowIndex, colIndex;
  2560   nsresult result = cellLayout->GetCellIndexes(rowIndex, colIndex);
  2561   if (NS_FAILED(result)) return result;
  2563   // Be sure we start at proper beginning
  2564   // (This allows us to select row or col given ANY cell!)
  2565   if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
  2566     colIndex = 0;
  2567   if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
  2568     rowIndex = 0;
  2570   nsCOMPtr<nsIContent> firstCell, lastCell;
  2571   while (true) {
  2572     // Loop through all cells in column or row to find first and last
  2573     nsCOMPtr<nsIContent> curCellContent =
  2574       tableFrame->GetCellAt(rowIndex, colIndex);
  2575     if (!curCellContent)
  2576       break;
  2578     if (!firstCell)
  2579       firstCell = curCellContent;
  2581     lastCell = curCellContent.forget();
  2583     // Move to next cell in cellmap, skipping spanned locations
  2584     if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
  2585       colIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
  2586     else
  2587       rowIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
  2590   // Use SelectBlockOfCells:
  2591   // This will replace existing selection,
  2592   //  but allow unselecting by dragging out of selected region
  2593   if (firstCell && lastCell)
  2595     if (!mStartSelectedCell)
  2597       // We are starting a new block, so select the first cell
  2598       result = SelectCellElement(firstCell);
  2599       if (NS_FAILED(result)) return result;
  2600       mStartSelectedCell = firstCell;
  2602     nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
  2603     result = SelectBlockOfCells(mStartSelectedCell, lastCellContent);
  2605     // This gets set to the cell at end of row/col, 
  2606     //   but we need it to be the cell under cursor
  2607     mEndSelectedCell = aCellContent;
  2608     return result;
  2611 #if 0
  2612 // This is a more efficient strategy that appends row to current selection,
  2613 //  but doesn't allow dragging OFF of an existing selection to unselect!
  2614   do {
  2615     // Loop through all cells in column or row
  2616     result = tableLayout->GetCellDataAt(rowIndex, colIndex,
  2617                                         getter_AddRefs(cellElement),
  2618                                         curRowIndex, curColIndex,
  2619                                         rowSpan, colSpan,
  2620                                         actualRowSpan, actualColSpan,
  2621                                         isSelected);
  2622     if (NS_FAILED(result)) return result;
  2623     // We're done when cell is not found
  2624     if (!cellElement) break;
  2627     // Check spans else we infinitely loop
  2628     NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
  2629     NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
  2631     // Skip cells that are already selected or span from outside our region
  2632     if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
  2634       result = SelectCellElement(cellElement);
  2635       if (NS_FAILED(result)) return result;
  2637     // Move to next row or column in cellmap, skipping spanned locations
  2638     if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
  2639       colIndex += actualColSpan;
  2640     else
  2641       rowIndex += actualRowSpan;
  2643   while (cellElement);
  2644 #endif
  2646   return NS_OK;
  2649 nsIContent*
  2650 nsFrameSelection::GetFirstCellNodeInRange(nsRange *aRange) const
  2652   if (!aRange) return nullptr;
  2654   nsINode* startParent = aRange->GetStartParent();
  2655   if (!startParent)
  2656     return nullptr;
  2658   int32_t offset = aRange->StartOffset();
  2660   nsIContent* childContent = startParent->GetChildAt(offset);
  2661   if (!childContent)
  2662     return nullptr;
  2663   // Don't return node if not a cell
  2664   if (!IsCell(childContent))
  2665     return nullptr;
  2667   return childContent;
  2670 nsRange*
  2671 nsFrameSelection::GetFirstCellRange()
  2673   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  2674   if (!mDomSelections[index])
  2675     return nullptr;
  2677   nsRange* firstRange = mDomSelections[index]->GetRangeAt(0);
  2678   if (!GetFirstCellNodeInRange(firstRange)) {
  2679     return nullptr;
  2682   // Setup for next cell
  2683   mSelectedCellIndex = 1;
  2685   return firstRange;
  2688 nsRange*
  2689 nsFrameSelection::GetNextCellRange()
  2691   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  2692   if (!mDomSelections[index])
  2693     return nullptr;
  2695   nsRange* range = mDomSelections[index]->GetRangeAt(mSelectedCellIndex);
  2697   // Get first node in next range of selection - test if it's a cell
  2698   if (!GetFirstCellNodeInRange(range)) {
  2699     return nullptr;
  2702   // Setup for next cell
  2703   mSelectedCellIndex++;
  2705   return range;
  2708 nsresult
  2709 nsFrameSelection::GetCellIndexes(nsIContent *aCell,
  2710                                  int32_t    &aRowIndex,
  2711                                  int32_t    &aColIndex)
  2713   if (!aCell) return NS_ERROR_NULL_POINTER;
  2715   aColIndex=0; // initialize out params
  2716   aRowIndex=0;
  2718   nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
  2719   if (!cellLayoutObject)  return NS_ERROR_FAILURE;
  2720   return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
  2723 nsIContent*
  2724 nsFrameSelection::IsInSameTable(nsIContent  *aContent1,
  2725                                 nsIContent  *aContent2) const
  2727   if (!aContent1 || !aContent2) return nullptr;
  2729   nsIContent* tableNode1 = GetParentTable(aContent1);
  2730   nsIContent* tableNode2 = GetParentTable(aContent2);
  2732   // Must be in the same table.  Note that we want to return false for
  2733   // the test if both tables are null.
  2734   return (tableNode1 == tableNode2) ? tableNode1 : nullptr;
  2737 nsIContent*
  2738 nsFrameSelection::GetParentTable(nsIContent *aCell) const
  2740   if (!aCell) {
  2741     return nullptr;
  2744   for (nsIContent* parent = aCell->GetParent(); parent;
  2745        parent = parent->GetParent()) {
  2746     if (parent->Tag() == nsGkAtoms::table &&
  2747         parent->IsHTML()) {
  2748       return parent;
  2752   return nullptr;
  2755 nsresult
  2756 nsFrameSelection::SelectCellElement(nsIContent *aCellElement)
  2758   nsIContent *parent = aCellElement->GetParent();
  2760   // Get child offset
  2761   int32_t offset = parent->IndexOf(aCellElement);
  2763   return CreateAndAddRange(parent, offset);
  2766 nsresult
  2767 Selection::getTableCellLocationFromRange(nsRange* aRange,
  2768                                          int32_t* aSelectionType,
  2769                                          int32_t* aRow, int32_t* aCol)
  2771   if (!aRange || !aSelectionType || !aRow || !aCol)
  2772     return NS_ERROR_NULL_POINTER;
  2774   *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
  2775   *aRow = 0;
  2776   *aCol = 0;
  2778   // Must have access to frame selection to get cell info
  2779   if (!mFrameSelection) return NS_OK;
  2781   nsresult result = GetTableSelectionType(aRange, aSelectionType);
  2782   if (NS_FAILED(result)) return result;
  2784   // Don't fail if range does not point to a single table cell,
  2785   //  let aSelectionType tell user if we don't have a cell
  2786   if (*aSelectionType  != nsISelectionPrivate::TABLESELECTION_CELL)
  2787     return NS_OK;
  2789   // Get the child content (the cell) pointed to by starting node of range
  2790   // We do minimal checking since GetTableSelectionType assures
  2791   //   us that this really is a table cell
  2792   nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
  2793   if (!content)
  2794     return NS_ERROR_FAILURE;
  2796   nsIContent *child = content->GetChildAt(aRange->StartOffset());
  2797   if (!child)
  2798     return NS_ERROR_FAILURE;
  2800   //Note: This is a non-ref-counted pointer to the frame
  2801   nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
  2802   if (NS_FAILED(result))
  2803     return result;
  2804   if (!cellLayout)
  2805     return NS_ERROR_FAILURE;
  2807   return cellLayout->GetCellIndexes(*aRow, *aCol);
  2810 nsresult
  2811 Selection::addTableCellRange(nsRange* aRange, bool* aDidAddRange,
  2812                              int32_t* aOutIndex)
  2814   if (!aDidAddRange || !aOutIndex)
  2815     return NS_ERROR_NULL_POINTER;
  2817   *aDidAddRange = false;
  2818   *aOutIndex = -1;
  2820   if (!mFrameSelection)
  2821     return NS_OK;
  2823   if (!aRange)
  2824     return NS_ERROR_NULL_POINTER;
  2826   nsresult result;
  2828   // Get if we are adding a cell selection and the row, col of cell if we are
  2829   int32_t newRow, newCol, tableMode;
  2830   result = getTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
  2831   if (NS_FAILED(result)) return result;
  2833   // If not adding a cell range, we are done here
  2834   if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
  2836     mFrameSelection->mSelectingTableCellMode = tableMode;
  2837     // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
  2838     return NS_OK;
  2841   // Set frame selection mode only if not already set to a table mode
  2842   //  so we don't lose the select row and column flags (not detected by getTableCellLocation)
  2843   if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE)
  2844     mFrameSelection->mSelectingTableCellMode = tableMode;
  2846   *aDidAddRange = true;
  2847   return AddItem(aRange, aOutIndex);
  2850 //TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
  2851 nsresult
  2852 Selection::GetTableSelectionType(nsIDOMRange* aDOMRange,
  2853                                  int32_t* aTableSelectionType)
  2855   if (!aDOMRange || !aTableSelectionType)
  2856     return NS_ERROR_NULL_POINTER;
  2857   nsRange* range = static_cast<nsRange*>(aDOMRange);
  2859   *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
  2861   // Must have access to frame selection to get cell info
  2862   if(!mFrameSelection) return NS_OK;
  2864   nsINode* startNode = range->GetStartParent();
  2865   if (!startNode) return NS_ERROR_FAILURE;
  2867   nsINode* endNode = range->GetEndParent();
  2868   if (!endNode) return NS_ERROR_FAILURE;
  2870   // Not a single selected node
  2871   if (startNode != endNode) return NS_OK;
  2873   int32_t startOffset = range->StartOffset();
  2874   int32_t endOffset = range->EndOffset();
  2876   // Not a single selected node
  2877   if ((endOffset - startOffset) != 1)
  2878     return NS_OK;
  2880   nsIContent* startContent = static_cast<nsIContent*>(startNode);
  2881   if (!(startNode->IsElement() && startContent->IsHTML())) {
  2882     // Implies a check for being an element; if we ever make this work
  2883     // for non-HTML, need to keep checking for elements.
  2884     return NS_OK;
  2887   nsIAtom *tag = startContent->Tag();
  2889   if (tag == nsGkAtoms::tr)
  2891     *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
  2893   else //check to see if we are selecting a table or row (column and all cells not done yet)
  2895     nsIContent *child = startNode->GetChildAt(startOffset);
  2896     if (!child)
  2897       return NS_ERROR_FAILURE;
  2899     tag = child->Tag();
  2901     if (tag == nsGkAtoms::table)
  2902       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
  2903     else if (tag == nsGkAtoms::tr)
  2904       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
  2907   return NS_OK;
  2910 nsresult
  2911 nsFrameSelection::CreateAndAddRange(nsINode *aParentNode, int32_t aOffset)
  2913   if (!aParentNode) return NS_ERROR_NULL_POINTER;
  2915   nsRefPtr<nsRange> range = new nsRange(aParentNode);
  2917   // Set range around child at given offset
  2918   nsresult result = range->SetStart(aParentNode, aOffset);
  2919   if (NS_FAILED(result)) return result;
  2920   result = range->SetEnd(aParentNode, aOffset+1);
  2921   if (NS_FAILED(result)) return result;
  2923   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  2924   if (!mDomSelections[index])
  2925     return NS_ERROR_NULL_POINTER;
  2927   return mDomSelections[index]->AddRange(range);
  2930 // End of Table Selection
  2932 void
  2933 nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter)
  2935   if (mAncestorLimiter != aLimiter) {
  2936     mAncestorLimiter = aLimiter;
  2937     int8_t index =
  2938       GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  2939     if (!mDomSelections[index])
  2940       return;
  2942     if (!IsValidSelectionPoint(this, mDomSelections[index]->GetFocusNode())) {
  2943       ClearNormalSelection();
  2944       if (mAncestorLimiter) {
  2945         PostReason(nsISelectionListener::NO_REASON);
  2946         TakeFocus(mAncestorLimiter, 0, 0, HINTLEFT, false, false);
  2952 //END nsFrameSelection methods
  2955 //BEGIN nsISelection interface implementations
  2959 nsresult
  2960 nsFrameSelection::DeleteFromDocument()
  2962   nsresult res;
  2964   // If we're already collapsed, then we do nothing (bug 719503).
  2965   bool isCollapsed;
  2966   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
  2967   if (!mDomSelections[index])
  2968     return NS_ERROR_NULL_POINTER;
  2970   mDomSelections[index]->GetIsCollapsed( &isCollapsed);
  2971   if (isCollapsed)
  2973     return NS_OK;
  2976   nsRefPtr<Selection> selection = mDomSelections[index];
  2977   for (int32_t rangeIdx = 0; rangeIdx < selection->GetRangeCount(); ++rangeIdx) {
  2978     nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
  2979     res = range->DeleteContents();
  2980     if (NS_FAILED(res))
  2981       return res;
  2984   // Collapse to the new location.
  2985   // If we deleted one character, then we move back one element.
  2986   // FIXME  We don't know how to do this past frame boundaries yet.
  2987   if (isCollapsed)
  2988     mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset()-1);
  2989   else if (mDomSelections[index]->AnchorOffset() > 0)
  2990     mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset());
  2991 #ifdef DEBUG
  2992   else
  2993     printf("Don't know how to set selection back past frame boundary\n");
  2994 #endif
  2996   return NS_OK;
  2999 void
  3000 nsFrameSelection::SetDelayedCaretData(WidgetMouseEvent* aMouseEvent)
  3002   if (aMouseEvent) {
  3003     mDelayedMouseEventValid = true;
  3004     mDelayedMouseEventIsShift = aMouseEvent->IsShift();
  3005     mDelayedMouseEventClickCount = aMouseEvent->clickCount;
  3006   } else {
  3007     mDelayedMouseEventValid = false;
  3011 void
  3012 nsFrameSelection::DisconnectFromPresShell()
  3014   StopAutoScrollTimer();
  3015   for (int32_t i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; i++) {
  3016     mDomSelections[i]->Clear(nullptr);
  3018   mShell = nullptr;
  3021 //END nsISelection interface implementations
  3023 #if 0
  3024 #pragma mark -
  3025 #endif
  3027 // mozilla::dom::Selection implementation
  3029 // note: this can return a nil anchor node
  3031 Selection::Selection()
  3032   : mCachedOffsetForFrame(nullptr)
  3033   , mDirection(eDirNext)
  3034   , mType(nsISelectionController::SELECTION_NORMAL)
  3036   SetIsDOMBinding();
  3039 Selection::Selection(nsFrameSelection* aList)
  3040   : mFrameSelection(aList)
  3041   , mCachedOffsetForFrame(nullptr)
  3042   , mDirection(eDirNext)
  3043   , mType(nsISelectionController::SELECTION_NORMAL)
  3045   SetIsDOMBinding();
  3048 Selection::~Selection()
  3050   setAnchorFocusRange(-1);
  3052   uint32_t count = mRanges.Length();
  3053   for (uint32_t i = 0; i < count; ++i) {
  3054     mRanges[i].mRange->SetInSelection(false);
  3057   if (mAutoScrollTimer) {
  3058     mAutoScrollTimer->Stop();
  3059     mAutoScrollTimer = nullptr;
  3062   mScrollEvent.Revoke();
  3064   if (mCachedOffsetForFrame) {
  3065     delete mCachedOffsetForFrame;
  3066     mCachedOffsetForFrame = nullptr;
  3070 nsIDocument*
  3071 Selection::GetParentObject() const
  3073   nsIPresShell* shell = GetPresShell();
  3074   if (shell) {
  3075     return shell->GetDocument();
  3077   return nullptr;
  3080 NS_IMPL_CYCLE_COLLECTION_CLASS(Selection)
  3082 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection)
  3083   // Unlink the selection listeners *before* we do RemoveAllRanges since
  3084   // we don't want to notify the listeners during JS GC (they could be
  3085   // in JS!).
  3086   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners)
  3087   tmp->RemoveAllRanges();
  3088   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameSelection)
  3089   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
  3090 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  3091 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Selection)
  3093     uint32_t i, count = tmp->mRanges.Length();
  3094     for (i = 0; i < count; ++i) {
  3095       NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRanges[i].mRange)
  3098   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorFocusRange)
  3099   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameSelection)
  3100   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListeners)
  3101   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
  3102 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  3103 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Selection)
  3105 DOMCI_DATA(Selection, Selection)
  3107 // QueryInterface implementation for Selection
  3108 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Selection)
  3109   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  3110   NS_INTERFACE_MAP_ENTRY(nsISelection)
  3111   NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
  3112   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  3113   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
  3114 NS_INTERFACE_MAP_END
  3116 NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection)
  3117 NS_IMPL_CYCLE_COLLECTING_RELEASE(Selection)
  3120 NS_IMETHODIMP
  3121 Selection::GetAnchorNode(nsIDOMNode** aAnchorNode)
  3123   nsINode* anchorNode = GetAnchorNode();
  3124   if (anchorNode) {
  3125     return CallQueryInterface(anchorNode, aAnchorNode);
  3128   *aAnchorNode = nullptr;
  3129   return NS_OK;
  3132 nsINode*
  3133 Selection::GetAnchorNode()
  3135   if (!mAnchorFocusRange)
  3136     return nullptr;
  3138   if (GetDirection() == eDirNext) {
  3139     return mAnchorFocusRange->GetStartParent();
  3142   return mAnchorFocusRange->GetEndParent();
  3145 NS_IMETHODIMP
  3146 Selection::GetAnchorOffset(int32_t* aAnchorOffset)
  3148   *aAnchorOffset = static_cast<int32_t>(AnchorOffset());
  3149   return NS_OK;
  3152 // note: this can return a nil focus node
  3153 NS_IMETHODIMP
  3154 Selection::GetFocusNode(nsIDOMNode** aFocusNode)
  3156   nsINode* focusNode = GetFocusNode();
  3157   if (focusNode) {
  3158     return CallQueryInterface(focusNode, aFocusNode);
  3161   *aFocusNode = nullptr;
  3162   return NS_OK;
  3165 nsINode*
  3166 Selection::GetFocusNode()
  3168   if (!mAnchorFocusRange)
  3169     return nullptr;
  3171   if (GetDirection() == eDirNext){
  3172     return mAnchorFocusRange->GetEndParent();
  3175   return mAnchorFocusRange->GetStartParent();
  3178 NS_IMETHODIMP
  3179 Selection::GetFocusOffset(int32_t* aFocusOffset)
  3181   *aFocusOffset = static_cast<int32_t>(FocusOffset());
  3182   return NS_OK;
  3185 void
  3186 Selection::setAnchorFocusRange(int32_t indx)
  3188   if (indx >= (int32_t)mRanges.Length())
  3189     return;
  3190   if (indx < 0) //release all
  3192     mAnchorFocusRange = nullptr;
  3194   else{
  3195     mAnchorFocusRange = mRanges[indx].mRange;
  3199 uint32_t
  3200 Selection::AnchorOffset()
  3202   if (!mAnchorFocusRange)
  3203     return 0;
  3205   if (GetDirection() == eDirNext){
  3206     return mAnchorFocusRange->StartOffset();
  3209   return mAnchorFocusRange->EndOffset();
  3212 uint32_t
  3213 Selection::FocusOffset()
  3215   if (!mAnchorFocusRange)
  3216     return 0;
  3218   if (GetDirection() == eDirNext){
  3219     return mAnchorFocusRange->EndOffset();
  3222   return mAnchorFocusRange->StartOffset();
  3225 static nsresult
  3226 CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
  3227                     nsRange* aRange, int32_t* aCmp)
  3229   nsINode* start = aRange->GetStartParent();
  3230   NS_ENSURE_STATE(aCompareNode && start);
  3231   // If the nodes that we're comparing are not in the same document,
  3232   // assume that aCompareNode will fall at the end of the ranges.
  3233   if (aCompareNode->GetCurrentDoc() != start->GetCurrentDoc() ||
  3234       !start->GetCurrentDoc()) {
  3235     *aCmp = 1;
  3236   } else {
  3237     *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
  3238                                           start, aRange->StartOffset());
  3240   return NS_OK;
  3243 static nsresult
  3244 CompareToRangeEnd(nsINode* aCompareNode, int32_t aCompareOffset,
  3245                   nsRange* aRange, int32_t* aCmp)
  3247   nsINode* end = aRange->GetEndParent();
  3248   NS_ENSURE_STATE(aCompareNode && end);
  3249   // If the nodes that we're comparing are not in the same document,
  3250   // assume that aCompareNode will fall at the end of the ranges.
  3251   if (aCompareNode->GetCurrentDoc() != end->GetCurrentDoc() ||
  3252       !end->GetCurrentDoc()) {
  3253     *aCmp = 1;
  3254   } else {
  3255     *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
  3256                                           end, aRange->EndOffset());
  3258   return NS_OK;
  3261 // Selection::FindInsertionPoint
  3262 //
  3263 //    Binary searches the given sorted array of ranges for the insertion point
  3264 //    for the given node/offset. The given comparator is used, and the index
  3265 //    where the point should appear in the array is placed in *aInsertionPoint.
  3266 //
  3267 //    If there is an item in the array equal to the input point, we will return
  3268 //    the index of this item.
  3270 nsresult
  3271 Selection::FindInsertionPoint(
  3272     nsTArray<RangeData>* aElementArray,
  3273     nsINode* aPointNode, int32_t aPointOffset,
  3274     nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*),
  3275     int32_t* aPoint)
  3277   *aPoint = 0;
  3278   int32_t beginSearch = 0;
  3279   int32_t endSearch = aElementArray->Length(); // one beyond what to check
  3281   if (endSearch) {
  3282     int32_t center = endSearch - 1; // Check last index, then binary search
  3283     do {
  3284       nsRange* range = (*aElementArray)[center].mRange;
  3286       int32_t cmp;
  3287       nsresult rv = aComparator(aPointNode, aPointOffset, range, &cmp);
  3288       NS_ENSURE_SUCCESS(rv, rv);
  3290       if (cmp < 0) {        // point < cur
  3291         endSearch = center;
  3292       } else if (cmp > 0) { // point > cur
  3293         beginSearch = center + 1;
  3294       } else {              // found match, done
  3295         beginSearch = center;
  3296         break;
  3298       center = (endSearch - beginSearch) / 2 + beginSearch;
  3299     } while (endSearch - beginSearch > 0);
  3302   *aPoint = beginSearch;
  3303   return NS_OK;
  3306 // Selection::SubtractRange
  3307 //
  3308 //    A helper function that subtracts aSubtract from aRange, and adds
  3309 //    1 or 2 RangeData objects representing the remaining non-overlapping
  3310 //    difference to aOutput. It is assumed that the caller has checked that
  3311 //    aRange and aSubtract do indeed overlap
  3313 nsresult
  3314 Selection::SubtractRange(RangeData* aRange, nsRange* aSubtract,
  3315                          nsTArray<RangeData>* aOutput)
  3317   nsRange* range = aRange->mRange;
  3319   // First we want to compare to the range start
  3320   int32_t cmp;
  3321   nsresult rv = CompareToRangeStart(range->GetStartParent(),
  3322                                     range->StartOffset(),
  3323                                     aSubtract, &cmp);
  3324   NS_ENSURE_SUCCESS(rv, rv);
  3326   // Also, make a comparison to the range end
  3327   int32_t cmp2;
  3328   rv = CompareToRangeEnd(range->GetEndParent(),
  3329                          range->EndOffset(),
  3330                          aSubtract, &cmp2);
  3331   NS_ENSURE_SUCCESS(rv, rv);
  3333   // If the existing range left overlaps the new range (aSubtract) then
  3334   // cmp < 0, and cmp2 < 0
  3335   // If it right overlaps the new range then cmp > 0 and cmp2 > 0
  3336   // If it fully contains the new range, then cmp < 0 and cmp2 > 0
  3338   if (cmp2 > 0) {
  3339     // We need to add a new RangeData to the output, running from
  3340     // the end of aSubtract to the end of range
  3341     nsRefPtr<nsRange> postOverlap = new nsRange(aSubtract->GetEndParent());
  3343     rv =
  3344       postOverlap->SetStart(aSubtract->GetEndParent(), aSubtract->EndOffset());
  3345     NS_ENSURE_SUCCESS(rv, rv);
  3346     rv =
  3347      postOverlap->SetEnd(range->GetEndParent(), range->EndOffset());
  3348     NS_ENSURE_SUCCESS(rv, rv);
  3349     if (!postOverlap->Collapsed()) {
  3350       if (!aOutput->InsertElementAt(0, RangeData(postOverlap)))
  3351         return NS_ERROR_OUT_OF_MEMORY;
  3352       (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
  3356   if (cmp < 0) {
  3357     // We need to add a new RangeData to the output, running from
  3358     // the start of the range to the start of aSubtract
  3359     nsRefPtr<nsRange> preOverlap = new nsRange(range->GetStartParent());
  3361     nsresult rv =
  3362      preOverlap->SetStart(range->GetStartParent(), range->StartOffset());
  3363     NS_ENSURE_SUCCESS(rv, rv);
  3364     rv =
  3365      preOverlap->SetEnd(aSubtract->GetStartParent(), aSubtract->StartOffset());
  3366     NS_ENSURE_SUCCESS(rv, rv);
  3368     if (!preOverlap->Collapsed()) {
  3369       if (!aOutput->InsertElementAt(0, RangeData(preOverlap)))
  3370         return NS_ERROR_OUT_OF_MEMORY;
  3371       (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
  3375   return NS_OK;
  3378 nsresult
  3379 Selection::AddItem(nsRange* aItem, int32_t* aOutIndex)
  3381   if (!aItem)
  3382     return NS_ERROR_NULL_POINTER;
  3383   if (!aItem->IsPositioned())
  3384     return NS_ERROR_UNEXPECTED;
  3386   NS_ASSERTION(aOutIndex, "aOutIndex can't be null");
  3388   *aOutIndex = -1;
  3390   // a common case is that we have no ranges yet
  3391   if (mRanges.Length() == 0) {
  3392     if (!mRanges.AppendElement(RangeData(aItem)))
  3393       return NS_ERROR_OUT_OF_MEMORY;
  3394     aItem->SetInSelection(true);
  3396     *aOutIndex = 0;
  3397     return NS_OK;
  3400   int32_t startIndex, endIndex;
  3401   nsresult rv = GetIndicesForInterval(aItem->GetStartParent(),
  3402                                       aItem->StartOffset(),
  3403                                       aItem->GetEndParent(),
  3404                                       aItem->EndOffset(), false,
  3405                                       &startIndex, &endIndex);
  3406   NS_ENSURE_SUCCESS(rv, rv);
  3408   if (endIndex == -1) {
  3409     // All ranges start after the given range. We can insert our range at
  3410     // position 0, knowing there are no overlaps (handled below)
  3411     startIndex = 0;
  3412     endIndex = 0;
  3413   } else if (startIndex == -1) {
  3414     // All ranges end before the given range. We can insert our range at
  3415     // the end of the array, knowing there are no overlaps (handled below)
  3416     startIndex = mRanges.Length();
  3417     endIndex = startIndex;
  3420   // If the range is already contained in mRanges, silently succeed
  3421   bool sameRange = EqualsRangeAtPoint(aItem->GetStartParent(),
  3422                                         aItem->StartOffset(),
  3423                                         aItem->GetEndParent(),
  3424                                         aItem->EndOffset(), startIndex);
  3425   if (sameRange) {
  3426     *aOutIndex = startIndex;
  3427     return NS_OK;
  3430   if (startIndex == endIndex) {
  3431     // The new range doesn't overlap any existing ranges
  3432     if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
  3433       return NS_ERROR_OUT_OF_MEMORY;
  3434     aItem->SetInSelection(true);
  3435     *aOutIndex = startIndex;
  3436     return NS_OK;
  3439   // We now know that at least 1 existing range overlaps with the range that
  3440   // we are trying to add. In fact, the only ranges of interest are those at
  3441   // the two end points, startIndex and endIndex - 1 (which may point to the
  3442   // same range) as these may partially overlap the new range. Any ranges
  3443   // between these indices are fully overlapped by the new range, and so can be
  3444   // removed
  3445   nsTArray<RangeData> overlaps;
  3446   if (!overlaps.InsertElementAt(0, mRanges[startIndex]))
  3447     return NS_ERROR_OUT_OF_MEMORY;
  3449   if (endIndex - 1 != startIndex) {
  3450     if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
  3451       return NS_ERROR_OUT_OF_MEMORY;
  3454   // Remove all the overlapping ranges
  3455   for (int32_t i = startIndex; i < endIndex; ++i) {
  3456     mRanges[i].mRange->SetInSelection(false);
  3458   mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
  3460   nsTArray<RangeData> temp;
  3461   for (int32_t i = overlaps.Length() - 1; i >= 0; i--) {
  3462     nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
  3463     NS_ENSURE_SUCCESS(rv, rv);
  3466   // Insert the new element into our "leftovers" array
  3467   int32_t insertionPoint;
  3468   rv = FindInsertionPoint(&temp, aItem->GetStartParent(),
  3469                           aItem->StartOffset(), CompareToRangeStart,
  3470                           &insertionPoint);
  3471   NS_ENSURE_SUCCESS(rv, rv);
  3473   if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
  3474     return NS_ERROR_OUT_OF_MEMORY;
  3476   // Merge the leftovers back in to mRanges
  3477   if (!mRanges.InsertElementsAt(startIndex, temp))
  3478     return NS_ERROR_OUT_OF_MEMORY;
  3480   for (uint32_t i = 0; i < temp.Length(); ++i) {
  3481     temp[i].mRange->SetInSelection(true);
  3484   *aOutIndex = startIndex + insertionPoint;
  3485   return NS_OK;
  3488 nsresult
  3489 Selection::RemoveItem(nsRange* aItem)
  3491   if (!aItem)
  3492     return NS_ERROR_NULL_POINTER;
  3494   // Find the range's index & remove it. We could use FindInsertionPoint to
  3495   // get O(log n) time, but that requires many expensive DOM comparisons.
  3496   // For even several thousand items, this is probably faster because the
  3497   // comparisons are so fast.
  3498   int32_t idx = -1;
  3499   uint32_t i;
  3500   for (i = 0; i < mRanges.Length(); i ++) {
  3501     if (mRanges[i].mRange == aItem) {
  3502       idx = (int32_t)i;
  3503       break;
  3506   if (idx < 0)
  3507     return NS_ERROR_INVALID_ARG;
  3509   mRanges.RemoveElementAt(idx);
  3510   aItem->SetInSelection(false);
  3511   return NS_OK;
  3514 nsresult
  3515 Selection::RemoveCollapsedRanges()
  3517   uint32_t i = 0;
  3518   while (i < mRanges.Length()) {
  3519     if (mRanges[i].mRange->Collapsed()) {
  3520       nsresult rv = RemoveItem(mRanges[i].mRange);
  3521       NS_ENSURE_SUCCESS(rv, rv);
  3522     } else {
  3523       ++i;
  3526   return NS_OK;
  3529 nsresult
  3530 Selection::Clear(nsPresContext* aPresContext)
  3532   setAnchorFocusRange(-1);
  3534   for (uint32_t i = 0; i < mRanges.Length(); ++i) {
  3535     mRanges[i].mRange->SetInSelection(false);
  3536     selectFrames(aPresContext, mRanges[i].mRange, false);
  3538   mRanges.Clear();
  3540   // Reset direction so for more dependable table selection range handling
  3541   SetDirection(eDirNext);
  3543   // If this was an ATTENTION selection, change it back to normal now
  3544   if (mFrameSelection &&
  3545       mFrameSelection->GetDisplaySelection() ==
  3546       nsISelectionController::SELECTION_ATTENTION) {
  3547     mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
  3550   return NS_OK;
  3553 NS_IMETHODIMP
  3554 Selection::GetType(int16_t* aType)
  3556   NS_ENSURE_ARG_POINTER(aType);
  3557   *aType = Type();
  3559   return NS_OK;
  3562 // RangeMatches*Point
  3563 //
  3564 //    Compares the range beginning or ending point, and returns true if it
  3565 //    exactly matches the given DOM point.
  3567 static inline bool
  3568 RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
  3570   return aRange->GetStartParent() == aNode && aRange->StartOffset() == aOffset;
  3573 static inline bool
  3574 RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
  3576   return aRange->GetEndParent() == aNode && aRange->EndOffset() == aOffset;
  3579 // Selection::EqualsRangeAtPoint
  3580 //
  3581 //    Utility method for checking equivalence of two ranges.
  3583 bool
  3584 Selection::EqualsRangeAtPoint(
  3585     nsINode* aBeginNode, int32_t aBeginOffset,
  3586     nsINode* aEndNode, int32_t aEndOffset,
  3587     int32_t aRangeIndex)
  3589   if (aRangeIndex >=0 && aRangeIndex < (int32_t) mRanges.Length()) {
  3590     nsRange* range = mRanges[aRangeIndex].mRange;
  3591     if (RangeMatchesBeginPoint(range, aBeginNode, aBeginOffset) &&
  3592         RangeMatchesEndPoint(range, aEndNode, aEndOffset))
  3593       return true;
  3595   return false;
  3598 // Selection::GetRangesForInterval
  3599 //
  3600 //    XPCOM wrapper for the nsTArray version
  3602 NS_IMETHODIMP
  3603 Selection::GetRangesForInterval(nsIDOMNode* aBeginNode, int32_t aBeginOffset,
  3604                                 nsIDOMNode* aEndNode, int32_t aEndOffset,
  3605                                 bool aAllowAdjacent,
  3606                                 uint32_t* aResultCount,
  3607                                 nsIDOMRange*** aResults)
  3609   if (!aBeginNode || ! aEndNode || ! aResultCount || ! aResults)
  3610     return NS_ERROR_NULL_POINTER;
  3612   *aResultCount = 0;
  3613   *aResults = nullptr;
  3615   nsTArray<nsRefPtr<nsRange>> results;
  3616   ErrorResult result;
  3617   nsCOMPtr<nsINode> beginNode = do_QueryInterface(aBeginNode);
  3618   nsCOMPtr<nsINode> endNode = do_QueryInterface(aEndNode);
  3619   NS_ENSURE_TRUE(beginNode && endNode, NS_ERROR_NULL_POINTER);
  3620   GetRangesForInterval(*beginNode, aBeginOffset, *endNode, aEndOffset,
  3621                        aAllowAdjacent, results, result);
  3622   if (result.Failed()) {
  3623     return result.ErrorCode();
  3625   *aResultCount = results.Length();
  3626   if (*aResultCount == 0) {
  3627     return NS_OK;
  3630   *aResults = static_cast<nsIDOMRange**>
  3631                          (nsMemory::Alloc(sizeof(nsIDOMRange*) * *aResultCount));
  3632   NS_ENSURE_TRUE(*aResults, NS_ERROR_OUT_OF_MEMORY);
  3634   for (uint32_t i = 0; i < *aResultCount; i++) {
  3635     (*aResults)[i] = results[i].forget().take();
  3637   return NS_OK;
  3641 void
  3642 Selection::GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset,
  3643                                 nsINode& aEndNode, int32_t aEndOffset,
  3644                                 bool aAllowAdjacent,
  3645                                 nsTArray<nsRefPtr<nsRange>>& aReturn,
  3646                                 mozilla::ErrorResult& aRv)
  3648   nsTArray<nsRange*> results;
  3649   nsresult rv = GetRangesForIntervalArray(&aBeginNode, aBeginOffset,
  3650                                           &aEndNode, aEndOffset,
  3651                                           aAllowAdjacent, &results);
  3652   if (NS_FAILED(rv)) {
  3653     aRv.Throw(rv);
  3654     return;
  3657   aReturn.SetLength(results.Length());
  3658   for (uint32_t i = 0; i < results.Length(); ++i) {
  3659     aReturn[i] = results[i]; // AddRefs
  3663 // Selection::GetRangesForIntervalArray
  3664 //
  3665 //    Fills a nsTArray with the ranges overlapping the range specified by
  3666 //    the given endpoints. Ranges in the selection exactly adjacent to the
  3667 //    input range are not returned unless aAllowAdjacent is set.
  3668 //
  3669 //    For example, if the following ranges were in the selection
  3670 //    (assume everything is within the same node)
  3671 //
  3672 //    Start Offset: 0 2 7 9
  3673 //      End Offset: 2 5 9 10
  3674 //
  3675 //    and passed aBeginOffset of 2 and aEndOffset of 9, then with
  3676 //    aAllowAdjacent set, all the ranges should be returned. If
  3677 //    aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
  3678 //    should be returned
  3679 //
  3680 //    Now that overlapping ranges are disallowed, there can be a maximum of
  3681 //    2 adjacent ranges
  3683 nsresult
  3684 Selection::GetRangesForIntervalArray(nsINode* aBeginNode, int32_t aBeginOffset,
  3685                                      nsINode* aEndNode, int32_t aEndOffset,
  3686                                      bool aAllowAdjacent,
  3687                                      nsTArray<nsRange*>* aRanges)
  3689   aRanges->Clear();
  3690   int32_t startIndex, endIndex;
  3691   nsresult res = GetIndicesForInterval(aBeginNode, aBeginOffset,
  3692                                        aEndNode, aEndOffset, aAllowAdjacent,
  3693                                        &startIndex, &endIndex);
  3694   NS_ENSURE_SUCCESS(res, res);
  3696   if (startIndex == -1 || endIndex == -1)
  3697     return NS_OK;
  3699   for (int32_t i = startIndex; i < endIndex; i++) {
  3700     if (!aRanges->AppendElement(mRanges[i].mRange))
  3701       return NS_ERROR_OUT_OF_MEMORY;
  3704   return NS_OK;
  3707 // Selection::GetIndicesForInterval
  3708 //
  3709 //    Works on the same principle as GetRangesForIntervalArray above, however
  3710 //    instead this returns the indices into mRanges between which the
  3711 //    overlapping ranges lie.
  3713 nsresult
  3714 Selection::GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset,
  3715                                  nsINode* aEndNode, int32_t aEndOffset,
  3716                                  bool aAllowAdjacent,
  3717                                  int32_t* aStartIndex, int32_t* aEndIndex)
  3719   int32_t startIndex;
  3720   int32_t endIndex;
  3722   if (!aStartIndex)
  3723     aStartIndex = &startIndex;
  3724   if (!aEndIndex)
  3725     aEndIndex = &endIndex;
  3727   *aStartIndex = -1;
  3728   *aEndIndex = -1;
  3730   if (mRanges.Length() == 0)
  3731     return NS_OK;
  3733   bool intervalIsCollapsed = aBeginNode == aEndNode &&
  3734     aBeginOffset == aEndOffset;
  3736   // Ranges that end before the given interval and begin after the given
  3737   // interval can be discarded
  3738   int32_t endsBeforeIndex;
  3739   if (NS_FAILED(FindInsertionPoint(&mRanges, aEndNode, aEndOffset,
  3740                                    &CompareToRangeStart,
  3741                                    &endsBeforeIndex))) {
  3742     return NS_OK;
  3745   if (endsBeforeIndex == 0) {
  3746     nsRange* endRange = mRanges[endsBeforeIndex].mRange;
  3748     // If the interval is strictly before the range at index 0, we can optimize
  3749     // by returning now - all ranges start after the given interval
  3750     if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
  3751       return NS_OK;
  3753     // We now know that the start point of mRanges[0].mRange equals the end of
  3754     // the interval. Thus, when aAllowadjacent is true, the caller is always
  3755     // interested in this range. However, when excluding adjacencies, we must
  3756     // remember to include the range when both it and the given interval are
  3757     // collapsed to the same point
  3758     if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed))
  3759       return NS_OK;
  3761   *aEndIndex = endsBeforeIndex;
  3763   int32_t beginsAfterIndex;
  3764   if (NS_FAILED(FindInsertionPoint(&mRanges, aBeginNode, aBeginOffset,
  3765                                    &CompareToRangeEnd,
  3766                                    &beginsAfterIndex))) {
  3767     return NS_OK;
  3769   if (beginsAfterIndex == (int32_t) mRanges.Length())
  3770     return NS_OK; // optimization: all ranges are strictly before us
  3772   if (aAllowAdjacent) {
  3773     // At this point, one of the following holds:
  3774     //   endsBeforeIndex == mRanges.Length(),
  3775     //   endsBeforeIndex points to a range whose start point does not equal the
  3776     //     given interval's start point
  3777     //   endsBeforeIndex points to a range whose start point equals the given
  3778     //     interval's start point
  3779     // In the final case, there can be two such ranges, a collapsed range, and
  3780     // an adjacent range (they will appear in mRanges in that order). For this
  3781     // final case, we need to increment endsBeforeIndex, until one of the
  3782     // first two possibilites hold
  3783     while (endsBeforeIndex < (int32_t) mRanges.Length()) {
  3784       nsRange* endRange = mRanges[endsBeforeIndex].mRange;
  3785       if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
  3786         break;
  3787       endsBeforeIndex++;
  3790     // Likewise, one of the following holds:
  3791     //   beginsAfterIndex == 0,
  3792     //   beginsAfterIndex points to a range whose end point does not equal
  3793     //     the given interval's end point
  3794     //   beginsOnOrAfter points to a range whose end point equals the given
  3795     //     interval's end point
  3796     // In the final case, there can be two such ranges, an adjacent range, and
  3797     // a collapsed range (they will appear in mRanges in that order). For this
  3798     // final case, we only need to take action if both those ranges exist, and
  3799     // we are pointing to the collapsed range - we need to point to the
  3800     // adjacent range
  3801     nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
  3802     if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
  3803         RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) {
  3804       beginRange = mRanges[beginsAfterIndex - 1].mRange;
  3805       if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset))
  3806         beginsAfterIndex--;
  3808   } else {
  3809     // See above for the possibilities at this point. The only case where we
  3810     // need to take action is when the range at beginsAfterIndex ends on
  3811     // the given interval's start point, but that range isn't collapsed (a
  3812     // collapsed range should be included in the returned results).
  3813     nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
  3814     if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset) &&
  3815         !beginRange->Collapsed())
  3816       beginsAfterIndex++;
  3818     // Again, see above for the meaning of endsBeforeIndex at this point.
  3819     // In particular, endsBeforeIndex may point to a collaped range which
  3820     // represents the point at the end of the interval - this range should be
  3821     // included
  3822     if (endsBeforeIndex < (int32_t) mRanges.Length()) {
  3823       nsRange* endRange = mRanges[endsBeforeIndex].mRange;
  3824       if (RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset) &&
  3825           endRange->Collapsed())
  3826         endsBeforeIndex++;
  3830   NS_ASSERTION(beginsAfterIndex <= endsBeforeIndex,
  3831                "Is mRanges not ordered?");
  3832   NS_ENSURE_STATE(beginsAfterIndex <= endsBeforeIndex);
  3834   *aStartIndex = beginsAfterIndex;
  3835   *aEndIndex = endsBeforeIndex;
  3836   return NS_OK;
  3839 NS_IMETHODIMP
  3840 Selection::GetPrimaryFrameForAnchorNode(nsIFrame** aReturnFrame)
  3842   if (!aReturnFrame)
  3843     return NS_ERROR_NULL_POINTER;
  3845   int32_t frameOffset = 0;
  3846   *aReturnFrame = 0;
  3847   nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode());
  3848   if (content && mFrameSelection)
  3850     *aReturnFrame = mFrameSelection->
  3851       GetFrameForNodeOffset(content, AnchorOffset(),
  3852                             mFrameSelection->GetHint(), &frameOffset);
  3853     if (*aReturnFrame)
  3854       return NS_OK;
  3856   return NS_ERROR_FAILURE;
  3859 NS_IMETHODIMP
  3860 Selection::GetPrimaryFrameForFocusNode(nsIFrame** aReturnFrame,
  3861                                        int32_t* aOffsetUsed,
  3862                                        bool aVisual)
  3864   if (!aReturnFrame)
  3865     return NS_ERROR_NULL_POINTER;
  3867   nsCOMPtr<nsIContent> content = do_QueryInterface(GetFocusNode());
  3868   if (!content || !mFrameSelection)
  3869     return NS_ERROR_FAILURE;
  3871   int32_t frameOffset = 0;
  3872   *aReturnFrame = 0;
  3873   if (!aOffsetUsed)
  3874     aOffsetUsed = &frameOffset;
  3876   nsFrameSelection::HINT hint = mFrameSelection->GetHint();
  3878   if (aVisual) {
  3879     nsIPresShell *presShell = mFrameSelection->GetShell();
  3880     if (!presShell)
  3881       return NS_ERROR_FAILURE;
  3883     nsRefPtr<nsCaret> caret = presShell->GetCaret();
  3884     if (!caret)
  3885       return NS_ERROR_FAILURE;
  3887     uint8_t caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
  3889     return caret->GetCaretFrameForNodeOffset(content, FocusOffset(),
  3890       hint, caretBidiLevel, aReturnFrame, aOffsetUsed);
  3893   *aReturnFrame = mFrameSelection->
  3894     GetFrameForNodeOffset(content, FocusOffset(),
  3895                           hint, aOffsetUsed);
  3896   if (!*aReturnFrame)
  3897     return NS_ERROR_FAILURE;
  3899   return NS_OK;
  3902 //select all content children of aContent
  3903 nsresult
  3904 Selection::SelectAllFramesForContent(nsIContentIterator* aInnerIter,
  3905                                      nsIContent* aContent,
  3906                                      bool aSelected)
  3908   nsresult result = aInnerIter->Init(aContent);
  3909   nsIFrame *frame;
  3910   if (NS_SUCCEEDED(result))
  3912     // First select frame of content passed in
  3913     frame = aContent->GetPrimaryFrame();
  3914     if (frame && frame->GetType() == nsGkAtoms::textFrame) {
  3915       nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
  3916       textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(), aSelected, mType);
  3918     // Now iterated through the child frames and set them
  3919     while (!aInnerIter->IsDone()) {
  3920       nsCOMPtr<nsIContent> innercontent =
  3921         do_QueryInterface(aInnerIter->GetCurrentNode());
  3923       frame = innercontent->GetPrimaryFrame();
  3924       if (frame) {
  3925         if (frame->GetType() == nsGkAtoms::textFrame) {
  3926           nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
  3927           textFrame->SetSelectedRange(0, innercontent->GetText()->GetLength(), aSelected, mType);
  3928         } else {
  3929           frame->InvalidateFrameSubtree();  // frame continuations?
  3933       aInnerIter->Next();
  3936     return NS_OK;
  3939   return NS_ERROR_FAILURE;
  3942 /**
  3943  * The idea of this helper method is to select or deselect "top to bottom",
  3944  * traversing through the frames
  3945  */
  3946 nsresult
  3947 Selection::selectFrames(nsPresContext* aPresContext, nsRange* aRange,
  3948                         bool aSelect)
  3950   if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
  3951     // nothing to do
  3952     return NS_OK;
  3954   MOZ_ASSERT(aRange);
  3956   if (mFrameSelection->GetTableCellSelection()) {
  3957     nsINode* node = aRange->GetCommonAncestor();
  3958     nsIFrame* frame = node->IsContent() ? node->AsContent()->GetPrimaryFrame()
  3959                                 : aPresContext->FrameManager()->GetRootFrame();
  3960     if (frame) {
  3961       frame->InvalidateFrameSubtree();
  3963     return NS_OK;
  3966   nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
  3967   iter->Init(aRange);
  3969   // Loop through the content iterator for each content node; for each text
  3970   // node, call SetSelected on it:
  3971   nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
  3972   NS_ENSURE_STATE(content);
  3974   // We must call first one explicitly
  3975   if (content->IsNodeOfType(nsINode::eTEXT)) {
  3976     nsIFrame* frame = content->GetPrimaryFrame();
  3977     // The frame could be an SVG text frame, in which case we'll ignore it.
  3978     if (frame && frame->GetType() == nsGkAtoms::textFrame) {
  3979       nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
  3980       uint32_t startOffset = aRange->StartOffset();
  3981       uint32_t endOffset;
  3982       if (aRange->GetEndParent() == content) {
  3983         endOffset = aRange->EndOffset();
  3984       } else {
  3985         endOffset = content->Length();
  3987       textFrame->SetSelectedRange(startOffset, endOffset, aSelect, mType);
  3991   iter->First();
  3992   nsCOMPtr<nsIContentIterator> inneriter = NS_NewContentIterator();
  3993   for (iter->First(); !iter->IsDone(); iter->Next()) {
  3994     content = do_QueryInterface(iter->GetCurrentNode());
  3995     SelectAllFramesForContent(inneriter, content, aSelect);
  3998   // We must now do the last one if it is not the same as the first
  3999   if (aRange->GetEndParent() != aRange->GetStartParent()) {
  4000     nsresult res;
  4001     content = do_QueryInterface(aRange->GetEndParent(), &res);
  4002     NS_ENSURE_SUCCESS(res, res);
  4003     NS_ENSURE_TRUE(content, res);
  4005     if (content->IsNodeOfType(nsINode::eTEXT)) {
  4006       nsIFrame* frame = content->GetPrimaryFrame();
  4007       // The frame could be an SVG text frame, in which case we'll ignore it.
  4008       if (frame && frame->GetType() == nsGkAtoms::textFrame) {
  4009         nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
  4010         textFrame->SetSelectedRange(0, aRange->EndOffset(), aSelect, mType);
  4014   return NS_OK;
  4018 // Selection::LookUpSelection
  4019 //
  4020 //    This function is called when a node wants to know where the selection is
  4021 //    over itself.
  4022 //
  4023 //    Usually, this is called when we already know there is a selection over
  4024 //    the node in question, and we only need to find the boundaries of it on
  4025 //    that node. This is when slowCheck is false--a strict test is not needed.
  4026 //    Other times, the caller has no idea, and wants us to test everything,
  4027 //    so we are supposed to determine whether there is a selection over the
  4028 //    node at all.
  4029 //
  4030 //    A previous version of this code used this flag to do less work when
  4031 //    inclusion was already known (slowCheck=false). However, our tree
  4032 //    structure allows us to quickly determine ranges overlapping the node,
  4033 //    so we just ignore the slowCheck flag and do the full test every time.
  4034 //
  4035 //    PERFORMANCE: a common case is that we are doing a fast check with exactly
  4036 //    one range in the selection. In this case, this function is slower than
  4037 //    brute force because of the overhead of checking the tree. We can optimize
  4038 //    this case to make it faster by doing the same thing the previous version
  4039 //    of this function did in the case of 1 range. This would also mean that
  4040 //    the aSlowCheck flag would have meaning again.
  4042 NS_IMETHODIMP
  4043 Selection::LookUpSelection(nsIContent* aContent, int32_t aContentOffset,
  4044                            int32_t aContentLength,
  4045                            SelectionDetails** aReturnDetails,
  4046                            SelectionType aType, bool aSlowCheck)
  4048   nsresult rv;
  4049   if (!aContent || ! aReturnDetails)
  4050     return NS_ERROR_NULL_POINTER;
  4052   // it is common to have no ranges, to optimize that
  4053   if (mRanges.Length() == 0)
  4054     return NS_OK;
  4056   nsTArray<nsRange*> overlappingRanges;
  4057   rv = GetRangesForIntervalArray(aContent, aContentOffset,
  4058                                  aContent, aContentOffset + aContentLength,
  4059                                  false,
  4060                                  &overlappingRanges);
  4061   NS_ENSURE_SUCCESS(rv, rv);
  4062   if (overlappingRanges.Length() == 0)
  4063     return NS_OK;
  4065   for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
  4066     nsRange* range = overlappingRanges[i];
  4067     nsINode* startNode = range->GetStartParent();
  4068     nsINode* endNode = range->GetEndParent();
  4069     int32_t startOffset = range->StartOffset();
  4070     int32_t endOffset = range->EndOffset();
  4072     int32_t start = -1, end = -1;
  4073     if (startNode == aContent && endNode == aContent) {
  4074       if (startOffset < (aContentOffset + aContentLength)  &&
  4075           endOffset > aContentOffset) {
  4076         // this range is totally inside the requested content range
  4077         start = std::max(0, startOffset - aContentOffset);
  4078         end = std::min(aContentLength, endOffset - aContentOffset);
  4080       // otherwise, range is inside the requested node, but does not intersect
  4081       // the requested content range, so ignore it
  4082     } else if (startNode == aContent) {
  4083       if (startOffset < (aContentOffset + aContentLength)) {
  4084         // the beginning of the range is inside the requested node, but the
  4085         // end is outside, select everything from there to the end
  4086         start = std::max(0, startOffset - aContentOffset);
  4087         end = aContentLength;
  4089     } else if (endNode == aContent) {
  4090       if (endOffset > aContentOffset) {
  4091         // the end of the range is inside the requested node, but the beginning
  4092         // is outside, select everything from the beginning to there
  4093         start = 0;
  4094         end = std::min(aContentLength, endOffset - aContentOffset);
  4096     } else {
  4097       // this range does not begin or end in the requested node, but since
  4098       // GetRangesForInterval returned this range, we know it overlaps.
  4099       // Therefore, this node is enclosed in the range, and we select all
  4100       // of it.
  4101       start = 0;
  4102       end = aContentLength;
  4104     if (start < 0)
  4105       continue; // the ranges do not overlap the input range
  4107     SelectionDetails* details = new SelectionDetails;
  4109     details->mNext = *aReturnDetails;
  4110     details->mStart = start;
  4111     details->mEnd = end;
  4112     details->mType = aType;
  4113     RangeData *rd = FindRangeData(range);
  4114     if (rd) {
  4115       details->mTextRangeStyle = rd->mTextRangeStyle;
  4117     *aReturnDetails = details;
  4119   return NS_OK;
  4122 NS_IMETHODIMP
  4123 Selection::Repaint(nsPresContext* aPresContext)
  4125   int32_t arrCount = (int32_t)mRanges.Length();
  4127   if (arrCount < 1)
  4128     return NS_OK;
  4130   int32_t i;
  4132   for (i = 0; i < arrCount; i++)
  4134     nsresult rv = selectFrames(aPresContext, mRanges[i].mRange, true);
  4136     if (NS_FAILED(rv)) {
  4137       return rv;
  4141   return NS_OK;
  4144 NS_IMETHODIMP
  4145 Selection::GetCanCacheFrameOffset(bool* aCanCacheFrameOffset)
  4147   NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
  4149   if (mCachedOffsetForFrame)
  4150     *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
  4151   else
  4152     *aCanCacheFrameOffset = false;
  4154   return NS_OK;
  4157 NS_IMETHODIMP    
  4158 Selection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset)
  4160   if (!mCachedOffsetForFrame) {
  4161     mCachedOffsetForFrame = new CachedOffsetForFrame;
  4164   mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
  4166   // clean up cached frame when turn off cache
  4167   // fix bug 207936
  4168   if (!aCanCacheFrameOffset) {
  4169     mCachedOffsetForFrame->mLastCaretFrame = nullptr;
  4172   return NS_OK;
  4175 NS_IMETHODIMP    
  4176 Selection::GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
  4177                                 nsPoint& aPoint)
  4179   if (!mCachedOffsetForFrame) {
  4180     mCachedOffsetForFrame = new CachedOffsetForFrame;
  4183   nsresult rv = NS_OK;
  4184   if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
  4185       mCachedOffsetForFrame->mLastCaretFrame &&
  4186       (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
  4187       (inOffset == mCachedOffsetForFrame->mLastContentOffset))
  4189      // get cached frame offset
  4190      aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
  4192   else
  4194      // Recalculate frame offset and cache it. Don't cache a frame offset if
  4195      // GetPointFromOffset fails, though.
  4196      rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
  4197      if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
  4198        mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
  4199        mCachedOffsetForFrame->mLastCaretFrame = aFrame;
  4200        mCachedOffsetForFrame->mLastContentOffset = inOffset; 
  4204   return rv;
  4207 NS_IMETHODIMP
  4208 Selection::SetAncestorLimiter(nsIContent* aContent)
  4210   if (mFrameSelection)
  4211     mFrameSelection->SetAncestorLimiter(aContent);
  4212   return NS_OK;
  4215 RangeData*
  4216 Selection::FindRangeData(nsIDOMRange* aRange)
  4218   NS_ENSURE_TRUE(aRange, nullptr);
  4219   for (uint32_t i = 0; i < mRanges.Length(); i++) {
  4220     if (mRanges[i].mRange == aRange)
  4221       return &mRanges[i];
  4223   return nullptr;
  4226 NS_IMETHODIMP
  4227 Selection::SetTextRangeStyle(nsIDOMRange* aRange,
  4228                              const TextRangeStyle& aTextRangeStyle)
  4230   NS_ENSURE_ARG_POINTER(aRange);
  4231   RangeData *rd = FindRangeData(aRange);
  4232   if (rd) {
  4233     rd->mTextRangeStyle = aTextRangeStyle;
  4235   return NS_OK;
  4238 nsresult
  4239 Selection::StartAutoScrollTimer(nsIFrame* aFrame, nsPoint& aPoint,
  4240                                 uint32_t aDelay)
  4242   NS_PRECONDITION(aFrame, "Need a frame");
  4244   nsresult result;
  4245   if (!mFrameSelection)
  4246     return NS_OK;//nothing to do
  4248   if (!mAutoScrollTimer)
  4250     mAutoScrollTimer = new nsAutoScrollTimer();
  4252     result = mAutoScrollTimer->Init(mFrameSelection, this);
  4254     if (NS_FAILED(result))
  4255       return result;
  4258   result = mAutoScrollTimer->SetDelay(aDelay);
  4260   if (NS_FAILED(result))
  4261     return result;
  4263   return DoAutoScroll(aFrame, aPoint);
  4266 nsresult
  4267 Selection::StopAutoScrollTimer()
  4269   if (mAutoScrollTimer)
  4270     return mAutoScrollTimer->Stop();
  4272   return NS_OK; 
  4275 nsresult
  4276 Selection::DoAutoScroll(nsIFrame* aFrame, nsPoint& aPoint)
  4278   NS_PRECONDITION(aFrame, "Need a frame");
  4280   if (mAutoScrollTimer)
  4281     (void)mAutoScrollTimer->Stop();
  4283   nsPresContext* presContext = aFrame->PresContext();
  4284   nsRootPresContext* rootPC = presContext->GetRootPresContext();
  4285   if (!rootPC)
  4286     return NS_OK;
  4287   nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
  4288   // Get the point relative to the root most frame because the scroll we are
  4289   // about to do will change the coordinates of aFrame.
  4290   nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
  4292   bool didScroll = presContext->PresShell()->ScrollFrameRectIntoView(
  4293     aFrame, 
  4294     nsRect(aPoint, nsSize(0, 0)),
  4295     nsIPresShell::ScrollAxis(),
  4296     nsIPresShell::ScrollAxis(),
  4297     0);
  4299   //
  4300   // Start the AutoScroll timer if necessary.
  4301   //
  4303   if (didScroll && mAutoScrollTimer)
  4305     nsPoint presContextPoint = globalPoint -
  4306       presContext->PresShell()->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
  4307     mAutoScrollTimer->Start(presContext, presContextPoint);
  4310   return NS_OK;
  4314 /** RemoveAllRanges zeroes the selection
  4315  */
  4316 NS_IMETHODIMP
  4317 Selection::RemoveAllRanges()
  4319   ErrorResult result;
  4320   RemoveAllRanges(result);
  4321   return result.ErrorCode();
  4324 void
  4325 Selection::RemoveAllRanges(ErrorResult& aRv)
  4327   if (!mFrameSelection)
  4328     return; // nothing to do
  4329   nsRefPtr<nsPresContext>  presContext = GetPresContext();
  4330   nsresult  result = Clear(presContext);
  4331   if (NS_FAILED(result)) {
  4332     aRv.Throw(result);
  4333     return;
  4336   // Turn off signal for table selection
  4337   mFrameSelection->ClearTableCellSelection();
  4339   result = mFrameSelection->NotifySelectionListeners(GetType());
  4340   // Also need to notify the frames!
  4341   // PresShell::CharacterDataChanged should do that on DocumentChanged
  4342   if (NS_FAILED(result)) {
  4343     aRv.Throw(result);
  4347 /** AddRange adds the specified range to the selection
  4348  *  @param aRange is the range to be added
  4349  */
  4350 NS_IMETHODIMP
  4351 Selection::AddRange(nsIDOMRange* aDOMRange)
  4353   if (!aDOMRange) {
  4354     return NS_ERROR_NULL_POINTER;
  4356   nsRange* range = static_cast<nsRange*>(aDOMRange);
  4357   ErrorResult result;
  4358   AddRange(*range, result);
  4359   return result.ErrorCode();
  4362 void
  4363 Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
  4365   // This inserts a table cell range in proper document order
  4366   // and returns NS_OK if range doesn't contain just one table cell
  4367   bool didAddRange;
  4368   int32_t rangeIndex;
  4369   nsresult result = addTableCellRange(&aRange, &didAddRange, &rangeIndex);
  4370   if (NS_FAILED(result)) {
  4371     aRv.Throw(result);
  4372     return;
  4375   if (!didAddRange)
  4377     result = AddItem(&aRange, &rangeIndex);
  4378     if (NS_FAILED(result)) {
  4379       aRv.Throw(result);
  4380       return;
  4384   NS_ASSERTION(rangeIndex >= 0, "Range index not returned");
  4385   setAnchorFocusRange(rangeIndex);
  4387   // Make sure the caret appears on the next line, if at a newline
  4388   if (mType == nsISelectionController::SELECTION_NORMAL)
  4389     SetInterlinePosition(true);
  4391   nsRefPtr<nsPresContext>  presContext = GetPresContext();
  4392   selectFrames(presContext, &aRange, true);
  4394   if (!mFrameSelection)
  4395     return;//nothing to do
  4397   result = mFrameSelection->NotifySelectionListeners(GetType());
  4398   if (NS_FAILED(result)) {
  4399     aRv.Throw(result);
  4403 // Selection::RemoveRange
  4404 //
  4405 //    Removes the given range from the selection. The tricky part is updating
  4406 //    the flags on the frames that indicate whether they have a selection or
  4407 //    not. There could be several selection ranges on the frame, and clearing
  4408 //    the bit would cause the selection to not be drawn, even when there is
  4409 //    another range on the frame (bug 346185).
  4410 //
  4411 //    We therefore find any ranges that intersect the same nodes as the range
  4412 //    being removed, and cause them to set the selected bits back on their
  4413 //    selected frames after we've cleared the bit from ours.
  4415 nsresult
  4416 Selection::RemoveRange(nsIDOMRange* aDOMRange)
  4418   if (!aDOMRange) {
  4419     return NS_ERROR_INVALID_ARG;
  4421   nsRange* range = static_cast<nsRange*>(aDOMRange);
  4422   ErrorResult result;
  4423   RemoveRange(*range, result);
  4424   return result.ErrorCode();
  4427 void
  4428 Selection::RemoveRange(nsRange& aRange, ErrorResult& aRv)
  4430   nsresult rv = RemoveItem(&aRange);
  4431   if (NS_FAILED(rv)) {
  4432     aRv.Throw(rv);
  4433     return;
  4436   nsINode* beginNode = aRange.GetStartParent();
  4437   nsINode* endNode = aRange.GetEndParent();
  4439   if (!beginNode || !endNode) {
  4440     // Detached range; nothing else to do here.
  4441     return;
  4444   // find out the length of the end node, so we can select all of it
  4445   int32_t beginOffset, endOffset;
  4446   if (endNode->IsNodeOfType(nsINode::eTEXT)) {
  4447     // Get the length of the text. We can't just use the offset because
  4448     // another range could be touching this text node but not intersect our
  4449     // range.
  4450     beginOffset = 0;
  4451     endOffset = static_cast<nsIContent*>(endNode)->TextLength();
  4452   } else {
  4453     // For non-text nodes, the given offsets should be sufficient.
  4454     beginOffset = aRange.StartOffset();
  4455     endOffset = aRange.EndOffset();
  4458   // clear the selected bit from the removed range's frames
  4459   nsRefPtr<nsPresContext>  presContext = GetPresContext();
  4460   selectFrames(presContext, &aRange, false);
  4462   // add back the selected bit for each range touching our nodes
  4463   nsTArray<nsRange*> affectedRanges;
  4464   rv = GetRangesForIntervalArray(beginNode, beginOffset,
  4465                                  endNode, endOffset,
  4466                                  true, &affectedRanges);
  4467   if (NS_FAILED(rv)) {
  4468     aRv.Throw(rv);
  4469     return;
  4471   for (uint32_t i = 0; i < affectedRanges.Length(); i++) {
  4472     selectFrames(presContext, affectedRanges[i], true);
  4475   int32_t cnt = mRanges.Length();
  4476   if (&aRange == mAnchorFocusRange) {
  4477     // Reset anchor to LAST range or clear it if there are no ranges.
  4478     setAnchorFocusRange(cnt - 1);
  4480     // When the selection is user-created it makes sense to scroll the range
  4481     // into view. The spell-check selection, however, is created and destroyed
  4482     // in the background. We don't want to scroll in this case or the view
  4483     // might appear to be moving randomly (bug 337871).
  4484     if (mType != nsISelectionController::SELECTION_SPELLCHECK && cnt > 0)
  4485       ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
  4488   if (!mFrameSelection)
  4489     return;//nothing to do
  4490   rv = mFrameSelection->NotifySelectionListeners(GetType());
  4491   if (NS_FAILED(rv)) {
  4492     aRv.Throw(rv);
  4498 /*
  4499  * Collapse sets the whole selection to be one point.
  4500  */
  4501 NS_IMETHODIMP
  4502 Selection::Collapse(nsIDOMNode* aParentNode, int32_t aOffset)
  4504   nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
  4505   return Collapse(parentNode, aOffset);
  4508 NS_IMETHODIMP
  4509 Selection::CollapseNative(nsINode* aParentNode, int32_t aOffset)
  4511   return Collapse(aParentNode, aOffset);
  4514 nsresult
  4515 Selection::Collapse(nsINode* aParentNode, int32_t aOffset)
  4517   if (!aParentNode)
  4518     return NS_ERROR_INVALID_ARG;
  4520   ErrorResult result;
  4521   Collapse(*aParentNode, static_cast<uint32_t>(aOffset), result);
  4522   return result.ErrorCode();
  4525 void
  4526 Selection::Collapse(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
  4528   if (!mFrameSelection) {
  4529     aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
  4530     return;
  4533   nsCOMPtr<nsINode> kungfuDeathGrip = &aParentNode;
  4535   mFrameSelection->InvalidateDesiredX();
  4536   if (!IsValidSelectionPoint(mFrameSelection, &aParentNode)) {
  4537     aRv.Throw(NS_ERROR_FAILURE);
  4538     return;
  4540   nsresult result;
  4542   nsRefPtr<nsPresContext> presContext = GetPresContext();
  4543   if (!presContext || presContext->Document() != aParentNode.OwnerDoc()) {
  4544     aRv.Throw(NS_ERROR_FAILURE);
  4545     return;
  4548   // Delete all of the current ranges
  4549   Clear(presContext);
  4551   // Turn off signal for table selection
  4552   mFrameSelection->ClearTableCellSelection();
  4554   nsRefPtr<nsRange> range = new nsRange(&aParentNode);
  4555   result = range->SetEnd(&aParentNode, aOffset);
  4556   if (NS_FAILED(result)) {
  4557     aRv.Throw(result);
  4558     return;
  4560   result = range->SetStart(&aParentNode, aOffset);
  4561   if (NS_FAILED(result)) {
  4562     aRv.Throw(result);
  4563     return;
  4566 #ifdef DEBUG_SELECTION
  4567   nsCOMPtr<nsIContent> content = do_QueryInterface(&aParentNode);
  4568   nsCOMPtr<nsIDocument> doc = do_QueryInterface(&aParentNode);
  4569   printf ("Sel. Collapse to %p %s %d\n", &aParentNode,
  4570           content ? nsAtomCString(content->Tag()).get()
  4571                   : (doc ? "DOCUMENT" : "???"),
  4572           aOffset);
  4573 #endif
  4575   int32_t rangeIndex = -1;
  4576   result = AddItem(range, &rangeIndex);
  4577   if (NS_FAILED(result)) {
  4578     aRv.Throw(result);
  4579     return;
  4581   setAnchorFocusRange(0);
  4582   selectFrames(presContext, range, true);
  4583   result = mFrameSelection->NotifySelectionListeners(GetType());
  4584   if (NS_FAILED(result)) {
  4585     aRv.Throw(result);
  4589 /*
  4590  * Sets the whole selection to be one point
  4591  * at the start of the current selection
  4592  */
  4593 NS_IMETHODIMP
  4594 Selection::CollapseToStart()
  4596   ErrorResult result;
  4597   CollapseToStart(result);
  4598   return result.ErrorCode();
  4601 void
  4602 Selection::CollapseToStart(ErrorResult& aRv)
  4604   int32_t cnt;
  4605   nsresult rv = GetRangeCount(&cnt);
  4606   if (NS_FAILED(rv) || cnt <= 0) {
  4607     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  4608     return;
  4611   // Get the first range
  4612   nsRange* firstRange = mRanges[0].mRange;
  4613   if (!firstRange) {
  4614     aRv.Throw(NS_ERROR_FAILURE);
  4615     return;
  4618   if (mFrameSelection) {
  4619     int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOSTART_REASON;
  4620     mFrameSelection->PostReason(reason);
  4622   nsINode* parent = firstRange->GetStartParent();
  4623   if (!parent) {
  4624     aRv.Throw(NS_ERROR_FAILURE);
  4625     return;
  4627   Collapse(*parent, firstRange->StartOffset(), aRv);
  4630 /*
  4631  * Sets the whole selection to be one point
  4632  * at the end of the current selection
  4633  */
  4634 NS_IMETHODIMP
  4635 Selection::CollapseToEnd()
  4637   ErrorResult result;
  4638   CollapseToEnd(result);
  4639   return result.ErrorCode();
  4642 void
  4643 Selection::CollapseToEnd(ErrorResult& aRv)
  4645   int32_t cnt;
  4646   nsresult rv = GetRangeCount(&cnt);
  4647   if (NS_FAILED(rv) || cnt <= 0) {
  4648     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  4649     return;
  4652   // Get the last range
  4653   nsRange* lastRange = mRanges[cnt - 1].mRange;
  4654   if (!lastRange) {
  4655     aRv.Throw(NS_ERROR_FAILURE);
  4656     return;
  4659   if (mFrameSelection) {
  4660     int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOEND_REASON;
  4661     mFrameSelection->PostReason(reason);
  4663   nsINode* parent = lastRange->GetEndParent();
  4664   if (!parent) {
  4665     aRv.Throw(NS_ERROR_FAILURE);
  4666     return;
  4668   Collapse(*parent, lastRange->EndOffset(), aRv);
  4671 /*
  4672  * IsCollapsed -- is the whole selection just one point, or unset?
  4673  */
  4674 bool
  4675 Selection::IsCollapsed()
  4677   uint32_t cnt = mRanges.Length();
  4678   if (cnt == 0) {
  4679     return true;
  4682   if (cnt != 1) {
  4683     return false;
  4686   return mRanges[0].mRange->Collapsed();
  4689 /* virtual */
  4690 bool
  4691 Selection::Collapsed()
  4693   return IsCollapsed();
  4696 NS_IMETHODIMP
  4697 Selection::GetIsCollapsed(bool* aIsCollapsed)
  4699   NS_ENSURE_TRUE(aIsCollapsed, NS_ERROR_NULL_POINTER);
  4701   *aIsCollapsed = IsCollapsed();
  4702   return NS_OK;
  4705 NS_IMETHODIMP
  4706 Selection::GetRangeCount(int32_t* aRangeCount)
  4708   *aRangeCount = (int32_t)RangeCount();
  4710   return NS_OK;
  4713 NS_IMETHODIMP
  4714 Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
  4716   ErrorResult result;
  4717   *aReturn = GetRangeAt(aIndex, result);
  4718   NS_IF_ADDREF(*aReturn);
  4719   return result.ErrorCode();
  4722 nsRange*
  4723 Selection::GetRangeAt(uint32_t aIndex, ErrorResult& aRv)
  4725   nsRange* range = GetRangeAt(aIndex);
  4726   if (!range) {
  4727     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  4728     return nullptr;
  4731   return range;
  4734 nsRange*
  4735 Selection::GetRangeAt(int32_t aIndex)
  4737   RangeData empty(nullptr);
  4738   return mRanges.SafeElementAt(aIndex, empty).mRange;
  4741 /*
  4742 utility function
  4743 */
  4744 nsresult
  4745 Selection::SetAnchorFocusToRange(nsRange* aRange)
  4747   NS_ENSURE_STATE(mAnchorFocusRange);
  4749   nsresult res = RemoveItem(mAnchorFocusRange);
  4750   if (NS_FAILED(res))
  4751     return res;
  4753   int32_t aOutIndex = -1;
  4754   res = AddItem(aRange, &aOutIndex);
  4755   if (NS_FAILED(res))
  4756     return res;
  4757   setAnchorFocusRange(aOutIndex);
  4759   return NS_OK;
  4762 void
  4763 Selection::ReplaceAnchorFocusRange(nsRange* aRange)
  4765   NS_ENSURE_TRUE_VOID(mAnchorFocusRange);
  4766   nsRefPtr<nsPresContext> presContext = GetPresContext();
  4767   if (presContext) {
  4768     selectFrames(presContext, mAnchorFocusRange, false);
  4769     SetAnchorFocusToRange(aRange);
  4770     selectFrames(presContext, mAnchorFocusRange, true);
  4774 /*
  4775 Notes which might come in handy for extend:
  4777 We can tell the direction of the selection by asking for the anchors selection
  4778 if the begin is less than the end then we know the selection is to the "right".
  4779 else it is a backwards selection.
  4780 a = anchor
  4781 1 = old cursor
  4782 2 = new cursor
  4784   if (a <= 1 && 1 <=2)    a,1,2  or (a1,2)
  4785   if (a < 2 && 1 > 2)     a,2,1
  4786   if (1 < a && a <2)      1,a,2
  4787   if (a > 2 && 2 >1)      1,2,a
  4788   if (2 < a && a <1)      2,a,1
  4789   if (a > 1 && 1 >2)      2,1,a
  4790 then execute
  4791 a  1  2 select from 1 to 2
  4792 a  2  1 deselect from 2 to 1
  4793 1  a  2 deselect from 1 to a select from a to 2
  4794 1  2  a deselect from 1 to 2
  4795 2  1  a = continue selection from 2 to 1
  4796 */
  4799 /*
  4800  * Extend extends the selection away from the anchor.
  4801  * We don't need to know the direction, because we always change the focus.
  4802  */
  4803 NS_IMETHODIMP
  4804 Selection::Extend(nsIDOMNode* aParentNode, int32_t aOffset)
  4806   nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
  4807   return Extend(parentNode, aOffset);
  4810 NS_IMETHODIMP
  4811 Selection::ExtendNative(nsINode* aParentNode, int32_t aOffset)
  4813   return Extend(aParentNode, aOffset);
  4816 nsresult
  4817 Selection::Extend(nsINode* aParentNode, int32_t aOffset)
  4819   if (!aParentNode)
  4820     return NS_ERROR_INVALID_ARG;
  4822   ErrorResult result;
  4823   Extend(*aParentNode, static_cast<uint32_t>(aOffset), result);
  4824   return result.ErrorCode();
  4827 void
  4828 Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
  4830   // First, find the range containing the old focus point:
  4831   if (!mAnchorFocusRange) {
  4832     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  4833     return;
  4836   if (!mFrameSelection) {
  4837     aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
  4838     return;
  4841   nsresult res;
  4842   if (!IsValidSelectionPoint(mFrameSelection, &aParentNode)) {
  4843     aRv.Throw(NS_ERROR_FAILURE);
  4844     return;
  4847   nsRefPtr<nsPresContext> presContext = GetPresContext();
  4848   if (!presContext || presContext->Document() != aParentNode.OwnerDoc()) {
  4849     aRv.Throw(NS_ERROR_FAILURE);
  4850     return;
  4853   //mFrameSelection->InvalidateDesiredX();
  4855   nsINode* anchorNode = GetAnchorNode();
  4856   nsINode* focusNode = GetFocusNode();
  4857   uint32_t anchorOffset = AnchorOffset();
  4858   uint32_t focusOffset = FocusOffset();
  4860   nsRefPtr<nsRange> range = mAnchorFocusRange->CloneRange();
  4862   nsINode* startNode = range->GetStartParent();
  4863   nsINode* endNode = range->GetEndParent();
  4864   int32_t startOffset = range->StartOffset();
  4865   int32_t endOffset = range->EndOffset();
  4867   nsDirection dir = GetDirection();
  4869   //compare anchor to old cursor.
  4871   // We pass |disconnected| to the following ComparePoints calls in order
  4872   // to avoid assertions. ComparePoints returns 1 in the disconnected case
  4873   // and we can end up in various cases below, but it is assumed that in
  4874   // any of the cases we end up, the nsRange implementation will collapse
  4875   // the range to the new point because we can not make a valid range with
  4876   // a disconnected point. This means that whatever range is currently
  4877   // selected will be cleared.
  4878   bool disconnected = false;
  4879   bool shouldClearRange = false;
  4880   int32_t result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
  4881                                                   focusNode, focusOffset,
  4882                                                   &disconnected);
  4883   //compare old cursor to new cursor
  4884   shouldClearRange |= disconnected;
  4885   int32_t result2 = nsContentUtils::ComparePoints(focusNode, focusOffset,
  4886                                                   &aParentNode, aOffset,
  4887                                                   &disconnected);
  4888   //compare anchor to new cursor
  4889   shouldClearRange |= disconnected;
  4890   int32_t result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
  4891                                                   &aParentNode, aOffset,
  4892                                                   &disconnected);
  4894   // If the points are disconnected, the range will be collapsed below,
  4895   // resulting in a range that selects nothing.
  4896   if (shouldClearRange) {
  4897     // Repaint the current range with the selection removed.
  4898     selectFrames(presContext, range, false);
  4901   nsRefPtr<nsRange> difRange = new nsRange(&aParentNode);
  4902   if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2  a,1,2
  4903     //select from 1 to 2 unless they are collapsed
  4904     range->SetEnd(aParentNode, aOffset, aRv);
  4905     if (aRv.Failed()) {
  4906       return;
  4908     dir = eDirNext;
  4909     res = difRange->SetEnd(range->GetEndParent(), range->EndOffset());
  4910     nsresult tmp = difRange->SetStart(focusNode, focusOffset);
  4911     if (NS_FAILED(tmp)) {
  4912       res = tmp;
  4914     if (NS_FAILED(res)) {
  4915       aRv.Throw(res);
  4916       return;
  4918     selectFrames(presContext, difRange , true);
  4919     res = SetAnchorFocusToRange(range);
  4920     if (NS_FAILED(res)) {
  4921       aRv.Throw(res);
  4922       return;
  4925   else if (result1 == 0 && result3 > 0){//2, a1
  4926     //select from 2 to 1a
  4927     dir = eDirPrevious;
  4928     range->SetStart(aParentNode, aOffset, aRv);
  4929     if (aRv.Failed()) {
  4930       return;
  4932     selectFrames(presContext, range, true);
  4933     res = SetAnchorFocusToRange(range);
  4934     if (NS_FAILED(res)) {
  4935       aRv.Throw(res);
  4936       return;
  4939   else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
  4940     //deselect from 2 to 1
  4941     res = difRange->SetEnd(focusNode, focusOffset);
  4942     difRange->SetStart(aParentNode, aOffset, aRv);
  4943     if (aRv.Failed()) {
  4944       return;
  4946     if (NS_FAILED(res)) {
  4947       aRv.Throw(res);
  4948       return;
  4951     range->SetEnd(aParentNode, aOffset, aRv);
  4952     if (aRv.Failed()) {
  4953       return;
  4955     res = SetAnchorFocusToRange(range);
  4956     if (NS_FAILED(res)) {
  4957       aRv.Throw(res);
  4958       return;
  4960     selectFrames(presContext, difRange, false); // deselect now
  4961     difRange->SetEnd(range->GetEndParent(), range->EndOffset());
  4962     selectFrames(presContext, difRange, true); // must reselect last node maybe more
  4964   else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
  4965     if (GetDirection() == eDirPrevious){
  4966       res = range->SetStart(endNode, endOffset);
  4967       if (NS_FAILED(res)) {
  4968         aRv.Throw(res);
  4969         return;
  4972     dir = eDirNext;
  4973     range->SetEnd(aParentNode, aOffset, aRv);
  4974     if (aRv.Failed()) {
  4975       return;
  4977     if (focusNode != anchorNode || focusOffset != anchorOffset) {//if collapsed diff dont do anything
  4978       res = difRange->SetStart(focusNode, focusOffset);
  4979       nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset);
  4980       if (NS_FAILED(tmp)) {
  4981         res = tmp;
  4983       if (NS_FAILED(res)) {
  4984         aRv.Throw(res);
  4985         return;
  4987       res = SetAnchorFocusToRange(range);
  4988       if (NS_FAILED(res)) {
  4989         aRv.Throw(res);
  4990         return;
  4992       //deselect from 1 to a
  4993       selectFrames(presContext, difRange , false);
  4995     else
  4997       res = SetAnchorFocusToRange(range);
  4998       if (NS_FAILED(res)) {
  4999         aRv.Throw(res);
  5000         return;
  5003     //select from a to 2
  5004     selectFrames(presContext, range , true);
  5006   else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
  5007     //deselect from 1 to 2
  5008     difRange->SetEnd(aParentNode, aOffset, aRv);
  5009     res = difRange->SetStart(focusNode, focusOffset);
  5010     if (aRv.Failed()) {
  5011       return;
  5013     if (NS_FAILED(res)) {
  5014       aRv.Throw(res);
  5015       return;
  5017     dir = eDirPrevious;
  5018     range->SetStart(aParentNode, aOffset, aRv);
  5019     if (aRv.Failed()) {
  5020       return;
  5023     res = SetAnchorFocusToRange(range);
  5024     if (NS_FAILED(res)) {
  5025       aRv.Throw(res);
  5026       return;
  5028     selectFrames(presContext, difRange , false);
  5029     difRange->SetStart(range->GetStartParent(), range->StartOffset());
  5030     selectFrames(presContext, difRange, true);//must reselect last node
  5032   else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
  5033     if (GetDirection() == eDirNext){
  5034       range->SetEnd(startNode, startOffset);
  5036     dir = eDirPrevious;
  5037     range->SetStart(aParentNode, aOffset, aRv);
  5038     if (aRv.Failed()) {
  5039       return;
  5041     //deselect from a to 1
  5042     if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything
  5043       res = difRange->SetStart(anchorNode, anchorOffset);
  5044       nsresult tmp = difRange->SetEnd(focusNode, focusOffset);
  5045       if (NS_FAILED(tmp)) {
  5046         res = tmp;
  5048       tmp = SetAnchorFocusToRange(range);
  5049       if (NS_FAILED(tmp)) {
  5050         res = tmp;
  5052       if (NS_FAILED(res)) {
  5053         aRv.Throw(res);
  5054         return;
  5056       selectFrames(presContext, difRange, false);
  5058     else
  5060       res = SetAnchorFocusToRange(range);
  5061       if (NS_FAILED(res)) {
  5062         aRv.Throw(res);
  5063         return;
  5066     //select from 2 to a
  5067     selectFrames(presContext, range , true);
  5069   else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
  5070     //select from 2 to 1
  5071     range->SetStart(aParentNode, aOffset, aRv);
  5072     if (aRv.Failed()) {
  5073       return;
  5075     dir = eDirPrevious;
  5076     res = difRange->SetEnd(focusNode, focusOffset);
  5077     nsresult tmp = difRange->SetStart(range->GetStartParent(), range->StartOffset());
  5078     if (NS_FAILED(tmp)) {
  5079       res = tmp;
  5081     if (NS_FAILED(res)) {
  5082       aRv.Throw(res);
  5083       return;
  5086     selectFrames(presContext, difRange, true);
  5087     res = SetAnchorFocusToRange(range);
  5088     if (NS_FAILED(res)) {
  5089       aRv.Throw(res);
  5090       return;
  5094   DEBUG_OUT_RANGE(range);
  5095 #ifdef DEBUG_SELECTION
  5096   if (eDirNext == mDirection)
  5097     printf("    direction = 1  LEFT TO RIGHT\n");
  5098   else
  5099     printf("    direction = 0  RIGHT TO LEFT\n");
  5100 #endif
  5101   SetDirection(dir);
  5102 #ifdef DEBUG_SELECTION
  5103   nsCOMPtr<nsIContent> content = do_QueryInterface(&aParentNode);
  5105   printf ("Sel. Extend to %p %s %d\n", content.get(),
  5106           nsAtomCString(content->Tag()).get(), aOffset);
  5107 #endif
  5108   res = mFrameSelection->NotifySelectionListeners(GetType());
  5109   if (NS_FAILED(res)) {
  5110     aRv.Throw(res);
  5114 NS_IMETHODIMP
  5115 Selection::SelectAllChildren(nsIDOMNode* aParentNode)
  5117   ErrorResult result;
  5118   nsCOMPtr<nsINode> node = do_QueryInterface(aParentNode);
  5119   NS_ENSURE_TRUE(node, NS_ERROR_INVALID_ARG);
  5120   SelectAllChildren(*node, result);
  5121   return result.ErrorCode();
  5124 void
  5125 Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
  5127   if (mFrameSelection)
  5129     mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
  5131   Collapse(aNode, 0, aRv);
  5132   if (aRv.Failed()) {
  5133     return;
  5136   if (mFrameSelection)
  5138     mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
  5140   Extend(aNode, aNode.GetChildCount(), aRv);
  5143 NS_IMETHODIMP
  5144 Selection::ContainsNode(nsIDOMNode* aNode, bool aAllowPartial, bool* aYes)
  5146   if (!aYes)
  5147     return NS_ERROR_NULL_POINTER;
  5148   *aYes = false;
  5150   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  5151   ErrorResult result;
  5152   *aYes = ContainsNode(node, aAllowPartial, result);
  5153   return result.ErrorCode();
  5156 bool
  5157 Selection::ContainsNode(nsINode* aNode, bool aAllowPartial, ErrorResult& aRv)
  5159   nsresult rv;
  5160   if (mRanges.Length() == 0 || !aNode)
  5161     return false;
  5163   // XXXbz this duplicates the GetNodeLength code in nsRange.cpp
  5164   uint32_t nodeLength;
  5165   bool isData = aNode->IsNodeOfType(nsINode::eDATA_NODE);
  5166   if (isData) {
  5167     nodeLength = static_cast<nsIContent*>(aNode)->TextLength();
  5168   } else {
  5169     nodeLength = aNode->GetChildCount();
  5172   nsTArray<nsRange*> overlappingRanges;
  5173   rv = GetRangesForIntervalArray(aNode, 0, aNode, nodeLength,
  5174                                  false, &overlappingRanges);
  5175   if (NS_FAILED(rv)) {
  5176     aRv.Throw(rv);
  5177     return false;
  5179   if (overlappingRanges.Length() == 0)
  5180     return false; // no ranges overlap
  5182   // if the caller said partial intersections are OK, we're done
  5183   if (aAllowPartial) {
  5184     return true;
  5187   // text nodes always count as inside
  5188   if (isData) {
  5189     return true;
  5192   // The caller wants to know if the node is entirely within the given range,
  5193   // so we have to check all intersecting ranges.
  5194   for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
  5195     bool nodeStartsBeforeRange, nodeEndsAfterRange;
  5196     if (NS_SUCCEEDED(nsRange::CompareNodeToRange(aNode, overlappingRanges[i],
  5197                                                  &nodeStartsBeforeRange,
  5198                                                  &nodeEndsAfterRange))) {
  5199       if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
  5200         return true;
  5204   return false;
  5208 nsPresContext*
  5209 Selection::GetPresContext() const
  5211   nsIPresShell *shell = GetPresShell();
  5212   if (!shell) {
  5213     return nullptr;
  5216   return shell->GetPresContext();
  5219 nsIPresShell*
  5220 Selection::GetPresShell() const
  5222   if (!mFrameSelection)
  5223     return nullptr;//nothing to do
  5225   return mFrameSelection->GetShell();
  5228 nsIFrame *
  5229 Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect)
  5231   if (!mFrameSelection)
  5232     return nullptr;  // nothing to do
  5234   NS_ENSURE_TRUE(aRect, nullptr);
  5236   aRect->SetRect(0, 0, 0, 0);
  5238   switch (aRegion) {
  5239     case nsISelectionController::SELECTION_ANCHOR_REGION:
  5240     case nsISelectionController::SELECTION_FOCUS_REGION:
  5241       return GetSelectionEndPointGeometry(aRegion, aRect);
  5242       break;
  5243     case nsISelectionController::SELECTION_WHOLE_SELECTION:
  5244       break;
  5245     default:
  5246       return nullptr;
  5249   NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION,
  5250     "should only be SELECTION_WHOLE_SELECTION here");
  5252   nsRect anchorRect;
  5253   nsIFrame* anchorFrame = GetSelectionEndPointGeometry(
  5254     nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect);
  5255   if (!anchorFrame)
  5256     return nullptr;
  5258   nsRect focusRect;
  5259   nsIFrame* focusFrame = GetSelectionEndPointGeometry(
  5260     nsISelectionController::SELECTION_FOCUS_REGION, &focusRect);
  5261   if (!focusFrame)
  5262     return nullptr;
  5264   NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(),
  5265     "points of selection in different documents?");
  5266   // make focusRect relative to anchorFrame
  5267   focusRect += focusFrame->GetOffsetTo(anchorFrame);
  5269   aRect->UnionRectEdges(anchorRect, focusRect);
  5270   return anchorFrame;
  5273 nsIFrame *
  5274 Selection::GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect* aRect)
  5276   if (!mFrameSelection)
  5277     return nullptr;  // nothing to do
  5279   NS_ENSURE_TRUE(aRect, nullptr);
  5281   aRect->SetRect(0, 0, 0, 0);
  5283   nsINode    *node       = nullptr;
  5284   uint32_t    nodeOffset = 0;
  5285   nsIFrame   *frame      = nullptr;
  5287   switch (aRegion) {
  5288     case nsISelectionController::SELECTION_ANCHOR_REGION:
  5289       node       = GetAnchorNode();
  5290       nodeOffset = AnchorOffset();
  5291       break;
  5292     case nsISelectionController::SELECTION_FOCUS_REGION:
  5293       node       = GetFocusNode();
  5294       nodeOffset = FocusOffset();
  5295       break;
  5296     default:
  5297       return nullptr;
  5300   if (!node)
  5301     return nullptr;
  5303   nsCOMPtr<nsIContent> content = do_QueryInterface(node);
  5304   NS_ENSURE_TRUE(content.get(), nullptr);
  5305   int32_t frameOffset = 0;
  5306   frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset,
  5307                                                  mFrameSelection->GetHint(),
  5308                                                  &frameOffset);
  5309   if (!frame)
  5310     return nullptr;
  5312   // Figure out what node type we have, then get the
  5313   // appropriate rect for it's nodeOffset.
  5314   bool isText = node->IsNodeOfType(nsINode::eTEXT);
  5316   nsPoint pt(0, 0);
  5317   if (isText) {
  5318     nsIFrame* childFrame = nullptr;
  5319     frameOffset = 0;
  5320     nsresult rv =
  5321       frame->GetChildFrameContainingOffset(nodeOffset,
  5322                                            mFrameSelection->GetHint(),
  5323                                            &frameOffset, &childFrame);
  5324     if (NS_FAILED(rv))
  5325       return nullptr;
  5326     if (!childFrame)
  5327       return nullptr;
  5329     frame = childFrame;
  5331     // Get the x coordinate of the offset into the text frame.
  5332     rv = GetCachedFrameOffset(frame, nodeOffset, pt);
  5333     if (NS_FAILED(rv))
  5334       return nullptr;
  5337   // Return the rect relative to the frame, with zero width.
  5338   if (isText) {
  5339     aRect->x = pt.x;
  5340   } else if (mFrameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
  5341     // It's the frame's right edge we're interested in.
  5342     aRect->x = frame->GetRect().width;
  5344   aRect->height = frame->GetRect().height;
  5346   return frame;
  5349 NS_IMETHODIMP
  5350 Selection::ScrollSelectionIntoViewEvent::Run()
  5352   if (!mSelection)
  5353     return NS_OK;  // event revoked
  5355   int32_t flags = Selection::SCROLL_DO_FLUSH |
  5356                   Selection::SCROLL_SYNCHRONOUS;
  5358   mSelection->mScrollEvent.Forget();
  5359   mSelection->ScrollIntoView(mRegion, mVerticalScroll,
  5360                              mHorizontalScroll, mFlags | flags);
  5361   return NS_OK;
  5364 nsresult
  5365 Selection::PostScrollSelectionIntoViewEvent(
  5366                                          SelectionRegion aRegion,
  5367                                          int32_t aFlags,
  5368                                          nsIPresShell::ScrollAxis aVertical,
  5369                                          nsIPresShell::ScrollAxis aHorizontal)
  5371   // If we've already posted an event, revoke it and place a new one at the
  5372   // end of the queue to make sure that any new pending reflow events are
  5373   // processed before we scroll. This will insure that we scroll to the
  5374   // correct place on screen.
  5375   mScrollEvent.Revoke();
  5377   nsRefPtr<ScrollSelectionIntoViewEvent> ev =
  5378       new ScrollSelectionIntoViewEvent(this, aRegion, aVertical, aHorizontal,
  5379                                        aFlags);
  5380   nsresult rv = NS_DispatchToCurrentThread(ev);
  5381   NS_ENSURE_SUCCESS(rv, rv);
  5383   mScrollEvent = ev;
  5384   return NS_OK;
  5387 NS_IMETHODIMP
  5388 Selection::ScrollIntoView(SelectionRegion aRegion, bool aIsSynchronous,
  5389                           int16_t aVPercent, int16_t aHPercent)
  5391   ErrorResult result;
  5392   ScrollIntoView(aRegion, aIsSynchronous, aVPercent, aHPercent, result);
  5393   if (result.Failed()) {
  5394     return result.ErrorCode();
  5396   return NS_OK;
  5399 void
  5400 Selection::ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
  5401                           int16_t aVPercent, int16_t aHPercent,
  5402                           ErrorResult& aRv)
  5404   nsresult rv = ScrollIntoViewInternal(aRegion, aIsSynchronous,
  5405                                        nsIPresShell::ScrollAxis(aVPercent),
  5406                                        nsIPresShell::ScrollAxis(aHPercent));
  5407   if (NS_FAILED(rv)) {
  5408     aRv.Throw(rv);
  5412 NS_IMETHODIMP
  5413 Selection::ScrollIntoViewInternal(SelectionRegion aRegion, bool aIsSynchronous,
  5414                                   nsIPresShell::ScrollAxis aVertical,
  5415                                   nsIPresShell::ScrollAxis aHorizontal)
  5417   return ScrollIntoView(aRegion, aVertical, aHorizontal,
  5418                         aIsSynchronous ? Selection::SCROLL_SYNCHRONOUS : 0);
  5421 nsresult
  5422 Selection::ScrollIntoView(SelectionRegion aRegion,
  5423                           nsIPresShell::ScrollAxis aVertical,
  5424                           nsIPresShell::ScrollAxis aHorizontal,
  5425                           int32_t aFlags)
  5427   if (!mFrameSelection)
  5428     return NS_OK;//nothing to do
  5430   nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell();
  5431   if (!presShell)
  5432     return NS_OK;
  5434   if (mFrameSelection->GetBatching())
  5435     return NS_OK;
  5437   if (!(aFlags & Selection::SCROLL_SYNCHRONOUS))
  5438     return PostScrollSelectionIntoViewEvent(aRegion, aFlags,
  5439       aVertical, aHorizontal);
  5441   // Now that text frame character offsets are always valid (though not
  5442   // necessarily correct), the worst that will happen if we don't flush here
  5443   // is that some callers might scroll to the wrong place.  Those should
  5444   // either manually flush if they're in a safe position for it or use the
  5445   // async version of this method.
  5446   if (aFlags & Selection::SCROLL_DO_FLUSH) {
  5447     presShell->FlushPendingNotifications(Flush_Layout);
  5449     // Reget the presshell, since it might have been Destroy'ed.
  5450     presShell = mFrameSelection ? mFrameSelection->GetShell() : nullptr;
  5451     if (!presShell)
  5452       return NS_OK;
  5455   //
  5456   // Scroll the selection region into view.
  5457   //
  5459   nsRect rect;
  5460   nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
  5461   if (!frame)
  5462     return NS_ERROR_FAILURE;
  5464   // Scroll vertically to get the caret into view, but only if the container
  5465   // is perceived to be scrollable in that direction (i.e. there is a visible
  5466   // vertical scrollbar or the scroll range is at least one device pixel)
  5467   aVertical.mOnlyIfPerceivedScrollableDirection = true;
  5469   uint32_t flags = 0;
  5470   if (aFlags & Selection::SCROLL_FIRST_ANCESTOR_ONLY) {
  5471     flags |= nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY;
  5473   if (aFlags & Selection::SCROLL_OVERFLOW_HIDDEN) {
  5474     flags |= nsIPresShell::SCROLL_OVERFLOW_HIDDEN;
  5477   presShell->ScrollFrameRectIntoView(frame, rect, aVertical, aHorizontal,
  5478     flags);
  5479   return NS_OK;
  5482 NS_IMETHODIMP
  5483 Selection::AddSelectionListener(nsISelectionListener* aNewListener)
  5485   if (!aNewListener)
  5486     return NS_ERROR_NULL_POINTER;
  5487   ErrorResult result;
  5488   AddSelectionListener(aNewListener, result);
  5489   if (result.Failed()) {
  5490     return result.ErrorCode();
  5492   return NS_OK;
  5495 void
  5496 Selection::AddSelectionListener(nsISelectionListener* aNewListener,
  5497                                 ErrorResult& aRv)
  5499   bool result = mSelectionListeners.AppendObject(aNewListener); // AddRefs
  5500   if (!result) {
  5501     aRv.Throw(NS_ERROR_FAILURE);
  5505 NS_IMETHODIMP
  5506 Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
  5508   if (!aListenerToRemove)
  5509     return NS_ERROR_NULL_POINTER;
  5510   ErrorResult result;
  5511   RemoveSelectionListener(aListenerToRemove, result);
  5512   if (result.Failed()) {
  5513     return result.ErrorCode();
  5515   return NS_OK;
  5518 void
  5519 Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove,
  5520                                    ErrorResult& aRv)
  5522   bool result = mSelectionListeners.RemoveObject(aListenerToRemove); // Releases
  5523   if (!result) {
  5524     aRv.Throw(NS_ERROR_FAILURE);
  5528 nsresult
  5529 Selection::NotifySelectionListeners()
  5531   if (!mFrameSelection)
  5532     return NS_OK;//nothing to do
  5534   if (mFrameSelection->GetBatching()) {
  5535     mFrameSelection->SetDirty();
  5536     return NS_OK;
  5538   nsCOMArray<nsISelectionListener> selectionListeners(mSelectionListeners);
  5539   int32_t cnt = selectionListeners.Count();
  5540   if (cnt != mSelectionListeners.Count()) {
  5541     return NS_ERROR_OUT_OF_MEMORY;  // nsCOMArray is fallible
  5544   nsCOMPtr<nsIDOMDocument> domdoc;
  5545   nsIPresShell* ps = GetPresShell();
  5546   if (ps) {
  5547     domdoc = do_QueryInterface(ps->GetDocument());
  5550   short reason = mFrameSelection->PopReason();
  5551   for (int32_t i = 0; i < cnt; i++) {
  5552     selectionListeners[i]->NotifySelectionChanged(domdoc, this, reason);
  5554   return NS_OK;
  5557 NS_IMETHODIMP
  5558 Selection::StartBatchChanges()
  5560   if (mFrameSelection)
  5561     mFrameSelection->StartBatchChanges();
  5563   return NS_OK;
  5568 NS_IMETHODIMP
  5569 Selection::EndBatchChanges()
  5571   if (mFrameSelection)
  5572     mFrameSelection->EndBatchChanges();
  5574   return NS_OK;
  5579 NS_IMETHODIMP
  5580 Selection::DeleteFromDocument()
  5582   ErrorResult result;
  5583   DeleteFromDocument(result);
  5584   return result.ErrorCode();
  5587 void
  5588 Selection::DeleteFromDocument(ErrorResult& aRv)
  5590   if (!mFrameSelection)
  5591     return;//nothing to do
  5592   nsresult rv = mFrameSelection->DeleteFromDocument();
  5593   if (NS_FAILED(rv)) {
  5594     aRv.Throw(rv);
  5598 NS_IMETHODIMP
  5599 Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
  5600                   const nsAString& aGranularity)
  5602   ErrorResult result;
  5603   Modify(aAlter, aDirection, aGranularity, result);
  5604   return result.ErrorCode();
  5607 void
  5608 Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
  5609                   const nsAString& aGranularity, ErrorResult& aRv)
  5611   // Silently exit if there's no selection or no focus node.
  5612   if (!mFrameSelection || !GetAnchorFocusRange() || !GetFocusNode()) {
  5613     return;
  5616   if (!aAlter.LowerCaseEqualsLiteral("move") &&
  5617       !aAlter.LowerCaseEqualsLiteral("extend")) {
  5618     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
  5619     return;
  5622   if (!aDirection.LowerCaseEqualsLiteral("forward") &&
  5623       !aDirection.LowerCaseEqualsLiteral("backward") &&
  5624       !aDirection.LowerCaseEqualsLiteral("left") &&
  5625       !aDirection.LowerCaseEqualsLiteral("right")) {
  5626     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
  5627     return;
  5630   // Line moves are always visual.
  5631   bool visual  = aDirection.LowerCaseEqualsLiteral("left") ||
  5632                    aDirection.LowerCaseEqualsLiteral("right") ||
  5633                    aGranularity.LowerCaseEqualsLiteral("line");
  5635   bool forward = aDirection.LowerCaseEqualsLiteral("forward") ||
  5636                    aDirection.LowerCaseEqualsLiteral("right");
  5638   bool extend  = aAlter.LowerCaseEqualsLiteral("extend");
  5640   // The uint32_t casts below prevent an enum mismatch warning.
  5641   nsSelectionAmount amount;
  5642   uint32_t keycode;
  5643   if (aGranularity.LowerCaseEqualsLiteral("character")) {
  5644     amount = eSelectCluster;
  5645     keycode = forward ? (uint32_t) nsIDOMKeyEvent::DOM_VK_RIGHT :
  5646                         (uint32_t) nsIDOMKeyEvent::DOM_VK_LEFT;
  5648   else if (aGranularity.LowerCaseEqualsLiteral("word")) {
  5649     amount = eSelectWordNoSpace;
  5650     keycode = forward ? (uint32_t) nsIDOMKeyEvent::DOM_VK_RIGHT :
  5651                         (uint32_t) nsIDOMKeyEvent::DOM_VK_LEFT;
  5653   else if (aGranularity.LowerCaseEqualsLiteral("line")) {
  5654     amount = eSelectLine;
  5655     keycode = forward ? (uint32_t) nsIDOMKeyEvent::DOM_VK_DOWN :
  5656                         (uint32_t) nsIDOMKeyEvent::DOM_VK_UP;
  5658   else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) {
  5659     amount = eSelectLine;
  5660     keycode = forward ? (uint32_t) nsIDOMKeyEvent::DOM_VK_END :
  5661                         (uint32_t) nsIDOMKeyEvent::DOM_VK_HOME;
  5663   else if (aGranularity.LowerCaseEqualsLiteral("sentence") ||
  5664            aGranularity.LowerCaseEqualsLiteral("sentenceboundary") ||
  5665            aGranularity.LowerCaseEqualsLiteral("paragraph") ||
  5666            aGranularity.LowerCaseEqualsLiteral("paragraphboundary") ||
  5667            aGranularity.LowerCaseEqualsLiteral("documentboundary")) {
  5668     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
  5670   else {
  5671     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
  5672     return;
  5675   // If the anchor doesn't equal the focus and we try to move without first
  5676   // collapsing the selection, MoveCaret will collapse the selection and quit.
  5677   // To avoid this, we need to collapse the selection first.
  5678   nsresult rv = NS_OK;
  5679   if (!extend) {
  5680     nsINode* focusNode = GetFocusNode();
  5681     // We should have checked earlier that there was a focus node.
  5682     if (!focusNode) {
  5683       aRv.Throw(NS_ERROR_UNEXPECTED);
  5684       return;
  5686     uint32_t focusOffset = FocusOffset();
  5687     Collapse(focusNode, focusOffset);
  5690   // If the base level of the focused frame is odd, we may have to swap the
  5691   // direction of the keycode.
  5692   nsIFrame *frame;
  5693   int32_t offset;
  5694   rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual);
  5695   if (NS_SUCCEEDED(rv) && frame) {
  5696     nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame);
  5698     if (baseLevel & 1) {
  5699       if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_RIGHT) {
  5700         keycode = nsIDOMKeyEvent::DOM_VK_LEFT;
  5702       else if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_LEFT) {
  5703         keycode = nsIDOMKeyEvent::DOM_VK_RIGHT;
  5705       else if (visual && keycode == nsIDOMKeyEvent::DOM_VK_HOME) {
  5706         keycode = nsIDOMKeyEvent::DOM_VK_END;
  5708       else if (visual && keycode == nsIDOMKeyEvent::DOM_VK_END) {
  5709         keycode = nsIDOMKeyEvent::DOM_VK_HOME;
  5714   // MoveCaret will return an error if it can't move in the specified
  5715   // direction, but we just ignore this error unless it's a line move, in which
  5716   // case we call nsISelectionController::CompleteMove to move the cursor to
  5717   // the beginning/end of the line.
  5718   rv = mFrameSelection->MoveCaret(keycode, extend, amount, visual);
  5720   if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) {
  5721     nsCOMPtr<nsISelectionController> shell =
  5722       do_QueryInterface(mFrameSelection->GetShell());
  5723     if (!shell)
  5724       return;
  5725     shell->CompleteMove(forward, extend);
  5729 /** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction
  5730  *  @param aLangRTL is true if the new language is right-to-left or false if the new language is left-to-right
  5731  */
  5732 NS_IMETHODIMP
  5733 Selection::SelectionLanguageChange(bool aLangRTL)
  5735   if (!mFrameSelection)
  5736     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
  5737   nsresult result;
  5738   nsIFrame *focusFrame = 0;
  5740   result = GetPrimaryFrameForFocusNode(&focusFrame, nullptr, false);
  5741   if (NS_FAILED(result)) {
  5742     return result;
  5744   if (!focusFrame) {
  5745     return NS_ERROR_FAILURE;
  5748   int32_t frameStart, frameEnd;
  5749   focusFrame->GetOffsets(frameStart, frameEnd);
  5750   nsRefPtr<nsPresContext> context = GetPresContext();
  5751   uint8_t levelBefore, levelAfter;
  5752   if (!context) {
  5753     return NS_ERROR_FAILURE;
  5756   uint8_t level = NS_GET_EMBEDDING_LEVEL(focusFrame);
  5757   int32_t focusOffset = static_cast<int32_t>(FocusOffset());
  5758   if ((focusOffset != frameStart) && (focusOffset != frameEnd))
  5759     // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
  5760     //  is equal to the frame level
  5761     levelBefore = levelAfter = level;
  5762   else {
  5763     // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
  5764     //  before and after the cursor
  5765     nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
  5766     /*
  5767     nsFrameSelection::HINT hint;
  5769     if ((focusOffset == frameStart && level)        // beginning of an RTL frame
  5770         || (focusOffset == frameEnd && !level)) {   // end of an LTR frame
  5771       hint = nsFrameSelection::HINTRIGHT;
  5773     else {                                          // end of an RTL frame or beginning of an LTR frame
  5774       hint = nsFrameSelection::HINTLEFT;
  5776     mFrameSelection->SetHint(hint);
  5777     */
  5778     nsPrevNextBidiLevels levels = mFrameSelection->
  5779       GetPrevNextBidiLevels(focusContent, focusOffset, false);
  5781     levelBefore = levels.mLevelBefore;
  5782     levelAfter = levels.mLevelAfter;
  5785   if ((levelBefore & 1) == (levelAfter & 1)) {
  5786     // if cursor is between two characters with the same orientation, changing the keyboard language
  5787     //  must toggle the cursor level between the level of the character with the lowest level
  5788     //  (if the new language corresponds to the orientation of that character) and this level plus 1
  5789     //  (if the new language corresponds to the opposite orientation)
  5790     if ((level != levelBefore) && (level != levelAfter))
  5791       level = std::min(levelBefore, levelAfter);
  5792     if ((level & 1) == aLangRTL)
  5793       mFrameSelection->SetCaretBidiLevel(level);
  5794     else
  5795       mFrameSelection->SetCaretBidiLevel(level + 1);
  5797   else {
  5798     // if cursor is between characters with opposite orientations, changing the keyboard language must change
  5799     //  the cursor level to that of the adjacent character with the orientation corresponding to the new language.
  5800     if ((levelBefore & 1) == aLangRTL)
  5801       mFrameSelection->SetCaretBidiLevel(levelBefore);
  5802     else
  5803       mFrameSelection->SetCaretBidiLevel(levelAfter);
  5806   // The caret might have moved, so invalidate the desired X position
  5807   // for future usages of up-arrow or down-arrow
  5808   mFrameSelection->InvalidateDesiredX();
  5810   return NS_OK;
  5813 NS_IMETHODIMP_(nsDirection)
  5814 Selection::GetSelectionDirection() {
  5815   return mDirection;
  5818 NS_IMETHODIMP_(void)
  5819 Selection::SetSelectionDirection(nsDirection aDirection) {
  5820   mDirection = aDirection;
  5823 JSObject*
  5824 Selection::WrapObject(JSContext* aCx)
  5826   return mozilla::dom::SelectionBinding::Wrap(aCx, this);
  5829 // nsAutoCopyListener
  5831 nsAutoCopyListener* nsAutoCopyListener::sInstance = nullptr;
  5833 NS_IMPL_ISUPPORTS(nsAutoCopyListener, nsISelectionListener)
  5835 /*
  5836  * What we do now:
  5837  * On every selection change, we copy to the clipboard anew, creating a
  5838  * HTML buffer, a transferable, an nsISupportsString and
  5839  * a huge mess every time.  This is basically what nsPresShell::DoCopy does
  5840  * to move the selection into the clipboard for Edit->Copy.
  5842  * What we should do, to make our end of the deal faster:
  5843  * Create a singleton transferable with our own magic converter.  When selection
  5844  * changes (use a quick cache to detect ``real'' changes), we put the new
  5845  * nsISelection in the transferable.  Our magic converter will take care of
  5846  * transferable->whatever-other-format when the time comes to actually
  5847  * hand over the clipboard contents.
  5849  * Other issues:
  5850  * - which X clipboard should we populate?
  5851  * - should we use a different one than Edit->Copy, so that inadvertant
  5852  *   selections (or simple clicks, which currently cause a selection
  5853  *   notification, regardless of if they're in the document which currently has
  5854  *   selection!) don't lose the contents of the ``application''?  Or should we
  5855  *   just put some intelligence in the ``is this a real selection?'' code to
  5856  *   protect our selection against clicks in other documents that don't create
  5857  *   selections?
  5858  * - maybe we should just never clear the X clipboard?  That would make this 
  5859  *   problem just go away, which is very tempting.
  5860  */
  5862 NS_IMETHODIMP
  5863 nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc,
  5864                                            nsISelection *aSel, int16_t aReason)
  5866   if (!(aReason & nsISelectionListener::MOUSEUP_REASON   || 
  5867         aReason & nsISelectionListener::SELECTALL_REASON ||
  5868         aReason & nsISelectionListener::KEYPRESS_REASON))
  5869     return NS_OK; //dont care if we are still dragging
  5871   bool collapsed;
  5872   if (!aDoc || !aSel ||
  5873       NS_FAILED(aSel->GetIsCollapsed(&collapsed)) || collapsed) {
  5874 #ifdef DEBUG_CLIPBOARD
  5875     fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n");
  5876 #endif
  5877     /* clear X clipboard? */
  5878     return NS_OK;
  5881   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
  5882   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
  5884   // call the copy code
  5885   return nsCopySupport::HTMLCopy(aSel, doc, nsIClipboard::kSelectionClipboard);

mercurial