LCOV - code coverage report
Current view: top level - dom/base - Selection.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 556 1942 28.6 %
Date: 2017-07-14 16:53:18 Functions: 75 172 43.6 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : /*
       8             :  * Implementation of mozilla::dom::Selection
       9             :  */
      10             : 
      11             : #include "mozilla/dom/Selection.h"
      12             : 
      13             : #include "mozilla/Attributes.h"
      14             : #include "mozilla/AutoRestore.h"
      15             : #include "mozilla/EventStates.h"
      16             : 
      17             : #include "nsCOMPtr.h"
      18             : #include "nsString.h"
      19             : #include "nsFrameSelection.h"
      20             : #include "nsISelectionListener.h"
      21             : #include "nsContentCID.h"
      22             : #include "nsDeviceContext.h"
      23             : #include "nsIContent.h"
      24             : #include "nsIDOMNode.h"
      25             : #include "nsRange.h"
      26             : #include "nsITableCellLayout.h"
      27             : #include "nsTArray.h"
      28             : #include "nsTableWrapperFrame.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>
      36             : #include "nsContentUtils.h"
      37             : 
      38             : #include "nsGkAtoms.h"
      39             : #include "nsLayoutUtils.h"
      40             : #include "nsBidiPresUtils.h"
      41             : #include "nsTextFrame.h"
      42             : 
      43             : #include "nsIDOMText.h"
      44             : 
      45             : #include "nsContentUtils.h"
      46             : #include "nsThreadUtils.h"
      47             : 
      48             : #include "nsPresContext.h"
      49             : #include "nsIPresShell.h"
      50             : #include "nsCaret.h"
      51             : 
      52             : #include "nsITimer.h"
      53             : #include "nsIDOMDocument.h"
      54             : #include "nsIDocument.h"
      55             : 
      56             : #include "nsISelectionController.h"//for the enums
      57             : #include "nsAutoCopyListener.h"
      58             : #include "SelectionChangeListener.h"
      59             : #include "nsCopySupport.h"
      60             : #include "nsIClipboard.h"
      61             : #include "nsIFrameInlines.h"
      62             : #include "nsRefreshDriver.h"
      63             : #include "nsIBidiKeyboard.h"
      64             : 
      65             : #include "nsError.h"
      66             : #include "mozilla/dom/Element.h"
      67             : #include "mozilla/dom/ShadowRoot.h"
      68             : #include "mozilla/ErrorResult.h"
      69             : #include "mozilla/dom/SelectionBinding.h"
      70             : #include "mozilla/AsyncEventDispatcher.h"
      71             : #include "mozilla/Telemetry.h"
      72             : #include "mozilla/layers/ScrollInputMethods.h"
      73             : #include "nsViewManager.h"
      74             : 
      75             : #include "nsIEditor.h"
      76             : #include "nsIHTMLEditor.h"
      77             : #include "nsFocusManager.h"
      78             : #include "nsPIDOMWindow.h"
      79             : 
      80             : using namespace mozilla;
      81             : using namespace mozilla::dom;
      82             : using mozilla::layers::ScrollInputMethod;
      83             : 
      84             : //#define DEBUG_TABLE 1
      85             : 
      86             : static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
      87             : 
      88             : #ifdef PRINT_RANGE
      89             : static void printRange(nsRange *aDomRange);
      90             : #define DEBUG_OUT_RANGE(x)  printRange(x)
      91             : #else
      92             : #define DEBUG_OUT_RANGE(x)
      93             : #endif // PRINT_RANGE
      94             : 
      95             : /******************************************************************************
      96             :  * Utility methods defined in nsISelectionController.idl
      97             :  ******************************************************************************/
      98             : 
      99             : namespace mozilla {
     100             : 
     101             : const char*
     102           0 : ToChar(SelectionType aSelectionType)
     103             : {
     104           0 :   switch (aSelectionType) {
     105             :     case SelectionType::eInvalid:
     106           0 :       return "SelectionType::eInvalid";
     107             :     case SelectionType::eNone:
     108           0 :       return "SelectionType::eNone";
     109             :     case SelectionType::eNormal:
     110           0 :       return "SelectionType::eNormal";
     111             :     case SelectionType::eSpellCheck:
     112           0 :       return "SelectionType::eSpellCheck";
     113             :     case SelectionType::eIMERawClause:
     114           0 :       return "SelectionType::eIMERawClause";
     115             :     case SelectionType::eIMESelectedRawClause:
     116           0 :       return "SelectionType::eIMESelectedRawClause";
     117             :     case SelectionType::eIMEConvertedClause:
     118           0 :       return "SelectionType::eIMEConvertedClause";
     119             :     case SelectionType::eIMESelectedClause:
     120           0 :       return "SelectionType::eIMESelectedClause";
     121             :     case SelectionType::eAccessibility:
     122           0 :       return "SelectionType::eAccessibility";
     123             :     case SelectionType::eFind:
     124           0 :       return "SelectionType::eFind";
     125             :     case SelectionType::eURLSecondary:
     126           0 :       return "SelectionType::eURLSecondary";
     127             :     case SelectionType::eURLStrikeout:
     128           0 :       return "SelectionType::eURLStrikeout";
     129             :     default:
     130           0 :       return "Invalid SelectionType";
     131             :   }
     132             : }
     133             : 
     134             : static bool
     135         108 : IsValidSelectionType(RawSelectionType aRawSelectionType)
     136             : {
     137         108 :   switch (static_cast<SelectionType>(aRawSelectionType)) {
     138             :     case SelectionType::eNone:
     139             :     case SelectionType::eNormal:
     140             :     case SelectionType::eSpellCheck:
     141             :     case SelectionType::eIMERawClause:
     142             :     case SelectionType::eIMESelectedRawClause:
     143             :     case SelectionType::eIMEConvertedClause:
     144             :     case SelectionType::eIMESelectedClause:
     145             :     case SelectionType::eAccessibility:
     146             :     case SelectionType::eFind:
     147             :     case SelectionType::eURLSecondary:
     148             :     case SelectionType::eURLStrikeout:
     149         108 :       return true;
     150             :     default:
     151           0 :       return false;
     152             :   }
     153             : }
     154             : 
     155             : SelectionType
     156         108 : ToSelectionType(RawSelectionType aRawSelectionType)
     157             : {
     158         108 :   if (!IsValidSelectionType(aRawSelectionType)) {
     159           0 :     return SelectionType::eInvalid;
     160             :   }
     161         108 :   return static_cast<SelectionType>(aRawSelectionType);
     162             : }
     163             : 
     164             : RawSelectionType
     165          37 : ToRawSelectionType(SelectionType aSelectionType)
     166             : {
     167          37 :   return static_cast<RawSelectionType>(aSelectionType);
     168             : }
     169             : 
     170          14 : bool operator &(SelectionType aSelectionType,
     171             :                 RawSelectionType aRawSelectionTypes)
     172             : {
     173          14 :   return (ToRawSelectionType(aSelectionType) & aRawSelectionTypes) != 0;
     174             : }
     175             : 
     176             : } // namespace mozilla
     177             : 
     178             : //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
     179             : //#define DEBUG_NAVIGATION
     180             : 
     181             : 
     182             : //#define DEBUG_TABLE_SELECTION 1
     183             : 
     184             : struct CachedOffsetForFrame {
     185           1 :   CachedOffsetForFrame()
     186           1 :   : mCachedFrameOffset(0, 0) // nsPoint ctor
     187             :   , mLastCaretFrame(nullptr)
     188             :   , mLastContentOffset(0)
     189           1 :   , mCanCacheFrameOffset(false)
     190           1 :   {}
     191             : 
     192             :   nsPoint      mCachedFrameOffset;      // cached frame offset
     193             :   nsIFrame*    mLastCaretFrame;         // store the frame the caret was last drawn in.
     194             :   int32_t      mLastContentOffset;      // store last content offset
     195             :   bool mCanCacheFrameOffset;    // cached frame offset is valid?
     196             : };
     197             : 
     198             : class nsAutoScrollTimer final : public nsITimerCallback
     199             : {
     200             : public:
     201             : 
     202             :   NS_DECL_ISUPPORTS
     203             : 
     204           0 :   nsAutoScrollTimer()
     205           0 :   : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30)
     206             :   {
     207           0 :   }
     208             : 
     209             :   // aPoint is relative to aPresContext's root frame
     210           0 :   nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
     211             :   {
     212           0 :     mPoint = aPoint;
     213             : 
     214             :     // Store the presentation context. The timer will be
     215             :     // stopped by the selection if the prescontext is destroyed.
     216           0 :     mPresContext = aPresContext;
     217             : 
     218           0 :     mContent = nsIPresShell::GetCapturingContent();
     219             : 
     220           0 :     if (!mTimer) {
     221             :       nsresult result;
     222           0 :       mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
     223           0 :       mTimer->SetTarget(
     224           0 :         mPresContext->Document()->EventTargetFor(TaskCategory::Other));
     225             : 
     226           0 :       if (NS_FAILED(result)) {
     227           0 :         return result;
     228             :       }
     229             :     }
     230             : 
     231           0 :     return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
     232             :   }
     233             : 
     234           0 :   nsresult Stop()
     235             :   {
     236           0 :     if (mTimer) {
     237           0 :       mTimer->Cancel();
     238           0 :       mTimer = nullptr;
     239             :     }
     240             : 
     241           0 :     mContent = nullptr;
     242           0 :     return NS_OK;
     243             :   }
     244             : 
     245           0 :   nsresult Init(nsFrameSelection* aFrameSelection, Selection* aSelection)
     246             :   {
     247           0 :     mFrameSelection = aFrameSelection;
     248           0 :     mSelection = aSelection;
     249           0 :     return NS_OK;
     250             :   }
     251             : 
     252           0 :   nsresult SetDelay(uint32_t aDelay)
     253             :   {
     254           0 :     mDelay = aDelay;
     255           0 :     return NS_OK;
     256             :   }
     257             : 
     258           0 :   NS_IMETHOD Notify(nsITimer *timer) override
     259             :   {
     260           0 :     if (mSelection && mPresContext)
     261             :     {
     262             :       AutoWeakFrame frame =
     263           0 :         mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
     264           0 :       if (!frame) {
     265           0 :         return NS_OK;
     266             :       }
     267           0 :       mContent = nullptr;
     268             : 
     269             :       nsPoint pt = mPoint -
     270           0 :         frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
     271           0 :       RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
     272           0 :       frameSelection->HandleDrag(frame, pt);
     273           0 :       if (!frame.IsAlive()) {
     274           0 :         return NS_OK;
     275             :       }
     276             : 
     277           0 :       NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
     278           0 :       mSelection->DoAutoScroll(frame, pt);
     279             :     }
     280           0 :     return NS_OK;
     281             :   }
     282             : 
     283             : protected:
     284           0 :   virtual ~nsAutoScrollTimer()
     285           0 :   {
     286           0 :     if (mTimer) {
     287           0 :       mTimer->Cancel();
     288             :     }
     289           0 :   }
     290             : 
     291             : private:
     292             :   nsFrameSelection *mFrameSelection;
     293             :   Selection* mSelection;
     294             :   nsPresContext *mPresContext;
     295             :   // relative to mPresContext's root frame
     296             :   nsPoint mPoint;
     297             :   nsCOMPtr<nsITimer> mTimer;
     298             :   nsCOMPtr<nsIContent> mContent;
     299             :   uint32_t mDelay;
     300             : };
     301             : 
     302           0 : NS_IMPL_ISUPPORTS(nsAutoScrollTimer, nsITimerCallback)
     303             : 
     304           0 : nsresult NS_NewDomSelection(nsISelection **aDomSelection)
     305             : {
     306           0 :   Selection* rlist = new Selection;
     307           0 :   *aDomSelection = (nsISelection *)rlist;
     308           0 :   NS_ADDREF(rlist);
     309           0 :   return NS_OK;
     310             : }
     311             : 
     312             : /*
     313             : The limiter is used specifically for the text areas and textfields
     314             : In that case it is the DIV tag that is anonymously created for the text
     315             : areas/fields.  Text nodes and BR nodes fall beneath it.  In the case of a
     316             : BR node the limiter will be the parent and the offset will point before or
     317             : after the BR node.  In the case of the text node the parent content is
     318             : the text node itself and the offset will be the exact character position.
     319             : The offset is not important to check for validity.  Simply look at the
     320             : passed in content.  If it equals the limiter then the selection point is valid.
     321             : If its parent it the limiter then the point is also valid.  In the case of
     322             : NO limiter all points are valid since you are in a topmost iframe. (browser
     323             : or composer)
     324             : */
     325             : bool
     326           7 : IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode)
     327             : {
     328           7 :   if (!aFrameSel || !aNode)
     329           0 :     return false;
     330             : 
     331           7 :   nsIContent *limiter = aFrameSel->GetLimiter();
     332           7 :   if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
     333             :     //if newfocus == the limiter. that's ok. but if not there and not parent bad
     334           0 :     return false; //not in the right content. tLimiter said so
     335             :   }
     336             : 
     337           7 :   limiter = aFrameSel->GetAncestorLimiter();
     338           7 :   return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
     339             : }
     340             : 
     341             : namespace mozilla {
     342             : struct MOZ_RAII AutoPrepareFocusRange
     343             : {
     344             :   AutoPrepareFocusRange(Selection* aSelection,
     345             :                         bool aContinueSelection,
     346             :                         bool aMultipleSelection
     347             :                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     348             :   {
     349             :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     350             : 
     351             :     if (aSelection->mRanges.Length() <= 1) {
     352             :       return;
     353             :     }
     354             : 
     355             :     if (aSelection->mFrameSelection->IsUserSelectionReason()) {
     356             :       mUserSelect.emplace(aSelection);
     357             :     }
     358             :     bool userSelection = aSelection->mUserInitiated;
     359             : 
     360             :     nsTArray<RangeData>& ranges = aSelection->mRanges;
     361             :     if (!userSelection ||
     362             :         (!aContinueSelection && aMultipleSelection)) {
     363             :       // Scripted command or the user is starting a new explicit multi-range
     364             :       // selection.
     365             :       for (RangeData& entry : ranges) {
     366             :         entry.mRange->SetIsGenerated(false);
     367             :       }
     368             :       return;
     369             :     }
     370             : 
     371             :     int16_t reason = aSelection->mFrameSelection->mSelectionChangeReason;
     372             :     bool isAnchorRelativeOp = (reason & (nsISelectionListener::DRAG_REASON |
     373             :                                          nsISelectionListener::MOUSEDOWN_REASON |
     374             :                                          nsISelectionListener::MOUSEUP_REASON |
     375             :                                          nsISelectionListener::COLLAPSETOSTART_REASON));
     376             :     if (!isAnchorRelativeOp) {
     377             :       return;
     378             :     }
     379             : 
     380             :     // This operation is against the anchor but our current mAnchorFocusRange
     381             :     // represents the focus in a multi-range selection.  The anchor from a user
     382             :     // perspective is the most distant generated range on the opposite side.
     383             :     // Find that range and make it the mAnchorFocusRange.
     384             :     const size_t len = ranges.Length();
     385             :     size_t newAnchorFocusIndex = size_t(-1);
     386             :     if (aSelection->GetDirection() == eDirNext) {
     387             :       for (size_t i = 0; i < len; ++i) {
     388             :         if (ranges[i].mRange->IsGenerated()) {
     389             :           newAnchorFocusIndex = i;
     390             :           break;
     391             :         }
     392             :       }
     393             :     } else {
     394             :       size_t i = len;
     395             :       while (i--) {
     396             :         if (ranges[i].mRange->IsGenerated()) {
     397             :           newAnchorFocusIndex = i;
     398             :           break;
     399             :         }
     400             :       }
     401             :     }
     402             : 
     403             :     if (newAnchorFocusIndex == size_t(-1)) {
     404             :       // There are no generated ranges - that's fine.
     405             :       return;
     406             :     }
     407             : 
     408             :     // Setup the new mAnchorFocusRange and mark the old one as generated.
     409             :     if (aSelection->mAnchorFocusRange) {
     410             :       aSelection->mAnchorFocusRange->SetIsGenerated(true);
     411             :     }
     412             :     nsRange* range = ranges[newAnchorFocusIndex].mRange;
     413             :     range->SetIsGenerated(false);
     414             :     aSelection->mAnchorFocusRange = range;
     415             : 
     416             :     // Remove all generated ranges (including the old mAnchorFocusRange).
     417             :     RefPtr<nsPresContext> presContext = aSelection->GetPresContext();
     418             :     size_t i = len;
     419             :     while (i--) {
     420             :       range = aSelection->mRanges[i].mRange;
     421             :       if (range->IsGenerated()) {
     422             :         range->SetSelection(nullptr);
     423             :         aSelection->SelectFrames(presContext, range, false);
     424             :         aSelection->mRanges.RemoveElementAt(i);
     425             :       }
     426             :     }
     427             :     if (aSelection->mFrameSelection) {
     428             :       aSelection->mFrameSelection->InvalidateDesiredPos();
     429             :     }
     430             :   }
     431             : 
     432             :   Maybe<Selection::AutoUserInitiated> mUserSelect;
     433             :   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     434             : };
     435             : 
     436             : } // namespace mozilla
     437             : 
     438             : 
     439             : #ifdef PRINT_RANGE
     440             : void printRange(nsRange *aDomRange)
     441             : {
     442             :   if (!aDomRange)
     443             :   {
     444             :     printf("NULL nsIDOMRange\n");
     445             :   }
     446             :   nsINode* startNode = aDomRange->GetStartContainer();
     447             :   nsINode* endNode = aDomRange->GetEndContainer();
     448             :   int32_t startOffset = aDomRange->StartOffset();
     449             :   int32_t endOffset = aDomRange->EndOffset();
     450             : 
     451             :   printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
     452             :          (unsigned long)aDomRange,
     453             :          (unsigned long)startNode, (long)startOffset,
     454             :          (unsigned long)endNode, (long)endOffset);
     455             : 
     456             : }
     457             : #endif /* PRINT_RANGE */
     458             : 
     459             : NS_IMETHODIMP
     460           0 : Selection::ToString(nsAString& aReturn)
     461             : {
     462             :   // We need FlushType::Frames here to make sure frames have been created for
     463             :   // the selected content.  Use mFrameSelection->GetShell() which returns
     464             :   // null if the Selection has been disconnected (the shell is Destroyed).
     465             :   nsCOMPtr<nsIPresShell> shell =
     466           0 :     mFrameSelection ? mFrameSelection->GetShell() : nullptr;
     467           0 :   if (!shell) {
     468           0 :     aReturn.Truncate();
     469           0 :     return NS_OK;
     470             :   }
     471           0 :   shell->FlushPendingNotifications(FlushType::Frames);
     472             : 
     473             :   return ToStringWithFormat("text/plain",
     474             :                             nsIDocumentEncoder::SkipInvisibleContent,
     475           0 :                             0, aReturn);
     476             : }
     477             : 
     478             : void
     479           0 : Selection::Stringify(nsAString& aResult)
     480             : {
     481             :   // Eat the error code
     482           0 :   ToString(aResult);
     483           0 : }
     484             : 
     485             : NS_IMETHODIMP
     486           0 : Selection::ToStringWithFormat(const char* aFormatType, uint32_t aFlags,
     487             :                               int32_t aWrapCol, nsAString& aReturn)
     488             : {
     489           0 :   ErrorResult result;
     490           0 :   NS_ConvertUTF8toUTF16 format(aFormatType);
     491           0 :   ToStringWithFormat(format, aFlags, aWrapCol, aReturn, result);
     492           0 :   if (result.Failed()) {
     493           0 :     return result.StealNSResult();
     494             :   }
     495           0 :   return NS_OK;
     496             : }
     497             : 
     498             : void
     499           0 : Selection::ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags,
     500             :                               int32_t aWrapCol, nsAString& aReturn,
     501             :                               ErrorResult& aRv)
     502             : {
     503           0 :   nsresult rv = NS_OK;
     504           0 :   NS_ConvertUTF8toUTF16 formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
     505           0 :   formatType.Append(aFormatType);
     506             :   nsCOMPtr<nsIDocumentEncoder> encoder =
     507           0 :            do_CreateInstance(NS_ConvertUTF16toUTF8(formatType).get(), &rv);
     508           0 :   if (NS_FAILED(rv)) {
     509           0 :     aRv.Throw(rv);
     510           0 :     return;
     511             :   }
     512             : 
     513           0 :   nsIPresShell* shell = GetPresShell();
     514           0 :   if (!shell) {
     515           0 :     aRv.Throw(NS_ERROR_FAILURE);
     516           0 :     return;
     517             :   }
     518             : 
     519           0 :   nsIDocument *doc = shell->GetDocument();
     520             : 
     521           0 :   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
     522           0 :   NS_ASSERTION(domDoc, "Need a document");
     523             : 
     524             :   // Flags should always include OutputSelectionOnly if we're coming from here:
     525           0 :   aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
     526           0 :   nsAutoString readstring;
     527           0 :   readstring.Assign(aFormatType);
     528           0 :   rv = encoder->Init(domDoc, readstring, aFlags);
     529           0 :   if (NS_FAILED(rv)) {
     530           0 :     aRv.Throw(rv);
     531           0 :     return;
     532             :   }
     533             : 
     534           0 :   encoder->SetSelection(this);
     535           0 :   if (aWrapCol != 0)
     536           0 :     encoder->SetWrapColumn(aWrapCol);
     537             : 
     538           0 :   rv = encoder->EncodeToString(aReturn);
     539           0 :   if (NS_FAILED(rv)) {
     540           0 :     aRv.Throw(rv);
     541             :   }
     542             : }
     543             : 
     544             : NS_IMETHODIMP
     545           8 : Selection::SetInterlinePosition(bool aHintRight)
     546             : {
     547          16 :   ErrorResult result;
     548           8 :   SetInterlinePosition(aHintRight, result);
     549           8 :   if (result.Failed()) {
     550           0 :     return result.StealNSResult();
     551             :   }
     552           8 :   return NS_OK;
     553             : }
     554             : 
     555             : void
     556           8 : Selection::SetInterlinePosition(bool aHintRight, ErrorResult& aRv)
     557             : {
     558           8 :   if (!mFrameSelection) {
     559           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
     560           0 :     return;
     561             :   }
     562           8 :   mFrameSelection->SetHint(aHintRight ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE);
     563             : }
     564             : 
     565             : NS_IMETHODIMP
     566           0 : Selection::GetInterlinePosition(bool* aHintRight)
     567             : {
     568           0 :   ErrorResult result;
     569           0 :   *aHintRight = GetInterlinePosition(result);
     570           0 :   if (result.Failed()) {
     571           0 :     return result.StealNSResult();
     572             :   }
     573           0 :   return NS_OK;
     574             : }
     575             : 
     576             : bool
     577           0 : Selection::GetInterlinePosition(ErrorResult& aRv)
     578             : {
     579           0 :   if (!mFrameSelection) {
     580           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
     581           0 :     return false;
     582             :   }
     583           0 :   return mFrameSelection->GetHint() == CARET_ASSOCIATE_AFTER;
     584             : }
     585             : 
     586             : Nullable<int16_t>
     587           0 : Selection::GetCaretBidiLevel(mozilla::ErrorResult& aRv) const
     588             : {
     589           0 :   if (!mFrameSelection) {
     590           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
     591           0 :     return Nullable<int16_t>();
     592             :   }
     593           0 :   nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
     594             :   return (caretBidiLevel & BIDI_LEVEL_UNDEFINED) ?
     595           0 :     Nullable<int16_t>() : Nullable<int16_t>(caretBidiLevel);
     596             : }
     597             : 
     598             : void
     599           0 : Selection::SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel, mozilla::ErrorResult& aRv)
     600             : {
     601           0 :   if (!mFrameSelection) {
     602           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
     603           0 :     return;
     604             :   }
     605           0 :   if (aCaretBidiLevel.IsNull()) {
     606           0 :     mFrameSelection->UndefineCaretBidiLevel();
     607             :   } else {
     608           0 :     mFrameSelection->SetCaretBidiLevel(aCaretBidiLevel.Value());
     609             :   }
     610             : }
     611             : 
     612             : nsresult
     613          10 : Selection::GetTableCellLocationFromRange(nsRange* aRange,
     614             :                                          int32_t* aSelectionType,
     615             :                                          int32_t* aRow, int32_t* aCol)
     616             : {
     617          10 :   if (!aRange || !aSelectionType || !aRow || !aCol)
     618           0 :     return NS_ERROR_NULL_POINTER;
     619             : 
     620          10 :   *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
     621          10 :   *aRow = 0;
     622          10 :   *aCol = 0;
     623             : 
     624             :   // Must have access to frame selection to get cell info
     625          10 :   if (!mFrameSelection) return NS_OK;
     626             : 
     627          10 :   nsresult result = GetTableSelectionType(aRange, aSelectionType);
     628          10 :   if (NS_FAILED(result)) return result;
     629             : 
     630             :   // Don't fail if range does not point to a single table cell,
     631             :   //  let aSelectionType tell user if we don't have a cell
     632          10 :   if (*aSelectionType  != nsISelectionPrivate::TABLESELECTION_CELL)
     633          10 :     return NS_OK;
     634             : 
     635             :   // Get the child content (the cell) pointed to by starting node of range
     636             :   // We do minimal checking since GetTableSelectionType assures
     637             :   //   us that this really is a table cell
     638           0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartContainer());
     639           0 :   if (!content)
     640           0 :     return NS_ERROR_FAILURE;
     641             : 
     642           0 :   nsIContent *child = content->GetChildAt(aRange->StartOffset());
     643           0 :   if (!child)
     644           0 :     return NS_ERROR_FAILURE;
     645             : 
     646             :   //Note: This is a non-ref-counted pointer to the frame
     647           0 :   nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
     648           0 :   if (NS_FAILED(result))
     649           0 :     return result;
     650           0 :   if (!cellLayout)
     651           0 :     return NS_ERROR_FAILURE;
     652             : 
     653           0 :   return cellLayout->GetCellIndexes(*aRow, *aCol);
     654             : }
     655             : 
     656             : nsresult
     657          10 : Selection::AddTableCellRange(nsRange* aRange, bool* aDidAddRange,
     658             :                              int32_t* aOutIndex)
     659             : {
     660          10 :   if (!aDidAddRange || !aOutIndex)
     661           0 :     return NS_ERROR_NULL_POINTER;
     662             : 
     663          10 :   *aDidAddRange = false;
     664          10 :   *aOutIndex = -1;
     665             : 
     666          10 :   if (!mFrameSelection)
     667           0 :     return NS_OK;
     668             : 
     669          10 :   if (!aRange)
     670           0 :     return NS_ERROR_NULL_POINTER;
     671             : 
     672             :   nsresult result;
     673             : 
     674             :   // Get if we are adding a cell selection and the row, col of cell if we are
     675             :   int32_t newRow, newCol, tableMode;
     676          10 :   result = GetTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
     677          10 :   if (NS_FAILED(result)) return result;
     678             : 
     679             :   // If not adding a cell range, we are done here
     680          10 :   if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
     681             :   {
     682          10 :     mFrameSelection->mSelectingTableCellMode = tableMode;
     683             :     // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
     684          10 :     return NS_OK;
     685             :   }
     686             : 
     687             :   // Set frame selection mode only if not already set to a table mode
     688             :   //  so we don't lose the select row and column flags (not detected by getTableCellLocation)
     689           0 :   if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE)
     690           0 :     mFrameSelection->mSelectingTableCellMode = tableMode;
     691             : 
     692           0 :   *aDidAddRange = true;
     693           0 :   return AddItem(aRange, aOutIndex);
     694             : }
     695             : 
     696             : //TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
     697             : nsresult
     698          10 : Selection::GetTableSelectionType(nsIDOMRange* aDOMRange,
     699             :                                  int32_t* aTableSelectionType)
     700             : {
     701          10 :   if (!aDOMRange || !aTableSelectionType)
     702           0 :     return NS_ERROR_NULL_POINTER;
     703          10 :   nsRange* range = static_cast<nsRange*>(aDOMRange);
     704             : 
     705          10 :   *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
     706             : 
     707             :   // Must have access to frame selection to get cell info
     708          10 :   if(!mFrameSelection) return NS_OK;
     709             : 
     710          10 :   nsINode* startNode = range->GetStartContainer();
     711          10 :   if (!startNode) return NS_ERROR_FAILURE;
     712             : 
     713          10 :   nsINode* endNode = range->GetEndContainer();
     714          10 :   if (!endNode) return NS_ERROR_FAILURE;
     715             : 
     716             :   // Not a single selected node
     717          10 :   if (startNode != endNode) return NS_OK;
     718             : 
     719          10 :   int32_t startOffset = range->StartOffset();
     720          10 :   int32_t endOffset = range->EndOffset();
     721             : 
     722             :   // Not a single selected node
     723          10 :   if ((endOffset - startOffset) != 1)
     724          10 :     return NS_OK;
     725             : 
     726           0 :   nsIContent* startContent = static_cast<nsIContent*>(startNode);
     727           0 :   if (!(startNode->IsElement() && startContent->IsHTMLElement())) {
     728             :     // Implies a check for being an element; if we ever make this work
     729             :     // for non-HTML, need to keep checking for elements.
     730           0 :     return NS_OK;
     731             :   }
     732             : 
     733           0 :   if (startContent->IsHTMLElement(nsGkAtoms::tr))
     734             :   {
     735           0 :     *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
     736             :   }
     737             :   else //check to see if we are selecting a table or row (column and all cells not done yet)
     738             :   {
     739           0 :     nsIContent *child = startNode->GetChildAt(startOffset);
     740           0 :     if (!child)
     741           0 :       return NS_ERROR_FAILURE;
     742             : 
     743           0 :     if (child->IsHTMLElement(nsGkAtoms::table))
     744           0 :       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
     745           0 :     else if (child->IsHTMLElement(nsGkAtoms::tr))
     746           0 :       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
     747             :   }
     748             : 
     749           0 :   return NS_OK;
     750             : }
     751             : 
     752           0 : Selection::Selection()
     753             :   : mCachedOffsetForFrame(nullptr)
     754             :   , mDirection(eDirNext)
     755             :   , mSelectionType(SelectionType::eNormal)
     756             :   , mCustomColors(nullptr)
     757             :   , mUserInitiated(false)
     758             :   , mCalledByJS(false)
     759           0 :   , mSelectionChangeBlockerCount(0)
     760             : {
     761           0 : }
     762             : 
     763         320 : Selection::Selection(nsFrameSelection* aList)
     764             :   : mFrameSelection(aList)
     765             :   , mCachedOffsetForFrame(nullptr)
     766             :   , mDirection(eDirNext)
     767             :   , mSelectionType(SelectionType::eNormal)
     768             :   , mCustomColors(nullptr)
     769             :   , mUserInitiated(false)
     770             :   , mCalledByJS(false)
     771         320 :   , mSelectionChangeBlockerCount(0)
     772             : {
     773         320 : }
     774             : 
     775           0 : Selection::~Selection()
     776             : {
     777           0 :   SetAnchorFocusRange(-1);
     778             : 
     779           0 :   uint32_t count = mRanges.Length();
     780           0 :   for (uint32_t i = 0; i < count; ++i) {
     781           0 :     mRanges[i].mRange->SetSelection(nullptr);
     782             :   }
     783             : 
     784           0 :   if (mAutoScrollTimer) {
     785           0 :     mAutoScrollTimer->Stop();
     786           0 :     mAutoScrollTimer = nullptr;
     787             :   }
     788             : 
     789           0 :   mScrollEvent.Revoke();
     790             : 
     791           0 :   if (mCachedOffsetForFrame) {
     792           0 :     delete mCachedOffsetForFrame;
     793           0 :     mCachedOffsetForFrame = nullptr;
     794             :   }
     795           0 : }
     796             : 
     797             : nsIDocument*
     798          37 : Selection::GetParentObject() const
     799             : {
     800          37 :   nsIPresShell* shell = GetPresShell();
     801          37 :   if (shell) {
     802          37 :     return shell->GetDocument();
     803             :   }
     804           0 :   return nullptr;
     805             : }
     806             : 
     807             : NS_IMPL_CYCLE_COLLECTION_CLASS(Selection)
     808             : 
     809           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection)
     810             :   // Unlink the selection listeners *before* we do RemoveAllRanges since
     811             :   // we don't want to notify the listeners during JS GC (they could be
     812             :   // in JS!).
     813           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners)
     814           0 :   tmp->RemoveAllRanges();
     815           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameSelection)
     816           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     817           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     818           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Selection)
     819             :   {
     820           0 :     uint32_t i, count = tmp->mRanges.Length();
     821           0 :     for (i = 0; i < count; ++i) {
     822           0 :       NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRanges[i].mRange)
     823             :     }
     824             :   }
     825           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorFocusRange)
     826           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameSelection)
     827           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListeners)
     828           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     829           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Selection)
     830             : 
     831             : // QueryInterface implementation for Selection
     832         501 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Selection)
     833         181 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     834         175 :   NS_INTERFACE_MAP_ENTRY(nsISelection)
     835          72 :   NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
     836          28 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     837           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
     838           0 : NS_INTERFACE_MAP_END
     839             : 
     840         725 : NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection)
     841         397 : NS_IMPL_CYCLE_COLLECTING_RELEASE(Selection)
     842             : 
     843             : 
     844             : NS_IMETHODIMP
     845           0 : Selection::GetAnchorNode(nsIDOMNode** aAnchorNode)
     846             : {
     847           0 :   nsINode* anchorNode = GetAnchorNode();
     848           0 :   if (anchorNode) {
     849           0 :     return CallQueryInterface(anchorNode, aAnchorNode);
     850             :   }
     851             : 
     852           0 :   *aAnchorNode = nullptr;
     853           0 :   return NS_OK;
     854             : }
     855             : 
     856             : nsINode*
     857           2 : Selection::GetAnchorNode()
     858             : {
     859           2 :   if (!mAnchorFocusRange)
     860           2 :     return nullptr;
     861             : 
     862           0 :   if (GetDirection() == eDirNext) {
     863           0 :     return mAnchorFocusRange->GetStartContainer();
     864             :   }
     865             : 
     866           0 :   return mAnchorFocusRange->GetEndContainer();
     867             : }
     868             : 
     869             : NS_IMETHODIMP
     870           2 : Selection::GetAnchorOffset(int32_t* aAnchorOffset)
     871             : {
     872           2 :   *aAnchorOffset = static_cast<int32_t>(AnchorOffset());
     873           2 :   return NS_OK;
     874             : }
     875             : 
     876             : // note: this can return a nil focus node
     877             : NS_IMETHODIMP
     878           0 : Selection::GetFocusNode(nsIDOMNode** aFocusNode)
     879             : {
     880           0 :   nsINode* focusNode = GetFocusNode();
     881           0 :   if (focusNode) {
     882           0 :     return CallQueryInterface(focusNode, aFocusNode);
     883             :   }
     884             : 
     885           0 :   *aFocusNode = nullptr;
     886           0 :   return NS_OK;
     887             : }
     888             : 
     889             : nsINode*
     890           3 : Selection::GetFocusNode()
     891             : {
     892           3 :   if (!mAnchorFocusRange)
     893           2 :     return nullptr;
     894             : 
     895           1 :   if (GetDirection() == eDirNext){
     896           1 :     return mAnchorFocusRange->GetEndContainer();
     897             :   }
     898             : 
     899           0 :   return mAnchorFocusRange->GetStartContainer();
     900             : }
     901             : 
     902             : NS_IMETHODIMP
     903           0 : Selection::GetFocusOffset(int32_t* aFocusOffset)
     904             : {
     905           0 :   *aFocusOffset = static_cast<int32_t>(FocusOffset());
     906           0 :   return NS_OK;
     907             : }
     908             : 
     909             : void
     910          99 : Selection::SetAnchorFocusRange(int32_t indx)
     911             : {
     912          99 :   if (indx >= (int32_t)mRanges.Length())
     913           0 :     return;
     914          99 :   if (indx < 0) //release all
     915             :   {
     916          82 :     mAnchorFocusRange = nullptr;
     917             :   }
     918             :   else{
     919          17 :     mAnchorFocusRange = mRanges[indx].mRange;
     920             :   }
     921             : }
     922             : 
     923             : uint32_t
     924           2 : Selection::AnchorOffset()
     925             : {
     926           2 :   if (!mAnchorFocusRange)
     927           2 :     return 0;
     928             : 
     929           0 :   if (GetDirection() == eDirNext){
     930           0 :     return mAnchorFocusRange->StartOffset();
     931             :   }
     932             : 
     933           0 :   return mAnchorFocusRange->EndOffset();
     934             : }
     935             : 
     936             : uint32_t
     937           1 : Selection::FocusOffset()
     938             : {
     939           1 :   if (!mAnchorFocusRange)
     940           0 :     return 0;
     941             : 
     942           1 :   if (GetDirection() == eDirNext){
     943           1 :     return mAnchorFocusRange->EndOffset();
     944             :   }
     945             : 
     946           0 :   return mAnchorFocusRange->StartOffset();
     947             : }
     948             : 
     949             : static nsresult
     950           4 : CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
     951             :                     nsRange* aRange, int32_t* aCmp)
     952             : {
     953           4 :   nsINode* start = aRange->GetStartContainer();
     954           4 :   NS_ENSURE_STATE(aCompareNode && start);
     955             :   // If the nodes that we're comparing are not in the same document,
     956             :   // assume that aCompareNode will fall at the end of the ranges.
     957           8 :   if (aCompareNode->GetComposedDoc() != start->GetComposedDoc() ||
     958           4 :       !start->GetComposedDoc()) {
     959           0 :     *aCmp = 1;
     960             :   } else {
     961           4 :     *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
     962             :                                           start, aRange->StartOffset());
     963             :   }
     964           4 :   return NS_OK;
     965             : }
     966             : 
     967             : static nsresult
     968           4 : CompareToRangeEnd(nsINode* aCompareNode, int32_t aCompareOffset,
     969             :                   nsRange* aRange, int32_t* aCmp)
     970             : {
     971           4 :   nsINode* end = aRange->GetEndContainer();
     972           4 :   NS_ENSURE_STATE(aCompareNode && end);
     973             :   // If the nodes that we're comparing are not in the same document,
     974             :   // assume that aCompareNode will fall at the end of the ranges.
     975           8 :   if (aCompareNode->GetComposedDoc() != end->GetComposedDoc() ||
     976           4 :       !end->GetComposedDoc()) {
     977           0 :     *aCmp = 1;
     978             :   } else {
     979           4 :     *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
     980             :                                           end, aRange->EndOffset());
     981             :   }
     982           4 :   return NS_OK;
     983             : }
     984             : 
     985             : // Selection::FindInsertionPoint
     986             : //
     987             : //    Binary searches the given sorted array of ranges for the insertion point
     988             : //    for the given node/offset. The given comparator is used, and the index
     989             : //    where the point should appear in the array is placed in *aInsertionPoint.
     990             : //
     991             : //    If there is an item in the array equal to the input point, we will return
     992             : //    the index of this item.
     993             : 
     994             : nsresult
     995           8 : Selection::FindInsertionPoint(
     996             :     nsTArray<RangeData>* aElementArray,
     997             :     nsINode* aPointNode, int32_t aPointOffset,
     998             :     nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*),
     999             :     int32_t* aPoint)
    1000             : {
    1001           8 :   *aPoint = 0;
    1002           8 :   int32_t beginSearch = 0;
    1003           8 :   int32_t endSearch = aElementArray->Length(); // one beyond what to check
    1004             : 
    1005           8 :   if (endSearch) {
    1006           8 :     int32_t center = endSearch - 1; // Check last index, then binary search
    1007           0 :     do {
    1008           8 :       nsRange* range = (*aElementArray)[center].mRange;
    1009             : 
    1010             :       int32_t cmp;
    1011           8 :       nsresult rv = aComparator(aPointNode, aPointOffset, range, &cmp);
    1012           8 :       NS_ENSURE_SUCCESS(rv, rv);
    1013             : 
    1014           8 :       if (cmp < 0) {        // point < cur
    1015           2 :         endSearch = center;
    1016           6 :       } else if (cmp > 0) { // point > cur
    1017           4 :         beginSearch = center + 1;
    1018             :       } else {              // found match, done
    1019           2 :         beginSearch = center;
    1020           2 :         break;
    1021             :       }
    1022           6 :       center = (endSearch - beginSearch) / 2 + beginSearch;
    1023           6 :     } while (endSearch - beginSearch > 0);
    1024             :   }
    1025             : 
    1026           8 :   *aPoint = beginSearch;
    1027           8 :   return NS_OK;
    1028             : }
    1029             : 
    1030             : // Selection::SubtractRange
    1031             : //
    1032             : //    A helper function that subtracts aSubtract from aRange, and adds
    1033             : //    1 or 2 RangeData objects representing the remaining non-overlapping
    1034             : //    difference to aOutput. It is assumed that the caller has checked that
    1035             : //    aRange and aSubtract do indeed overlap
    1036             : 
    1037             : nsresult
    1038           0 : Selection::SubtractRange(RangeData* aRange, nsRange* aSubtract,
    1039             :                          nsTArray<RangeData>* aOutput)
    1040             : {
    1041           0 :   nsRange* range = aRange->mRange;
    1042             : 
    1043             :   // First we want to compare to the range start
    1044             :   int32_t cmp;
    1045           0 :   nsresult rv = CompareToRangeStart(range->GetStartContainer(),
    1046             :                                     range->StartOffset(),
    1047           0 :                                     aSubtract, &cmp);
    1048           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1049             : 
    1050             :   // Also, make a comparison to the range end
    1051             :   int32_t cmp2;
    1052           0 :   rv = CompareToRangeEnd(range->GetEndContainer(),
    1053             :                          range->EndOffset(),
    1054           0 :                          aSubtract, &cmp2);
    1055           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1056             : 
    1057             :   // If the existing range left overlaps the new range (aSubtract) then
    1058             :   // cmp < 0, and cmp2 < 0
    1059             :   // If it right overlaps the new range then cmp > 0 and cmp2 > 0
    1060             :   // If it fully contains the new range, then cmp < 0 and cmp2 > 0
    1061             : 
    1062           0 :   if (cmp2 > 0) {
    1063             :     // We need to add a new RangeData to the output, running from
    1064             :     // the end of aSubtract to the end of range
    1065           0 :     RefPtr<nsRange> postOverlap = new nsRange(aSubtract->GetEndContainer());
    1066           0 :     rv = postOverlap->SetStartAndEnd(
    1067             :                         aSubtract->GetEndContainer(), aSubtract->EndOffset(),
    1068           0 :                         range->GetEndContainer(), range->EndOffset());
    1069           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1070           0 :       return rv;
    1071             :     }
    1072           0 :     if (!postOverlap->Collapsed()) {
    1073           0 :       if (!aOutput->InsertElementAt(0, RangeData(postOverlap)))
    1074           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1075           0 :       (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
    1076             :     }
    1077             :   }
    1078             : 
    1079           0 :   if (cmp < 0) {
    1080             :     // We need to add a new RangeData to the output, running from
    1081             :     // the start of the range to the start of aSubtract
    1082           0 :     RefPtr<nsRange> preOverlap = new nsRange(range->GetStartContainer());
    1083           0 :     rv = preOverlap->SetStartAndEnd(range->GetStartContainer(),
    1084             :                                     range->StartOffset(),
    1085             :                                     aSubtract->GetStartContainer(),
    1086           0 :                                     aSubtract->StartOffset());
    1087           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1088           0 :       return rv;
    1089             :     }
    1090           0 :     if (!preOverlap->Collapsed()) {
    1091           0 :       if (!aOutput->InsertElementAt(0, RangeData(preOverlap)))
    1092           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1093           0 :       (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
    1094             :     }
    1095             :   }
    1096             : 
    1097           0 :   return NS_OK;
    1098             : }
    1099             : 
    1100             : void
    1101           0 : Selection::UserSelectRangesToAdd(nsRange* aItem, nsTArray<RefPtr<nsRange>>& aRangesToAdd)
    1102             : {
    1103           0 :   aItem->ExcludeNonSelectableNodes(&aRangesToAdd);
    1104           0 :   if (aRangesToAdd.IsEmpty()) {
    1105           0 :     ErrorResult err;
    1106           0 :     nsINode* node = aItem->GetStartContainer(err);
    1107           0 :     if (node && node->IsContent() && node->AsContent()->GetEditingHost()) {
    1108             :       // A contenteditable node with user-select:none, for example.
    1109             :       // Allow it to have a collapsed selection (for the caret).
    1110           0 :       aItem->Collapse(GetDirection() == eDirPrevious);
    1111           0 :       aRangesToAdd.AppendElement(aItem);
    1112             :     }
    1113             :   }
    1114           0 : }
    1115             : 
    1116             : nsresult
    1117          17 : Selection::AddItem(nsRange* aItem, int32_t* aOutIndex, bool aNoStartSelect)
    1118             : {
    1119          17 :   if (!aItem)
    1120           0 :     return NS_ERROR_NULL_POINTER;
    1121          17 :   if (!aItem->IsPositioned())
    1122           0 :     return NS_ERROR_UNEXPECTED;
    1123             : 
    1124          17 :   NS_ASSERTION(aOutIndex, "aOutIndex can't be null");
    1125             : 
    1126          17 :   if (mUserInitiated) {
    1127           0 :     AutoTArray<RefPtr<nsRange>, 4> rangesToAdd;
    1128           0 :     *aOutIndex = int32_t(mRanges.Length()) - 1;
    1129             : 
    1130           0 :     nsIDocument* doc = GetParentObject();
    1131             :     bool selectEventsEnabled =
    1132           0 :       nsFrameSelection::sSelectionEventsEnabled ||
    1133           0 :       (doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
    1134             : 
    1135           0 :     if (!aNoStartSelect &&
    1136           0 :         mSelectionType == SelectionType::eNormal &&
    1137           0 :         selectEventsEnabled && Collapsed() &&
    1138           0 :         !IsBlockingSelectionChangeEvents()) {
    1139             :       // First, we generate the ranges to add with a scratch range, which is a
    1140             :       // clone of the original range passed in. We do this seperately, because the
    1141             :       // selectstart event could have caused the world to change, and required
    1142             :       // ranges to be re-generated
    1143           0 :       RefPtr<nsRange> scratchRange = aItem->CloneRange();
    1144           0 :       UserSelectRangesToAdd(scratchRange, rangesToAdd);
    1145           0 :       bool newRangesNonEmpty = rangesToAdd.Length() > 1 ||
    1146           0 :         (rangesToAdd.Length() == 1 && !rangesToAdd[0]->Collapsed());
    1147             : 
    1148           0 :       MOZ_ASSERT(!newRangesNonEmpty || nsContentUtils::IsSafeToRunScript());
    1149           0 :       if (newRangesNonEmpty && nsContentUtils::IsSafeToRunScript()) {
    1150             :         // We consider a selection to be starting if we are currently collapsed,
    1151             :         // and the selection is becoming uncollapsed, and this is caused by a user
    1152             :         // initiated event.
    1153           0 :         bool defaultAction = true;
    1154             : 
    1155             :         // The spec currently doesn't say that we should dispatch this event
    1156             :         // on text controls, so for now we only support doing that under a
    1157             :         // pref, disabled by default.
    1158             :         // See https://github.com/w3c/selection-api/issues/53.
    1159           0 :         bool dispatchEvent = true;
    1160           0 :         nsCOMPtr<nsINode> target = aItem->GetStartContainer();
    1161           0 :         if (nsFrameSelection::sSelectionEventsOnTextControlsEnabled) {
    1162             :           // Get the first element which isn't in a native anonymous subtree
    1163           0 :           while (target && target->IsInNativeAnonymousSubtree()) {
    1164           0 :             target = target->GetParent();
    1165             :           }
    1166             :         } else {
    1167           0 :           if (target->IsInNativeAnonymousSubtree()) {
    1168             :             // This is a selection under a text control, so don't dispatch the
    1169             :             // event.
    1170           0 :             dispatchEvent = false;
    1171             :           }
    1172             :         }
    1173             : 
    1174           0 :         if (dispatchEvent) {
    1175           0 :           nsContentUtils::DispatchTrustedEvent(GetParentObject(), target,
    1176           0 :                                                NS_LITERAL_STRING("selectstart"),
    1177           0 :                                                true, true, &defaultAction);
    1178             : 
    1179           0 :           if (!defaultAction) {
    1180           0 :             return NS_OK;
    1181             :           }
    1182             : 
    1183             :           // As we just dispatched an event to the DOM, something could have
    1184             :           // changed under our feet. Re-generate the rangesToAdd array, and ensure
    1185             :           // that the range we are about to add is still valid.
    1186           0 :           if (!aItem->IsPositioned()) {
    1187           0 :             return NS_ERROR_UNEXPECTED;
    1188             :           }
    1189             :         }
    1190             :       }
    1191             : 
    1192             :       // The scratch ranges we generated may be invalid now, throw them out
    1193           0 :       rangesToAdd.ClearAndRetainStorage();
    1194             :     }
    1195             : 
    1196             :     // Generate the ranges to add
    1197           0 :     UserSelectRangesToAdd(aItem, rangesToAdd);
    1198             :     size_t newAnchorFocusIndex =
    1199           0 :       GetDirection() == eDirPrevious ? 0 : rangesToAdd.Length() - 1;
    1200           0 :     for (size_t i = 0; i < rangesToAdd.Length(); ++i) {
    1201             :       int32_t index;
    1202           0 :       nsresult rv = AddItemInternal(rangesToAdd[i], &index);
    1203           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1204           0 :       if (i == newAnchorFocusIndex) {
    1205           0 :         *aOutIndex = index;
    1206           0 :         rangesToAdd[i]->SetIsGenerated(false);
    1207             :       } else {
    1208           0 :         rangesToAdd[i]->SetIsGenerated(true);
    1209             :       }
    1210             :     }
    1211           0 :     return NS_OK;
    1212             :   }
    1213          17 :   return AddItemInternal(aItem, aOutIndex);
    1214             : }
    1215             : 
    1216             : nsresult
    1217          17 : Selection::AddItemInternal(nsRange* aItem, int32_t* aOutIndex)
    1218             : {
    1219          17 :   MOZ_ASSERT(aItem);
    1220          17 :   MOZ_ASSERT(aItem->IsPositioned());
    1221          17 :   MOZ_ASSERT(aOutIndex);
    1222             : 
    1223          17 :   *aOutIndex = -1;
    1224             : 
    1225             :   // a common case is that we have no ranges yet
    1226          17 :   if (mRanges.Length() == 0) {
    1227          17 :     if (!mRanges.AppendElement(RangeData(aItem)))
    1228           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1229          17 :     aItem->SetSelection(this);
    1230             : 
    1231          17 :     *aOutIndex = 0;
    1232          17 :     return NS_OK;
    1233             :   }
    1234             : 
    1235             :   int32_t startIndex, endIndex;
    1236           0 :   nsresult rv = GetIndicesForInterval(aItem->GetStartContainer(),
    1237             :                                       aItem->StartOffset(),
    1238             :                                       aItem->GetEndContainer(),
    1239             :                                       aItem->EndOffset(), false,
    1240           0 :                                       &startIndex, &endIndex);
    1241           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1242             : 
    1243           0 :   if (endIndex == -1) {
    1244             :     // All ranges start after the given range. We can insert our range at
    1245             :     // position 0, knowing there are no overlaps (handled below)
    1246           0 :     startIndex = 0;
    1247           0 :     endIndex = 0;
    1248           0 :   } else if (startIndex == -1) {
    1249             :     // All ranges end before the given range. We can insert our range at
    1250             :     // the end of the array, knowing there are no overlaps (handled below)
    1251           0 :     startIndex = mRanges.Length();
    1252           0 :     endIndex = startIndex;
    1253             :   }
    1254             : 
    1255             :   // If the range is already contained in mRanges, silently succeed
    1256           0 :   bool sameRange = EqualsRangeAtPoint(aItem->GetStartContainer(),
    1257             :                                       aItem->StartOffset(),
    1258             :                                       aItem->GetEndContainer(),
    1259           0 :                                       aItem->EndOffset(), startIndex);
    1260           0 :   if (sameRange) {
    1261           0 :     *aOutIndex = startIndex;
    1262           0 :     return NS_OK;
    1263             :   }
    1264             : 
    1265           0 :   if (startIndex == endIndex) {
    1266             :     // The new range doesn't overlap any existing ranges
    1267           0 :     if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
    1268           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1269           0 :     aItem->SetSelection(this);
    1270           0 :     *aOutIndex = startIndex;
    1271           0 :     return NS_OK;
    1272             :   }
    1273             : 
    1274             :   // We now know that at least 1 existing range overlaps with the range that
    1275             :   // we are trying to add. In fact, the only ranges of interest are those at
    1276             :   // the two end points, startIndex and endIndex - 1 (which may point to the
    1277             :   // same range) as these may partially overlap the new range. Any ranges
    1278             :   // between these indices are fully overlapped by the new range, and so can be
    1279             :   // removed
    1280           0 :   nsTArray<RangeData> overlaps;
    1281           0 :   if (!overlaps.InsertElementAt(0, mRanges[startIndex]))
    1282           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1283             : 
    1284           0 :   if (endIndex - 1 != startIndex) {
    1285           0 :     if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
    1286           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1287             :   }
    1288             : 
    1289             :   // Remove all the overlapping ranges
    1290           0 :   for (int32_t i = startIndex; i < endIndex; ++i) {
    1291           0 :     mRanges[i].mRange->SetSelection(nullptr);
    1292             :   }
    1293           0 :   mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
    1294             : 
    1295           0 :   nsTArray<RangeData> temp;
    1296           0 :   for (int32_t i = overlaps.Length() - 1; i >= 0; i--) {
    1297           0 :     nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
    1298           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1299             :   }
    1300             : 
    1301             :   // Insert the new element into our "leftovers" array
    1302             :   int32_t insertionPoint;
    1303           0 :   rv = FindInsertionPoint(&temp, aItem->GetStartContainer(),
    1304             :                           aItem->StartOffset(), CompareToRangeStart,
    1305           0 :                           &insertionPoint);
    1306           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1307             : 
    1308           0 :   if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
    1309           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1310             : 
    1311             :   // Merge the leftovers back in to mRanges
    1312           0 :   if (!mRanges.InsertElementsAt(startIndex, temp))
    1313           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1314             : 
    1315           0 :   for (uint32_t i = 0; i < temp.Length(); ++i) {
    1316           0 :     temp[i].mRange->SetSelection(this);
    1317             :   }
    1318             : 
    1319           0 :   *aOutIndex = startIndex + insertionPoint;
    1320           0 :   return NS_OK;
    1321             : }
    1322             : 
    1323             : nsresult
    1324           0 : Selection::RemoveItem(nsRange* aItem)
    1325             : {
    1326           0 :   if (!aItem)
    1327           0 :     return NS_ERROR_NULL_POINTER;
    1328             : 
    1329             :   // Find the range's index & remove it. We could use FindInsertionPoint to
    1330             :   // get O(log n) time, but that requires many expensive DOM comparisons.
    1331             :   // For even several thousand items, this is probably faster because the
    1332             :   // comparisons are so fast.
    1333           0 :   int32_t idx = -1;
    1334             :   uint32_t i;
    1335           0 :   for (i = 0; i < mRanges.Length(); i ++) {
    1336           0 :     if (mRanges[i].mRange == aItem) {
    1337           0 :       idx = (int32_t)i;
    1338           0 :       break;
    1339             :     }
    1340             :   }
    1341           0 :   if (idx < 0)
    1342           0 :     return NS_ERROR_DOM_NOT_FOUND_ERR;
    1343             : 
    1344           0 :   mRanges.RemoveElementAt(idx);
    1345           0 :   aItem->SetSelection(nullptr);
    1346           0 :   return NS_OK;
    1347             : }
    1348             : 
    1349             : nsresult
    1350           0 : Selection::RemoveCollapsedRanges()
    1351             : {
    1352           0 :   uint32_t i = 0;
    1353           0 :   while (i < mRanges.Length()) {
    1354           0 :     if (mRanges[i].mRange->Collapsed()) {
    1355           0 :       nsresult rv = RemoveItem(mRanges[i].mRange);
    1356           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1357             :     } else {
    1358           0 :       ++i;
    1359             :     }
    1360             :   }
    1361           0 :   return NS_OK;
    1362             : }
    1363             : 
    1364             : nsresult
    1365          82 : Selection::Clear(nsPresContext* aPresContext)
    1366             : {
    1367          82 :   SetAnchorFocusRange(-1);
    1368             : 
    1369          95 :   for (uint32_t i = 0; i < mRanges.Length(); ++i) {
    1370          13 :     mRanges[i].mRange->SetSelection(nullptr);
    1371          13 :     SelectFrames(aPresContext, mRanges[i].mRange, false);
    1372             :   }
    1373          82 :   mRanges.Clear();
    1374             : 
    1375             :   // Reset direction so for more dependable table selection range handling
    1376          82 :   SetDirection(eDirNext);
    1377             : 
    1378             :   // If this was an ATTENTION selection, change it back to normal now
    1379         164 :   if (mFrameSelection &&
    1380          82 :       mFrameSelection->GetDisplaySelection() ==
    1381          82 :       nsISelectionController::SELECTION_ATTENTION) {
    1382           0 :     mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
    1383             :   }
    1384             : 
    1385          82 :   return NS_OK;
    1386             : }
    1387             : 
    1388             : NS_IMETHODIMP
    1389           0 : Selection::GetType(int16_t* aType)
    1390             : {
    1391           0 :   NS_ENSURE_ARG_POINTER(aType);
    1392           0 :   *aType = ToRawSelectionType(Type());
    1393           0 :   return NS_OK;
    1394             : }
    1395             : 
    1396             : // RangeMatches*Point
    1397             : //
    1398             : //    Compares the range beginning or ending point, and returns true if it
    1399             : //    exactly matches the given DOM point.
    1400             : 
    1401             : static inline bool
    1402           0 : RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
    1403             : {
    1404           0 :   return aRange->GetStartContainer() == aNode &&
    1405           0 :          aRange->StartOffset() == aOffset;
    1406             : }
    1407             : 
    1408             : static inline bool
    1409           4 : RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
    1410             : {
    1411           4 :   return aRange->GetEndContainer() == aNode && aRange->EndOffset() == aOffset;
    1412             : }
    1413             : 
    1414             : // Selection::EqualsRangeAtPoint
    1415             : //
    1416             : //    Utility method for checking equivalence of two ranges.
    1417             : 
    1418             : bool
    1419           0 : Selection::EqualsRangeAtPoint(
    1420             :     nsINode* aBeginNode, int32_t aBeginOffset,
    1421             :     nsINode* aEndNode, int32_t aEndOffset,
    1422             :     int32_t aRangeIndex)
    1423             : {
    1424           0 :   if (aRangeIndex >=0 && aRangeIndex < (int32_t) mRanges.Length()) {
    1425           0 :     nsRange* range = mRanges[aRangeIndex].mRange;
    1426           0 :     if (RangeMatchesBeginPoint(range, aBeginNode, aBeginOffset) &&
    1427           0 :         RangeMatchesEndPoint(range, aEndNode, aEndOffset))
    1428           0 :       return true;
    1429             :   }
    1430           0 :   return false;
    1431             : }
    1432             : 
    1433             : // Selection::GetRangesForInterval
    1434             : //
    1435             : //    XPCOM wrapper for the nsTArray version
    1436             : 
    1437             : NS_IMETHODIMP
    1438           0 : Selection::GetRangesForInterval(nsIDOMNode* aBeginNode, int32_t aBeginOffset,
    1439             :                                 nsIDOMNode* aEndNode, int32_t aEndOffset,
    1440             :                                 bool aAllowAdjacent,
    1441             :                                 uint32_t* aResultCount,
    1442             :                                 nsIDOMRange*** aResults)
    1443             : {
    1444           0 :   if (!aBeginNode || ! aEndNode || ! aResultCount || ! aResults)
    1445           0 :     return NS_ERROR_NULL_POINTER;
    1446             : 
    1447           0 :   *aResultCount = 0;
    1448           0 :   *aResults = nullptr;
    1449             : 
    1450           0 :   nsTArray<RefPtr<nsRange>> results;
    1451           0 :   ErrorResult result;
    1452           0 :   nsCOMPtr<nsINode> beginNode = do_QueryInterface(aBeginNode);
    1453           0 :   nsCOMPtr<nsINode> endNode = do_QueryInterface(aEndNode);
    1454           0 :   NS_ENSURE_TRUE(beginNode && endNode, NS_ERROR_NULL_POINTER);
    1455           0 :   GetRangesForInterval(*beginNode, aBeginOffset, *endNode, aEndOffset,
    1456           0 :                        aAllowAdjacent, results, result);
    1457           0 :   if (result.Failed()) {
    1458           0 :     return result.StealNSResult();
    1459             :   }
    1460           0 :   *aResultCount = results.Length();
    1461           0 :   if (*aResultCount == 0) {
    1462           0 :     return NS_OK;
    1463             :   }
    1464             : 
    1465           0 :   *aResults = static_cast<nsIDOMRange**>
    1466           0 :                          (moz_xmalloc(sizeof(nsIDOMRange*) * *aResultCount));
    1467           0 :   NS_ENSURE_TRUE(*aResults, NS_ERROR_OUT_OF_MEMORY);
    1468             : 
    1469           0 :   for (uint32_t i = 0; i < *aResultCount; i++) {
    1470           0 :     (*aResults)[i] = results[i].forget().take();
    1471             :   }
    1472           0 :   return NS_OK;
    1473             : }
    1474             : 
    1475             : 
    1476             : void
    1477           0 : Selection::GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset,
    1478             :                                 nsINode& aEndNode, int32_t aEndOffset,
    1479             :                                 bool aAllowAdjacent,
    1480             :                                 nsTArray<RefPtr<nsRange>>& aReturn,
    1481             :                                 mozilla::ErrorResult& aRv)
    1482             : {
    1483           0 :   nsTArray<nsRange*> results;
    1484           0 :   nsresult rv = GetRangesForIntervalArray(&aBeginNode, aBeginOffset,
    1485             :                                           &aEndNode, aEndOffset,
    1486           0 :                                           aAllowAdjacent, &results);
    1487           0 :   if (NS_FAILED(rv)) {
    1488           0 :     aRv.Throw(rv);
    1489           0 :     return;
    1490             :   }
    1491             : 
    1492           0 :   aReturn.SetLength(results.Length());
    1493           0 :   for (uint32_t i = 0; i < results.Length(); ++i) {
    1494           0 :     aReturn[i] = results[i]; // AddRefs
    1495             :   }
    1496             : }
    1497             : 
    1498             : // Selection::GetRangesForIntervalArray
    1499             : //
    1500             : //    Fills a nsTArray with the ranges overlapping the range specified by
    1501             : //    the given endpoints. Ranges in the selection exactly adjacent to the
    1502             : //    input range are not returned unless aAllowAdjacent is set.
    1503             : //
    1504             : //    For example, if the following ranges were in the selection
    1505             : //    (assume everything is within the same node)
    1506             : //
    1507             : //    Start Offset: 0 2 7 9
    1508             : //      End Offset: 2 5 9 10
    1509             : //
    1510             : //    and passed aBeginOffset of 2 and aEndOffset of 9, then with
    1511             : //    aAllowAdjacent set, all the ranges should be returned. If
    1512             : //    aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
    1513             : //    should be returned
    1514             : //
    1515             : //    Now that overlapping ranges are disallowed, there can be a maximum of
    1516             : //    2 adjacent ranges
    1517             : 
    1518             : nsresult
    1519           4 : Selection::GetRangesForIntervalArray(nsINode* aBeginNode, int32_t aBeginOffset,
    1520             :                                      nsINode* aEndNode, int32_t aEndOffset,
    1521             :                                      bool aAllowAdjacent,
    1522             :                                      nsTArray<nsRange*>* aRanges)
    1523             : {
    1524           4 :   aRanges->Clear();
    1525             :   int32_t startIndex, endIndex;
    1526           4 :   nsresult res = GetIndicesForInterval(aBeginNode, aBeginOffset,
    1527             :                                        aEndNode, aEndOffset, aAllowAdjacent,
    1528           4 :                                        &startIndex, &endIndex);
    1529           4 :   NS_ENSURE_SUCCESS(res, res);
    1530             : 
    1531           4 :   if (startIndex == -1 || endIndex == -1)
    1532           0 :     return NS_OK;
    1533             : 
    1534           8 :   for (int32_t i = startIndex; i < endIndex; i++) {
    1535           4 :     if (!aRanges->AppendElement(mRanges[i].mRange))
    1536           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1537             :   }
    1538             : 
    1539           4 :   return NS_OK;
    1540             : }
    1541             : 
    1542             : // Selection::GetIndicesForInterval
    1543             : //
    1544             : //    Works on the same principle as GetRangesForIntervalArray above, however
    1545             : //    instead this returns the indices into mRanges between which the
    1546             : //    overlapping ranges lie.
    1547             : 
    1548             : nsresult
    1549           4 : Selection::GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset,
    1550             :                                  nsINode* aEndNode, int32_t aEndOffset,
    1551             :                                  bool aAllowAdjacent,
    1552             :                                  int32_t* aStartIndex, int32_t* aEndIndex)
    1553             : {
    1554             :   int32_t startIndex;
    1555             :   int32_t endIndex;
    1556             : 
    1557           4 :   if (!aStartIndex)
    1558           0 :     aStartIndex = &startIndex;
    1559           4 :   if (!aEndIndex)
    1560           0 :     aEndIndex = &endIndex;
    1561             : 
    1562           4 :   *aStartIndex = -1;
    1563           4 :   *aEndIndex = -1;
    1564             : 
    1565           4 :   if (mRanges.Length() == 0)
    1566           0 :     return NS_OK;
    1567             : 
    1568           4 :   bool intervalIsCollapsed = aBeginNode == aEndNode &&
    1569           4 :     aBeginOffset == aEndOffset;
    1570             : 
    1571             :   // Ranges that end before the given interval and begin after the given
    1572             :   // interval can be discarded
    1573             :   int32_t endsBeforeIndex;
    1574           4 :   if (NS_FAILED(FindInsertionPoint(&mRanges, aEndNode, aEndOffset,
    1575             :                                    &CompareToRangeStart,
    1576             :                                    &endsBeforeIndex))) {
    1577           0 :     return NS_OK;
    1578             :   }
    1579             : 
    1580           4 :   if (endsBeforeIndex == 0) {
    1581           0 :     nsRange* endRange = mRanges[endsBeforeIndex].mRange;
    1582             : 
    1583             :     // If the interval is strictly before the range at index 0, we can optimize
    1584             :     // by returning now - all ranges start after the given interval
    1585           0 :     if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
    1586           0 :       return NS_OK;
    1587             : 
    1588             :     // We now know that the start point of mRanges[0].mRange equals the end of
    1589             :     // the interval. Thus, when aAllowadjacent is true, the caller is always
    1590             :     // interested in this range. However, when excluding adjacencies, we must
    1591             :     // remember to include the range when both it and the given interval are
    1592             :     // collapsed to the same point
    1593           0 :     if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed))
    1594           0 :       return NS_OK;
    1595             :   }
    1596           4 :   *aEndIndex = endsBeforeIndex;
    1597             : 
    1598             :   int32_t beginsAfterIndex;
    1599           4 :   if (NS_FAILED(FindInsertionPoint(&mRanges, aBeginNode, aBeginOffset,
    1600             :                                    &CompareToRangeEnd,
    1601             :                                    &beginsAfterIndex))) {
    1602           0 :     return NS_OK;
    1603             :   }
    1604           4 :   if (beginsAfterIndex == (int32_t) mRanges.Length())
    1605           0 :     return NS_OK; // optimization: all ranges are strictly before us
    1606             : 
    1607           4 :   if (aAllowAdjacent) {
    1608             :     // At this point, one of the following holds:
    1609             :     //   endsBeforeIndex == mRanges.Length(),
    1610             :     //   endsBeforeIndex points to a range whose start point does not equal the
    1611             :     //     given interval's start point
    1612             :     //   endsBeforeIndex points to a range whose start point equals the given
    1613             :     //     interval's start point
    1614             :     // In the final case, there can be two such ranges, a collapsed range, and
    1615             :     // an adjacent range (they will appear in mRanges in that order). For this
    1616             :     // final case, we need to increment endsBeforeIndex, until one of the
    1617             :     // first two possibilites hold
    1618           0 :     while (endsBeforeIndex < (int32_t) mRanges.Length()) {
    1619           0 :       nsRange* endRange = mRanges[endsBeforeIndex].mRange;
    1620           0 :       if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
    1621           0 :         break;
    1622           0 :       endsBeforeIndex++;
    1623             :     }
    1624             : 
    1625             :     // Likewise, one of the following holds:
    1626             :     //   beginsAfterIndex == 0,
    1627             :     //   beginsAfterIndex points to a range whose end point does not equal
    1628             :     //     the given interval's end point
    1629             :     //   beginsOnOrAfter points to a range whose end point equals the given
    1630             :     //     interval's end point
    1631             :     // In the final case, there can be two such ranges, an adjacent range, and
    1632             :     // a collapsed range (they will appear in mRanges in that order). For this
    1633             :     // final case, we only need to take action if both those ranges exist, and
    1634             :     // we are pointing to the collapsed range - we need to point to the
    1635             :     // adjacent range
    1636           0 :     nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
    1637           0 :     if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
    1638           0 :         RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) {
    1639           0 :       beginRange = mRanges[beginsAfterIndex - 1].mRange;
    1640           0 :       if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset))
    1641           0 :         beginsAfterIndex--;
    1642             :     }
    1643             :   } else {
    1644             :     // See above for the possibilities at this point. The only case where we
    1645             :     // need to take action is when the range at beginsAfterIndex ends on
    1646             :     // the given interval's start point, but that range isn't collapsed (a
    1647             :     // collapsed range should be included in the returned results).
    1648           4 :     nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
    1649           6 :     if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset) &&
    1650           2 :         !beginRange->Collapsed())
    1651           0 :       beginsAfterIndex++;
    1652             : 
    1653             :     // Again, see above for the meaning of endsBeforeIndex at this point.
    1654             :     // In particular, endsBeforeIndex may point to a collaped range which
    1655             :     // represents the point at the end of the interval - this range should be
    1656             :     // included
    1657           4 :     if (endsBeforeIndex < (int32_t) mRanges.Length()) {
    1658           0 :       nsRange* endRange = mRanges[endsBeforeIndex].mRange;
    1659           0 :       if (RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset) &&
    1660           0 :           endRange->Collapsed())
    1661           0 :         endsBeforeIndex++;
    1662             :      }
    1663             :   }
    1664             : 
    1665           4 :   NS_ASSERTION(beginsAfterIndex <= endsBeforeIndex,
    1666             :                "Is mRanges not ordered?");
    1667           4 :   NS_ENSURE_STATE(beginsAfterIndex <= endsBeforeIndex);
    1668             : 
    1669           4 :   *aStartIndex = beginsAfterIndex;
    1670           4 :   *aEndIndex = endsBeforeIndex;
    1671           4 :   return NS_OK;
    1672             : }
    1673             : 
    1674             : NS_IMETHODIMP
    1675           0 : Selection::GetPrimaryFrameForAnchorNode(nsIFrame** aReturnFrame)
    1676             : {
    1677           0 :   if (!aReturnFrame)
    1678           0 :     return NS_ERROR_NULL_POINTER;
    1679             : 
    1680           0 :   int32_t frameOffset = 0;
    1681           0 :   *aReturnFrame = 0;
    1682           0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode());
    1683           0 :   if (content && mFrameSelection)
    1684             :   {
    1685           0 :     *aReturnFrame = mFrameSelection->
    1686           0 :       GetFrameForNodeOffset(content, AnchorOffset(),
    1687             :                             mFrameSelection->GetHint(), &frameOffset);
    1688           0 :     if (*aReturnFrame)
    1689           0 :       return NS_OK;
    1690             :   }
    1691           0 :   return NS_ERROR_FAILURE;
    1692             : }
    1693             : 
    1694             : NS_IMETHODIMP
    1695           0 : Selection::GetPrimaryFrameForFocusNode(nsIFrame** aReturnFrame,
    1696             :                                        int32_t* aOffsetUsed,
    1697             :                                        bool aVisual)
    1698             : {
    1699           0 :   if (!aReturnFrame)
    1700           0 :     return NS_ERROR_NULL_POINTER;
    1701             : 
    1702           0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(GetFocusNode());
    1703           0 :   if (!content || !mFrameSelection)
    1704           0 :     return NS_ERROR_FAILURE;
    1705             : 
    1706           0 :   int32_t frameOffset = 0;
    1707           0 :   *aReturnFrame = 0;
    1708           0 :   if (!aOffsetUsed)
    1709           0 :     aOffsetUsed = &frameOffset;
    1710             : 
    1711           0 :   CaretAssociationHint hint = mFrameSelection->GetHint();
    1712             : 
    1713           0 :   if (aVisual) {
    1714           0 :     nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
    1715             : 
    1716           0 :     return nsCaret::GetCaretFrameForNodeOffset(mFrameSelection,
    1717           0 :       content, FocusOffset(), hint, caretBidiLevel, aReturnFrame, aOffsetUsed);
    1718             :   }
    1719             : 
    1720           0 :   *aReturnFrame = mFrameSelection->
    1721           0 :     GetFrameForNodeOffset(content, FocusOffset(),
    1722             :                           hint, aOffsetUsed);
    1723           0 :   if (!*aReturnFrame)
    1724           0 :     return NS_ERROR_FAILURE;
    1725             : 
    1726           0 :   return NS_OK;
    1727             : }
    1728             : 
    1729             : void
    1730          17 : Selection::SelectFramesForContent(nsIContent* aContent,
    1731             :                                   bool aSelected)
    1732             : {
    1733          17 :   nsIFrame* frame = aContent->GetPrimaryFrame();
    1734          17 :   if (!frame) {
    1735           0 :     return;
    1736             :   }
    1737             :   // The frame could be an SVG text frame, in which case we don't treat it
    1738             :   // as a text frame.
    1739          17 :   if (frame->IsTextFrame()) {
    1740           0 :     nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
    1741           0 :     textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(),
    1742           0 :                                 aSelected, mSelectionType);
    1743             :   } else {
    1744          17 :     frame->InvalidateFrameSubtree();  // frame continuations?
    1745             :   }
    1746             : }
    1747             : 
    1748             : //select all content children of aContent
    1749             : nsresult
    1750           0 : Selection::SelectAllFramesForContent(nsIContentIterator* aInnerIter,
    1751             :                                      nsIContent* aContent,
    1752             :                                      bool aSelected)
    1753             : {
    1754             :   // If aContent doesn't have children, we should avoid to use the content
    1755             :   // iterator for performance reason.
    1756           0 :   if (!aContent->HasChildren()) {
    1757           0 :     SelectFramesForContent(aContent, aSelected);
    1758           0 :     return NS_OK;
    1759             :   }
    1760             : 
    1761           0 :   if (NS_WARN_IF(NS_FAILED(aInnerIter->Init(aContent)))) {
    1762           0 :     return NS_ERROR_FAILURE;
    1763             :   }
    1764             : 
    1765           0 :   for (; !aInnerIter->IsDone(); aInnerIter->Next()) {
    1766           0 :     nsINode* node = aInnerIter->GetCurrentNode();
    1767           0 :     MOZ_ASSERT(node);
    1768           0 :     nsIContent* innercontent = node->IsContent() ? node->AsContent() : nullptr;
    1769           0 :     SelectFramesForContent(innercontent, aSelected);
    1770             :   }
    1771             : 
    1772           0 :   return NS_OK;
    1773             : }
    1774             : 
    1775             : /**
    1776             :  * The idea of this helper method is to select or deselect "top to bottom",
    1777             :  * traversing through the frames
    1778             :  */
    1779             : nsresult
    1780          30 : Selection::SelectFrames(nsPresContext* aPresContext, nsRange* aRange,
    1781             :                         bool aSelect)
    1782             : {
    1783          30 :   if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
    1784             :     // nothing to do
    1785           1 :     return NS_OK;
    1786             :   }
    1787          29 :   MOZ_ASSERT(aRange && aRange->IsPositioned());
    1788             : 
    1789          29 :   if (mFrameSelection->GetTableCellSelection()) {
    1790           0 :     nsINode* node = aRange->GetCommonAncestor();
    1791           0 :     nsIFrame* frame = node->IsContent() ? node->AsContent()->GetPrimaryFrame()
    1792           0 :                                 : aPresContext->FrameManager()->GetRootFrame();
    1793           0 :     if (frame) {
    1794           0 :       frame->InvalidateFrameSubtree();
    1795             :     }
    1796           0 :     return NS_OK;
    1797             :   }
    1798             : 
    1799             : 
    1800             :   // Loop through the content iterator for each content node; for each text
    1801             :   // node, call SetSelected on it:
    1802          29 :   nsINode* startNode = aRange->GetStartContainer();
    1803             :   nsIContent* startContent =
    1804          29 :     startNode->IsContent() ? startNode->AsContent() : nullptr;
    1805          29 :   if (!startContent) {
    1806             :     // Don't warn, bug 1055722
    1807             :     // XXX The range can start from a document node and such range can be
    1808             :     //     added to Selection with JS.  Therefore, even in such cases,
    1809             :     //     shouldn't we handle selection in the range?
    1810           0 :     return NS_ERROR_UNEXPECTED;
    1811             :   }
    1812             : 
    1813             :   // We must call first one explicitly
    1814          29 :   bool isFirstContentTextNode = startContent->IsNodeOfType(nsINode::eTEXT);
    1815          29 :   nsINode* endNode = aRange->GetEndContainer();
    1816          29 :   if (isFirstContentTextNode) {
    1817          12 :     nsIFrame* frame = startContent->GetPrimaryFrame();
    1818             :     // The frame could be an SVG text frame, in which case we don't treat it
    1819             :     // as a text frame.
    1820          12 :     if (frame) {
    1821           6 :       if (frame->IsTextFrame()) {
    1822           6 :         nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
    1823           6 :         uint32_t startOffset = aRange->StartOffset();
    1824             :         uint32_t endOffset;
    1825           6 :         if (endNode == startContent) {
    1826           6 :           endOffset = aRange->EndOffset();
    1827             :         } else {
    1828           0 :           endOffset = startContent->Length();
    1829             :         }
    1830           6 :         textFrame->SetSelectedRange(startOffset, endOffset, aSelect,
    1831           6 :                                     mSelectionType);
    1832             :       } else {
    1833           0 :         frame->InvalidateFrameSubtree();
    1834             :       }
    1835             :     }
    1836             :   }
    1837             : 
    1838             :   // If the range is in a node and the node is a leaf node, we don't need to
    1839             :   // walk the subtree.
    1840          58 :   if (aRange->Collapsed() ||
    1841           7 :       (startNode == endNode && !startNode->HasChildren())) {
    1842          29 :     if (!isFirstContentTextNode) {
    1843          17 :       SelectFramesForContent(startContent, aSelect);
    1844             :     }
    1845          29 :     return NS_OK;
    1846             :   }
    1847             : 
    1848           0 :   nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
    1849           0 :   iter->Init(aRange);
    1850           0 :   if (isFirstContentTextNode && !iter->IsDone() &&
    1851           0 :       iter->GetCurrentNode() == startNode) {
    1852           0 :     iter->Next(); // first content has already been handled.
    1853             :   }
    1854           0 :   nsCOMPtr<nsIContentIterator> inneriter = NS_NewContentIterator();
    1855           0 :   for (; !iter->IsDone(); iter->Next()) {
    1856           0 :     nsINode* node = iter->GetCurrentNode();
    1857           0 :     MOZ_ASSERT(node);
    1858           0 :     nsIContent* content = node->IsContent() ? node->AsContent() : nullptr;
    1859           0 :     SelectAllFramesForContent(inneriter, content, aSelect);
    1860             :   }
    1861             : 
    1862             :   // We must now do the last one if it is not the same as the first
    1863           0 :   if (endNode != startNode) {
    1864             :     nsIContent* endContent =
    1865           0 :       endNode->IsContent() ? endNode->AsContent() : nullptr;
    1866             :     // XXX The range can end at a document node and such range can be
    1867             :     //     added to Selection with JS.  Therefore, even in such cases,
    1868             :     //     shouldn't we handle selection in the range?
    1869           0 :     if (NS_WARN_IF(!endContent)) {
    1870           0 :       return NS_ERROR_UNEXPECTED;
    1871             :     }
    1872           0 :     if (endContent->IsNodeOfType(nsINode::eTEXT)) {
    1873           0 :       nsIFrame* frame = endContent->GetPrimaryFrame();
    1874             :       // The frame could be an SVG text frame, in which case we'll ignore it.
    1875           0 :       if (frame && frame->IsTextFrame()) {
    1876           0 :         nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
    1877           0 :         textFrame->SetSelectedRange(0, aRange->EndOffset(), aSelect,
    1878           0 :                                     mSelectionType);
    1879             :       }
    1880             :     }
    1881             :   }
    1882           0 :   return NS_OK;
    1883             : }
    1884             : 
    1885             : 
    1886             : // Selection::LookUpSelection
    1887             : //
    1888             : //    This function is called when a node wants to know where the selection is
    1889             : //    over itself.
    1890             : //
    1891             : //    Usually, this is called when we already know there is a selection over
    1892             : //    the node in question, and we only need to find the boundaries of it on
    1893             : //    that node. This is when slowCheck is false--a strict test is not needed.
    1894             : //    Other times, the caller has no idea, and wants us to test everything,
    1895             : //    so we are supposed to determine whether there is a selection over the
    1896             : //    node at all.
    1897             : //
    1898             : //    A previous version of this code used this flag to do less work when
    1899             : //    inclusion was already known (slowCheck=false). However, our tree
    1900             : //    structure allows us to quickly determine ranges overlapping the node,
    1901             : //    so we just ignore the slowCheck flag and do the full test every time.
    1902             : //
    1903             : //    PERFORMANCE: a common case is that we are doing a fast check with exactly
    1904             : //    one range in the selection. In this case, this function is slower than
    1905             : //    brute force because of the overhead of checking the tree. We can optimize
    1906             : //    this case to make it faster by doing the same thing the previous version
    1907             : //    of this function did in the case of 1 range. This would also mean that
    1908             : //    the aSlowCheck flag would have meaning again.
    1909             : 
    1910             : UniquePtr<SelectionDetails>
    1911          20 : Selection::LookUpSelection(nsIContent* aContent, int32_t aContentOffset,
    1912             :                            int32_t aContentLength,
    1913             :                            UniquePtr<SelectionDetails> aDetailsHead,
    1914             :                            SelectionType aSelectionType,
    1915             :                            bool aSlowCheck)
    1916             : {
    1917          20 :   if (!aContent) {
    1918           0 :     return aDetailsHead;
    1919             :   }
    1920             : 
    1921             :   // it is common to have no ranges, to optimize that
    1922          20 :   if (mRanges.Length() == 0) {
    1923          16 :     return aDetailsHead;
    1924             :   }
    1925             : 
    1926           8 :   nsTArray<nsRange*> overlappingRanges;
    1927           4 :   nsresult rv = GetRangesForIntervalArray(aContent, aContentOffset,
    1928             :                                           aContent, aContentOffset + aContentLength,
    1929             :                                           false,
    1930           4 :                                           &overlappingRanges);
    1931           4 :   if (NS_FAILED(rv)) {
    1932           0 :     return aDetailsHead;
    1933             :   }
    1934             : 
    1935           4 :   if (overlappingRanges.Length() == 0) {
    1936           0 :     return aDetailsHead;
    1937             :   }
    1938             : 
    1939           8 :   UniquePtr<SelectionDetails> detailsHead = Move(aDetailsHead);
    1940             : 
    1941           8 :   for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
    1942           4 :     nsRange* range = overlappingRanges[i];
    1943           4 :     nsINode* startNode = range->GetStartContainer();
    1944           4 :     nsINode* endNode = range->GetEndContainer();
    1945           4 :     int32_t startOffset = range->StartOffset();
    1946           4 :     int32_t endOffset = range->EndOffset();
    1947             : 
    1948           4 :     int32_t start = -1, end = -1;
    1949           4 :     if (startNode == aContent && endNode == aContent) {
    1950           8 :       if (startOffset < (aContentOffset + aContentLength)  &&
    1951             :           endOffset > aContentOffset) {
    1952             :         // this range is totally inside the requested content range
    1953           2 :         start = std::max(0, startOffset - aContentOffset);
    1954           2 :         end = std::min(aContentLength, endOffset - aContentOffset);
    1955             :       }
    1956             :       // otherwise, range is inside the requested node, but does not intersect
    1957             :       // the requested content range, so ignore it
    1958           0 :     } else if (startNode == aContent) {
    1959           0 :       if (startOffset < (aContentOffset + aContentLength)) {
    1960             :         // the beginning of the range is inside the requested node, but the
    1961             :         // end is outside, select everything from there to the end
    1962           0 :         start = std::max(0, startOffset - aContentOffset);
    1963           0 :         end = aContentLength;
    1964             :       }
    1965           0 :     } else if (endNode == aContent) {
    1966           0 :       if (endOffset > aContentOffset) {
    1967             :         // the end of the range is inside the requested node, but the beginning
    1968             :         // is outside, select everything from the beginning to there
    1969           0 :         start = 0;
    1970           0 :         end = std::min(aContentLength, endOffset - aContentOffset);
    1971             :       }
    1972             :     } else {
    1973             :       // this range does not begin or end in the requested node, but since
    1974             :       // GetRangesForInterval returned this range, we know it overlaps.
    1975             :       // Therefore, this node is enclosed in the range, and we select all
    1976             :       // of it.
    1977           0 :       start = 0;
    1978           0 :       end = aContentLength;
    1979             :     }
    1980           4 :     if (start < 0)
    1981           2 :       continue; // the ranges do not overlap the input range
    1982             : 
    1983           4 :     auto newHead = MakeUnique<SelectionDetails>();
    1984             : 
    1985           2 :     newHead->mNext = Move(detailsHead);
    1986           2 :     newHead->mStart = start;
    1987           2 :     newHead->mEnd = end;
    1988           2 :     newHead->mSelectionType = aSelectionType;
    1989           2 :     RangeData *rd = FindRangeData(range);
    1990           2 :     if (rd) {
    1991           2 :       newHead->mTextRangeStyle = rd->mTextRangeStyle;
    1992             :     }
    1993           2 :     detailsHead = Move(newHead);
    1994             :   }
    1995           4 :   return detailsHead;
    1996             : }
    1997             : 
    1998             : NS_IMETHODIMP
    1999           3 : Selection::Repaint(nsPresContext* aPresContext)
    2000             : {
    2001           3 :   int32_t arrCount = (int32_t)mRanges.Length();
    2002             : 
    2003           3 :   if (arrCount < 1)
    2004           3 :     return NS_OK;
    2005             : 
    2006             :   int32_t i;
    2007             : 
    2008           0 :   for (i = 0; i < arrCount; i++)
    2009             :   {
    2010           0 :     nsresult rv = SelectFrames(aPresContext, mRanges[i].mRange, true);
    2011             : 
    2012           0 :     if (NS_FAILED(rv)) {
    2013           0 :       return rv;
    2014             :     }
    2015             :   }
    2016             : 
    2017           0 :   return NS_OK;
    2018             : }
    2019             : 
    2020             : NS_IMETHODIMP
    2021           0 : Selection::GetCanCacheFrameOffset(bool* aCanCacheFrameOffset)
    2022             : {
    2023           0 :   NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
    2024             : 
    2025           0 :   if (mCachedOffsetForFrame)
    2026           0 :     *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
    2027             :   else
    2028           0 :     *aCanCacheFrameOffset = false;
    2029             : 
    2030           0 :   return NS_OK;
    2031             : }
    2032             : 
    2033             : NS_IMETHODIMP
    2034           2 : Selection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset)
    2035             : {
    2036           2 :   if (!mCachedOffsetForFrame) {
    2037           1 :     mCachedOffsetForFrame = new CachedOffsetForFrame;
    2038             :   }
    2039             : 
    2040           2 :   mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
    2041             : 
    2042             :   // clean up cached frame when turn off cache
    2043             :   // fix bug 207936
    2044           2 :   if (!aCanCacheFrameOffset) {
    2045           1 :     mCachedOffsetForFrame->mLastCaretFrame = nullptr;
    2046             :   }
    2047             : 
    2048           2 :   return NS_OK;
    2049             : }
    2050             : 
    2051             : NS_IMETHODIMP
    2052           1 : Selection::GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
    2053             :                                 nsPoint& aPoint)
    2054             : {
    2055           1 :   if (!mCachedOffsetForFrame) {
    2056           0 :     mCachedOffsetForFrame = new CachedOffsetForFrame;
    2057             :   }
    2058             : 
    2059           1 :   nsresult rv = NS_OK;
    2060           1 :   if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
    2061           0 :       mCachedOffsetForFrame->mLastCaretFrame &&
    2062           0 :       (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
    2063           0 :       (inOffset == mCachedOffsetForFrame->mLastContentOffset))
    2064             :   {
    2065             :      // get cached frame offset
    2066           0 :      aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
    2067             :   }
    2068             :   else
    2069             :   {
    2070             :      // Recalculate frame offset and cache it. Don't cache a frame offset if
    2071             :      // GetPointFromOffset fails, though.
    2072           1 :      rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
    2073           1 :      if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
    2074           0 :        mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
    2075           0 :        mCachedOffsetForFrame->mLastCaretFrame = aFrame;
    2076           0 :        mCachedOffsetForFrame->mLastContentOffset = inOffset;
    2077             :      }
    2078             :   }
    2079             : 
    2080           1 :   return rv;
    2081             : }
    2082             : 
    2083             : NS_IMETHODIMP
    2084           0 : Selection::GetAncestorLimiter(nsIContent** aContent)
    2085             : {
    2086           0 :   if (mFrameSelection) {
    2087           0 :     nsCOMPtr<nsIContent> c = mFrameSelection->GetAncestorLimiter();
    2088           0 :     c.forget(aContent);
    2089             :   }
    2090           0 :   return NS_OK;
    2091             : }
    2092             : 
    2093             : NS_IMETHODIMP
    2094           0 : Selection::SetAncestorLimiter(nsIContent* aContent)
    2095             : {
    2096           0 :   if (mFrameSelection) {
    2097           0 :     RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    2098           0 :     frameSelection->SetAncestorLimiter(aContent);
    2099             :   }
    2100           0 :   return NS_OK;
    2101             : }
    2102             : 
    2103             : RangeData*
    2104           2 : Selection::FindRangeData(nsIDOMRange* aRange)
    2105             : {
    2106           2 :   NS_ENSURE_TRUE(aRange, nullptr);
    2107           2 :   for (uint32_t i = 0; i < mRanges.Length(); i++) {
    2108           2 :     if (mRanges[i].mRange == aRange)
    2109           2 :       return &mRanges[i];
    2110             :   }
    2111           0 :   return nullptr;
    2112             : }
    2113             : 
    2114             : NS_IMETHODIMP
    2115           0 : Selection::SetTextRangeStyle(nsIDOMRange* aRange,
    2116             :                              const TextRangeStyle& aTextRangeStyle)
    2117             : {
    2118           0 :   NS_ENSURE_ARG_POINTER(aRange);
    2119           0 :   RangeData *rd = FindRangeData(aRange);
    2120           0 :   if (rd) {
    2121           0 :     rd->mTextRangeStyle = aTextRangeStyle;
    2122             :   }
    2123           0 :   return NS_OK;
    2124             : }
    2125             : 
    2126             : nsresult
    2127           0 : Selection::StartAutoScrollTimer(nsIFrame* aFrame, nsPoint& aPoint,
    2128             :                                 uint32_t aDelay)
    2129             : {
    2130           0 :   NS_PRECONDITION(aFrame, "Need a frame");
    2131             : 
    2132             :   nsresult result;
    2133           0 :   if (!mFrameSelection) {
    2134           0 :     return NS_OK;//nothing to do
    2135             :   }
    2136             : 
    2137           0 :   if (!mAutoScrollTimer) {
    2138           0 :     mAutoScrollTimer = new nsAutoScrollTimer();
    2139             : 
    2140           0 :     result = mAutoScrollTimer->Init(mFrameSelection, this);
    2141             : 
    2142           0 :     if (NS_FAILED(result)) {
    2143           0 :       return result;
    2144             :     }
    2145             :   }
    2146             : 
    2147           0 :   result = mAutoScrollTimer->SetDelay(aDelay);
    2148             : 
    2149           0 :   if (NS_FAILED(result)) {
    2150           0 :     return result;
    2151             :   }
    2152             : 
    2153           0 :   return DoAutoScroll(aFrame, aPoint);
    2154             : }
    2155             : 
    2156             : nsresult
    2157           6 : Selection::StopAutoScrollTimer()
    2158             : {
    2159           6 :   if (mAutoScrollTimer) {
    2160           0 :     return mAutoScrollTimer->Stop();
    2161             :   }
    2162           6 :   return NS_OK;
    2163             : }
    2164             : 
    2165             : nsresult
    2166           0 : Selection::DoAutoScroll(nsIFrame* aFrame, nsPoint& aPoint)
    2167             : {
    2168           0 :   NS_PRECONDITION(aFrame, "Need a frame");
    2169             : 
    2170           0 :   if (mAutoScrollTimer) {
    2171           0 :     (void)mAutoScrollTimer->Stop();
    2172             :   }
    2173             : 
    2174           0 :   nsPresContext* presContext = aFrame->PresContext();
    2175           0 :   nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
    2176           0 :   nsRootPresContext* rootPC = presContext->GetRootPresContext();
    2177           0 :   if (!rootPC)
    2178           0 :     return NS_OK;
    2179           0 :   nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
    2180           0 :   AutoWeakFrame weakRootFrame(rootmostFrame);
    2181           0 :   AutoWeakFrame weakFrame(aFrame);
    2182             :   // Get the point relative to the root most frame because the scroll we are
    2183             :   // about to do will change the coordinates of aFrame.
    2184           0 :   nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
    2185             : 
    2186           0 :   bool done = false;
    2187             :   bool didScroll;
    2188             :   while (true) {
    2189           0 :     didScroll = shell->ScrollFrameRectIntoView(
    2190           0 :                   aFrame, nsRect(aPoint, nsSize(0, 0)),
    2191             :                   nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
    2192           0 :                   0);
    2193           0 :     if (!weakFrame || !weakRootFrame) {
    2194           0 :       return NS_OK;
    2195             :     }
    2196           0 :     if (!didScroll && !done) {
    2197             :       // If aPoint is at the screen edge then try to scroll anyway, once.
    2198           0 :       RefPtr<nsDeviceContext> dx = shell->GetViewManager()->GetDeviceContext();
    2199           0 :       nsRect screen;
    2200           0 :       dx->GetRect(screen);
    2201             :       nsPoint screenPoint = globalPoint +
    2202           0 :                             rootmostFrame->GetScreenRectInAppUnits().TopLeft();
    2203           0 :       nscoord onePx = nsPresContext::AppUnitsPerCSSPixel();
    2204           0 :       nscoord scrollAmount = 10 * onePx;
    2205           0 :       if (std::abs(screen.x - screenPoint.x) <= onePx) {
    2206           0 :         aPoint.x -= scrollAmount;
    2207           0 :       } else if (std::abs(screen.XMost() - screenPoint.x) <= onePx) {
    2208           0 :         aPoint.x += scrollAmount;
    2209           0 :       } else if (std::abs(screen.y - screenPoint.y) <= onePx) {
    2210           0 :         aPoint.y -= scrollAmount;
    2211           0 :       } else if (std::abs(screen.YMost() - screenPoint.y) <= onePx) {
    2212           0 :         aPoint.y += scrollAmount;
    2213             :       } else {
    2214           0 :         break;
    2215             :       }
    2216           0 :       done = true;
    2217           0 :       continue;
    2218             :     }
    2219           0 :     break;
    2220           0 :   }
    2221             : 
    2222             :   // Start the AutoScroll timer if necessary.
    2223           0 :   if (didScroll && mAutoScrollTimer) {
    2224             :     nsPoint presContextPoint = globalPoint -
    2225           0 :       shell->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
    2226           0 :     mAutoScrollTimer->Start(presContext, presContextPoint);
    2227             :   }
    2228             : 
    2229           0 :   return NS_OK;
    2230             : }
    2231             : 
    2232             : 
    2233             : /** RemoveAllRanges zeroes the selection
    2234             :  */
    2235             : NS_IMETHODIMP
    2236           9 : Selection::RemoveAllRanges()
    2237             : {
    2238          18 :   ErrorResult result;
    2239           9 :   RemoveAllRanges(result);
    2240          18 :   return result.StealNSResult();
    2241             : }
    2242             : 
    2243             : void
    2244          15 : Selection::RemoveAllRanges(ErrorResult& aRv)
    2245             : {
    2246          15 :   if (!mFrameSelection)
    2247           0 :     return; // nothing to do
    2248          30 :   RefPtr<nsPresContext>  presContext = GetPresContext();
    2249          15 :   nsresult  result = Clear(presContext);
    2250          15 :   if (NS_FAILED(result)) {
    2251           0 :     aRv.Throw(result);
    2252           0 :     return;
    2253             :   }
    2254             : 
    2255             :   // Turn off signal for table selection
    2256          30 :   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    2257          15 :   frameSelection->ClearTableCellSelection();
    2258             : 
    2259             :   // Be aware, this instance may be destroyed after this call.
    2260             :   // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
    2261          15 :   result = frameSelection->NotifySelectionListeners(GetType());
    2262             : 
    2263             :   // Also need to notify the frames!
    2264             :   // PresShell::CharacterDataChanged should do that on DocumentChanged
    2265          15 :   if (NS_FAILED(result)) {
    2266           0 :     aRv.Throw(result);
    2267             :   }
    2268             : }
    2269             : 
    2270             : /** AddRange adds the specified range to the selection
    2271             :  *  @param aRange is the range to be added
    2272             :  */
    2273             : NS_IMETHODIMP
    2274           8 : Selection::AddRange(nsIDOMRange* aDOMRange)
    2275             : {
    2276           8 :   if (!aDOMRange) {
    2277           0 :     return NS_ERROR_NULL_POINTER;
    2278             :   }
    2279           8 :   nsRange* range = static_cast<nsRange*>(aDOMRange);
    2280          16 :   ErrorResult result;
    2281           8 :   AddRange(*range, result);
    2282           8 :   return result.StealNSResult();
    2283             : }
    2284             : 
    2285             : void
    2286           2 : Selection::AddRangeJS(nsRange& aRange, ErrorResult& aRv)
    2287             : {
    2288           4 :   AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
    2289           2 :   mCalledByJS = true;
    2290           2 :   AddRange(aRange, aRv);
    2291           2 : }
    2292             : 
    2293             : void
    2294          10 : Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
    2295             : {
    2296          10 :   return AddRangeInternal(aRange, GetParentObject(), aRv);
    2297             : }
    2298             : 
    2299             : void
    2300          10 : Selection::AddRangeInternal(nsRange& aRange, nsIDocument* aDocument,
    2301             :                             ErrorResult& aRv)
    2302             : {
    2303          10 :   nsINode* rangeRoot = aRange.GetRoot();
    2304          18 :   if (aDocument != rangeRoot && (!rangeRoot ||
    2305           8 :                                  aDocument != rangeRoot->GetComposedDoc())) {
    2306             :     // http://w3c.github.io/selection-api/#dom-selection-addrange
    2307             :     // "...  if the root of the range's boundary points are the document
    2308             :     // associated with context object. Otherwise, this method must do nothing."
    2309           0 :     return;
    2310             :   }
    2311             : 
    2312             :   // This inserts a table cell range in proper document order
    2313             :   // and returns NS_OK if range doesn't contain just one table cell
    2314             :   bool didAddRange;
    2315             :   int32_t rangeIndex;
    2316          10 :   nsresult result = AddTableCellRange(&aRange, &didAddRange, &rangeIndex);
    2317          10 :   if (NS_FAILED(result)) {
    2318           0 :     aRv.Throw(result);
    2319           0 :     return;
    2320             :   }
    2321             : 
    2322          10 :   if (!didAddRange) {
    2323          10 :     result = AddItem(&aRange, &rangeIndex);
    2324          10 :     if (NS_FAILED(result)) {
    2325           0 :       aRv.Throw(result);
    2326           0 :       return;
    2327             :     }
    2328             :   }
    2329             : 
    2330          10 :   if (rangeIndex < 0) {
    2331           0 :     return;
    2332             :   }
    2333             : 
    2334          10 :   SetAnchorFocusRange(rangeIndex);
    2335             : 
    2336             :   // Make sure the caret appears on the next line, if at a newline
    2337          10 :   if (mSelectionType == SelectionType::eNormal) {
    2338           8 :     SetInterlinePosition(true);
    2339             :   }
    2340             : 
    2341          20 :   RefPtr<nsPresContext>  presContext = GetPresContext();
    2342          10 :   SelectFrames(presContext, &aRange, true);
    2343             : 
    2344          10 :   if (!mFrameSelection)
    2345           0 :     return;//nothing to do
    2346             : 
    2347             :   // Be aware, this instance may be destroyed after this call.
    2348             :   // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
    2349          20 :   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    2350          10 :   result = frameSelection->NotifySelectionListeners(GetType());
    2351          10 :   if (NS_FAILED(result)) {
    2352           0 :     aRv.Throw(result);
    2353             :   }
    2354             : }
    2355             : 
    2356             : // Selection::RemoveRange
    2357             : //
    2358             : //    Removes the given range from the selection. The tricky part is updating
    2359             : //    the flags on the frames that indicate whether they have a selection or
    2360             : //    not. There could be several selection ranges on the frame, and clearing
    2361             : //    the bit would cause the selection to not be drawn, even when there is
    2362             : //    another range on the frame (bug 346185).
    2363             : //
    2364             : //    We therefore find any ranges that intersect the same nodes as the range
    2365             : //    being removed, and cause them to set the selected bits back on their
    2366             : //    selected frames after we've cleared the bit from ours.
    2367             : 
    2368             : nsresult
    2369           0 : Selection::RemoveRange(nsIDOMRange* aDOMRange)
    2370             : {
    2371           0 :   if (!aDOMRange) {
    2372           0 :     return NS_ERROR_INVALID_ARG;
    2373             :   }
    2374           0 :   nsRange* range = static_cast<nsRange*>(aDOMRange);
    2375           0 :   ErrorResult result;
    2376           0 :   RemoveRange(*range, result);
    2377           0 :   return result.StealNSResult();
    2378             : }
    2379             : 
    2380             : void
    2381           0 : Selection::RemoveRange(nsRange& aRange, ErrorResult& aRv)
    2382             : {
    2383           0 :   nsresult rv = RemoveItem(&aRange);
    2384           0 :   if (NS_FAILED(rv)) {
    2385           0 :     aRv.Throw(rv);
    2386           0 :     return;
    2387             :   }
    2388             : 
    2389           0 :   nsINode* beginNode = aRange.GetStartContainer();
    2390           0 :   nsINode* endNode = aRange.GetEndContainer();
    2391             : 
    2392           0 :   if (!beginNode || !endNode) {
    2393             :     // Detached range; nothing else to do here.
    2394           0 :     return;
    2395             :   }
    2396             : 
    2397             :   // find out the length of the end node, so we can select all of it
    2398             :   int32_t beginOffset, endOffset;
    2399           0 :   if (endNode->IsNodeOfType(nsINode::eTEXT)) {
    2400             :     // Get the length of the text. We can't just use the offset because
    2401             :     // another range could be touching this text node but not intersect our
    2402             :     // range.
    2403           0 :     beginOffset = 0;
    2404           0 :     endOffset = static_cast<nsIContent*>(endNode)->TextLength();
    2405             :   } else {
    2406             :     // For non-text nodes, the given offsets should be sufficient.
    2407           0 :     beginOffset = aRange.StartOffset();
    2408           0 :     endOffset = aRange.EndOffset();
    2409             :   }
    2410             : 
    2411             :   // clear the selected bit from the removed range's frames
    2412           0 :   RefPtr<nsPresContext>  presContext = GetPresContext();
    2413           0 :   SelectFrames(presContext, &aRange, false);
    2414             : 
    2415             :   // add back the selected bit for each range touching our nodes
    2416           0 :   nsTArray<nsRange*> affectedRanges;
    2417             :   rv = GetRangesForIntervalArray(beginNode, beginOffset,
    2418             :                                  endNode, endOffset,
    2419           0 :                                  true, &affectedRanges);
    2420           0 :   if (NS_FAILED(rv)) {
    2421           0 :     aRv.Throw(rv);
    2422           0 :     return;
    2423             :   }
    2424           0 :   for (uint32_t i = 0; i < affectedRanges.Length(); i++) {
    2425           0 :     SelectFrames(presContext, affectedRanges[i], true);
    2426             :   }
    2427             : 
    2428           0 :   int32_t cnt = mRanges.Length();
    2429           0 :   if (&aRange == mAnchorFocusRange) {
    2430             :     // Reset anchor to LAST range or clear it if there are no ranges.
    2431           0 :     SetAnchorFocusRange(cnt - 1);
    2432             : 
    2433             :     // When the selection is user-created it makes sense to scroll the range
    2434             :     // into view. The spell-check selection, however, is created and destroyed
    2435             :     // in the background. We don't want to scroll in this case or the view
    2436             :     // might appear to be moving randomly (bug 337871).
    2437           0 :     if (mSelectionType != SelectionType::eSpellCheck && cnt > 0) {
    2438           0 :       ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
    2439             :     }
    2440             :   }
    2441             : 
    2442           0 :   if (!mFrameSelection)
    2443           0 :     return;//nothing to do
    2444             : 
    2445             :   // Be aware, this instance may be destroyed after this call.
    2446             :   // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
    2447           0 :   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    2448           0 :   rv = frameSelection->NotifySelectionListeners(GetType());
    2449           0 :   if (NS_FAILED(rv)) {
    2450           0 :     aRv.Throw(rv);
    2451             :   }
    2452             : }
    2453             : 
    2454             : 
    2455             : 
    2456             : /*
    2457             :  * Collapse sets the whole selection to be one point.
    2458             :  */
    2459             : NS_IMETHODIMP
    2460           0 : Selection::Collapse(nsIDOMNode* aContainer, int32_t aOffset)
    2461             : {
    2462           0 :   nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
    2463           0 :   return Collapse(container, aOffset);
    2464             : }
    2465             : 
    2466             : NS_IMETHODIMP
    2467           0 : Selection::CollapseNative(nsINode* aContainer, int32_t aOffset)
    2468             : {
    2469           0 :   return Collapse(aContainer, aOffset);
    2470             : }
    2471             : 
    2472             : void
    2473           0 : Selection::CollapseJS(nsINode* aContainer, uint32_t aOffset, ErrorResult& aRv)
    2474             : {
    2475           0 :   AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
    2476           0 :   mCalledByJS = true;
    2477           0 :   if (!aContainer) {
    2478           0 :     RemoveAllRanges(aRv);
    2479           0 :     return;
    2480             :   }
    2481           0 :   Collapse(*aContainer, aOffset, aRv);
    2482             : }
    2483             : 
    2484             : nsresult
    2485           5 : Selection::Collapse(nsINode* aContainer, int32_t aOffset)
    2486             : {
    2487           5 :   if (!aContainer) {
    2488           0 :     return NS_ERROR_INVALID_ARG;
    2489             :   }
    2490             : 
    2491          10 :   ErrorResult result;
    2492           5 :   Collapse(*aContainer, static_cast<uint32_t>(aOffset), result);
    2493           5 :   return result.StealNSResult();
    2494             : }
    2495             : 
    2496             : void
    2497           7 : Selection::Collapse(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
    2498             : {
    2499           7 :   if (!mFrameSelection) {
    2500           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
    2501           0 :     return;
    2502             :   }
    2503             : 
    2504          14 :   nsCOMPtr<nsINode> container = &aContainer;
    2505             : 
    2506          14 :   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    2507           7 :   frameSelection->InvalidateDesiredPos();
    2508           7 :   if (!IsValidSelectionPoint(frameSelection, container)) {
    2509           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2510           0 :     return;
    2511             :   }
    2512             :   nsresult result;
    2513             : 
    2514          14 :   RefPtr<nsPresContext> presContext = GetPresContext();
    2515           7 :   if (!presContext || presContext->Document() != container->OwnerDoc()) {
    2516           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2517           0 :     return;
    2518             :   }
    2519             : 
    2520             :   // Cache current range is if there is because it may be reusable.
    2521          14 :   RefPtr<nsRange> oldRange = !mRanges.IsEmpty() ? mRanges[0].mRange : nullptr;
    2522             : 
    2523             :   // Delete all of the current ranges
    2524           7 :   Clear(presContext);
    2525             : 
    2526             :   // Turn off signal for table selection
    2527           7 :   frameSelection->ClearTableCellSelection();
    2528             : 
    2529             :   // Hack to display the caret on the right line (bug 1237236).
    2530          11 :   if (frameSelection->GetHint() != CARET_ASSOCIATE_AFTER &&
    2531           4 :       container->IsContent()) {
    2532             :     int32_t frameOffset;
    2533             :     nsTextFrame* f =
    2534           8 :       do_QueryFrame(nsCaret::GetFrameAndOffset(this, container,
    2535           4 :                                                aOffset, &frameOffset));
    2536           4 :     if (f && f->IsAtEndOfLine() && f->HasSignificantTerminalNewline()) {
    2537           0 :       if ((container->AsContent() == f->GetContent() &&
    2538           0 :            f->GetContentEnd() == int32_t(aOffset)) ||
    2539           0 :           (container == f->GetContent()->GetParentNode() &&
    2540           0 :            container->IndexOf(f->GetContent()) + 1 == int32_t(aOffset))) {
    2541           0 :         frameSelection->SetHint(CARET_ASSOCIATE_AFTER);
    2542             :       }
    2543             :     }
    2544             :   }
    2545             : 
    2546          14 :   RefPtr<nsRange> range;
    2547             :   // If the old range isn't referred by anybody other than this method,
    2548             :   // we should reuse it for reducing the recreation cost.
    2549           7 :   if (oldRange && oldRange->GetRefCount() == 1) {
    2550           2 :     range = oldRange;
    2551             :   } else {
    2552          10 :     range = new nsRange(container);
    2553             :   }
    2554           7 :   result = range->CollapseTo(container, aOffset);
    2555           7 :   if (NS_FAILED(result)) {
    2556           0 :     aRv.Throw(result);
    2557           0 :     return;
    2558             :   }
    2559             : 
    2560             : #ifdef DEBUG_SELECTION
    2561             :   nsCOMPtr<nsIContent> content = do_QueryInterface(container);
    2562             :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(container);
    2563             :   printf ("Sel. Collapse to %p %s %d\n", container.get(),
    2564             :           content ? nsAtomCString(content->NodeInfo()->NameAtom()).get()
    2565             :                   : (doc ? "DOCUMENT" : "???"),
    2566             :           aOffset);
    2567             : #endif
    2568             : 
    2569           7 :   int32_t rangeIndex = -1;
    2570           7 :   result = AddItem(range, &rangeIndex);
    2571           7 :   if (NS_FAILED(result)) {
    2572           0 :     aRv.Throw(result);
    2573           0 :     return;
    2574             :   }
    2575           7 :   SetAnchorFocusRange(0);
    2576           7 :   SelectFrames(presContext, range, true);
    2577             : 
    2578             :   // Be aware, this instance may be destroyed after this call.
    2579             :   // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
    2580           7 :   result = frameSelection->NotifySelectionListeners(GetType());
    2581           7 :   if (NS_FAILED(result)) {
    2582           0 :     aRv.Throw(result);
    2583             :   }
    2584             : }
    2585             : 
    2586             : /*
    2587             :  * Sets the whole selection to be one point
    2588             :  * at the start of the current selection
    2589             :  */
    2590             : NS_IMETHODIMP
    2591           2 : Selection::CollapseToStart()
    2592             : {
    2593           4 :   ErrorResult result;
    2594           2 :   CollapseToStart(result);
    2595           4 :   return result.StealNSResult();
    2596             : }
    2597             : 
    2598             : void
    2599           0 : Selection::CollapseToStartJS(ErrorResult& aRv)
    2600             : {
    2601           0 :   AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
    2602           0 :   mCalledByJS = true;
    2603           0 :   CollapseToStart(aRv);
    2604           0 : }
    2605             : 
    2606             : void
    2607           2 : Selection::CollapseToStart(ErrorResult& aRv)
    2608             : {
    2609             :   int32_t cnt;
    2610           2 :   nsresult rv = GetRangeCount(&cnt);
    2611           2 :   if (NS_FAILED(rv) || cnt <= 0) {
    2612           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2613           0 :     return;
    2614             :   }
    2615             : 
    2616             :   // Get the first range
    2617           2 :   nsRange* firstRange = mRanges[0].mRange;
    2618           2 :   if (!firstRange) {
    2619           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2620           0 :     return;
    2621             :   }
    2622             : 
    2623           2 :   if (mFrameSelection) {
    2624           2 :     int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOSTART_REASON;
    2625           2 :     mFrameSelection->PostReason(reason);
    2626             :   }
    2627           2 :   nsINode* container = firstRange->GetStartContainer();
    2628           2 :   if (!container) {
    2629           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2630           0 :     return;
    2631             :   }
    2632           2 :   Collapse(*container, firstRange->StartOffset(), aRv);
    2633             : }
    2634             : 
    2635             : /*
    2636             :  * Sets the whole selection to be one point
    2637             :  * at the end of the current selection
    2638             :  */
    2639             : NS_IMETHODIMP
    2640           0 : Selection::CollapseToEnd()
    2641             : {
    2642           0 :   ErrorResult result;
    2643           0 :   CollapseToEnd(result);
    2644           0 :   return result.StealNSResult();
    2645             : }
    2646             : 
    2647             : void
    2648           0 : Selection::CollapseToEndJS(ErrorResult& aRv)
    2649             : {
    2650           0 :   AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
    2651           0 :   mCalledByJS = true;
    2652           0 :   CollapseToEnd(aRv);
    2653           0 : }
    2654             : 
    2655             : void
    2656           0 : Selection::CollapseToEnd(ErrorResult& aRv)
    2657             : {
    2658             :   int32_t cnt;
    2659           0 :   nsresult rv = GetRangeCount(&cnt);
    2660           0 :   if (NS_FAILED(rv) || cnt <= 0) {
    2661           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2662           0 :     return;
    2663             :   }
    2664             : 
    2665             :   // Get the last range
    2666           0 :   nsRange* lastRange = mRanges[cnt - 1].mRange;
    2667           0 :   if (!lastRange) {
    2668           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2669           0 :     return;
    2670             :   }
    2671             : 
    2672           0 :   if (mFrameSelection) {
    2673           0 :     int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOEND_REASON;
    2674           0 :     mFrameSelection->PostReason(reason);
    2675             :   }
    2676           0 :   nsINode* container = lastRange->GetEndContainer();
    2677           0 :   if (!container) {
    2678           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2679           0 :     return;
    2680             :   }
    2681           0 :   Collapse(*container, lastRange->EndOffset(), aRv);
    2682             : }
    2683             : 
    2684             : /*
    2685             :  * IsCollapsed -- is the whole selection just one point, or unset?
    2686             :  */
    2687             : bool
    2688          20 : Selection::IsCollapsed() const
    2689             : {
    2690          20 :   uint32_t cnt = mRanges.Length();
    2691          20 :   if (cnt == 0) {
    2692           9 :     return true;
    2693             :   }
    2694             : 
    2695          11 :   if (cnt != 1) {
    2696           0 :     return false;
    2697             :   }
    2698             : 
    2699          11 :   return mRanges[0].mRange->Collapsed();
    2700             : }
    2701             : 
    2702             : /* virtual */
    2703             : bool
    2704           0 : Selection::Collapsed()
    2705             : {
    2706           0 :   return IsCollapsed();
    2707             : }
    2708             : 
    2709             : NS_IMETHODIMP
    2710          20 : Selection::GetIsCollapsed(bool* aIsCollapsed)
    2711             : {
    2712          20 :   NS_ENSURE_TRUE(aIsCollapsed, NS_ERROR_NULL_POINTER);
    2713             : 
    2714          20 :   *aIsCollapsed = IsCollapsed();
    2715          20 :   return NS_OK;
    2716             : }
    2717             : 
    2718             : NS_IMETHODIMP
    2719           4 : Selection::GetRangeCount(int32_t* aRangeCount)
    2720             : {
    2721           4 :   *aRangeCount = (int32_t)RangeCount();
    2722             : 
    2723           4 :   return NS_OK;
    2724             : }
    2725             : 
    2726             : NS_IMETHODIMP
    2727           0 : Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
    2728             : {
    2729           0 :   ErrorResult result;
    2730           0 :   *aReturn = GetRangeAt(aIndex, result);
    2731           0 :   NS_IF_ADDREF(*aReturn);
    2732           0 :   return result.StealNSResult();
    2733             : }
    2734             : 
    2735             : nsRange*
    2736           0 : Selection::GetRangeAt(uint32_t aIndex, ErrorResult& aRv)
    2737             : {
    2738           0 :   nsRange* range = GetRangeAt(aIndex);
    2739           0 :   if (!range) {
    2740           0 :     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    2741           0 :     return nullptr;
    2742             :   }
    2743             : 
    2744           0 :   return range;
    2745             : }
    2746             : 
    2747             : nsRange*
    2748          24 : Selection::GetRangeAt(int32_t aIndex) const
    2749             : {
    2750          48 :   RangeData empty(nullptr);
    2751          48 :   return mRanges.SafeElementAt(aIndex, empty).mRange;
    2752             : }
    2753             : 
    2754             : /*
    2755             : utility function
    2756             : */
    2757             : nsresult
    2758           0 : Selection::SetAnchorFocusToRange(nsRange* aRange)
    2759             : {
    2760           0 :   NS_ENSURE_STATE(mAnchorFocusRange);
    2761             : 
    2762           0 :   bool collapsed = Collapsed();
    2763             : 
    2764           0 :   nsresult res = RemoveItem(mAnchorFocusRange);
    2765           0 :   if (NS_FAILED(res))
    2766           0 :     return res;
    2767             : 
    2768           0 :   int32_t aOutIndex = -1;
    2769           0 :   res = AddItem(aRange, &aOutIndex, !collapsed);
    2770           0 :   if (NS_FAILED(res))
    2771           0 :     return res;
    2772           0 :   SetAnchorFocusRange(aOutIndex);
    2773             : 
    2774           0 :   return NS_OK;
    2775             : }
    2776             : 
    2777             : void
    2778           0 : Selection::ReplaceAnchorFocusRange(nsRange* aRange)
    2779             : {
    2780           0 :   NS_ENSURE_TRUE_VOID(mAnchorFocusRange);
    2781           0 :   RefPtr<nsPresContext> presContext = GetPresContext();
    2782           0 :   if (presContext) {
    2783           0 :     SelectFrames(presContext, mAnchorFocusRange, false);
    2784           0 :     SetAnchorFocusToRange(aRange);
    2785           0 :     SelectFrames(presContext, mAnchorFocusRange, true);
    2786             :   }
    2787             : }
    2788             : 
    2789             : void
    2790           0 : Selection::AdjustAnchorFocusForMultiRange(nsDirection aDirection)
    2791             : {
    2792           0 :   if (aDirection == mDirection) {
    2793           0 :     return;
    2794             :   }
    2795           0 :   SetDirection(aDirection);
    2796             : 
    2797           0 :   if (RangeCount() <= 1) {
    2798           0 :     return;
    2799             :   }
    2800             : 
    2801           0 :   nsRange* firstRange = GetRangeAt(0);
    2802           0 :   nsRange* lastRange = GetRangeAt(RangeCount() - 1);
    2803             : 
    2804           0 :   if (mDirection == eDirPrevious) {
    2805           0 :     firstRange->SetIsGenerated(false);
    2806           0 :     lastRange->SetIsGenerated(true);
    2807           0 :     SetAnchorFocusRange(0);
    2808             :   } else { // aDir == eDirNext
    2809           0 :     firstRange->SetIsGenerated(true);
    2810           0 :     lastRange->SetIsGenerated(false);
    2811           0 :     SetAnchorFocusRange(RangeCount() - 1);
    2812             :   }
    2813             : }
    2814             : 
    2815             : /*
    2816             : Notes which might come in handy for extend:
    2817             : 
    2818             : We can tell the direction of the selection by asking for the anchors selection
    2819             : if the begin is less than the end then we know the selection is to the "right".
    2820             : else it is a backwards selection.
    2821             : a = anchor
    2822             : 1 = old cursor
    2823             : 2 = new cursor
    2824             : 
    2825             :   if (a <= 1 && 1 <=2)    a,1,2  or (a1,2)
    2826             :   if (a < 2 && 1 > 2)     a,2,1
    2827             :   if (1 < a && a <2)      1,a,2
    2828             :   if (a > 2 && 2 >1)      1,2,a
    2829             :   if (2 < a && a <1)      2,a,1
    2830             :   if (a > 1 && 1 >2)      2,1,a
    2831             : then execute
    2832             : a  1  2 select from 1 to 2
    2833             : a  2  1 deselect from 2 to 1
    2834             : 1  a  2 deselect from 1 to a select from a to 2
    2835             : 1  2  a deselect from 1 to 2
    2836             : 2  1  a = continue selection from 2 to 1
    2837             : */
    2838             : 
    2839             : 
    2840             : /*
    2841             :  * Extend extends the selection away from the anchor.
    2842             :  * We don't need to know the direction, because we always change the focus.
    2843             :  */
    2844             : NS_IMETHODIMP
    2845           0 : Selection::Extend(nsIDOMNode* aContainer, int32_t aOffset)
    2846             : {
    2847           0 :   nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
    2848           0 :   return Extend(container, aOffset);
    2849             : }
    2850             : 
    2851             : NS_IMETHODIMP
    2852           0 : Selection::ExtendNative(nsINode* aContainer, int32_t aOffset)
    2853             : {
    2854           0 :   return Extend(aContainer, aOffset);
    2855             : }
    2856             : 
    2857             : void
    2858           0 : Selection::ExtendJS(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
    2859             : {
    2860           0 :   AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
    2861           0 :   mCalledByJS = true;
    2862           0 :   Extend(aContainer, aOffset, aRv);
    2863           0 : }
    2864             : 
    2865             : nsresult
    2866           0 : Selection::Extend(nsINode* aContainer, int32_t aOffset)
    2867             : {
    2868           0 :   if (!aContainer) {
    2869           0 :     return NS_ERROR_INVALID_ARG;
    2870             :   }
    2871             : 
    2872           0 :   ErrorResult result;
    2873           0 :   Extend(*aContainer, static_cast<uint32_t>(aOffset), result);
    2874           0 :   return result.StealNSResult();
    2875             : }
    2876             : 
    2877             : void
    2878           0 : Selection::Extend(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
    2879             : {
    2880             :   // First, find the range containing the old focus point:
    2881           0 :   if (!mAnchorFocusRange) {
    2882           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2883           0 :     return;
    2884             :   }
    2885             : 
    2886           0 :   if (!mFrameSelection) {
    2887           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
    2888           0 :     return;
    2889             :   }
    2890             : 
    2891             :   nsresult res;
    2892           0 :   if (!IsValidSelectionPoint(mFrameSelection, &aContainer)) {
    2893           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2894           0 :     return;
    2895             :   }
    2896             : 
    2897           0 :   RefPtr<nsPresContext> presContext = GetPresContext();
    2898           0 :   if (!presContext || presContext->Document() != aContainer.OwnerDoc()) {
    2899           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2900           0 :     return;
    2901             :   }
    2902             : 
    2903             : #ifdef DEBUG_SELECTION
    2904             :   nsDirection oldDirection = GetDirection();
    2905             : #endif
    2906           0 :   nsINode* anchorNode = GetAnchorNode();
    2907           0 :   nsINode* focusNode = GetFocusNode();
    2908           0 :   uint32_t anchorOffset = AnchorOffset();
    2909           0 :   uint32_t focusOffset = FocusOffset();
    2910             : 
    2911           0 :   RefPtr<nsRange> range = mAnchorFocusRange->CloneRange();
    2912             : 
    2913           0 :   nsINode* startNode = range->GetStartContainer();
    2914           0 :   nsINode* endNode = range->GetEndContainer();
    2915           0 :   int32_t startOffset = range->StartOffset();
    2916           0 :   int32_t endOffset = range->EndOffset();
    2917             : 
    2918             :   //compare anchor to old cursor.
    2919             : 
    2920             :   // We pass |disconnected| to the following ComparePoints calls in order
    2921             :   // to avoid assertions. ComparePoints returns 1 in the disconnected case
    2922             :   // and we can end up in various cases below, but it is assumed that in
    2923             :   // any of the cases we end up, the nsRange implementation will collapse
    2924             :   // the range to the new point because we can not make a valid range with
    2925             :   // a disconnected point. This means that whatever range is currently
    2926             :   // selected will be cleared.
    2927           0 :   bool disconnected = false;
    2928           0 :   bool shouldClearRange = false;
    2929           0 :   int32_t result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
    2930             :                                                   focusNode, focusOffset,
    2931           0 :                                                   &disconnected);
    2932             :   //compare old cursor to new cursor
    2933           0 :   shouldClearRange |= disconnected;
    2934           0 :   int32_t result2 = nsContentUtils::ComparePoints(focusNode, focusOffset,
    2935             :                                                   &aContainer, aOffset,
    2936           0 :                                                   &disconnected);
    2937             :   //compare anchor to new cursor
    2938           0 :   shouldClearRange |= disconnected;
    2939           0 :   int32_t result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
    2940             :                                                   &aContainer, aOffset,
    2941           0 :                                                   &disconnected);
    2942             : 
    2943             :   // If the points are disconnected, the range will be collapsed below,
    2944             :   // resulting in a range that selects nothing.
    2945           0 :   if (shouldClearRange) {
    2946             :     // Repaint the current range with the selection removed.
    2947           0 :     SelectFrames(presContext, range, false);
    2948             :   }
    2949             : 
    2950           0 :   RefPtr<nsRange> difRange = new nsRange(&aContainer);
    2951           0 :   if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2  a,1,2
    2952             :     //select from 1 to 2 unless they are collapsed
    2953           0 :     range->SetEnd(aContainer, aOffset, aRv);
    2954           0 :     if (aRv.Failed()) {
    2955           0 :       return;
    2956             :     }
    2957           0 :     SetDirection(eDirNext);
    2958           0 :     res = difRange->SetStartAndEnd(focusNode, focusOffset,
    2959             :                                    range->GetEndContainer(),
    2960           0 :                                    range->EndOffset());
    2961           0 :     if (NS_FAILED(res)) {
    2962           0 :       aRv.Throw(res);
    2963           0 :       return;
    2964             :     }
    2965           0 :     SelectFrames(presContext, difRange , true);
    2966           0 :     res = SetAnchorFocusToRange(range);
    2967           0 :     if (NS_FAILED(res)) {
    2968           0 :       aRv.Throw(res);
    2969           0 :       return;
    2970             :     }
    2971             :   }
    2972           0 :   else if (result1 == 0 && result3 > 0){//2, a1
    2973             :     //select from 2 to 1a
    2974           0 :     SetDirection(eDirPrevious);
    2975           0 :     range->SetStart(aContainer, aOffset, aRv);
    2976           0 :     if (aRv.Failed()) {
    2977           0 :       return;
    2978             :     }
    2979           0 :     SelectFrames(presContext, range, true);
    2980           0 :     res = SetAnchorFocusToRange(range);
    2981           0 :     if (NS_FAILED(res)) {
    2982           0 :       aRv.Throw(res);
    2983           0 :       return;
    2984             :     }
    2985             :   }
    2986           0 :   else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
    2987             :     //deselect from 2 to 1
    2988           0 :     res = difRange->SetStartAndEnd(&aContainer, aOffset,
    2989           0 :                                    focusNode, focusOffset);
    2990           0 :     if (NS_FAILED(res)) {
    2991           0 :       aRv.Throw(res);
    2992           0 :       return;
    2993             :     }
    2994             : 
    2995           0 :     range->SetEnd(aContainer, aOffset, aRv);
    2996           0 :     if (aRv.Failed()) {
    2997           0 :       return;
    2998             :     }
    2999           0 :     res = SetAnchorFocusToRange(range);
    3000           0 :     if (NS_FAILED(res)) {
    3001           0 :       aRv.Throw(res);
    3002           0 :       return;
    3003             :     }
    3004           0 :     SelectFrames(presContext, difRange, false); // deselect now
    3005           0 :     difRange->SetEnd(range->GetEndContainer(), range->EndOffset());
    3006           0 :     SelectFrames(presContext, difRange, true); // must reselect last node
    3007             :                                                // maybe more
    3008             :   }
    3009           0 :   else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
    3010           0 :     if (GetDirection() == eDirPrevious){
    3011           0 :       res = range->SetStart(endNode, endOffset);
    3012           0 :       if (NS_FAILED(res)) {
    3013           0 :         aRv.Throw(res);
    3014           0 :         return;
    3015             :       }
    3016             :     }
    3017           0 :     SetDirection(eDirNext);
    3018           0 :     range->SetEnd(aContainer, aOffset, aRv);
    3019           0 :     if (aRv.Failed()) {
    3020           0 :       return;
    3021             :     }
    3022           0 :     if (focusNode != anchorNode || focusOffset != anchorOffset) {//if collapsed diff dont do anything
    3023           0 :       res = difRange->SetStart(focusNode, focusOffset);
    3024           0 :       nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset);
    3025           0 :       if (NS_FAILED(tmp)) {
    3026           0 :         res = tmp;
    3027             :       }
    3028           0 :       if (NS_FAILED(res)) {
    3029           0 :         aRv.Throw(res);
    3030           0 :         return;
    3031             :       }
    3032           0 :       res = SetAnchorFocusToRange(range);
    3033           0 :       if (NS_FAILED(res)) {
    3034           0 :         aRv.Throw(res);
    3035           0 :         return;
    3036             :       }
    3037             :       //deselect from 1 to a
    3038           0 :       SelectFrames(presContext, difRange , false);
    3039             :     }
    3040             :     else
    3041             :     {
    3042           0 :       res = SetAnchorFocusToRange(range);
    3043           0 :       if (NS_FAILED(res)) {
    3044           0 :         aRv.Throw(res);
    3045           0 :         return;
    3046             :       }
    3047             :     }
    3048             :     //select from a to 2
    3049           0 :     SelectFrames(presContext, range , true);
    3050             :   }
    3051           0 :   else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
    3052             :     //deselect from 1 to 2
    3053           0 :     res = difRange->SetStartAndEnd(focusNode, focusOffset,
    3054           0 :                                    &aContainer, aOffset);
    3055           0 :     if (NS_FAILED(res)) {
    3056           0 :       aRv.Throw(res);
    3057           0 :       return;
    3058             :     }
    3059           0 :     SetDirection(eDirPrevious);
    3060           0 :     range->SetStart(aContainer, aOffset, aRv);
    3061           0 :     if (aRv.Failed()) {
    3062           0 :       return;
    3063             :     }
    3064             : 
    3065           0 :     res = SetAnchorFocusToRange(range);
    3066           0 :     if (NS_FAILED(res)) {
    3067           0 :       aRv.Throw(res);
    3068           0 :       return;
    3069             :     }
    3070           0 :     SelectFrames(presContext, difRange , false);
    3071           0 :     difRange->SetStart(range->GetStartContainer(), range->StartOffset());
    3072           0 :     SelectFrames(presContext, difRange, true); // must reselect last node
    3073             :   }
    3074           0 :   else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
    3075           0 :     if (GetDirection() == eDirNext){
    3076           0 :       range->SetEnd(startNode, startOffset);
    3077             :     }
    3078           0 :     SetDirection(eDirPrevious);
    3079           0 :     range->SetStart(aContainer, aOffset, aRv);
    3080           0 :     if (aRv.Failed()) {
    3081           0 :       return;
    3082             :     }
    3083             :     //deselect from a to 1
    3084           0 :     if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything
    3085           0 :       res = difRange->SetStartAndEnd(anchorNode, anchorOffset,
    3086           0 :                                      focusNode, focusOffset);
    3087           0 :       nsresult tmp = SetAnchorFocusToRange(range);
    3088           0 :       if (NS_FAILED(tmp)) {
    3089           0 :         res = tmp;
    3090             :       }
    3091           0 :       if (NS_FAILED(res)) {
    3092           0 :         aRv.Throw(res);
    3093           0 :         return;
    3094             :       }
    3095           0 :       SelectFrames(presContext, difRange, false);
    3096             :     }
    3097             :     else
    3098             :     {
    3099           0 :       res = SetAnchorFocusToRange(range);
    3100           0 :       if (NS_FAILED(res)) {
    3101           0 :         aRv.Throw(res);
    3102           0 :         return;
    3103             :       }
    3104             :     }
    3105             :     //select from 2 to a
    3106           0 :     SelectFrames(presContext, range , true);
    3107             :   }
    3108           0 :   else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
    3109             :     //select from 2 to 1
    3110           0 :     range->SetStart(aContainer, aOffset, aRv);
    3111           0 :     if (aRv.Failed()) {
    3112           0 :       return;
    3113             :     }
    3114           0 :     SetDirection(eDirPrevious);
    3115           0 :     res = difRange->SetStartAndEnd(
    3116             :                       range->GetStartContainer(), range->StartOffset(),
    3117           0 :                       focusNode, focusOffset);
    3118           0 :     if (NS_FAILED(res)) {
    3119           0 :       aRv.Throw(res);
    3120           0 :       return;
    3121             :     }
    3122             : 
    3123           0 :     SelectFrames(presContext, difRange, true);
    3124           0 :     res = SetAnchorFocusToRange(range);
    3125           0 :     if (NS_FAILED(res)) {
    3126           0 :       aRv.Throw(res);
    3127           0 :       return;
    3128             :     }
    3129             :   }
    3130             : 
    3131           0 :   if (mRanges.Length() > 1) {
    3132           0 :     for (size_t i = 0; i < mRanges.Length(); ++i) {
    3133           0 :       nsRange* range = mRanges[i].mRange;
    3134           0 :       MOZ_ASSERT(range->IsInSelection());
    3135           0 :       SelectFrames(presContext, range, range->IsInSelection());
    3136             :     }
    3137             :   }
    3138             : 
    3139             :   DEBUG_OUT_RANGE(range);
    3140             : #ifdef DEBUG_SELECTION
    3141             :   if (GetDirection() != oldDirection) {
    3142             :     printf("    direction changed to %s\n",
    3143             :            GetDirection() == eDirNext? "eDirNext":"eDirPrevious");
    3144             :   }
    3145             :   nsCOMPtr<nsIContent> content = do_QueryInterface(&aContainer);
    3146             :   printf ("Sel. Extend to %p %s %d\n", content.get(),
    3147             :           nsAtomCString(content->NodeInfo()->NameAtom()).get(), aOffset);
    3148             : #endif
    3149             : 
    3150             :   // Be aware, this instance may be destroyed after this call.
    3151             :   // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
    3152           0 :   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    3153           0 :   res = frameSelection->NotifySelectionListeners(GetType());
    3154           0 :   if (NS_FAILED(res)) {
    3155           0 :     aRv.Throw(res);
    3156             :   }
    3157             : }
    3158             : 
    3159             : NS_IMETHODIMP
    3160           0 : Selection::SelectAllChildren(nsIDOMNode* aNode)
    3161             : {
    3162           0 :   ErrorResult result;
    3163           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
    3164           0 :   NS_ENSURE_TRUE(node, NS_ERROR_INVALID_ARG);
    3165           0 :   SelectAllChildren(*node, result);
    3166           0 :   return result.StealNSResult();
    3167             : }
    3168             : 
    3169             : void
    3170           0 : Selection::SelectAllChildrenJS(nsINode& aNode, ErrorResult& aRv)
    3171             : {
    3172           0 :   AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
    3173           0 :   mCalledByJS = true;
    3174           0 :   SelectAllChildren(aNode, aRv);
    3175           0 : }
    3176             : 
    3177             : void
    3178           0 : Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
    3179             : {
    3180           0 :   if (mFrameSelection) {
    3181           0 :     mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
    3182             :   }
    3183           0 :   SelectionBatcher batch(this);
    3184             : 
    3185           0 :   Collapse(aNode, 0, aRv);
    3186           0 :   if (aRv.Failed()) {
    3187           0 :     return;
    3188             :   }
    3189             : 
    3190           0 :   Extend(aNode, aNode.GetChildCount(), aRv);
    3191             : }
    3192             : 
    3193             : NS_IMETHODIMP
    3194           0 : Selection::ContainsNode(nsIDOMNode* aNode, bool aAllowPartial, bool* aYes)
    3195             : {
    3196           0 :   if (!aYes) {
    3197           0 :     return NS_ERROR_NULL_POINTER;
    3198             :   }
    3199           0 :   *aYes = false;
    3200             : 
    3201           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
    3202           0 :   if (!node) {
    3203           0 :     return NS_ERROR_NULL_POINTER;
    3204             :   }
    3205           0 :   ErrorResult result;
    3206           0 :   *aYes = ContainsNode(*node, aAllowPartial, result);
    3207           0 :   return result.StealNSResult();
    3208             : }
    3209             : 
    3210             : bool
    3211           0 : Selection::ContainsNode(nsINode& aNode, bool aAllowPartial, ErrorResult& aRv)
    3212             : {
    3213             :   nsresult rv;
    3214           0 :   if (mRanges.Length() == 0) {
    3215           0 :     return false;
    3216             :   }
    3217             : 
    3218             :   // XXXbz this duplicates the GetNodeLength code in nsRange.cpp
    3219             :   uint32_t nodeLength;
    3220           0 :   bool isData = aNode.IsNodeOfType(nsINode::eDATA_NODE);
    3221           0 :   if (isData) {
    3222           0 :     nodeLength = static_cast<nsIContent&>(aNode).TextLength();
    3223             :   } else {
    3224           0 :     nodeLength = aNode.GetChildCount();
    3225             :   }
    3226             : 
    3227           0 :   nsTArray<nsRange*> overlappingRanges;
    3228           0 :   rv = GetRangesForIntervalArray(&aNode, 0, &aNode, nodeLength,
    3229           0 :                                  false, &overlappingRanges);
    3230           0 :   if (NS_FAILED(rv)) {
    3231           0 :     aRv.Throw(rv);
    3232           0 :     return false;
    3233             :   }
    3234           0 :   if (overlappingRanges.Length() == 0)
    3235           0 :     return false; // no ranges overlap
    3236             : 
    3237             :   // if the caller said partial intersections are OK, we're done
    3238           0 :   if (aAllowPartial) {
    3239           0 :     return true;
    3240             :   }
    3241             : 
    3242             :   // text nodes always count as inside
    3243           0 :   if (isData) {
    3244           0 :     return true;
    3245             :   }
    3246             : 
    3247             :   // The caller wants to know if the node is entirely within the given range,
    3248             :   // so we have to check all intersecting ranges.
    3249           0 :   for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
    3250             :     bool nodeStartsBeforeRange, nodeEndsAfterRange;
    3251           0 :     if (NS_SUCCEEDED(nsRange::CompareNodeToRange(&aNode, overlappingRanges[i],
    3252             :                                                  &nodeStartsBeforeRange,
    3253             :                                                  &nodeEndsAfterRange))) {
    3254           0 :       if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
    3255           0 :         return true;
    3256             :       }
    3257             :     }
    3258             :   }
    3259           0 :   return false;
    3260             : }
    3261             : 
    3262             : class PointInRectChecker : public nsLayoutUtils::RectCallback {
    3263             : public:
    3264           0 :   explicit PointInRectChecker(const nsPoint& aPoint)
    3265           0 :     : mPoint(aPoint)
    3266           0 :     , mMatchFound(false)
    3267             :   {
    3268           0 :   }
    3269             : 
    3270           0 :   void AddRect(const nsRect& aRect) override
    3271             :   {
    3272           0 :     mMatchFound = mMatchFound || aRect.Contains(mPoint);
    3273           0 :   }
    3274             : 
    3275           0 :   bool MatchFound()
    3276             :   {
    3277           0 :     return mMatchFound;
    3278             :   }
    3279             : 
    3280             : private:
    3281             :   nsPoint mPoint;
    3282             :   bool mMatchFound;
    3283             : };
    3284             : 
    3285             : bool
    3286           0 : Selection::ContainsPoint(const nsPoint& aPoint)
    3287             : {
    3288           0 :   if (IsCollapsed()) {
    3289           0 :     return false;
    3290             :   }
    3291           0 :   PointInRectChecker checker(aPoint);
    3292           0 :   for (uint32_t i = 0; i < RangeCount(); i++) {
    3293           0 :     nsRange* range = GetRangeAt(i);
    3294           0 :     nsRange::CollectClientRectsAndText(&checker, nullptr, range,
    3295             :                                        range->GetStartContainer(),
    3296             :                                        range->StartOffset(),
    3297             :                                        range->GetEndContainer(),
    3298             :                                        range->EndOffset(),
    3299           0 :                                        true, false);
    3300           0 :     if (checker.MatchFound()) {
    3301           0 :       return true;
    3302             :     }
    3303             :   }
    3304           0 :   return false;
    3305             : }
    3306             : 
    3307             : nsPresContext*
    3308          34 : Selection::GetPresContext() const
    3309             : {
    3310          34 :   nsIPresShell *shell = GetPresShell();
    3311          34 :   if (!shell) {
    3312           0 :     return nullptr;
    3313             :   }
    3314             : 
    3315          34 :   return shell->GetPresContext();
    3316             : }
    3317             : 
    3318             : nsIPresShell*
    3319         102 : Selection::GetPresShell() const
    3320             : {
    3321         102 :   if (!mFrameSelection)
    3322           0 :     return nullptr;//nothing to do
    3323             : 
    3324         102 :   return mFrameSelection->GetShell();
    3325             : }
    3326             : 
    3327             : nsIDocument*
    3328           0 : Selection::GetDocument() const
    3329             : {
    3330           0 :   nsIPresShell* presShell = GetPresShell();
    3331           0 :   return presShell ? presShell->GetDocument() : nullptr;
    3332             : }
    3333             : 
    3334             : nsPIDOMWindowOuter*
    3335           0 : Selection::GetWindow() const
    3336             : {
    3337           0 :   nsIDocument* document = GetDocument();
    3338           0 :   return document ? document->GetWindow() : nullptr;
    3339             : }
    3340             : 
    3341             : nsIEditor*
    3342           0 : Selection::GetEditor() const
    3343             : {
    3344           0 :   nsPresContext* presContext = GetPresContext();
    3345           0 :   if (!presContext) {
    3346           0 :     return nullptr;
    3347             :   }
    3348           0 :   return nsContentUtils::GetHTMLEditor(presContext);
    3349             : }
    3350             : 
    3351             : nsIFrame *
    3352           1 : Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect)
    3353             : {
    3354           1 :   if (!mFrameSelection)
    3355           0 :     return nullptr;  // nothing to do
    3356             : 
    3357           1 :   NS_ENSURE_TRUE(aRect, nullptr);
    3358             : 
    3359           1 :   aRect->SetRect(0, 0, 0, 0);
    3360             : 
    3361           1 :   switch (aRegion) {
    3362             :     case nsISelectionController::SELECTION_ANCHOR_REGION:
    3363             :     case nsISelectionController::SELECTION_FOCUS_REGION:
    3364           1 :       return GetSelectionEndPointGeometry(aRegion, aRect);
    3365             :     case nsISelectionController::SELECTION_WHOLE_SELECTION:
    3366           0 :       break;
    3367             :     default:
    3368           0 :       return nullptr;
    3369             :   }
    3370             : 
    3371           0 :   NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION,
    3372             :     "should only be SELECTION_WHOLE_SELECTION here");
    3373             : 
    3374           0 :   nsRect anchorRect;
    3375             :   nsIFrame* anchorFrame = GetSelectionEndPointGeometry(
    3376           0 :     nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect);
    3377           0 :   if (!anchorFrame)
    3378           0 :     return nullptr;
    3379             : 
    3380           0 :   nsRect focusRect;
    3381             :   nsIFrame* focusFrame = GetSelectionEndPointGeometry(
    3382           0 :     nsISelectionController::SELECTION_FOCUS_REGION, &focusRect);
    3383           0 :   if (!focusFrame)
    3384           0 :     return nullptr;
    3385             : 
    3386           0 :   NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(),
    3387             :     "points of selection in different documents?");
    3388             :   // make focusRect relative to anchorFrame
    3389           0 :   focusRect += focusFrame->GetOffsetTo(anchorFrame);
    3390             : 
    3391           0 :   aRect->UnionRectEdges(anchorRect, focusRect);
    3392           0 :   return anchorFrame;
    3393             : }
    3394             : 
    3395             : nsIFrame *
    3396           1 : Selection::GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect* aRect)
    3397             : {
    3398           1 :   if (!mFrameSelection)
    3399           0 :     return nullptr;  // nothing to do
    3400             : 
    3401           1 :   NS_ENSURE_TRUE(aRect, nullptr);
    3402             : 
    3403           1 :   aRect->SetRect(0, 0, 0, 0);
    3404             : 
    3405           1 :   nsINode    *node       = nullptr;
    3406           1 :   uint32_t    nodeOffset = 0;
    3407           1 :   nsIFrame   *frame      = nullptr;
    3408             : 
    3409           1 :   switch (aRegion) {
    3410             :     case nsISelectionController::SELECTION_ANCHOR_REGION:
    3411           0 :       node       = GetAnchorNode();
    3412           0 :       nodeOffset = AnchorOffset();
    3413           0 :       break;
    3414             :     case nsISelectionController::SELECTION_FOCUS_REGION:
    3415           1 :       node       = GetFocusNode();
    3416           1 :       nodeOffset = FocusOffset();
    3417           1 :       break;
    3418             :     default:
    3419           0 :       return nullptr;
    3420             :   }
    3421             : 
    3422           1 :   if (!node)
    3423           0 :     return nullptr;
    3424             : 
    3425           2 :   nsCOMPtr<nsIContent> content = do_QueryInterface(node);
    3426           1 :   NS_ENSURE_TRUE(content.get(), nullptr);
    3427           1 :   int32_t frameOffset = 0;
    3428           1 :   frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset,
    3429             :                                                  mFrameSelection->GetHint(),
    3430           1 :                                                  &frameOffset);
    3431           1 :   if (!frame)
    3432           0 :     return nullptr;
    3433             : 
    3434             :   // Figure out what node type we have, then get the
    3435             :   // appropriate rect for it's nodeOffset.
    3436           1 :   bool isText = node->IsNodeOfType(nsINode::eTEXT);
    3437             : 
    3438           1 :   nsPoint pt(0, 0);
    3439           1 :   if (isText) {
    3440           1 :     nsIFrame* childFrame = nullptr;
    3441           1 :     frameOffset = 0;
    3442             :     nsresult rv =
    3443           1 :       frame->GetChildFrameContainingOffset(nodeOffset,
    3444           1 :                                            mFrameSelection->GetHint(),
    3445           2 :                                            &frameOffset, &childFrame);
    3446           1 :     if (NS_FAILED(rv))
    3447           0 :       return nullptr;
    3448           1 :     if (!childFrame)
    3449           0 :       return nullptr;
    3450             : 
    3451           1 :     frame = childFrame;
    3452             : 
    3453             :     // Get the x coordinate of the offset into the text frame.
    3454           1 :     rv = GetCachedFrameOffset(frame, nodeOffset, pt);
    3455           1 :     if (NS_FAILED(rv))
    3456           0 :       return nullptr;
    3457             :   }
    3458             : 
    3459             :   // Return the rect relative to the frame, with zero width.
    3460           1 :   if (isText) {
    3461           1 :     aRect->x = pt.x;
    3462           0 :   } else if (mFrameSelection->GetHint() == CARET_ASSOCIATE_BEFORE) {
    3463             :     // It's the frame's right edge we're interested in.
    3464           0 :     aRect->x = frame->GetRect().width;
    3465             :   }
    3466           1 :   aRect->height = frame->GetRect().height;
    3467             : 
    3468           1 :   return frame;
    3469             : }
    3470             : 
    3471             : NS_IMETHODIMP
    3472           2 : Selection::ScrollSelectionIntoViewEvent::Run()
    3473             : {
    3474           2 :   if (!mSelection)
    3475           1 :     return NS_OK;  // event revoked
    3476             : 
    3477             :   int32_t flags = Selection::SCROLL_DO_FLUSH |
    3478           1 :                   Selection::SCROLL_SYNCHRONOUS;
    3479             : 
    3480           1 :   Selection* sel = mSelection; // workaround to satisfy static analysis
    3481           2 :   RefPtr<Selection> kungFuDeathGrip(sel);
    3482           1 :   mSelection->mScrollEvent.Forget();
    3483           1 :   mSelection->ScrollIntoView(mRegion, mVerticalScroll,
    3484           2 :                              mHorizontalScroll, mFlags | flags);
    3485           1 :   return NS_OK;
    3486             : }
    3487             : 
    3488             : nsresult
    3489           2 : Selection::PostScrollSelectionIntoViewEvent(
    3490             :                                          SelectionRegion aRegion,
    3491             :                                          int32_t aFlags,
    3492             :                                          nsIPresShell::ScrollAxis aVertical,
    3493             :                                          nsIPresShell::ScrollAxis aHorizontal)
    3494             : {
    3495             :   // If we've already posted an event, revoke it and place a new one at the
    3496             :   // end of the queue to make sure that any new pending reflow events are
    3497             :   // processed before we scroll. This will insure that we scroll to the
    3498             :   // correct place on screen.
    3499           2 :   mScrollEvent.Revoke();
    3500           2 :   nsPresContext* presContext = GetPresContext();
    3501           2 :   NS_ENSURE_STATE(presContext);
    3502           2 :   nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
    3503           2 :   NS_ENSURE_STATE(refreshDriver);
    3504             : 
    3505             :   RefPtr<ScrollSelectionIntoViewEvent> ev =
    3506             :     new ScrollSelectionIntoViewEvent(this, aRegion, aVertical, aHorizontal,
    3507           6 :                                      aFlags);
    3508           2 :   mScrollEvent = ev;
    3509           2 :   refreshDriver->AddEarlyRunner(ev);
    3510           2 :   return NS_OK;
    3511             : }
    3512             : 
    3513             : NS_IMETHODIMP
    3514           0 : Selection::ScrollIntoView(SelectionRegion aRegion, bool aIsSynchronous,
    3515             :                           int16_t aVPercent, int16_t aHPercent)
    3516             : {
    3517           0 :   ErrorResult result;
    3518           0 :   ScrollIntoView(aRegion, aIsSynchronous, aVPercent, aHPercent, result);
    3519           0 :   if (result.Failed()) {
    3520           0 :     return result.StealNSResult();
    3521             :   }
    3522           0 :   return NS_OK;
    3523             : }
    3524             : 
    3525             : void
    3526           0 : Selection::ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
    3527             :                           int16_t aVPercent, int16_t aHPercent,
    3528             :                           ErrorResult& aRv)
    3529             : {
    3530           0 :   nsresult rv = ScrollIntoViewInternal(aRegion, aIsSynchronous,
    3531             :                                        nsIPresShell::ScrollAxis(aVPercent),
    3532           0 :                                        nsIPresShell::ScrollAxis(aHPercent));
    3533           0 :   if (NS_FAILED(rv)) {
    3534           0 :     aRv.Throw(rv);
    3535             :   }
    3536           0 : }
    3537             : 
    3538             : NS_IMETHODIMP
    3539           0 : Selection::ScrollIntoViewInternal(SelectionRegion aRegion, bool aIsSynchronous,
    3540             :                                   nsIPresShell::ScrollAxis aVertical,
    3541             :                                   nsIPresShell::ScrollAxis aHorizontal)
    3542             : {
    3543           0 :   return ScrollIntoView(aRegion, aVertical, aHorizontal,
    3544           0 :                         aIsSynchronous ? Selection::SCROLL_SYNCHRONOUS : 0);
    3545             : }
    3546             : 
    3547             : nsresult
    3548           4 : Selection::ScrollIntoView(SelectionRegion aRegion,
    3549             :                           nsIPresShell::ScrollAxis aVertical,
    3550             :                           nsIPresShell::ScrollAxis aHorizontal,
    3551             :                           int32_t aFlags)
    3552             : {
    3553           4 :   if (!mFrameSelection)
    3554           0 :     return NS_OK;//nothing to do
    3555             : 
    3556           8 :   nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell();
    3557           4 :   if (!presShell)
    3558           0 :     return NS_OK;
    3559             : 
    3560           4 :   if (mFrameSelection->GetBatching())
    3561           1 :     return NS_OK;
    3562             : 
    3563           3 :   if (!(aFlags & Selection::SCROLL_SYNCHRONOUS))
    3564           2 :     return PostScrollSelectionIntoViewEvent(aRegion, aFlags,
    3565           2 :       aVertical, aHorizontal);
    3566             : 
    3567             :   // Now that text frame character offsets are always valid (though not
    3568             :   // necessarily correct), the worst that will happen if we don't flush here
    3569             :   // is that some callers might scroll to the wrong place.  Those should
    3570             :   // either manually flush if they're in a safe position for it or use the
    3571             :   // async version of this method.
    3572           1 :   if (aFlags & Selection::SCROLL_DO_FLUSH) {
    3573           1 :     presShell->FlushPendingNotifications(FlushType::Layout);
    3574             : 
    3575             :     // Reget the presshell, since it might have been Destroy'ed.
    3576           1 :     presShell = mFrameSelection ? mFrameSelection->GetShell() : nullptr;
    3577           1 :     if (!presShell)
    3578           0 :       return NS_OK;
    3579             :   }
    3580             : 
    3581             :   //
    3582             :   // Scroll the selection region into view.
    3583             :   //
    3584             : 
    3585           2 :   nsRect rect;
    3586           1 :   nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
    3587           1 :   if (!frame)
    3588           0 :     return NS_ERROR_FAILURE;
    3589             : 
    3590             :   // Scroll vertically to get the caret into view, but only if the container
    3591             :   // is perceived to be scrollable in that direction (i.e. there is a visible
    3592             :   // vertical scrollbar or the scroll range is at least one device pixel)
    3593           1 :   aVertical.mOnlyIfPerceivedScrollableDirection = true;
    3594             : 
    3595           1 :   uint32_t flags = 0;
    3596           1 :   if (aFlags & Selection::SCROLL_FIRST_ANCESTOR_ONLY) {
    3597           1 :     flags |= nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY;
    3598             :   }
    3599           1 :   if (aFlags & Selection::SCROLL_OVERFLOW_HIDDEN) {
    3600           0 :     flags |= nsIPresShell::SCROLL_OVERFLOW_HIDDEN;
    3601             :   }
    3602             : 
    3603           1 :   if (aFlags & Selection::SCROLL_FOR_CARET_MOVE) {
    3604             :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
    3605           0 :         (uint32_t) ScrollInputMethod::MainThreadScrollCaretIntoView);
    3606             :   }
    3607             : 
    3608           1 :   presShell->ScrollFrameRectIntoView(frame, rect, aVertical, aHorizontal,
    3609           1 :     flags);
    3610           1 :   return NS_OK;
    3611             : }
    3612             : 
    3613             : NS_IMETHODIMP
    3614         128 : Selection::AddSelectionListener(nsISelectionListener* aNewListener)
    3615             : {
    3616         128 :   if (!aNewListener)
    3617           0 :     return NS_ERROR_NULL_POINTER;
    3618         256 :   ErrorResult result;
    3619         128 :   AddSelectionListener(aNewListener, result);
    3620         128 :   if (result.Failed()) {
    3621           0 :     return result.StealNSResult();
    3622             :   }
    3623         128 :   return NS_OK;
    3624             : }
    3625             : 
    3626             : void
    3627         128 : Selection::AddSelectionListener(nsISelectionListener* aNewListener,
    3628             :                                 ErrorResult& aRv)
    3629             : {
    3630         128 :   bool result = mSelectionListeners.AppendElement(aNewListener, fallible); // AddRefs
    3631         128 :   if (!result) {
    3632           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3633             :   }
    3634         128 : }
    3635             : 
    3636             : NS_IMETHODIMP
    3637          10 : Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
    3638             : {
    3639          10 :   if (!aListenerToRemove)
    3640           0 :     return NS_ERROR_NULL_POINTER;
    3641          20 :   ErrorResult result;
    3642          10 :   RemoveSelectionListener(aListenerToRemove, result);
    3643          10 :   if (result.Failed()) {
    3644           0 :     return result.StealNSResult();
    3645             :   }
    3646          10 :   return NS_OK;
    3647             : }
    3648             : 
    3649             : void
    3650          10 : Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove,
    3651             :                                    ErrorResult& aRv)
    3652             : {
    3653          10 :   bool result = mSelectionListeners.RemoveElement(aListenerToRemove); // Releases
    3654          10 :   if (!result) {
    3655           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3656             :   }
    3657          10 : }
    3658             : 
    3659             : Element*
    3660           0 : Selection::GetCommonEditingHostForAllRanges()
    3661             : {
    3662           0 :   Element* editingHost = nullptr;
    3663           0 :   for (RangeData& rangeData : mRanges) {
    3664           0 :     nsRange* range = rangeData.mRange;
    3665           0 :     MOZ_ASSERT(range);
    3666           0 :     nsINode* commonAncestorNode = range->GetCommonAncestor();
    3667           0 :     if (!commonAncestorNode || !commonAncestorNode->IsContent()) {
    3668           0 :       return nullptr;
    3669             :     }
    3670           0 :     nsIContent* commonAncestor = commonAncestorNode->AsContent();
    3671           0 :     Element* foundEditingHost = commonAncestor->GetEditingHost();
    3672             :     // Even when common ancestor is a non-editable element in a contenteditable
    3673             :     // element, we don't need to move focus to the contenteditable element
    3674             :     // because Chromium doesn't set focus to it.
    3675           0 :     if (!foundEditingHost) {
    3676           0 :       return nullptr;
    3677             :     }
    3678           0 :     if (!editingHost) {
    3679           0 :       editingHost = foundEditingHost;
    3680           0 :       continue;
    3681             :     }
    3682           0 :     if (editingHost == foundEditingHost) {
    3683           0 :       continue;
    3684             :     }
    3685           0 :     if (nsContentUtils::ContentIsDescendantOf(foundEditingHost, editingHost)) {
    3686           0 :       continue;
    3687             :     }
    3688           0 :     if (nsContentUtils::ContentIsDescendantOf(editingHost, foundEditingHost)) {
    3689           0 :       editingHost = foundEditingHost;
    3690           0 :       continue;
    3691             :     }
    3692             :     // editingHost and foundEditingHost are not a descendant of the other.
    3693             :     // So, there is no common editing host.
    3694           0 :     return nullptr;
    3695             :   }
    3696           0 :   return editingHost;
    3697             : }
    3698             : 
    3699             : nsresult
    3700           0 : Selection::NotifySelectionListeners(bool aCalledByJS)
    3701             : {
    3702           0 :   AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
    3703           0 :   mCalledByJS = aCalledByJS;
    3704           0 :   return NotifySelectionListeners();
    3705             : }
    3706             : 
    3707             : nsresult
    3708          35 : Selection::NotifySelectionListeners()
    3709             : {
    3710          35 :   if (!mFrameSelection)
    3711           0 :     return NS_OK;//nothing to do
    3712             : 
    3713             :   // Our internal code should not move focus with using this class while
    3714             :   // this moves focus nor from selection listeners.
    3715          70 :   AutoRestore<bool> calledByJSRestorer(mCalledByJS);
    3716          35 :   mCalledByJS = false;
    3717             : 
    3718             :   // When normal selection is changed by Selection API, we need to move focus
    3719             :   // if common ancestor of all ranges are in an editing host.  Note that we
    3720             :   // don't need to move focus *to* the other focusable node because other
    3721             :   // browsers don't do it either.
    3722          62 :   if (mSelectionType == SelectionType::eNormal &&
    3723          27 :       calledByJSRestorer.SavedValue()) {
    3724           0 :     nsPIDOMWindowOuter* window = GetWindow();
    3725           0 :     nsIDocument* document = GetDocument();
    3726             :     // If the document is in design mode or doesn't have contenteditable
    3727             :     // element, we don't need to move focus.
    3728           0 :     if (window && document && !document->HasFlag(NODE_IS_EDITABLE) &&
    3729           0 :         GetEditor()) {
    3730           0 :       RefPtr<Element> newEditingHost = GetCommonEditingHostForAllRanges();
    3731           0 :       nsFocusManager* fm = nsFocusManager::GetFocusManager();
    3732           0 :       nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
    3733             :       nsIContent* focusedContent =
    3734           0 :         fm->GetFocusedDescendant(window, false, getter_AddRefs(focusedWindow));
    3735           0 :       nsCOMPtr<Element> focusedElement = do_QueryInterface(focusedContent);
    3736             :       // When all selected ranges are in an editing host, it should take focus.
    3737             :       // But otherwise, we shouldn't move focus since Chromium doesn't move
    3738             :       // focus but only selection range is updated.
    3739           0 :       if (newEditingHost && newEditingHost != focusedElement) {
    3740           0 :         MOZ_ASSERT(!newEditingHost->IsInNativeAnonymousSubtree());
    3741             :         nsCOMPtr<nsIDOMElement> domElementToFocus =
    3742           0 :           do_QueryInterface(newEditingHost->AsDOMNode());
    3743             :         // Note that don't steal focus from focused window if the window doesn't
    3744             :         // have focus and if the window isn't focused window, shouldn't be
    3745             :         // scrolled to the new focused element.
    3746           0 :         uint32_t flags = nsIFocusManager::FLAG_NOSWITCHFRAME;
    3747           0 :         if (focusedWindow != fm->GetFocusedWindow()) {
    3748           0 :           flags |= nsIFocusManager::FLAG_NOSCROLL;
    3749             :         }
    3750           0 :         fm->SetFocus(domElementToFocus, flags);
    3751             :       }
    3752             :     }
    3753             :   }
    3754             : 
    3755          70 :   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    3756          35 :   if (frameSelection->GetBatching()) {
    3757           4 :     frameSelection->SetDirty();
    3758           4 :     return NS_OK;
    3759             :   }
    3760             :   AutoTArray<nsCOMPtr<nsISelectionListener>, 8>
    3761          62 :     selectionListeners(mSelectionListeners);
    3762             : 
    3763          62 :   nsCOMPtr<nsIDOMDocument> domdoc;
    3764          31 :   nsIPresShell* ps = GetPresShell();
    3765          31 :   if (ps) {
    3766          31 :     domdoc = do_QueryInterface(ps->GetDocument());
    3767             :   }
    3768             : 
    3769          31 :   short reason = frameSelection->PopReason();
    3770         123 :   for (auto& listener : selectionListeners) {
    3771          92 :     listener->NotifySelectionChanged(domdoc, this, reason);
    3772             :   }
    3773          31 :   return NS_OK;
    3774             : }
    3775             : 
    3776             : NS_IMETHODIMP
    3777           7 : Selection::StartBatchChanges()
    3778             : {
    3779           7 :   if (mFrameSelection) {
    3780          14 :     RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    3781           7 :     frameSelection->StartBatchChanges();
    3782             :   }
    3783           7 :   return NS_OK;
    3784             : }
    3785             : 
    3786             : 
    3787             : 
    3788             : NS_IMETHODIMP
    3789           6 : Selection::EndBatchChanges()
    3790             : {
    3791           6 :   return EndBatchChangesInternal();
    3792             : }
    3793             : 
    3794             : nsresult
    3795           7 : Selection::EndBatchChangesInternal(int16_t aReason)
    3796             : {
    3797           7 :   if (mFrameSelection) {
    3798          14 :     RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    3799           7 :     frameSelection->EndBatchChanges(aReason);
    3800             :   }
    3801           7 :   return NS_OK;
    3802             : }
    3803             : 
    3804             : void
    3805           6 : Selection::AddSelectionChangeBlocker()
    3806             : {
    3807           6 :   mSelectionChangeBlockerCount++;
    3808           6 : }
    3809             : 
    3810             : void
    3811           6 : Selection::RemoveSelectionChangeBlocker()
    3812             : {
    3813           6 :   MOZ_ASSERT(mSelectionChangeBlockerCount > 0,
    3814             :              "mSelectionChangeBlockerCount has an invalid value - "
    3815             :              "maybe you have a mismatched RemoveSelectionChangeBlocker?");
    3816           6 :   mSelectionChangeBlockerCount--;
    3817           6 : }
    3818             : 
    3819             : bool
    3820          11 : Selection::IsBlockingSelectionChangeEvents() const
    3821             : {
    3822          11 :   return mSelectionChangeBlockerCount > 0;
    3823             : }
    3824             : 
    3825             : NS_IMETHODIMP
    3826           0 : Selection::DeleteFromDocument()
    3827             : {
    3828           0 :   ErrorResult result;
    3829           0 :   DeleteFromDocument(result);
    3830           0 :   return result.StealNSResult();
    3831             : }
    3832             : 
    3833             : void
    3834           0 : Selection::DeleteFromDocument(ErrorResult& aRv)
    3835             : {
    3836           0 :   if (!mFrameSelection)
    3837           0 :     return;//nothing to do
    3838           0 :   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    3839           0 :   nsresult rv = frameSelection->DeleteFromDocument();
    3840           0 :   if (NS_FAILED(rv)) {
    3841           0 :     aRv.Throw(rv);
    3842             :   }
    3843             : }
    3844             : 
    3845             : NS_IMETHODIMP
    3846           0 : Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
    3847             :                   const nsAString& aGranularity)
    3848             : {
    3849           0 :   ErrorResult result;
    3850           0 :   Modify(aAlter, aDirection, aGranularity, result);
    3851           0 :   return result.StealNSResult();
    3852             : }
    3853             : 
    3854             : void
    3855           0 : Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
    3856             :                   const nsAString& aGranularity, ErrorResult& aRv)
    3857             : {
    3858             :   // Silently exit if there's no selection or no focus node.
    3859           0 :   if (!mFrameSelection || !GetAnchorFocusRange() || !GetFocusNode()) {
    3860           0 :     return;
    3861             :   }
    3862             : 
    3863           0 :   if (!aAlter.LowerCaseEqualsLiteral("move") &&
    3864           0 :       !aAlter.LowerCaseEqualsLiteral("extend")) {
    3865           0 :     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
    3866           0 :     return;
    3867             :   }
    3868             : 
    3869           0 :   if (!aDirection.LowerCaseEqualsLiteral("forward") &&
    3870           0 :       !aDirection.LowerCaseEqualsLiteral("backward") &&
    3871           0 :       !aDirection.LowerCaseEqualsLiteral("left") &&
    3872           0 :       !aDirection.LowerCaseEqualsLiteral("right")) {
    3873           0 :     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
    3874           0 :     return;
    3875             :   }
    3876             : 
    3877             :   // Line moves are always visual.
    3878           0 :   bool visual  = aDirection.LowerCaseEqualsLiteral("left") ||
    3879           0 :                    aDirection.LowerCaseEqualsLiteral("right") ||
    3880           0 :                    aGranularity.LowerCaseEqualsLiteral("line");
    3881             : 
    3882           0 :   bool forward = aDirection.LowerCaseEqualsLiteral("forward") ||
    3883           0 :                    aDirection.LowerCaseEqualsLiteral("right");
    3884             : 
    3885           0 :   bool extend  = aAlter.LowerCaseEqualsLiteral("extend");
    3886             : 
    3887             :   nsSelectionAmount amount;
    3888           0 :   if (aGranularity.LowerCaseEqualsLiteral("character")) {
    3889           0 :     amount = eSelectCluster;
    3890           0 :   } else if (aGranularity.LowerCaseEqualsLiteral("word")) {
    3891           0 :     amount = eSelectWordNoSpace;
    3892           0 :   } else if (aGranularity.LowerCaseEqualsLiteral("line")) {
    3893           0 :     amount = eSelectLine;
    3894           0 :   } else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) {
    3895           0 :     amount = forward ? eSelectEndLine : eSelectBeginLine;
    3896           0 :   } else if (aGranularity.LowerCaseEqualsLiteral("sentence") ||
    3897           0 :              aGranularity.LowerCaseEqualsLiteral("sentenceboundary") ||
    3898           0 :              aGranularity.LowerCaseEqualsLiteral("paragraph") ||
    3899           0 :              aGranularity.LowerCaseEqualsLiteral("paragraphboundary") ||
    3900           0 :              aGranularity.LowerCaseEqualsLiteral("documentboundary")) {
    3901           0 :     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
    3902           0 :     return;
    3903             :   } else {
    3904           0 :     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
    3905           0 :     return;
    3906             :   }
    3907             : 
    3908             :   // If the anchor doesn't equal the focus and we try to move without first
    3909             :   // collapsing the selection, MoveCaret will collapse the selection and quit.
    3910             :   // To avoid this, we need to collapse the selection first.
    3911           0 :   nsresult rv = NS_OK;
    3912           0 :   if (!extend) {
    3913           0 :     nsINode* focusNode = GetFocusNode();
    3914             :     // We should have checked earlier that there was a focus node.
    3915           0 :     if (!focusNode) {
    3916           0 :       aRv.Throw(NS_ERROR_UNEXPECTED);
    3917           0 :       return;
    3918             :     }
    3919           0 :     uint32_t focusOffset = FocusOffset();
    3920           0 :     Collapse(focusNode, focusOffset);
    3921             :   }
    3922             : 
    3923             :   // If the paragraph direction of the focused frame is right-to-left,
    3924             :   // we may have to swap the direction of movement.
    3925             :   nsIFrame *frame;
    3926             :   int32_t offset;
    3927           0 :   rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual);
    3928           0 :   if (NS_SUCCEEDED(rv) && frame) {
    3929           0 :     nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
    3930             : 
    3931           0 :     if (paraDir == NSBIDI_RTL && visual) {
    3932           0 :       if (amount == eSelectBeginLine) {
    3933           0 :         amount = eSelectEndLine;
    3934           0 :         forward = !forward;
    3935           0 :       } else if (amount == eSelectEndLine) {
    3936           0 :         amount = eSelectBeginLine;
    3937           0 :         forward = !forward;
    3938             :       }
    3939             :     }
    3940             :   }
    3941             : 
    3942             :   // MoveCaret will return an error if it can't move in the specified
    3943             :   // direction, but we just ignore this error unless it's a line move, in which
    3944             :   // case we call nsISelectionController::CompleteMove to move the cursor to
    3945             :   // the beginning/end of the line.
    3946           0 :   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    3947           0 :   rv = frameSelection->MoveCaret(forward ? eDirNext : eDirPrevious,
    3948             :                                  extend, amount,
    3949             :                                  visual ? nsFrameSelection::eVisual
    3950           0 :                                         : nsFrameSelection::eLogical);
    3951             : 
    3952           0 :   if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) {
    3953             :     nsCOMPtr<nsISelectionController> shell =
    3954           0 :       do_QueryInterface(frameSelection->GetShell());
    3955           0 :     if (!shell)
    3956           0 :       return;
    3957           0 :     shell->CompleteMove(forward, extend);
    3958             :   }
    3959             : }
    3960             : 
    3961             : void
    3962           0 : Selection::SetBaseAndExtentJS(nsINode& aAnchorNode,
    3963             :                               uint32_t aAnchorOffset,
    3964             :                               nsINode& aFocusNode,
    3965             :                               uint32_t aFocusOffset,
    3966             :                               ErrorResult& aRv)
    3967             : {
    3968           0 :   AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
    3969           0 :   mCalledByJS = true;
    3970             :   SetBaseAndExtent(aAnchorNode, aAnchorOffset,
    3971           0 :                    aFocusNode, aFocusOffset, aRv);
    3972           0 : }
    3973             : 
    3974             : void
    3975           0 : Selection::SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
    3976             :                             nsINode& aFocusNode, uint32_t aFocusOffset,
    3977             :                             ErrorResult& aRv)
    3978             : {
    3979           0 :   if (!mFrameSelection) {
    3980           0 :     return;
    3981             :   }
    3982             : 
    3983           0 :   SelectionBatcher batch(this);
    3984             : 
    3985             :   int32_t relativePosition =
    3986           0 :     nsContentUtils::ComparePoints(&aAnchorNode, aAnchorOffset,
    3987           0 :                                   &aFocusNode, aFocusOffset);
    3988           0 :   nsINode* start = &aAnchorNode;
    3989           0 :   nsINode* end = &aFocusNode;
    3990           0 :   uint32_t startOffset = aAnchorOffset;
    3991           0 :   uint32_t endOffset = aFocusOffset;
    3992           0 :   if (relativePosition > 0) {
    3993           0 :     start = &aFocusNode;
    3994           0 :     end = &aAnchorNode;
    3995           0 :     startOffset = aFocusOffset;
    3996           0 :     endOffset = aAnchorOffset;
    3997             :   }
    3998             : 
    3999           0 :   RefPtr<nsRange> newRange;
    4000           0 :   nsresult rv = nsRange::CreateRange(start, startOffset, end, endOffset,
    4001           0 :                                      getter_AddRefs(newRange));
    4002             :   // CreateRange returns IndexSizeError if any offset is out of bounds.
    4003           0 :   if (NS_FAILED(rv)) {
    4004           0 :     aRv.Throw(rv);
    4005           0 :     return;
    4006             :   }
    4007             : 
    4008           0 :   rv = RemoveAllRanges();
    4009           0 :   if (NS_FAILED(rv)) {
    4010           0 :     aRv.Throw(rv);
    4011           0 :     return;
    4012             :   }
    4013             : 
    4014           0 :   rv = AddRange(newRange);
    4015           0 :   if (NS_FAILED(rv)) {
    4016           0 :     aRv.Throw(rv);
    4017           0 :     return;
    4018             :   }
    4019             : 
    4020           0 :   SetDirection(relativePosition > 0 ? eDirPrevious : eDirNext);
    4021             : }
    4022             : 
    4023             : /** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction
    4024             :  *  @param aLangRTL is true if the new language is right-to-left or false if the new language is left-to-right
    4025             :  */
    4026             : NS_IMETHODIMP
    4027           0 : Selection::SelectionLanguageChange(bool aLangRTL)
    4028             : {
    4029           0 :   if (!mFrameSelection)
    4030           0 :     return NS_ERROR_NOT_INITIALIZED; // Can't do selection
    4031             : 
    4032           0 :   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    4033             : 
    4034             :   // if the direction of the language hasn't changed, nothing to do
    4035           0 :   nsBidiLevel kbdBidiLevel = aLangRTL ? NSBIDI_RTL : NSBIDI_LTR;
    4036           0 :   if (kbdBidiLevel == frameSelection->mKbdBidiLevel) {
    4037           0 :     return NS_OK;
    4038             :   }
    4039             : 
    4040           0 :   frameSelection->mKbdBidiLevel = kbdBidiLevel;
    4041             : 
    4042             :   nsresult result;
    4043           0 :   nsIFrame *focusFrame = 0;
    4044             : 
    4045           0 :   result = GetPrimaryFrameForFocusNode(&focusFrame, nullptr, false);
    4046           0 :   if (NS_FAILED(result)) {
    4047           0 :     return result;
    4048             :   }
    4049           0 :   if (!focusFrame) {
    4050           0 :     return NS_ERROR_FAILURE;
    4051             :   }
    4052             : 
    4053             :   int32_t frameStart, frameEnd;
    4054           0 :   focusFrame->GetOffsets(frameStart, frameEnd);
    4055           0 :   RefPtr<nsPresContext> context = GetPresContext();
    4056             :   nsBidiLevel levelBefore, levelAfter;
    4057           0 :   if (!context) {
    4058           0 :     return NS_ERROR_FAILURE;
    4059             :   }
    4060             : 
    4061           0 :   nsBidiLevel level = focusFrame->GetEmbeddingLevel();
    4062           0 :   int32_t focusOffset = static_cast<int32_t>(FocusOffset());
    4063           0 :   if ((focusOffset != frameStart) && (focusOffset != frameEnd))
    4064             :     // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
    4065             :     //  is equal to the frame level
    4066           0 :     levelBefore = levelAfter = level;
    4067             :   else {
    4068             :     // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
    4069             :     //  before and after the cursor
    4070           0 :     nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
    4071             :     nsPrevNextBidiLevels levels = frameSelection->
    4072           0 :       GetPrevNextBidiLevels(focusContent, focusOffset, false);
    4073             : 
    4074           0 :     levelBefore = levels.mLevelBefore;
    4075           0 :     levelAfter = levels.mLevelAfter;
    4076             :   }
    4077             : 
    4078           0 :   if (IS_SAME_DIRECTION(levelBefore, levelAfter)) {
    4079             :     // if cursor is between two characters with the same orientation, changing the keyboard language
    4080             :     //  must toggle the cursor level between the level of the character with the lowest level
    4081             :     //  (if the new language corresponds to the orientation of that character) and this level plus 1
    4082             :     //  (if the new language corresponds to the opposite orientation)
    4083           0 :     if ((level != levelBefore) && (level != levelAfter))
    4084           0 :       level = std::min(levelBefore, levelAfter);
    4085           0 :     if (IS_SAME_DIRECTION(level, kbdBidiLevel))
    4086           0 :       frameSelection->SetCaretBidiLevel(level);
    4087             :     else
    4088           0 :       frameSelection->SetCaretBidiLevel(level + 1);
    4089             :   }
    4090             :   else {
    4091             :     // if cursor is between characters with opposite orientations, changing the keyboard language must change
    4092             :     //  the cursor level to that of the adjacent character with the orientation corresponding to the new language.
    4093           0 :     if (IS_SAME_DIRECTION(levelBefore, kbdBidiLevel))
    4094           0 :       frameSelection->SetCaretBidiLevel(levelBefore);
    4095             :     else
    4096           0 :       frameSelection->SetCaretBidiLevel(levelAfter);
    4097             :   }
    4098             : 
    4099             :   // The caret might have moved, so invalidate the desired position
    4100             :   // for future usages of up-arrow or down-arrow
    4101           0 :   frameSelection->InvalidateDesiredPos();
    4102             : 
    4103           0 :   return NS_OK;
    4104             : }
    4105             : 
    4106             : NS_IMETHODIMP
    4107           0 : Selection::SetColors(const nsAString& aForegroundColor,
    4108             :                      const nsAString& aBackgroundColor,
    4109             :                      const nsAString& aAltForegroundColor,
    4110             :                      const nsAString& aAltBackgroundColor)
    4111             : {
    4112           0 :   ErrorResult result;
    4113             :   SetColors(aForegroundColor, aBackgroundColor,
    4114           0 :             aAltForegroundColor, aAltBackgroundColor, result);
    4115           0 :   return result.StealNSResult();
    4116             : }
    4117             : 
    4118             : void
    4119           0 : Selection::SetColors(const nsAString& aForegroundColor,
    4120             :                      const nsAString& aBackgroundColor,
    4121             :                      const nsAString& aAltForegroundColor,
    4122             :                      const nsAString& aAltBackgroundColor,
    4123             :                      ErrorResult& aRv)
    4124             : {
    4125           0 :   if (mSelectionType != SelectionType::eFind) {
    4126           0 :     aRv.Throw(NS_ERROR_FAILURE);
    4127           0 :     return;
    4128             :   }
    4129             : 
    4130           0 :   mCustomColors.reset(new SelectionCustomColors);
    4131             : 
    4132           0 :   NS_NAMED_LITERAL_STRING(currentColorStr, "currentColor");
    4133           0 :   NS_NAMED_LITERAL_STRING(transparentStr, "transparent");
    4134             : 
    4135           0 :   if (!aForegroundColor.Equals(currentColorStr)) {
    4136             :     nscolor foregroundColor;
    4137           0 :     nsAttrValue aForegroundColorValue;
    4138           0 :     aForegroundColorValue.ParseColor(aForegroundColor);
    4139           0 :     if (!aForegroundColorValue.GetColorValue(foregroundColor)) {
    4140           0 :       aRv.Throw(NS_ERROR_INVALID_ARG);
    4141           0 :       return;
    4142             :     }
    4143           0 :     mCustomColors->mForegroundColor = Some(foregroundColor);
    4144             :   } else {
    4145           0 :     mCustomColors->mForegroundColor = Nothing();
    4146             :   }
    4147             : 
    4148           0 :   if (!aBackgroundColor.Equals(transparentStr)) {
    4149             :     nscolor backgroundColor;
    4150           0 :     nsAttrValue aBackgroundColorValue;
    4151           0 :     aBackgroundColorValue.ParseColor(aBackgroundColor);
    4152           0 :     if (!aBackgroundColorValue.GetColorValue(backgroundColor)) {
    4153           0 :       aRv.Throw(NS_ERROR_INVALID_ARG);
    4154           0 :       return;
    4155             :     }
    4156           0 :     mCustomColors->mBackgroundColor = Some(backgroundColor);
    4157             :   } else {
    4158           0 :     mCustomColors->mBackgroundColor = Nothing();
    4159             :   }
    4160             : 
    4161           0 :   if (!aAltForegroundColor.Equals(currentColorStr)) {
    4162             :     nscolor altForegroundColor;
    4163           0 :     nsAttrValue aAltForegroundColorValue;
    4164           0 :     aAltForegroundColorValue.ParseColor(aAltForegroundColor);
    4165           0 :     if (!aAltForegroundColorValue.GetColorValue(altForegroundColor)) {
    4166           0 :       aRv.Throw(NS_ERROR_INVALID_ARG);
    4167           0 :       return;
    4168             :     }
    4169           0 :     mCustomColors->mAltForegroundColor = Some(altForegroundColor);
    4170             :   } else {
    4171           0 :     mCustomColors->mAltForegroundColor = Nothing();
    4172             :   }
    4173             : 
    4174           0 :   if (!aAltBackgroundColor.Equals(transparentStr)) {
    4175             :     nscolor altBackgroundColor;
    4176           0 :     nsAttrValue aAltBackgroundColorValue;
    4177           0 :     aAltBackgroundColorValue.ParseColor(aAltBackgroundColor);
    4178           0 :     if (!aAltBackgroundColorValue.GetColorValue(altBackgroundColor)) {
    4179           0 :       aRv.Throw(NS_ERROR_INVALID_ARG);
    4180           0 :       return;
    4181             :     }
    4182           0 :     mCustomColors->mAltBackgroundColor = Some(altBackgroundColor);
    4183             :   } else {
    4184           0 :     mCustomColors->mAltBackgroundColor = Nothing();
    4185             :   }
    4186             : }
    4187             : 
    4188             : NS_IMETHODIMP
    4189           0 : Selection::ResetColors()
    4190             : {
    4191           0 :   ErrorResult result;
    4192           0 :   ResetColors(result);
    4193           0 :   return result.StealNSResult();
    4194             : }
    4195             : 
    4196             : void
    4197           0 : Selection::ResetColors(ErrorResult& aRv)
    4198             : {
    4199           0 :   mCustomColors = nullptr;
    4200           0 : }
    4201             : 
    4202             : NS_IMETHODIMP_(nsDirection)
    4203           5 : Selection::GetSelectionDirection() {
    4204           5 :   return mDirection;
    4205             : }
    4206             : 
    4207             : NS_IMETHODIMP_(void)
    4208           6 : Selection::SetSelectionDirection(nsDirection aDirection) {
    4209           6 :   mDirection = aDirection;
    4210           6 : }
    4211             : 
    4212             : JSObject*
    4213           4 : Selection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
    4214             : {
    4215           4 :   return mozilla::dom::SelectionBinding::Wrap(aCx, this, aGivenProto);
    4216             : }
    4217             : 
    4218             : // AutoHideSelectionChanges
    4219           6 : AutoHideSelectionChanges::AutoHideSelectionChanges(const nsFrameSelection* aFrame)
    4220             :   : AutoHideSelectionChanges(
    4221           6 :       aFrame ? aFrame->GetSelection(SelectionType::eNormal) : nullptr)
    4222           6 : {}

Generated by: LCOV version 1.13