LCOV - code coverage report
Current view: top level - layout/generic - nsFrameSelection.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 175 1297 13.5 %
Date: 2017-07-14 16:53:18 Functions: 22 83 26.5 %
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 nsFrameSelection
       9             :  */
      10             : 
      11             : #include "nsFrameSelection.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 "nsISelectionListener.h"
      20             : #include "nsContentCID.h"
      21             : #include "nsDeviceContext.h"
      22             : #include "nsIContent.h"
      23             : #include "nsIDOMNode.h"
      24             : #include "nsRange.h"
      25             : #include "nsITableCellLayout.h"
      26             : #include "nsTArray.h"
      27             : #include "nsTableWrapperFrame.h"
      28             : #include "nsTableCellFrame.h"
      29             : #include "nsIScrollableFrame.h"
      30             : #include "nsCCUncollectableMarker.h"
      31             : #include "nsIContentIterator.h"
      32             : #include "nsIDocumentEncoder.h"
      33             : #include "nsTextFragment.h"
      34             : #include <algorithm>
      35             : #include "nsContentUtils.h"
      36             : 
      37             : #include "nsGkAtoms.h"
      38             : #include "nsIFrameTraversal.h"
      39             : #include "nsLayoutUtils.h"
      40             : #include "nsLayoutCID.h"
      41             : #include "nsBidiPresUtils.h"
      42             : static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
      43             : #include "nsTextFrame.h"
      44             : 
      45             : #include "nsIDOMText.h"
      46             : 
      47             : #include "nsContentUtils.h"
      48             : #include "nsThreadUtils.h"
      49             : #include "mozilla/Preferences.h"
      50             : #include "nsDOMClassInfoID.h"
      51             : 
      52             : #include "nsPresContext.h"
      53             : #include "nsIPresShell.h"
      54             : #include "nsCaret.h"
      55             : #include "AccessibleCaretEventHub.h"
      56             : 
      57             : #include "mozilla/MouseEvents.h"
      58             : #include "mozilla/TextEvents.h"
      59             : 
      60             : #include "nsITimer.h"
      61             : // notifications
      62             : #include "nsIDOMDocument.h"
      63             : #include "nsIDocument.h"
      64             : 
      65             : #include "nsISelectionController.h"//for the enums
      66             : #include "nsAutoCopyListener.h"
      67             : #include "SelectionChangeListener.h"
      68             : #include "nsCopySupport.h"
      69             : #include "nsIClipboard.h"
      70             : #include "nsIFrameInlines.h"
      71             : 
      72             : #include "nsIBidiKeyboard.h"
      73             : 
      74             : #include "nsError.h"
      75             : #include "mozilla/dom/Element.h"
      76             : #include "mozilla/dom/Selection.h"
      77             : #include "mozilla/dom/ShadowRoot.h"
      78             : #include "mozilla/ErrorResult.h"
      79             : #include "mozilla/dom/SelectionBinding.h"
      80             : #include "mozilla/AsyncEventDispatcher.h"
      81             : #include "mozilla/Telemetry.h"
      82             : #include "mozilla/layers/ScrollInputMethods.h"
      83             : 
      84             : #include "nsIEditor.h"
      85             : #include "nsIHTMLEditor.h"
      86             : #include "nsFocusManager.h"
      87             : #include "nsPIDOMWindow.h"
      88             : 
      89             : using namespace mozilla;
      90             : using namespace mozilla::dom;
      91             : using mozilla::layers::ScrollInputMethod;
      92             : 
      93             : //#define DEBUG_TABLE 1
      94             : 
      95             : static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
      96             : 
      97             : static nsIAtom *GetTag(nsINode *aNode);
      98             : // returns the parent
      99             : static nsINode* ParentOffset(nsINode *aNode, int32_t *aChildOffset);
     100             : static nsINode* GetCellParent(nsINode *aDomNode);
     101             : 
     102             : #ifdef PRINT_RANGE
     103             : static void printRange(nsRange *aDomRange);
     104             : #define DEBUG_OUT_RANGE(x)  printRange(x)
     105             : #else
     106             : #define DEBUG_OUT_RANGE(x)
     107             : #endif // PRINT_RANGE
     108             : 
     109             : 
     110             : /******************************************************************************
     111             :  * nsPeekOffsetStruct
     112             :  ******************************************************************************/
     113             : 
     114             : //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
     115             : //#define DEBUG_NAVIGATION
     116             : 
     117             : 
     118             : //#define DEBUG_TABLE_SELECTION 1
     119             : 
     120           0 : nsPeekOffsetStruct::nsPeekOffsetStruct(nsSelectionAmount aAmount,
     121             :                                        nsDirection aDirection,
     122             :                                        int32_t aStartOffset,
     123             :                                        nsPoint aDesiredPos,
     124             :                                        bool aJumpLines,
     125             :                                        bool aScrollViewStop,
     126             :                                        bool aIsKeyboardSelect,
     127             :                                        bool aVisual,
     128             :                                        bool aExtend,
     129           0 :                                        EWordMovementType aWordMovementType)
     130             :   : mAmount(aAmount)
     131             :   , mDirection(aDirection)
     132             :   , mStartOffset(aStartOffset)
     133             :   , mDesiredPos(aDesiredPos)
     134             :   , mWordMovementType(aWordMovementType)
     135             :   , mJumpLines(aJumpLines)
     136             :   , mScrollViewStop(aScrollViewStop)
     137             :   , mIsKeyboardSelect(aIsKeyboardSelect)
     138             :   , mVisual(aVisual)
     139             :   , mExtend(aExtend)
     140             :   , mResultContent()
     141             :   , mResultFrame(nullptr)
     142             :   , mContentOffset(0)
     143           0 :   , mAttach(CARET_ASSOCIATE_BEFORE)
     144             : {
     145           0 : }
     146             : 
     147             : static int8_t
     148         237 : GetIndexFromSelectionType(SelectionType aSelectionType)
     149             : {
     150         237 :   switch (aSelectionType) {
     151             :     case SelectionType::eNormal:
     152         223 :       return 0;
     153             :     case SelectionType::eSpellCheck:
     154           0 :       return 1;
     155             :     case SelectionType::eIMERawClause:
     156           0 :       return 2;
     157             :     case SelectionType::eIMESelectedRawClause:
     158           0 :       return 3;
     159             :     case SelectionType::eIMEConvertedClause:
     160           0 :       return 4;
     161             :     case SelectionType::eIMESelectedClause:
     162           0 :       return 5;
     163             :     case SelectionType::eAccessibility:
     164           0 :       return 6;
     165             :     case SelectionType::eFind:
     166           0 :       return 7;
     167             :     case SelectionType::eURLSecondary:
     168           8 :       return 8;
     169             :     case SelectionType::eURLStrikeout:
     170           6 :       return 9;
     171             :     default:
     172           0 :       return -1;
     173             :   }
     174             :   /* NOTREACHED */
     175             : }
     176             : 
     177             : static SelectionType
     178         320 : GetSelectionTypeFromIndex(int8_t aIndex)
     179             : {
     180             :   static const SelectionType kSelectionTypes[] = {
     181             :     SelectionType::eNormal,
     182             :     SelectionType::eSpellCheck,
     183             :     SelectionType::eIMERawClause,
     184             :     SelectionType::eIMESelectedRawClause,
     185             :     SelectionType::eIMEConvertedClause,
     186             :     SelectionType::eIMESelectedClause,
     187             :     SelectionType::eAccessibility,
     188             :     SelectionType::eFind,
     189             :     SelectionType::eURLSecondary,
     190             :     SelectionType::eURLStrikeout
     191             :   };
     192         640 :   if (NS_WARN_IF(aIndex < 0) ||
     193         320 :       NS_WARN_IF(static_cast<size_t>(aIndex) >= ArrayLength(kSelectionTypes))) {
     194           0 :     return SelectionType::eNormal;
     195             :   }
     196         320 :   return kSelectionTypes[aIndex];
     197             : }
     198             : 
     199             : /*
     200             : The limiter is used specifically for the text areas and textfields
     201             : In that case it is the DIV tag that is anonymously created for the text
     202             : areas/fields.  Text nodes and BR nodes fall beneath it.  In the case of a
     203             : BR node the limiter will be the parent and the offset will point before or
     204             : after the BR node.  In the case of the text node the parent content is
     205             : the text node itself and the offset will be the exact character position.
     206             : The offset is not important to check for validity.  Simply look at the
     207             : passed in content.  If it equals the limiter then the selection point is valid.
     208             : If its parent it the limiter then the point is also valid.  In the case of
     209             : NO limiter all points are valid since you are in a topmost iframe. (browser
     210             : or composer)
     211             : */
     212             : bool
     213           0 : IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode)
     214             : {
     215           0 :   if (!aFrameSel || !aNode)
     216           0 :     return false;
     217             : 
     218           0 :   nsIContent *limiter = aFrameSel->GetLimiter();
     219           0 :   if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
     220             :     //if newfocus == the limiter. that's ok. but if not there and not parent bad
     221           0 :     return false; //not in the right content. tLimiter said so
     222             :   }
     223             : 
     224           0 :   limiter = aFrameSel->GetAncestorLimiter();
     225           0 :   return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
     226             : }
     227             : 
     228             : namespace mozilla {
     229           0 : struct MOZ_RAII AutoPrepareFocusRange
     230             : {
     231           0 :   AutoPrepareFocusRange(Selection* aSelection,
     232             :                         bool aContinueSelection,
     233             :                         bool aMultipleSelection
     234             :                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     235           0 :   {
     236           0 :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     237             : 
     238           0 :     if (aSelection->mRanges.Length() <= 1) {
     239           0 :       return;
     240             :     }
     241             : 
     242           0 :     if (aSelection->mFrameSelection->IsUserSelectionReason()) {
     243           0 :       mUserSelect.emplace(aSelection);
     244             :     }
     245           0 :     bool userSelection = aSelection->mUserInitiated;
     246             : 
     247           0 :     nsTArray<RangeData>& ranges = aSelection->mRanges;
     248           0 :     if (!userSelection ||
     249           0 :         (!aContinueSelection && aMultipleSelection)) {
     250             :       // Scripted command or the user is starting a new explicit multi-range
     251             :       // selection.
     252           0 :       for (RangeData& entry : ranges) {
     253           0 :         entry.mRange->SetIsGenerated(false);
     254             :       }
     255           0 :       return;
     256             :     }
     257             : 
     258           0 :     int16_t reason = aSelection->mFrameSelection->mSelectionChangeReason;
     259           0 :     bool isAnchorRelativeOp = (reason & (nsISelectionListener::DRAG_REASON |
     260             :                                          nsISelectionListener::MOUSEDOWN_REASON |
     261             :                                          nsISelectionListener::MOUSEUP_REASON |
     262           0 :                                          nsISelectionListener::COLLAPSETOSTART_REASON));
     263           0 :     if (!isAnchorRelativeOp) {
     264           0 :       return;
     265             :     }
     266             : 
     267             :     // This operation is against the anchor but our current mAnchorFocusRange
     268             :     // represents the focus in a multi-range selection.  The anchor from a user
     269             :     // perspective is the most distant generated range on the opposite side.
     270             :     // Find that range and make it the mAnchorFocusRange.
     271           0 :     const size_t len = ranges.Length();
     272           0 :     size_t newAnchorFocusIndex = size_t(-1);
     273           0 :     if (aSelection->GetDirection() == eDirNext) {
     274           0 :       for (size_t i = 0; i < len; ++i) {
     275           0 :         if (ranges[i].mRange->IsGenerated()) {
     276           0 :           newAnchorFocusIndex = i;
     277           0 :           break;
     278             :         }
     279             :       }
     280             :     } else {
     281           0 :       size_t i = len;
     282           0 :       while (i--) {
     283           0 :         if (ranges[i].mRange->IsGenerated()) {
     284           0 :           newAnchorFocusIndex = i;
     285           0 :           break;
     286             :         }
     287             :       }
     288             :     }
     289             : 
     290           0 :     if (newAnchorFocusIndex == size_t(-1)) {
     291             :       // There are no generated ranges - that's fine.
     292           0 :       return;
     293             :     }
     294             : 
     295             :     // Setup the new mAnchorFocusRange and mark the old one as generated.
     296           0 :     if (aSelection->mAnchorFocusRange) {
     297           0 :       aSelection->mAnchorFocusRange->SetIsGenerated(true);
     298             :     }
     299           0 :     nsRange* range = ranges[newAnchorFocusIndex].mRange;
     300           0 :     range->SetIsGenerated(false);
     301           0 :     aSelection->mAnchorFocusRange = range;
     302             : 
     303             :     // Remove all generated ranges (including the old mAnchorFocusRange).
     304           0 :     RefPtr<nsPresContext> presContext = aSelection->GetPresContext();
     305           0 :     size_t i = len;
     306           0 :     while (i--) {
     307           0 :       range = aSelection->mRanges[i].mRange;
     308           0 :       if (range->IsGenerated()) {
     309           0 :         range->SetSelection(nullptr);
     310           0 :         aSelection->SelectFrames(presContext, range, false);
     311           0 :         aSelection->mRanges.RemoveElementAt(i);
     312             :       }
     313             :     }
     314           0 :     if (aSelection->mFrameSelection) {
     315           0 :       aSelection->mFrameSelection->InvalidateDesiredPos();
     316             :     }
     317             :   }
     318             : 
     319             :   Maybe<Selection::AutoUserInitiated> mUserSelect;
     320             :   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     321             : };
     322             : 
     323             : } // namespace mozilla
     324             : 
     325             : ////////////BEGIN nsFrameSelection methods
     326             : 
     327          32 : nsFrameSelection::nsFrameSelection()
     328             : {
     329         352 :   for (size_t i = 0; i < kPresentSelectionTypeCount; i++){
     330         320 :     mDomSelections[i] = new Selection(this);
     331         320 :     mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
     332             :   }
     333          32 :   mBatching = 0;
     334          32 :   mChangesDuringBatching = false;
     335          32 :   mNotifyFrames = true;
     336             : 
     337          32 :   mMouseDoubleDownState = false;
     338             : 
     339          32 :   mHint = CARET_ASSOCIATE_BEFORE;
     340          32 :   mCaretBidiLevel = BIDI_LEVEL_UNDEFINED;
     341          32 :   mKbdBidiLevel = NSBIDI_LTR;
     342             : 
     343          32 :   mDragSelectingCells = false;
     344          32 :   mSelectingTableCellMode = 0;
     345          32 :   mSelectedCellIndex = 0;
     346             : 
     347          32 :   nsAutoCopyListener *autoCopy = nullptr;
     348             :   // On macOS, cache the current selection to send to osx service menu.
     349             : #ifdef XP_MACOSX
     350             :   autoCopy = nsAutoCopyListener::GetInstance(nsIClipboard::kSelectionCache);
     351             : #endif
     352             : 
     353             :   // Check to see if the autocopy pref is enabled
     354             :   //   and add the autocopy listener if it is
     355          32 :   if (Preferences::GetBool("clipboard.autocopy")) {
     356          32 :     autoCopy = nsAutoCopyListener::GetInstance(nsIClipboard::kSelectionClipboard);
     357             :   }
     358             : 
     359          32 :   if (autoCopy) {
     360          32 :     int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
     361          32 :     if (mDomSelections[index]) {
     362          32 :       autoCopy->Listen(mDomSelections[index]);
     363             :     }
     364             :   }
     365             : 
     366          32 :   mDisplaySelection = nsISelectionController::SELECTION_OFF;
     367          32 :   mSelectionChangeReason = nsISelectionListener::NO_REASON;
     368             : 
     369          32 :   mDelayedMouseEventValid = false;
     370             :   // These values are not used since they are only valid when
     371             :   // mDelayedMouseEventValid is true, and setting mDelayedMouseEventValid
     372             :   //alwaysoverrides these values.
     373          32 :   mDelayedMouseEventIsShift = false;
     374          32 :   mDelayedMouseEventClickCount = 0;
     375          32 : }
     376             : 
     377           0 : nsFrameSelection::~nsFrameSelection()
     378             : {
     379           0 : }
     380             : 
     381             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection)
     382             : 
     383           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection)
     384           0 :   for (size_t i = 0; i < kPresentSelectionTypeCount; ++i) {
     385           0 :     tmp->mDomSelections[i] = nullptr;
     386             :   }
     387             : 
     388           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCellParent)
     389           0 :   tmp->mSelectingTableCellMode = 0;
     390           0 :   tmp->mDragSelectingCells = false;
     391           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartSelectedCell)
     392           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSelectedCell)
     393           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAppendStartSelectedCell)
     394           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnselectCellOnMouseUp)
     395           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaintainRange)
     396           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLimiter)
     397           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAncestorLimiter)
     398           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     399           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameSelection)
     400           0 :   if (tmp->mShell && tmp->mShell->GetDocument() &&
     401           0 :       nsCCUncollectableMarker::InGeneration(cb,
     402           0 :                                             tmp->mShell->GetDocument()->
     403             :                                               GetMarkedCCGeneration())) {
     404           0 :     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
     405             :   }
     406           0 :   for (size_t i = 0; i < kPresentSelectionTypeCount; ++i) {
     407           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDomSelections[i])
     408             :   }
     409             : 
     410           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellParent)
     411           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartSelectedCell)
     412           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSelectedCell)
     413           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAppendStartSelectedCell)
     414           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnselectCellOnMouseUp)
     415           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMaintainRange)
     416           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLimiter)
     417           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAncestorLimiter)
     418           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     419             : 
     420           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsFrameSelection, AddRef)
     421           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsFrameSelection, Release)
     422             : 
     423             : // Get the x (or y, in vertical writing mode) position requested
     424             : // by the Key Handling for line-up/down
     425             : nsresult
     426           0 : nsFrameSelection::FetchDesiredPos(nsPoint &aDesiredPos)
     427             : {
     428           0 :   if (!mShell) {
     429           0 :     NS_ERROR("fetch desired position failed");
     430           0 :     return NS_ERROR_FAILURE;
     431             :   }
     432           0 :   if (mDesiredPosSet) {
     433           0 :     aDesiredPos = mDesiredPos;
     434           0 :     return NS_OK;
     435             :   }
     436             : 
     437           0 :   RefPtr<nsCaret> caret = mShell->GetCaret();
     438           0 :   if (!caret) {
     439           0 :     return NS_ERROR_NULL_POINTER;
     440             :   }
     441             : 
     442           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
     443           0 :   caret->SetSelection(mDomSelections[index]);
     444             : 
     445           0 :   nsRect coord;
     446           0 :   nsIFrame* caretFrame = caret->GetGeometry(&coord);
     447           0 :   if (!caretFrame) {
     448           0 :     return NS_ERROR_FAILURE;
     449             :   }
     450           0 :   nsPoint viewOffset(0, 0);
     451           0 :   nsView* view = nullptr;
     452           0 :   caretFrame->GetOffsetFromView(viewOffset, &view);
     453           0 :   if (view) {
     454           0 :     coord += viewOffset;
     455             :   }
     456           0 :   aDesiredPos = coord.TopLeft();
     457           0 :   return NS_OK;
     458             : }
     459             : 
     460             : void
     461           7 : nsFrameSelection::InvalidateDesiredPos() // do not listen to mDesiredPos;
     462             :                                          // you must get another.
     463             : {
     464           7 :   mDesiredPosSet = false;
     465           7 : }
     466             : 
     467             : void
     468           0 : nsFrameSelection::SetDesiredPos(nsPoint aPos)
     469             : {
     470           0 :   mDesiredPos = aPos;
     471           0 :   mDesiredPosSet = true;
     472           0 : }
     473             : 
     474             : nsresult
     475           0 : nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(nsIFrame  *aFrame,
     476             :                                                         nsPoint&   aPoint,
     477             :                                                         nsIFrame **aRetFrame,
     478             :                                                         nsPoint&   aRetPoint)
     479             : {
     480             :   //
     481             :   // The whole point of this method is to return a frame and point that
     482             :   // that lie within the same valid subtree as the anchor node's frame,
     483             :   // for use with the method GetContentAndOffsetsFromPoint().
     484             :   //
     485             :   // A valid subtree is defined to be one where all the content nodes in
     486             :   // the tree have a valid parent-child relationship.
     487             :   //
     488             :   // If the anchor frame and aFrame are in the same subtree, aFrame will
     489             :   // be returned in aRetFrame. If they are in different subtrees, we
     490             :   // return the frame for the root of the subtree.
     491             :   //
     492             : 
     493           0 :   if (!aFrame || !aRetFrame)
     494           0 :     return NS_ERROR_NULL_POINTER;
     495             : 
     496           0 :   *aRetFrame = aFrame;
     497           0 :   aRetPoint  = aPoint;
     498             : 
     499             :   //
     500             :   // Get the frame and content for the selection's anchor point!
     501             :   //
     502             : 
     503             :   nsresult result;
     504           0 :   nsCOMPtr<nsIDOMNode> anchorNode;
     505           0 :   int32_t anchorOffset = 0;
     506             : 
     507           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
     508           0 :   if (!mDomSelections[index])
     509           0 :     return NS_ERROR_NULL_POINTER;
     510             : 
     511           0 :   result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode));
     512             : 
     513           0 :   if (NS_FAILED(result))
     514           0 :     return result;
     515             : 
     516           0 :   if (!anchorNode)
     517           0 :     return NS_OK;
     518             : 
     519           0 :   result = mDomSelections[index]->GetAnchorOffset(&anchorOffset);
     520             : 
     521           0 :   if (NS_FAILED(result))
     522           0 :     return result;
     523             : 
     524           0 :   nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
     525             : 
     526           0 :   if (!anchorContent)
     527           0 :     return NS_ERROR_FAILURE;
     528             : 
     529             :   //
     530             :   // Now find the root of the subtree containing the anchor's content.
     531             :   //
     532             : 
     533           0 :   NS_ENSURE_STATE(mShell);
     534           0 :   nsIContent* anchorRoot = anchorContent->GetSelectionRootContent(mShell);
     535           0 :   NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED);
     536             : 
     537             :   //
     538             :   // Now find the root of the subtree containing aFrame's content.
     539             :   //
     540             : 
     541           0 :   nsIContent* content = aFrame->GetContent();
     542             : 
     543           0 :   if (content)
     544             :   {
     545           0 :     nsIContent* contentRoot = content->GetSelectionRootContent(mShell);
     546           0 :     NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED);
     547             : 
     548           0 :     if (anchorRoot == contentRoot)
     549             :     {
     550             :       // If the aFrame's content isn't the capturing content, it should be
     551             :       // a descendant.  At this time, we can return simply.
     552           0 :       nsIContent* capturedContent = nsIPresShell::GetCapturingContent();
     553           0 :       if (capturedContent != content)
     554             :       {
     555           0 :         return NS_OK;
     556             :       }
     557             : 
     558             :       // Find the frame under the mouse cursor with the root frame.
     559             :       // At this time, don't use the anchor's frame because it may not have
     560             :       // fixed positioned frames.
     561           0 :       nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame();
     562           0 :       nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame);
     563             :       nsIFrame* cursorFrame =
     564           0 :         nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
     565             : 
     566             :       // If the mouse cursor in on a frame which is descendant of same
     567             :       // selection root, we can expand the selection to the frame.
     568           0 :       if (cursorFrame && cursorFrame->PresContext()->PresShell() == mShell)
     569             :       {
     570           0 :         nsIContent* cursorContent = cursorFrame->GetContent();
     571           0 :         NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE);
     572             :         nsIContent* cursorContentRoot =
     573           0 :           cursorContent->GetSelectionRootContent(mShell);
     574           0 :         NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED);
     575           0 :         if (cursorContentRoot == anchorRoot)
     576             :         {
     577           0 :           *aRetFrame = cursorFrame;
     578           0 :           aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame);
     579           0 :           return NS_OK;
     580             :         }
     581             :       }
     582             :       // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse
     583             :       // cursor is out of the window), we should use the frame of the anchor
     584             :       // root.
     585             :     }
     586             :   }
     587             : 
     588             :   //
     589             :   // When we can't find a frame which is under the mouse cursor and has a same
     590             :   // selection root as the anchor node's, we should return the selection root
     591             :   // frame.
     592             :   //
     593             : 
     594           0 :   *aRetFrame = anchorRoot->GetPrimaryFrame();
     595             : 
     596           0 :   if (!*aRetFrame)
     597           0 :     return NS_ERROR_FAILURE;
     598             : 
     599             :   //
     600             :   // Now make sure that aRetPoint is converted to the same coordinate
     601             :   // system used by aRetFrame.
     602             :   //
     603             : 
     604           0 :   aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
     605             : 
     606           0 :   return NS_OK;
     607             : }
     608             : 
     609             : void
     610           0 : nsFrameSelection::SetCaretBidiLevel(nsBidiLevel aLevel)
     611             : {
     612             :   // If the current level is undefined, we have just inserted new text.
     613             :   // In this case, we don't want to reset the keyboard language
     614           0 :   mCaretBidiLevel = aLevel;
     615             : 
     616           0 :   RefPtr<nsCaret> caret;
     617           0 :   if (mShell && (caret = mShell->GetCaret())) {
     618           0 :     caret->SchedulePaint();
     619             :   }
     620             : 
     621           0 :   return;
     622             : }
     623             : 
     624             : nsBidiLevel
     625           4 : nsFrameSelection::GetCaretBidiLevel() const
     626             : {
     627           4 :   return mCaretBidiLevel;
     628             : }
     629             : 
     630             : void
     631           1 : nsFrameSelection::UndefineCaretBidiLevel()
     632             : {
     633           1 :   mCaretBidiLevel |= BIDI_LEVEL_UNDEFINED;
     634           1 : }
     635             : 
     636             : #ifdef PRINT_RANGE
     637             : void printRange(nsRange *aDomRange)
     638             : {
     639             :   if (!aDomRange)
     640             :   {
     641             :     printf("NULL nsIDOMRange\n");
     642             :   }
     643             :   nsINode* startNode = aDomRange->GetStartContainer();
     644             :   nsINode* endNode = aDomRange->GetEndContainer();
     645             :   int32_t startOffset = aDomRange->StartOffset();
     646             :   int32_t endOffset = aDomRange->EndOffset();
     647             : 
     648             :   printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
     649             :          (unsigned long)aDomRange,
     650             :          (unsigned long)startNode, (long)startOffset,
     651             :          (unsigned long)endNode, (long)endOffset);
     652             : 
     653             : }
     654             : #endif /* PRINT_RANGE */
     655             : 
     656             : static
     657           0 : nsIAtom *GetTag(nsINode *aNode)
     658             : {
     659           0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
     660           0 :   if (!content)
     661             :   {
     662           0 :     NS_NOTREACHED("bad node passed to GetTag()");
     663           0 :     return nullptr;
     664             :   }
     665             : 
     666           0 :   return content->NodeInfo()->NameAtom();
     667             : }
     668             : 
     669             : // Returns the parent
     670             : nsINode*
     671           0 : ParentOffset(nsINode *aNode, int32_t *aChildOffset)
     672             : {
     673           0 :   if (!aNode || !aChildOffset)
     674           0 :     return nullptr;
     675             : 
     676           0 :   nsIContent* parent = aNode->GetParent();
     677           0 :   if (parent)
     678             :   {
     679           0 :     *aChildOffset = parent->IndexOf(aNode);
     680             : 
     681           0 :     return parent;
     682             :   }
     683             : 
     684           0 :   return nullptr;
     685             : }
     686             : 
     687             : static nsINode*
     688           0 : GetCellParent(nsINode *aDomNode)
     689             : {
     690           0 :     if (!aDomNode)
     691           0 :       return nullptr;
     692           0 :     nsINode* current = aDomNode;
     693             :     // Start with current node and look for a table cell
     694           0 :     while (current)
     695             :     {
     696           0 :       nsIAtom* tag = GetTag(current);
     697           0 :       if (tag == nsGkAtoms::td || tag == nsGkAtoms::th)
     698           0 :         return current;
     699           0 :       current = current->GetParent();
     700             :     }
     701           0 :     return nullptr;
     702             : }
     703             : 
     704             : void
     705          32 : nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
     706             : {
     707          32 :   mShell = aShell;
     708          32 :   mDragState = false;
     709          32 :   mDesiredPosSet = false;
     710          32 :   mLimiter = aLimiter;
     711          32 :   mCaretMovementStyle =
     712          32 :     Preferences::GetInt("bidi.edit.caret_movement_style", 2);
     713             : 
     714             :   // This should only ever be initialized on the main thread, so we are OK here.
     715             :   static bool prefCachesInitialized = false;
     716          32 :   if (!prefCachesInitialized) {
     717           2 :     prefCachesInitialized = true;
     718             : 
     719             :     Preferences::AddBoolVarCache(&sSelectionEventsEnabled,
     720           2 :                                  "dom.select_events.enabled", false);
     721             :     Preferences::AddBoolVarCache(&sSelectionEventsOnTextControlsEnabled,
     722           2 :                                  "dom.select_events.textcontrols.enabled", false);
     723             :   }
     724             : 
     725          64 :   RefPtr<AccessibleCaretEventHub> eventHub = mShell->GetAccessibleCaretEventHub();
     726          32 :   if (eventHub) {
     727           0 :     int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
     728           0 :     if (mDomSelections[index]) {
     729           0 :       mDomSelections[index]->AddSelectionListener(eventHub);
     730             :     }
     731             :   }
     732             : 
     733          32 :   nsIDocument* doc = aShell->GetDocument();
     734          64 :   if (sSelectionEventsEnabled ||
     735           0 :       (doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
     736          32 :     int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
     737          32 :     if (mDomSelections[index]) {
     738             :       // The Selection instance will hold a strong reference to its selectionchangelistener
     739             :       // so we don't have to worry about that!
     740          64 :       RefPtr<SelectionChangeListener> listener = new SelectionChangeListener;
     741          32 :       mDomSelections[index]->AddSelectionListener(listener);
     742             :     }
     743             :   }
     744          32 : }
     745             : 
     746             : bool nsFrameSelection::sSelectionEventsEnabled = false;
     747             : bool nsFrameSelection::sSelectionEventsOnTextControlsEnabled = false;
     748             : 
     749             : nsresult
     750           0 : nsFrameSelection::MoveCaret(nsDirection       aDirection,
     751             :                             bool              aContinueSelection,
     752             :                             nsSelectionAmount aAmount,
     753             :                             CaretMovementStyle aMovementStyle)
     754             : {
     755           0 :   bool visualMovement = aMovementStyle == eVisual ||
     756           0 :     (aMovementStyle == eUsePrefStyle &&
     757           0 :       (mCaretMovementStyle == 1 ||
     758           0 :         (mCaretMovementStyle == 2 && !aContinueSelection)));
     759             : 
     760           0 :   NS_ENSURE_STATE(mShell);
     761             :   // Flush out layout, since we need it to be up to date to do caret
     762             :   // positioning.
     763           0 :   mShell->FlushPendingNotifications(FlushType::Layout);
     764             : 
     765           0 :   if (!mShell) {
     766           0 :     return NS_OK;
     767             :   }
     768             : 
     769           0 :   nsPresContext *context = mShell->GetPresContext();
     770           0 :   if (!context)
     771           0 :     return NS_ERROR_FAILURE;
     772             : 
     773             :   bool isCollapsed;
     774           0 :   nsPoint desiredPos(0, 0); //we must keep this around and revalidate it when its just UP/DOWN
     775             : 
     776           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
     777           0 :   RefPtr<Selection> sel = mDomSelections[index];
     778           0 :   if (!sel)
     779           0 :     return NS_ERROR_NULL_POINTER;
     780             : 
     781           0 :   int32_t scrollFlags = Selection::SCROLL_FOR_CARET_MOVE;
     782           0 :   nsINode* focusNode = sel->GetFocusNode();
     783           0 :   if (focusNode &&
     784           0 :       (focusNode->IsEditable() ||
     785           0 :        (focusNode->IsElement() &&
     786           0 :         focusNode->AsElement()->State().
     787           0 :           HasState(NS_EVENT_STATE_MOZ_READWRITE)))) {
     788             :     // If caret moves in editor, it should cause scrolling even if it's in
     789             :     // overflow: hidden;.
     790           0 :     scrollFlags |= Selection::SCROLL_OVERFLOW_HIDDEN;
     791             :   }
     792             : 
     793           0 :   nsresult result = sel->GetIsCollapsed(&isCollapsed);
     794           0 :   if (NS_FAILED(result)) {
     795           0 :     return result;
     796             :   }
     797             : 
     798           0 :   int32_t caretStyle = Preferences::GetInt("layout.selection.caret_style", 0);
     799           0 :   if (caretStyle == 0
     800             : #ifdef XP_WIN
     801             :       && aAmount != eSelectLine
     802             : #endif
     803             :      ) {
     804             :     // Put caret at the selection edge in the |aDirection| direction.
     805           0 :     caretStyle = 2;
     806             :   }
     807             : 
     808           0 :   bool doCollapse = !isCollapsed && !aContinueSelection && caretStyle == 2 &&
     809           0 :                     aAmount <= eSelectLine;
     810           0 :   if (doCollapse) {
     811           0 :     if (aDirection == eDirPrevious) {
     812           0 :       PostReason(nsISelectionListener::COLLAPSETOSTART_REASON);
     813           0 :       mHint = CARET_ASSOCIATE_AFTER;
     814             :     } else {
     815           0 :       PostReason(nsISelectionListener::COLLAPSETOEND_REASON);
     816           0 :       mHint = CARET_ASSOCIATE_BEFORE;
     817             :     }
     818             :   } else {
     819           0 :     PostReason(nsISelectionListener::KEYPRESS_REASON);
     820             :   }
     821             : 
     822           0 :   AutoPrepareFocusRange prep(sel, aContinueSelection, false);
     823             : 
     824           0 :   if (aAmount == eSelectLine) {
     825           0 :     result = FetchDesiredPos(desiredPos);
     826           0 :     if (NS_FAILED(result)) {
     827           0 :       return result;
     828             :     }
     829           0 :     SetDesiredPos(desiredPos);
     830             :   }
     831             : 
     832           0 :   if (doCollapse) {
     833           0 :     const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
     834           0 :     if (anchorFocusRange) {
     835             :       nsINode* node;
     836             :       int32_t offset;
     837           0 :       if (aDirection == eDirPrevious) {
     838           0 :         node =  anchorFocusRange->GetStartContainer();
     839           0 :         offset = anchorFocusRange->StartOffset();
     840             :       } else {
     841           0 :         node = anchorFocusRange->GetEndContainer();
     842           0 :         offset = anchorFocusRange->EndOffset();
     843             :       }
     844           0 :       sel->Collapse(node, offset);
     845             :     }
     846           0 :     sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
     847             :                         nsIPresShell::ScrollAxis(),
     848           0 :                         nsIPresShell::ScrollAxis(), scrollFlags);
     849           0 :     return NS_OK;
     850             :   }
     851             : 
     852             :   nsIFrame *frame;
     853           0 :   int32_t offsetused = 0;
     854           0 :   result = sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
     855             :                                             visualMovement);
     856             : 
     857           0 :   if (NS_FAILED(result) || !frame)
     858           0 :     return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
     859             : 
     860             :   //set data using mLimiter to stop on scroll views.  If we have a limiter then we stop peeking
     861             :   //when we hit scrollable views.  If no limiter then just let it go ahead
     862             :   nsPeekOffsetStruct pos(aAmount, eDirPrevious, offsetused, desiredPos,
     863           0 :                          true, mLimiter != nullptr, true, visualMovement,
     864           0 :                          aContinueSelection);
     865             : 
     866           0 :   nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
     867             : 
     868           0 :   CaretAssociateHint tHint(mHint); //temporary variable so we dont set mHint until it is necessary
     869           0 :   switch (aAmount){
     870             :    case eSelectCharacter:
     871             :     case eSelectCluster:
     872             :     case eSelectWord:
     873             :     case eSelectWordNoSpace:
     874           0 :       InvalidateDesiredPos();
     875           0 :       pos.mAmount = aAmount;
     876           0 :       pos.mDirection = (visualMovement && paraDir == NSBIDI_RTL)
     877           0 :                        ? nsDirection(1 - aDirection) : aDirection;
     878           0 :       break;
     879             :     case eSelectLine:
     880           0 :       pos.mAmount = aAmount;
     881           0 :       pos.mDirection = aDirection;
     882           0 :       break;
     883             :     case eSelectBeginLine:
     884             :     case eSelectEndLine:
     885           0 :       InvalidateDesiredPos();
     886           0 :       pos.mAmount = aAmount;
     887           0 :       pos.mDirection = (visualMovement && paraDir == NSBIDI_RTL)
     888           0 :                        ? nsDirection(1 - aDirection) : aDirection;
     889           0 :       break;
     890             :     default:
     891           0 :       return NS_ERROR_FAILURE;
     892             :   }
     893             : 
     894           0 :   if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent)
     895             :   {
     896             :     nsIFrame *theFrame;
     897             :     int32_t currentOffset, frameStart, frameEnd;
     898             : 
     899           0 :     if (aAmount <= eSelectWordNoSpace)
     900             :     {
     901             :       // For left/right, PeekOffset() sets pos.mResultFrame correctly, but does not set pos.mAttachForward,
     902             :       // so determine the hint here based on the result frame and offset:
     903             :       // If we're at the end of a text frame, set the hint to ASSOCIATE_BEFORE to indicate that we
     904             :       // want the caret displayed at the end of this frame, not at the beginning of the next one.
     905           0 :       theFrame = pos.mResultFrame;
     906           0 :       theFrame->GetOffsets(frameStart, frameEnd);
     907           0 :       currentOffset = pos.mContentOffset;
     908           0 :       if (frameEnd == currentOffset && !(frameStart == 0 && frameEnd == 0))
     909           0 :         tHint = CARET_ASSOCIATE_BEFORE;
     910             :       else
     911           0 :         tHint = CARET_ASSOCIATE_AFTER;
     912             :     } else {
     913             :       // For up/down and home/end, pos.mResultFrame might not be set correctly, or not at all.
     914             :       // In these cases, get the frame based on the content and hint returned by PeekOffset().
     915           0 :       tHint = pos.mAttach;
     916           0 :       theFrame = GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset,
     917           0 :                                        tHint, &currentOffset);
     918           0 :       if (!theFrame)
     919           0 :         return NS_ERROR_FAILURE;
     920             : 
     921           0 :       theFrame->GetOffsets(frameStart, frameEnd);
     922             :     }
     923             : 
     924           0 :     if (context->BidiEnabled())
     925             :     {
     926           0 :       switch (aAmount) {
     927             :         case eSelectBeginLine:
     928             :         case eSelectEndLine: {
     929             :           // In Bidi contexts, PeekOffset calculates pos.mContentOffset
     930             :           // differently depending on whether the movement is visual or logical.
     931             :           // For visual movement, pos.mContentOffset depends on the direction-
     932             :           // ality of the first/last frame on the line (theFrame), and the caret
     933             :           // directionality must correspond.
     934           0 :           FrameBidiData bidiData = theFrame->GetBidiData();
     935           0 :           SetCaretBidiLevel(visualMovement ? bidiData.embeddingLevel
     936           0 :                                            : bidiData.baseLevel);
     937           0 :           break;
     938             :         }
     939             :         default:
     940             :           // If the current position is not a frame boundary, it's enough just
     941             :           // to take the Bidi level of the current frame
     942           0 :           if ((pos.mContentOffset != frameStart &&
     943           0 :                pos.mContentOffset != frameEnd) ||
     944             :               eSelectLine == aAmount) {
     945           0 :             SetCaretBidiLevel(theFrame->GetEmbeddingLevel());
     946             :           }
     947             :           else {
     948           0 :             BidiLevelFromMove(mShell, pos.mResultContent, pos.mContentOffset,
     949           0 :                               aAmount, tHint);
     950             :           }
     951             :       }
     952             :     }
     953           0 :     result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset,
     954             :                        tHint, aContinueSelection, false);
     955           0 :   } else if (aAmount <= eSelectWordNoSpace && aDirection == eDirNext &&
     956           0 :              !aContinueSelection) {
     957             :     // Collapse selection if PeekOffset failed, we either
     958             :     //  1. bumped into the BRFrame, bug 207623
     959             :     //  2. had select-all in a text input (DIV range), bug 352759.
     960           0 :     bool isBRFrame = frame->IsBrFrame();
     961           0 :     sel->Collapse(sel->GetFocusNode(), sel->FocusOffset());
     962             :     // Note: 'frame' might be dead here.
     963           0 :     if (!isBRFrame) {
     964           0 :       mHint = CARET_ASSOCIATE_BEFORE; // We're now at the end of the frame to the left.
     965             :     }
     966           0 :     result = NS_OK;
     967             :   }
     968           0 :   if (NS_SUCCEEDED(result))
     969             :   {
     970           0 :     result = mDomSelections[index]->
     971           0 :       ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
     972             :                      nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
     973             :                      scrollFlags);
     974             :   }
     975             : 
     976           0 :   return result;
     977             : }
     978             : 
     979             : nsPrevNextBidiLevels
     980           0 : nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
     981             :                                         uint32_t    aContentOffset,
     982             :                                         bool        aJumpLines) const
     983             : {
     984           0 :   return GetPrevNextBidiLevels(aNode, aContentOffset, mHint, aJumpLines);
     985             : }
     986             : 
     987             : nsPrevNextBidiLevels
     988           0 : nsFrameSelection::GetPrevNextBidiLevels(nsIContent*        aNode,
     989             :                                         uint32_t           aContentOffset,
     990             :                                         CaretAssociateHint aHint,
     991             :                                         bool               aJumpLines) const
     992             : {
     993             :   // Get the level of the frames on each side
     994             :   nsIFrame    *currentFrame;
     995             :   int32_t     currentOffset;
     996             :   int32_t     frameStart, frameEnd;
     997             :   nsDirection direction;
     998             : 
     999             :   nsPrevNextBidiLevels levels;
    1000           0 :   levels.SetData(nullptr, nullptr, 0, 0);
    1001             : 
    1002           0 :   currentFrame = GetFrameForNodeOffset(aNode, aContentOffset,
    1003           0 :                                        aHint, &currentOffset);
    1004           0 :   if (!currentFrame)
    1005           0 :     return levels;
    1006             : 
    1007           0 :   currentFrame->GetOffsets(frameStart, frameEnd);
    1008             : 
    1009           0 :   if (0 == frameStart && 0 == frameEnd)
    1010           0 :     direction = eDirPrevious;
    1011           0 :   else if (frameStart == currentOffset)
    1012           0 :     direction = eDirPrevious;
    1013           0 :   else if (frameEnd == currentOffset)
    1014           0 :     direction = eDirNext;
    1015             :   else {
    1016             :     // we are neither at the beginning nor at the end of the frame, so we have no worries
    1017           0 :     nsBidiLevel currentLevel = currentFrame->GetEmbeddingLevel();
    1018           0 :     levels.SetData(currentFrame, currentFrame, currentLevel, currentLevel);
    1019           0 :     return levels;
    1020             :   }
    1021             : 
    1022             :   nsIFrame *newFrame;
    1023             :   int32_t offset;
    1024             :   bool jumpedLine, movedOverNonSelectableText;
    1025           0 :   nsresult rv = currentFrame->GetFrameFromDirection(direction, false,
    1026             :                                                     aJumpLines, true,
    1027             :                                                     &newFrame, &offset, &jumpedLine,
    1028           0 :                                                     &movedOverNonSelectableText);
    1029           0 :   if (NS_FAILED(rv))
    1030           0 :     newFrame = nullptr;
    1031             : 
    1032           0 :   FrameBidiData currentBidi = currentFrame->GetBidiData();
    1033           0 :   nsBidiLevel currentLevel = currentBidi.embeddingLevel;
    1034           0 :   nsBidiLevel newLevel = newFrame ? newFrame->GetEmbeddingLevel()
    1035           0 :                                   : currentBidi.baseLevel;
    1036             : 
    1037             :   // If not jumping lines, disregard br frames, since they might be positioned incorrectly.
    1038             :   // XXX This could be removed once bug 339786 is fixed.
    1039           0 :   if (!aJumpLines) {
    1040           0 :     if (currentFrame->IsBrFrame()) {
    1041           0 :       currentFrame = nullptr;
    1042           0 :       currentLevel = currentBidi.baseLevel;
    1043             :     }
    1044           0 :     if (newFrame && newFrame->IsBrFrame()) {
    1045           0 :       newFrame = nullptr;
    1046           0 :       newLevel = currentBidi.baseLevel;
    1047             :     }
    1048             :   }
    1049             : 
    1050           0 :   if (direction == eDirNext)
    1051           0 :     levels.SetData(currentFrame, newFrame, currentLevel, newLevel);
    1052             :   else
    1053           0 :     levels.SetData(newFrame, currentFrame, newLevel, currentLevel);
    1054             : 
    1055           0 :   return levels;
    1056             : }
    1057             : 
    1058             : nsresult
    1059           0 : nsFrameSelection::GetFrameFromLevel(nsIFrame    *aFrameIn,
    1060             :                                     nsDirection  aDirection,
    1061             :                                     nsBidiLevel  aBidiLevel,
    1062             :                                     nsIFrame   **aFrameOut) const
    1063             : {
    1064           0 :   NS_ENSURE_STATE(mShell);
    1065           0 :   nsBidiLevel foundLevel = 0;
    1066           0 :   nsIFrame *foundFrame = aFrameIn;
    1067             : 
    1068           0 :   nsCOMPtr<nsIFrameEnumerator> frameTraversal;
    1069             :   nsresult result;
    1070           0 :   nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
    1071           0 :   if (NS_FAILED(result))
    1072           0 :       return result;
    1073             : 
    1074           0 :   result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
    1075           0 :                                    mShell->GetPresContext(), aFrameIn,
    1076             :                                    eLeaf,
    1077             :                                    false, // aVisual
    1078             :                                    false, // aLockInScrollView
    1079             :                                    false, // aFollowOOFs
    1080             :                                    false  // aSkipPopupChecks
    1081           0 :                                    );
    1082           0 :   if (NS_FAILED(result))
    1083           0 :     return result;
    1084             : 
    1085           0 :   do {
    1086           0 :     *aFrameOut = foundFrame;
    1087           0 :     if (aDirection == eDirNext)
    1088           0 :       frameTraversal->Next();
    1089             :     else
    1090           0 :       frameTraversal->Prev();
    1091             : 
    1092           0 :     foundFrame = frameTraversal->CurrentItem();
    1093           0 :     if (!foundFrame)
    1094           0 :       return NS_ERROR_FAILURE;
    1095           0 :     foundLevel = foundFrame->GetEmbeddingLevel();
    1096             : 
    1097           0 :   } while (foundLevel > aBidiLevel);
    1098             : 
    1099           0 :   return NS_OK;
    1100             : }
    1101             : 
    1102             : 
    1103             : nsresult
    1104           0 : nsFrameSelection::MaintainSelection(nsSelectionAmount aAmount)
    1105             : {
    1106           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    1107           0 :   if (!mDomSelections[index])
    1108           0 :     return NS_ERROR_NULL_POINTER;
    1109             : 
    1110           0 :   mMaintainedAmount = aAmount;
    1111             : 
    1112             :   const nsRange* anchorFocusRange =
    1113           0 :     mDomSelections[index]->GetAnchorFocusRange();
    1114           0 :   if (anchorFocusRange && aAmount != eSelectNoAmount) {
    1115           0 :     mMaintainRange = anchorFocusRange->CloneRange();
    1116           0 :     return NS_OK;
    1117             :   }
    1118             : 
    1119           0 :   mMaintainRange = nullptr;
    1120           0 :   return NS_OK;
    1121             : }
    1122             : 
    1123             : 
    1124             : /** After moving the caret, its Bidi level is set according to the following rules:
    1125             :  *
    1126             :  *  After moving over a character with left/right arrow, set to the Bidi level of the last moved over character.
    1127             :  *  After Home and End, set to the paragraph embedding level.
    1128             :  *  After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters.
    1129             :  *  After mouse click, set to the level of the current frame.
    1130             :  *
    1131             :  *  The following two methods use GetPrevNextBidiLevels to determine the new Bidi level.
    1132             :  *  BidiLevelFromMove is called when the caret is moved in response to a keyboard event
    1133             :  *
    1134             :  * @param aPresShell is the presentation shell
    1135             :  * @param aNode is the content node
    1136             :  * @param aContentOffset is the new caret position, as an offset into aNode
    1137             :  * @param aAmount is the amount of the move that gave the caret its new position
    1138             :  * @param aHint is the hint indicating in what logical direction the caret moved
    1139             :  */
    1140           0 : void nsFrameSelection::BidiLevelFromMove(nsIPresShell*      aPresShell,
    1141             :                                          nsIContent*        aNode,
    1142             :                                          uint32_t           aContentOffset,
    1143             :                                          nsSelectionAmount  aAmount,
    1144             :                                          CaretAssociateHint aHint)
    1145             : {
    1146           0 :   switch (aAmount) {
    1147             : 
    1148             :     // Movement within the line: the new cursor Bidi level is the level of the
    1149             :     // last character moved over
    1150             :     case eSelectCharacter:
    1151             :     case eSelectCluster:
    1152             :     case eSelectWord:
    1153             :     case eSelectWordNoSpace:
    1154             :     case eSelectBeginLine:
    1155             :     case eSelectEndLine:
    1156             :     case eSelectNoAmount:
    1157             :     {
    1158             :       nsPrevNextBidiLevels levels = GetPrevNextBidiLevels(aNode, aContentOffset,
    1159           0 :                                                           aHint, false);
    1160             : 
    1161           0 :       SetCaretBidiLevel(aHint == CARET_ASSOCIATE_BEFORE ?
    1162           0 :           levels.mLevelBefore : levels.mLevelAfter);
    1163           0 :       break;
    1164             :     }
    1165             :       /*
    1166             :     // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters
    1167             :     case eSelectLine:
    1168             :     case eSelectParagraph:
    1169             :       GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
    1170             :       aPresShell->SetCaretBidiLevel(std::min(firstLevel, secondLevel));
    1171             :       break;
    1172             :       */
    1173             : 
    1174             :     default:
    1175           0 :       UndefineCaretBidiLevel();
    1176             :   }
    1177           0 : }
    1178             : 
    1179             : /**
    1180             :  * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse
    1181             :  *
    1182             :  * @param aNode is the content node
    1183             :  * @param aContentOffset is the new caret position, as an offset into aNode
    1184             :  */
    1185           0 : void nsFrameSelection::BidiLevelFromClick(nsIContent *aNode,
    1186             :                                           uint32_t    aContentOffset)
    1187             : {
    1188           0 :   nsIFrame* clickInFrame=nullptr;
    1189             :   int32_t OffsetNotUsed;
    1190             : 
    1191           0 :   clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &OffsetNotUsed);
    1192           0 :   if (!clickInFrame)
    1193           0 :     return;
    1194             : 
    1195           0 :   SetCaretBidiLevel(clickInFrame->GetEmbeddingLevel());
    1196             : }
    1197             : 
    1198             : 
    1199             : bool
    1200           0 : nsFrameSelection::AdjustForMaintainedSelection(nsIContent *aContent,
    1201             :                                                int32_t     aOffset)
    1202             : {
    1203           0 :   if (!mMaintainRange)
    1204           0 :     return false;
    1205             : 
    1206           0 :   if (!aContent) {
    1207           0 :     return false;
    1208             :   }
    1209             : 
    1210           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    1211           0 :   if (!mDomSelections[index])
    1212           0 :     return false;
    1213             : 
    1214           0 :   nsINode* rangeStartNode = mMaintainRange->GetStartContainer();
    1215           0 :   nsINode* rangeEndNode = mMaintainRange->GetEndContainer();
    1216           0 :   int32_t rangeStartOffset = mMaintainRange->StartOffset();
    1217           0 :   int32_t rangeEndOffset = mMaintainRange->EndOffset();
    1218             : 
    1219             :   int32_t relToStart =
    1220             :     nsContentUtils::ComparePoints(rangeStartNode, rangeStartOffset,
    1221           0 :                                   aContent, aOffset);
    1222             :   int32_t relToEnd =
    1223             :     nsContentUtils::ComparePoints(rangeEndNode, rangeEndOffset,
    1224           0 :                                   aContent, aOffset);
    1225             : 
    1226             :   // If aContent/aOffset is inside the maintained selection, or if it is on the
    1227             :   // "anchor" side of the maintained selection, we need to do something.
    1228           0 :   if ((relToStart < 0 && relToEnd > 0) ||
    1229           0 :       (relToStart > 0 &&
    1230           0 :        mDomSelections[index]->GetDirection() == eDirNext) ||
    1231           0 :       (relToEnd < 0 &&
    1232           0 :        mDomSelections[index]->GetDirection() == eDirPrevious)) {
    1233             :     // Set the current range to the maintained range.
    1234           0 :     mDomSelections[index]->ReplaceAnchorFocusRange(mMaintainRange);
    1235           0 :     if (relToStart < 0 && relToEnd > 0) {
    1236             :       // We're inside the maintained selection, just keep it selected.
    1237           0 :       return true;
    1238             :     }
    1239             :     // Reverse the direction of the selection so that the anchor will be on the
    1240             :     // far side of the maintained selection, relative to aContent/aOffset.
    1241           0 :     mDomSelections[index]->SetDirection(relToStart > 0 ? eDirPrevious : eDirNext);
    1242             :   }
    1243           0 :   return false;
    1244             : }
    1245             : 
    1246             : 
    1247             : nsresult
    1248           0 : nsFrameSelection::HandleClick(nsIContent*        aNewFocus,
    1249             :                               uint32_t           aContentOffset,
    1250             :                               uint32_t           aContentEndOffset,
    1251             :                               bool               aContinueSelection,
    1252             :                               bool               aMultipleSelection,
    1253             :                               CaretAssociateHint aHint)
    1254             : {
    1255           0 :   if (!aNewFocus)
    1256           0 :     return NS_ERROR_INVALID_ARG;
    1257             : 
    1258           0 :   InvalidateDesiredPos();
    1259             : 
    1260           0 :   if (!aContinueSelection) {
    1261           0 :     mMaintainRange = nullptr;
    1262           0 :     if (!IsValidSelectionPoint(this, aNewFocus)) {
    1263           0 :       mAncestorLimiter = nullptr;
    1264             :     }
    1265             :   }
    1266             : 
    1267             :   // Don't take focus when dragging off of a table
    1268           0 :   if (!mDragSelectingCells)
    1269             :   {
    1270           0 :     BidiLevelFromClick(aNewFocus, aContentOffset);
    1271           0 :     PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
    1272           0 :     if (aContinueSelection &&
    1273           0 :         AdjustForMaintainedSelection(aNewFocus, aContentOffset))
    1274           0 :       return NS_OK; //shift clicked to maintained selection. rejected.
    1275             : 
    1276           0 :     int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    1277           0 :     AutoPrepareFocusRange prep(mDomSelections[index], aContinueSelection, aMultipleSelection);
    1278           0 :     return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aHint,
    1279           0 :                      aContinueSelection, aMultipleSelection);
    1280             :   }
    1281             : 
    1282           0 :   return NS_OK;
    1283             : }
    1284             : 
    1285             : void
    1286           0 : nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint)
    1287             : {
    1288           0 :   if (!aFrame || !mShell)
    1289           0 :     return;
    1290             : 
    1291             :   nsresult result;
    1292           0 :   nsIFrame *newFrame = 0;
    1293           0 :   nsPoint   newPoint;
    1294             : 
    1295           0 :   result = ConstrainFrameAndPointToAnchorSubtree(aFrame, aPoint, &newFrame, newPoint);
    1296           0 :   if (NS_FAILED(result))
    1297           0 :     return;
    1298           0 :   if (!newFrame)
    1299           0 :     return;
    1300             : 
    1301             :   nsIFrame::ContentOffsets offsets =
    1302           0 :       newFrame->GetContentOffsetsFromPoint(newPoint);
    1303           0 :   if (!offsets.content)
    1304           0 :     return;
    1305             : 
    1306           0 :   if (newFrame->IsSelected() &&
    1307           0 :       AdjustForMaintainedSelection(offsets.content, offsets.offset))
    1308           0 :     return;
    1309             : 
    1310             :   // Adjust offsets according to maintained amount
    1311           0 :   if (mMaintainRange &&
    1312           0 :       mMaintainedAmount != eSelectNoAmount) {
    1313             : 
    1314           0 :     nsINode* rangenode = mMaintainRange->GetStartContainer();
    1315           0 :     int32_t rangeOffset = mMaintainRange->StartOffset();
    1316             :     int32_t relativePosition =
    1317           0 :       nsContentUtils::ComparePoints(rangenode, rangeOffset,
    1318           0 :                                     offsets.content, offsets.offset);
    1319             : 
    1320           0 :     nsDirection direction = relativePosition > 0 ? eDirPrevious : eDirNext;
    1321           0 :     nsSelectionAmount amount = mMaintainedAmount;
    1322           0 :     if (amount == eSelectBeginLine && direction == eDirNext)
    1323           0 :       amount = eSelectEndLine;
    1324             : 
    1325             :     int32_t offset;
    1326           0 :     nsIFrame* frame = GetFrameForNodeOffset(offsets.content, offsets.offset,
    1327           0 :         CARET_ASSOCIATE_AFTER, &offset);
    1328             : 
    1329           0 :     if (frame && amount == eSelectWord && direction == eDirPrevious) {
    1330             :       // To avoid selecting the previous word when at start of word,
    1331             :       // first move one character forward.
    1332             :       nsPeekOffsetStruct charPos(eSelectCharacter, eDirNext, offset,
    1333           0 :                                  nsPoint(0, 0), false, mLimiter != nullptr,
    1334           0 :                                  false, false, false);
    1335           0 :       if (NS_SUCCEEDED(frame->PeekOffset(&charPos))) {
    1336           0 :         frame = charPos.mResultFrame;
    1337           0 :         offset = charPos.mContentOffset;
    1338             :       }
    1339             :     }
    1340             : 
    1341           0 :     nsPeekOffsetStruct pos(amount, direction, offset, nsPoint(0, 0),
    1342           0 :                            false, mLimiter != nullptr, false, false, false);
    1343             : 
    1344           0 :     if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos)) && pos.mResultContent) {
    1345           0 :       offsets.content = pos.mResultContent;
    1346           0 :       offsets.offset = pos.mContentOffset;
    1347             :     }
    1348             :   }
    1349             : 
    1350           0 :   HandleClick(offsets.content, offsets.offset, offsets.offset,
    1351           0 :               true, false, offsets.associate);
    1352             : }
    1353             : 
    1354             : nsresult
    1355           0 : nsFrameSelection::StartAutoScrollTimer(nsIFrame *aFrame,
    1356             :                                        nsPoint   aPoint,
    1357             :                                        uint32_t  aDelay)
    1358             : {
    1359           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    1360           0 :   if (!mDomSelections[index]) {
    1361           0 :     return NS_ERROR_NULL_POINTER;
    1362             :   }
    1363             : 
    1364           0 :   return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay);
    1365             : }
    1366             : 
    1367             : void
    1368           6 : nsFrameSelection::StopAutoScrollTimer()
    1369             : {
    1370           6 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    1371           6 :   if (!mDomSelections[index]) {
    1372           0 :     return;
    1373             :   }
    1374             : 
    1375           6 :   mDomSelections[index]->StopAutoScrollTimer();
    1376             : }
    1377             : 
    1378             : /**
    1379             : hard to go from nodes to frames, easy the other way!
    1380             :  */
    1381             : nsresult
    1382           0 : nsFrameSelection::TakeFocus(nsIContent*        aNewFocus,
    1383             :                             uint32_t           aContentOffset,
    1384             :                             uint32_t           aContentEndOffset,
    1385             :                             CaretAssociateHint aHint,
    1386             :                             bool               aContinueSelection,
    1387             :                             bool               aMultipleSelection)
    1388             : {
    1389           0 :   if (!aNewFocus)
    1390           0 :     return NS_ERROR_NULL_POINTER;
    1391             : 
    1392           0 :   NS_ENSURE_STATE(mShell);
    1393             : 
    1394           0 :   if (!IsValidSelectionPoint(this,aNewFocus))
    1395           0 :     return NS_ERROR_FAILURE;
    1396             : 
    1397             :   // Clear all table selection data
    1398           0 :   mSelectingTableCellMode = 0;
    1399           0 :   mDragSelectingCells = false;
    1400           0 :   mStartSelectedCell = nullptr;
    1401           0 :   mEndSelectedCell = nullptr;
    1402           0 :   mAppendStartSelectedCell = nullptr;
    1403           0 :   mHint = aHint;
    1404             : 
    1405           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    1406           0 :   if (!mDomSelections[index])
    1407           0 :     return NS_ERROR_NULL_POINTER;
    1408             : 
    1409           0 :   Maybe<Selection::AutoUserInitiated> userSelect;
    1410           0 :   if (IsUserSelectionReason()) {
    1411           0 :     userSelect.emplace(mDomSelections[index]);
    1412             :   }
    1413             : 
    1414             :   //traverse through document and unselect crap here
    1415           0 :   if (!aContinueSelection) {//single click? setting cursor down
    1416           0 :     uint32_t batching = mBatching;//hack to use the collapse code.
    1417           0 :     bool changes = mChangesDuringBatching;
    1418           0 :     mBatching = 1;
    1419             : 
    1420           0 :     if (aMultipleSelection) {
    1421             :       // Remove existing collapsed ranges as there's no point in having
    1422             :       // non-anchor/focus collapsed ranges.
    1423           0 :       mDomSelections[index]->RemoveCollapsedRanges();
    1424             : 
    1425           0 :       RefPtr<nsRange> newRange = new nsRange(aNewFocus);
    1426             : 
    1427           0 :       newRange->CollapseTo(aNewFocus, aContentOffset);
    1428           0 :       mDomSelections[index]->AddRange(newRange);
    1429           0 :       mBatching = batching;
    1430           0 :       mChangesDuringBatching = changes;
    1431             :     } else {
    1432           0 :       bool oldDesiredPosSet = mDesiredPosSet; //need to keep old desired position if it was set.
    1433           0 :       mDomSelections[index]->Collapse(aNewFocus, aContentOffset);
    1434           0 :       mDesiredPosSet = oldDesiredPosSet; //now reset desired pos back.
    1435           0 :       mBatching = batching;
    1436           0 :       mChangesDuringBatching = changes;
    1437             :     }
    1438           0 :     if (aContentEndOffset != aContentOffset) {
    1439           0 :       mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);
    1440             :     }
    1441             : 
    1442             :     //find out if we are inside a table. if so, find out which one and which cell
    1443             :     //once we do that, the next time we get a takefocus, check the parent tree.
    1444             :     //if we are no longer inside same table ,cell then switch to table selection mode.
    1445             :     // BUT only do this in an editor
    1446             : 
    1447           0 :     NS_ENSURE_STATE(mShell);
    1448           0 :     bool editableCell = false;
    1449           0 :     RefPtr<nsPresContext> context = mShell->GetPresContext();
    1450           0 :     if (context) {
    1451           0 :       nsCOMPtr<nsIHTMLEditor> editor = do_QueryInterface(nsContentUtils::GetHTMLEditor(context));
    1452           0 :       if (editor) {
    1453           0 :         nsINode* cellparent = GetCellParent(aNewFocus);
    1454           0 :         nsCOMPtr<nsINode> editorHostNode = editor->GetActiveEditingHost();
    1455           0 :         editableCell = cellparent && editorHostNode &&
    1456           0 :                    nsContentUtils::ContentIsDescendantOf(cellparent, editorHostNode);
    1457           0 :         if (editableCell) {
    1458           0 :           mCellParent = cellparent;
    1459             : #ifdef DEBUG_TABLE_SELECTION
    1460             :           printf(" * TakeFocus - Collapsing into new cell\n");
    1461             : #endif
    1462             :         }
    1463             :       }
    1464             :     }
    1465             :   }
    1466             :   else {
    1467             :     // Now update the range list:
    1468           0 :     if (aContinueSelection && aNewFocus)
    1469             :     {
    1470             :       int32_t offset;
    1471           0 :       nsINode *cellparent = GetCellParent(aNewFocus);
    1472           0 :       if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode
    1473             :       {
    1474             : #ifdef DEBUG_TABLE_SELECTION
    1475             : printf(" * TakeFocus - moving into new cell\n");
    1476             : #endif
    1477             :         WidgetMouseEvent event(false, eVoidEvent, nullptr,
    1478           0 :                                WidgetMouseEvent::eReal);
    1479             : 
    1480             :         // Start selecting in the cell we were in before
    1481           0 :         nsINode* parent = ParentOffset(mCellParent, &offset);
    1482           0 :         if (parent)
    1483             :           HandleTableSelection(parent, offset,
    1484           0 :                                nsISelectionPrivate::TABLESELECTION_CELL, &event);
    1485             : 
    1486             :         // Find the parent of this new cell and extend selection to it
    1487           0 :         parent = ParentOffset(cellparent, &offset);
    1488             : 
    1489             :         // XXXX We need to REALLY get the current key shift state
    1490             :         //  (we'd need to add event listener -- let's not bother for now)
    1491           0 :         event.mModifiers &= ~MODIFIER_SHIFT; //aContinueSelection;
    1492           0 :         if (parent)
    1493             :         {
    1494           0 :           mCellParent = cellparent;
    1495             :           // Continue selection into next cell
    1496             :           HandleTableSelection(parent, offset,
    1497           0 :                                nsISelectionPrivate::TABLESELECTION_CELL, &event);
    1498             :         }
    1499             :       }
    1500             :       else
    1501             :       {
    1502             :         // XXXX Problem: Shift+click in browser is appending text selection to selected table!!!
    1503             :         //   is this the place to erase seleced cells ?????
    1504           0 :         if (mDomSelections[index]->GetDirection() == eDirNext && aContentEndOffset > aContentOffset) //didn't go far enough
    1505             :         {
    1506           0 :           mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);//this will only redraw the diff
    1507             :         }
    1508             :         else
    1509           0 :           mDomSelections[index]->Extend(aNewFocus, aContentOffset);
    1510             :       }
    1511             :     }
    1512             :   }
    1513             : 
    1514             :   // Don't notify selection listeners if batching is on:
    1515           0 :   if (GetBatching())
    1516           0 :     return NS_OK;
    1517             : 
    1518             :   // Be aware, the Selection instance may be destroyed after this call.
    1519           0 :   return NotifySelectionListeners(SelectionType::eNormal);
    1520             : }
    1521             : 
    1522             : 
    1523             : UniquePtr<SelectionDetails>
    1524           2 : nsFrameSelection::LookUpSelection(nsIContent *aContent,
    1525             :                                   int32_t aContentOffset,
    1526             :                                   int32_t aContentLength,
    1527             :                                   bool aSlowCheck) const
    1528             : {
    1529           2 :   if (!aContent || !mShell)
    1530           0 :     return nullptr;
    1531             : 
    1532           4 :   UniquePtr<SelectionDetails> details;
    1533             : 
    1534          22 :   for (size_t j = 0; j < kPresentSelectionTypeCount; j++) {
    1535          20 :     if (mDomSelections[j]) {
    1536          80 :       details = mDomSelections[j]->LookUpSelection(aContent, aContentOffset,
    1537          20 :                                                    aContentLength, Move(details),
    1538          20 :                                                    ToSelectionType(1 << j),
    1539          20 :                                                    aSlowCheck);
    1540             :     }
    1541             :   }
    1542             : 
    1543           2 :   return details;
    1544             : }
    1545             : 
    1546             : void
    1547           4 : nsFrameSelection::SetDragState(bool aState)
    1548             : {
    1549           4 :   if (mDragState == aState)
    1550           4 :     return;
    1551             : 
    1552           0 :   mDragState = aState;
    1553             : 
    1554           0 :   if (!mDragState)
    1555             :   {
    1556           0 :     mDragSelectingCells = false;
    1557             :     // Notify that reason is mouse up.
    1558           0 :     PostReason(nsISelectionListener::MOUSEUP_REASON);
    1559             :     // Be aware, the Selection instance may be destroyed after this call.
    1560           0 :     NotifySelectionListeners(SelectionType::eNormal);
    1561             :   }
    1562             : }
    1563             : 
    1564             : Selection*
    1565         126 : nsFrameSelection::GetSelection(SelectionType aSelectionType) const
    1566             : {
    1567         126 :   int8_t index = GetIndexFromSelectionType(aSelectionType);
    1568         126 :   if (index < 0)
    1569           0 :     return nullptr;
    1570             : 
    1571         126 :   return mDomSelections[index];
    1572             : }
    1573             : 
    1574             : nsresult
    1575           3 : nsFrameSelection::ScrollSelectionIntoView(SelectionType aSelectionType,
    1576             :                                           SelectionRegion aRegion,
    1577             :                                           int16_t         aFlags) const
    1578             : {
    1579           3 :   int8_t index = GetIndexFromSelectionType(aSelectionType);
    1580           3 :   if (index < 0)
    1581           0 :     return NS_ERROR_INVALID_ARG;
    1582             : 
    1583           3 :   if (!mDomSelections[index])
    1584           0 :     return NS_ERROR_NULL_POINTER;
    1585             : 
    1586           3 :   nsIPresShell::ScrollAxis verticalScroll = nsIPresShell::ScrollAxis();
    1587           3 :   int32_t flags = Selection::SCROLL_DO_FLUSH;
    1588           3 :   if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) {
    1589           0 :     flags |= Selection::SCROLL_SYNCHRONOUS;
    1590           3 :   } else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) {
    1591           2 :     flags |= Selection::SCROLL_FIRST_ANCESTOR_ONLY;
    1592             :   }
    1593           3 :   if (aFlags & nsISelectionController::SCROLL_OVERFLOW_HIDDEN) {
    1594           1 :     flags |= Selection::SCROLL_OVERFLOW_HIDDEN;
    1595             :   }
    1596           3 :   if (aFlags & nsISelectionController::SCROLL_CENTER_VERTICALLY) {
    1597           0 :     verticalScroll = nsIPresShell::ScrollAxis(
    1598             :       nsIPresShell::SCROLL_CENTER, nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE);
    1599             :   }
    1600           3 :   if (aFlags & nsISelectionController::SCROLL_FOR_CARET_MOVE) {
    1601           0 :     flags |= Selection::SCROLL_FOR_CARET_MOVE;
    1602             :   }
    1603             : 
    1604             :   // After ScrollSelectionIntoView(), the pending notifications might be
    1605             :   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
    1606           6 :   RefPtr<Selection> sel = mDomSelections[index];
    1607           6 :   return sel->ScrollIntoView(aRegion, verticalScroll,
    1608           3 :                              nsIPresShell::ScrollAxis(), flags);
    1609             : }
    1610             : 
    1611             : nsresult
    1612           3 : nsFrameSelection::RepaintSelection(SelectionType aSelectionType)
    1613             : {
    1614           3 :   int8_t index = GetIndexFromSelectionType(aSelectionType);
    1615           3 :   if (index < 0)
    1616           0 :     return NS_ERROR_INVALID_ARG;
    1617           3 :   if (!mDomSelections[index])
    1618           0 :     return NS_ERROR_NULL_POINTER;
    1619           3 :   NS_ENSURE_STATE(mShell);
    1620             : 
    1621             : // On macOS, update the selection cache to the new active selection
    1622             : // aka the current selection.
    1623             : #ifdef XP_MACOSX
    1624             :   nsFocusManager* fm = nsFocusManager::GetFocusManager();
    1625             :   // Check an active window exists otherwise there cannot be a current selection
    1626             :   // and that it's a normal selection.
    1627             :   if (fm->GetActiveWindow() && aSelectionType == SelectionType::eNormal) {
    1628             :     UpdateSelectionCacheOnRepaintSelection(mDomSelections[index]);
    1629             :   }
    1630             : #endif
    1631           3 :   return mDomSelections[index]->Repaint(mShell->GetPresContext());
    1632             : }
    1633             : 
    1634             : nsIFrame*
    1635           5 : nsFrameSelection::GetFrameForNodeOffset(nsIContent*        aNode,
    1636             :                                         int32_t            aOffset,
    1637             :                                         CaretAssociateHint aHint,
    1638             :                                         int32_t*           aReturnOffset) const
    1639             : {
    1640           5 :   if (!aNode || !aReturnOffset || !mShell)
    1641           0 :     return nullptr;
    1642             : 
    1643           5 :   if (aOffset < 0)
    1644           0 :     return nullptr;
    1645             : 
    1646           5 :   if (!aNode->GetPrimaryFrame() &&
    1647           0 :       !mShell->FrameManager()->GetDisplayContentsStyleFor(aNode)) {
    1648           0 :     return nullptr;
    1649             :   }
    1650             : 
    1651           5 :   nsIFrame* returnFrame = nullptr;
    1652          10 :   nsCOMPtr<nsIContent> theNode;
    1653             : 
    1654             :   while (true) {
    1655           5 :     *aReturnOffset = aOffset;
    1656             : 
    1657           5 :     theNode = aNode;
    1658             : 
    1659           5 :     if (aNode->IsElement()) {
    1660           4 :       int32_t childIndex  = 0;
    1661           4 :       int32_t numChildren = theNode->GetChildCount();
    1662             : 
    1663           4 :       if (aHint == CARET_ASSOCIATE_BEFORE) {
    1664           4 :         if (aOffset > 0) {
    1665           2 :           childIndex = aOffset - 1;
    1666             :         } else {
    1667           2 :           childIndex = aOffset;
    1668             :         }
    1669             :       } else {
    1670           0 :         NS_ASSERTION(aHint == CARET_ASSOCIATE_AFTER, "unknown direction");
    1671           0 :         if (aOffset >= numChildren) {
    1672           0 :           if (numChildren > 0) {
    1673           0 :             childIndex = numChildren - 1;
    1674             :           } else {
    1675           0 :             childIndex = 0;
    1676             :           }
    1677             :         } else {
    1678           0 :           childIndex = aOffset;
    1679             :         }
    1680             :       }
    1681             : 
    1682           4 :       if (childIndex > 0 || numChildren > 0) {
    1683           8 :         nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex);
    1684             : 
    1685           4 :         if (!childNode) {
    1686           0 :           break;
    1687             :         }
    1688             : 
    1689           4 :         theNode = childNode;
    1690             :       }
    1691             : 
    1692             :       // Now that we have the child node, check if it too
    1693             :       // can contain children. If so, descend into child.
    1694          12 :       if (theNode->IsElement() &&
    1695           4 :           theNode->GetChildCount() &&
    1696           0 :           !theNode->HasIndependentSelection()) {
    1697           0 :         aNode = theNode;
    1698           0 :         aOffset = aOffset > childIndex ? theNode->GetChildCount() : 0;
    1699           0 :         continue;
    1700             :       } else {
    1701             :         // Check to see if theNode is a text node. If it is, translate
    1702             :         // aOffset into an offset into the text node.
    1703             : 
    1704           8 :         nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode);
    1705           4 :         if (textNode) {
    1706           0 :           if (theNode->GetPrimaryFrame()) {
    1707           0 :             if (aOffset > childIndex) {
    1708           0 :               uint32_t textLength = 0;
    1709           0 :               nsresult rv = textNode->GetLength(&textLength);
    1710           0 :               if (NS_FAILED(rv)) {
    1711           0 :                 break;
    1712             :               }
    1713             : 
    1714           0 :               *aReturnOffset = (int32_t)textLength;
    1715             :             } else {
    1716           0 :               *aReturnOffset = 0;
    1717             :             }
    1718             :           } else {
    1719           0 :             int32_t numChildren = aNode->GetChildCount();
    1720             :             int32_t newChildIndex =
    1721           0 :               aHint == CARET_ASSOCIATE_BEFORE ? childIndex - 1 : childIndex + 1;
    1722             : 
    1723           0 :             if (newChildIndex >= 0 && newChildIndex < numChildren) {
    1724           0 :               nsCOMPtr<nsIContent> newChildNode = aNode->GetChildAt(newChildIndex);
    1725           0 :               if (!newChildNode) {
    1726           0 :                 return nullptr;
    1727             :               }
    1728             : 
    1729           0 :               aNode = newChildNode;
    1730           0 :               aOffset = aHint == CARET_ASSOCIATE_BEFORE ? aNode->GetChildCount() : 0;
    1731           0 :               continue;
    1732             :             } else {
    1733             :               // newChildIndex is illegal which means we're at first or last
    1734             :               // child. Just use original node to get the frame.
    1735           0 :               theNode = aNode;
    1736             :             }
    1737             :           }
    1738             :         }
    1739             :       }
    1740             :     }
    1741             : 
    1742             :     // If the node is a ShadowRoot, the frame needs to be adjusted,
    1743             :     // because a ShadowRoot does not get a frame. Its children are rendered
    1744             :     // as children of the host.
    1745             :     mozilla::dom::ShadowRoot* shadowRoot =
    1746           5 :       mozilla::dom::ShadowRoot::FromNode(theNode);
    1747           5 :     if (shadowRoot) {
    1748           0 :       theNode = shadowRoot->GetHost();
    1749             :     }
    1750             : 
    1751           5 :     returnFrame = theNode->GetPrimaryFrame();
    1752           5 :     if (!returnFrame) {
    1753           0 :       if (aHint == CARET_ASSOCIATE_BEFORE) {
    1754           0 :         if (aOffset > 0) {
    1755           0 :           --aOffset;
    1756           0 :           continue;
    1757             :         } else {
    1758           0 :           break;
    1759             :         }
    1760             :       } else {
    1761           0 :         int32_t end = theNode->GetChildCount();
    1762           0 :         if (aOffset < end) {
    1763           0 :           ++aOffset;
    1764           0 :           continue;
    1765             :         } else {
    1766           0 :           break;
    1767             :         }
    1768             :       }
    1769             :     }
    1770             : 
    1771           5 :     break;
    1772           0 :   } // end while
    1773             : 
    1774           5 :   if (!returnFrame)
    1775           0 :     return nullptr;
    1776             : 
    1777             :   // If we ended up here and were asked to position the caret after a visible
    1778             :   // break, let's return the frame on the next line instead if it exists.
    1779           7 :   if (aOffset > 0 &&  (uint32_t) aOffset >= aNode->Length() &&
    1780           2 :       theNode == aNode->GetLastChild()) {
    1781             :     nsIFrame* newFrame;
    1782           2 :     nsLayoutUtils::IsInvisibleBreak(theNode, &newFrame);
    1783           2 :     if (newFrame) {
    1784           0 :       returnFrame = newFrame;
    1785           0 :       *aReturnOffset = 0;
    1786             :     }
    1787             :   }
    1788             : 
    1789             :   // find the child frame containing the offset we want
    1790           5 :   returnFrame->GetChildFrameContainingOffset(*aReturnOffset, aHint == CARET_ASSOCIATE_AFTER,
    1791          10 :                                              &aOffset, &returnFrame);
    1792           5 :   return returnFrame;
    1793             : }
    1794             : 
    1795             : void
    1796           0 : nsFrameSelection::CommonPageMove(bool aForward,
    1797             :                                  bool aExtend,
    1798             :                                  nsIScrollableFrame* aScrollableFrame)
    1799             : {
    1800             :   // expected behavior for PageMove is to scroll AND move the caret
    1801             :   // and remain relative position of the caret in view. see Bug 4302.
    1802             : 
    1803             :   //get the frame from the scrollable view
    1804             : 
    1805           0 :   nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
    1806           0 :   if (!scrolledFrame)
    1807           0 :     return;
    1808             : 
    1809             :   // find out where the caret is.
    1810             :   // we should know mDesiredPos value of nsFrameSelection, but I havent seen that behavior in other windows applications yet.
    1811           0 :   nsISelection* domSel = GetSelection(SelectionType::eNormal);
    1812           0 :   if (!domSel) {
    1813           0 :     return;
    1814             :   }
    1815             : 
    1816           0 :   nsRect caretPos;
    1817           0 :   nsIFrame* caretFrame = nsCaret::GetGeometry(domSel, &caretPos);
    1818           0 :   if (!caretFrame)
    1819           0 :     return;
    1820             : 
    1821             :   //need to adjust caret jump by percentage scroll
    1822           0 :   nsSize scrollDelta = aScrollableFrame->GetPageScrollAmount();
    1823             : 
    1824           0 :   if (aForward)
    1825           0 :     caretPos.y += scrollDelta.height;
    1826             :   else
    1827           0 :     caretPos.y -= scrollDelta.height;
    1828             : 
    1829           0 :   caretPos += caretFrame->GetOffsetTo(scrolledFrame);
    1830             : 
    1831             :   // get a content at desired location
    1832           0 :   nsPoint desiredPoint;
    1833           0 :   desiredPoint.x = caretPos.x;
    1834           0 :   desiredPoint.y = caretPos.y + caretPos.height/2;
    1835             :   nsIFrame::ContentOffsets offsets =
    1836           0 :       scrolledFrame->GetContentOffsetsFromPoint(desiredPoint);
    1837             : 
    1838           0 :   if (!offsets.content)
    1839           0 :     return;
    1840             : 
    1841             :   // scroll one page
    1842             :   mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
    1843           0 :       (uint32_t) ScrollInputMethod::MainThreadScrollPage);
    1844           0 :   aScrollableFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
    1845             :                              nsIScrollableFrame::PAGES,
    1846           0 :                              nsIScrollableFrame::SMOOTH);
    1847             : 
    1848             :   // place the caret
    1849           0 :   HandleClick(offsets.content, offsets.offset,
    1850           0 :               offsets.offset, aExtend, false, CARET_ASSOCIATE_AFTER);
    1851             : }
    1852             : 
    1853             : nsresult
    1854           0 : nsFrameSelection::PhysicalMove(int16_t aDirection, int16_t aAmount,
    1855             :                                bool aExtend)
    1856             : {
    1857           0 :   NS_ENSURE_STATE(mShell);
    1858             :   // Flush out layout, since we need it to be up to date to do caret
    1859             :   // positioning.
    1860           0 :   mShell->FlushPendingNotifications(FlushType::Layout);
    1861             : 
    1862           0 :   if (!mShell) {
    1863           0 :     return NS_OK;
    1864             :   }
    1865             : 
    1866             :   // Check that parameters are safe
    1867           0 :   if (aDirection < 0 || aDirection > 3 || aAmount < 0 || aAmount > 1) {
    1868           0 :     return NS_ERROR_FAILURE;
    1869             :   }
    1870             : 
    1871           0 :   nsPresContext *context = mShell->GetPresContext();
    1872           0 :   if (!context) {
    1873           0 :     return NS_ERROR_FAILURE;
    1874             :   }
    1875             : 
    1876           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    1877           0 :   RefPtr<Selection> sel = mDomSelections[index];
    1878           0 :   if (!sel) {
    1879           0 :     return NS_ERROR_NULL_POINTER;
    1880             :   }
    1881             : 
    1882             :   // Map the abstract movement amounts (0-1) to direction-specific
    1883             :   // selection units.
    1884             :   static const nsSelectionAmount inlineAmount[] =
    1885             :     { eSelectCluster, eSelectWord };
    1886             :   static const nsSelectionAmount blockPrevAmount[] =
    1887             :     { eSelectLine, eSelectBeginLine };
    1888             :   static const nsSelectionAmount blockNextAmount[] =
    1889             :     { eSelectLine, eSelectEndLine };
    1890             : 
    1891             :   struct PhysicalToLogicalMapping {
    1892             :     nsDirection direction;
    1893             :     const nsSelectionAmount *amounts;
    1894             :   };
    1895             :   static const PhysicalToLogicalMapping verticalLR[4] = {
    1896             :     { eDirPrevious, blockPrevAmount },  // left
    1897             :     { eDirNext, blockNextAmount },      // right
    1898             :     { eDirPrevious, inlineAmount }, // up
    1899             :     { eDirNext, inlineAmount }      // down
    1900             :   };
    1901             :   static const PhysicalToLogicalMapping verticalRL[4] = {
    1902             :     { eDirNext, blockNextAmount },
    1903             :     { eDirPrevious, blockPrevAmount },
    1904             :     { eDirPrevious, inlineAmount },
    1905             :     { eDirNext, inlineAmount }
    1906             :   };
    1907             :   static const PhysicalToLogicalMapping horizontal[4] = {
    1908             :     { eDirPrevious, inlineAmount },
    1909             :     { eDirNext, inlineAmount },
    1910             :     { eDirPrevious, blockPrevAmount },
    1911             :     { eDirNext, blockNextAmount }
    1912             :   };
    1913             : 
    1914           0 :   WritingMode wm;
    1915           0 :   nsIFrame *frame = nullptr;
    1916           0 :   int32_t offsetused = 0;
    1917           0 :   if (NS_SUCCEEDED(sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
    1918             :                                                     true))) {
    1919           0 :     if (frame) {
    1920           0 :       if (!frame->StyleContext()->IsTextCombined()) {
    1921           0 :         wm = frame->GetWritingMode();
    1922             :       } else {
    1923             :         // Using different direction for horizontal-in-vertical would
    1924             :         // make it hard to navigate via keyboard. Inherit the moving
    1925             :         // direction from its parent.
    1926           0 :         MOZ_ASSERT(frame->IsTextFrame());
    1927           0 :         wm = frame->GetParent()->GetWritingMode();
    1928           0 :         MOZ_ASSERT(wm.IsVertical(), "Text combined "
    1929             :                    "can only appear in vertical text");
    1930             :       }
    1931             :     }
    1932             :   }
    1933             : 
    1934             :   const PhysicalToLogicalMapping& mapping =
    1935           0 :     wm.IsVertical()
    1936           0 :       ? wm.IsVerticalLR() ? verticalLR[aDirection] : verticalRL[aDirection]
    1937           0 :       : horizontal[aDirection];
    1938             : 
    1939           0 :   nsresult rv = MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount],
    1940           0 :                           eVisual);
    1941           0 :   if (NS_FAILED(rv)) {
    1942             :     // If we tried to do a line move, but couldn't move in the given direction,
    1943             :     // then we'll "promote" this to a line-edge move instead.
    1944           0 :     if (mapping.amounts[aAmount] == eSelectLine) {
    1945           0 :       rv = MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount + 1],
    1946           0 :                      eVisual);
    1947             :     }
    1948             :     // And if it was a next-word move that failed (which can happen when
    1949             :     // eat_space_to_next_word is true, see bug 1153237), then just move forward
    1950             :     // to the line-edge.
    1951           0 :     else if (mapping.amounts[aAmount] == eSelectWord &&
    1952           0 :              mapping.direction == eDirNext) {
    1953           0 :       rv = MoveCaret(eDirNext, aExtend, eSelectEndLine, eVisual);
    1954             :     }
    1955             :   }
    1956             : 
    1957           0 :   return rv;
    1958             : }
    1959             : 
    1960             : nsresult
    1961           0 : nsFrameSelection::CharacterMove(bool aForward, bool aExtend)
    1962             : {
    1963           0 :   return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectCluster,
    1964           0 :                    eUsePrefStyle);
    1965             : }
    1966             : 
    1967             : nsresult
    1968           0 : nsFrameSelection::CharacterExtendForDelete()
    1969             : {
    1970           0 :   return MoveCaret(eDirNext, true, eSelectCluster, eLogical);
    1971             : }
    1972             : 
    1973             : nsresult
    1974           0 : nsFrameSelection::CharacterExtendForBackspace()
    1975             : {
    1976           0 :   return MoveCaret(eDirPrevious, true, eSelectCharacter, eLogical);
    1977             : }
    1978             : 
    1979             : nsresult
    1980           0 : nsFrameSelection::WordMove(bool aForward, bool aExtend)
    1981             : {
    1982           0 :   return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectWord,
    1983           0 :                    eUsePrefStyle);
    1984             : }
    1985             : 
    1986             : nsresult
    1987           0 : nsFrameSelection::WordExtendForDelete(bool aForward)
    1988             : {
    1989           0 :   return MoveCaret(aForward ? eDirNext : eDirPrevious, true, eSelectWord,
    1990           0 :                    eLogical);
    1991             : }
    1992             : 
    1993             : nsresult
    1994           0 : nsFrameSelection::LineMove(bool aForward, bool aExtend)
    1995             : {
    1996           0 :   return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectLine,
    1997           0 :                    eUsePrefStyle);
    1998             : }
    1999             : 
    2000             : nsresult
    2001           0 : nsFrameSelection::IntraLineMove(bool aForward, bool aExtend)
    2002             : {
    2003           0 :   if (aForward) {
    2004           0 :     return MoveCaret(eDirNext, aExtend, eSelectEndLine, eLogical);
    2005             :   } else {
    2006           0 :     return MoveCaret(eDirPrevious, aExtend, eSelectBeginLine, eLogical);
    2007             :   }
    2008             : }
    2009             : 
    2010             : nsresult
    2011           0 : nsFrameSelection::SelectAll()
    2012             : {
    2013           0 :   nsCOMPtr<nsIContent> rootContent;
    2014           0 :   if (mLimiter)
    2015             :   {
    2016           0 :     rootContent = mLimiter;//addrefit
    2017             :   }
    2018           0 :   else if (mAncestorLimiter) {
    2019           0 :     rootContent = mAncestorLimiter;
    2020             :   }
    2021             :   else
    2022             :   {
    2023           0 :     NS_ENSURE_STATE(mShell);
    2024           0 :     nsIDocument *doc = mShell->GetDocument();
    2025           0 :     if (!doc)
    2026           0 :       return NS_ERROR_FAILURE;
    2027           0 :     rootContent = doc->GetRootElement();
    2028           0 :     if (!rootContent)
    2029           0 :       return NS_ERROR_FAILURE;
    2030             :   }
    2031           0 :   int32_t numChildren = rootContent->GetChildCount();
    2032           0 :   PostReason(nsISelectionListener::NO_REASON);
    2033           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2034           0 :   AutoPrepareFocusRange prep(mDomSelections[index], false, false);
    2035           0 :   return TakeFocus(rootContent, 0, numChildren, CARET_ASSOCIATE_BEFORE, false, false);
    2036             : }
    2037             : 
    2038             : //////////END FRAMESELECTION
    2039             : 
    2040             : void
    2041           7 : nsFrameSelection::StartBatchChanges()
    2042             : {
    2043           7 :   mBatching++;
    2044           7 : }
    2045             : 
    2046             : void
    2047           7 : nsFrameSelection::EndBatchChanges(int16_t aReason)
    2048             : {
    2049           7 :   mBatching--;
    2050           7 :   NS_ASSERTION(mBatching >=0,"Bad mBatching");
    2051             : 
    2052           7 :   if (mBatching == 0 && mChangesDuringBatching) {
    2053           3 :     int16_t postReason = PopReason() | aReason;
    2054           3 :     PostReason(postReason);
    2055           3 :     mChangesDuringBatching = false;
    2056             :     // Be aware, the Selection instance may be destroyed after this call.
    2057           3 :     NotifySelectionListeners(SelectionType::eNormal);
    2058             :   }
    2059           7 : }
    2060             : 
    2061             : 
    2062             : nsresult
    2063          35 : nsFrameSelection::NotifySelectionListeners(SelectionType aSelectionType)
    2064             : {
    2065          35 :   int8_t index = GetIndexFromSelectionType(aSelectionType);
    2066          35 :   if (index >=0 && mDomSelections[index])
    2067             :   {
    2068          70 :     RefPtr<Selection> selection = mDomSelections[index];
    2069          35 :     return selection->NotifySelectionListeners();
    2070             :   }
    2071           0 :   return NS_ERROR_FAILURE;
    2072             : }
    2073             : 
    2074             : // Start of Table Selection methods
    2075             : 
    2076           0 : static bool IsCell(nsIContent *aContent)
    2077             : {
    2078           0 :   return aContent->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th);
    2079             : }
    2080             : 
    2081             : nsITableCellLayout*
    2082           0 : nsFrameSelection::GetCellLayout(nsIContent *aCellContent) const
    2083             : {
    2084           0 :   NS_ENSURE_TRUE(mShell, nullptr);
    2085             :   nsITableCellLayout *cellLayoutObject =
    2086           0 :     do_QueryFrame(aCellContent->GetPrimaryFrame());
    2087           0 :   return cellLayoutObject;
    2088             : }
    2089             : 
    2090             : nsresult
    2091           0 : nsFrameSelection::ClearNormalSelection()
    2092             : {
    2093           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2094           0 :   if (!mDomSelections[index])
    2095           0 :     return NS_ERROR_NULL_POINTER;
    2096             : 
    2097           0 :   return mDomSelections[index]->RemoveAllRanges();
    2098             : }
    2099             : 
    2100             : static nsIContent*
    2101           0 : GetFirstSelectedContent(nsRange* aRange)
    2102             : {
    2103           0 :   if (!aRange) {
    2104           0 :     return nullptr;
    2105             :   }
    2106             : 
    2107           0 :   NS_PRECONDITION(aRange->GetStartContainer(), "Must have start parent!");
    2108           0 :   NS_PRECONDITION(aRange->GetStartContainer()->IsElement(),
    2109             :                   "Unexpected parent");
    2110             : 
    2111           0 :   return aRange->GetStartContainer()->GetChildAt(aRange->StartOffset());
    2112             : }
    2113             : 
    2114             : // Table selection support.
    2115             : // TODO: Separate table methods into a separate nsITableSelection interface
    2116             : nsresult
    2117           0 : nsFrameSelection::HandleTableSelection(nsINode* aParentContent,
    2118             :                                        int32_t aContentOffset,
    2119             :                                        int32_t aTarget,
    2120             :                                        WidgetMouseEvent* aMouseEvent)
    2121             : {
    2122           0 :   NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER);
    2123           0 :   NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER);
    2124             : 
    2125           0 :   if (mDragState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE))
    2126             :   {
    2127             :     // We were selecting cells and user drags mouse in table border or inbetween cells,
    2128             :     //  just do nothing
    2129           0 :       return NS_OK;
    2130             :   }
    2131             : 
    2132           0 :   nsresult result = NS_OK;
    2133             : 
    2134           0 :   nsIContent *childContent = aParentContent->GetChildAt(aContentOffset);
    2135             : 
    2136             :   // When doing table selection, always set the direction to next so
    2137             :   // we can be sure that anchorNode's offset always points to the
    2138             :   // selected cell
    2139           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2140           0 :   if (!mDomSelections[index])
    2141           0 :     return NS_ERROR_NULL_POINTER;
    2142             : 
    2143           0 :   mDomSelections[index]->SetDirection(eDirNext);
    2144             : 
    2145             :   // Stack-class to wrap all table selection changes in
    2146             :   //  BeginBatchChanges() / EndBatchChanges()
    2147           0 :   SelectionBatcher selectionBatcher(mDomSelections[index]);
    2148             : 
    2149             :   int32_t startRowIndex, startColIndex, curRowIndex, curColIndex;
    2150           0 :   if (mDragState && mDragSelectingCells)
    2151             :   {
    2152             :     // We are drag-selecting
    2153           0 :     if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE)
    2154             :     {
    2155             :       // If dragging in the same cell as last event, do nothing
    2156           0 :       if (mEndSelectedCell == childContent)
    2157           0 :         return NS_OK;
    2158             : 
    2159             : #ifdef DEBUG_TABLE_SELECTION
    2160             :       printf(" mStartSelectedCell = %p, mEndSelectedCell = %p, childContent = %p \n",
    2161             :              mStartSelectedCell.get(), mEndSelectedCell.get(), childContent);
    2162             : #endif
    2163             :       // aTarget can be any "cell mode",
    2164             :       //  so we can easily drag-select rows and columns
    2165             :       // Once we are in row or column mode,
    2166             :       //  we can drift into any cell to stay in that mode
    2167             :       //  even if aTarget = TABLESELECTION_CELL
    2168             : 
    2169           0 :       if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW ||
    2170           0 :           mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN)
    2171             :       {
    2172           0 :         if (mEndSelectedCell)
    2173             :         {
    2174             :           // Also check if cell is in same row/col
    2175           0 :           result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
    2176           0 :           if (NS_FAILED(result)) return result;
    2177           0 :           result = GetCellIndexes(childContent, curRowIndex, curColIndex);
    2178           0 :           if (NS_FAILED(result)) return result;
    2179             : 
    2180             : #ifdef DEBUG_TABLE_SELECTION
    2181             : printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex);
    2182             : #endif
    2183           0 :           if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
    2184           0 :               (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex))
    2185           0 :             return NS_OK;
    2186             :         }
    2187             : #ifdef DEBUG_TABLE_SELECTION
    2188             : printf(" Dragged into a new column or row\n");
    2189             : #endif
    2190             :         // Continue dragging row or column selection
    2191           0 :         return SelectRowOrColumn(childContent, mSelectingTableCellMode);
    2192             :       }
    2193           0 :       else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL)
    2194             :       {
    2195             : #ifdef DEBUG_TABLE_SELECTION
    2196             : printf("HandleTableSelection: Dragged into a new cell\n");
    2197             : #endif
    2198             :         // Trick for quick selection of rows and columns
    2199             :         // Hold down shift, then start selecting in one direction
    2200             :         // If next cell dragged into is in same row, select entire row,
    2201             :         //   if next cell is in same column, select entire column
    2202           0 :         if (mStartSelectedCell && aMouseEvent->IsShift())
    2203             :         {
    2204           0 :           result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
    2205           0 :           if (NS_FAILED(result)) return result;
    2206           0 :           result = GetCellIndexes(childContent, curRowIndex, curColIndex);
    2207           0 :           if (NS_FAILED(result)) return result;
    2208             : 
    2209           0 :           if (startRowIndex == curRowIndex ||
    2210           0 :               startColIndex == curColIndex)
    2211             :           {
    2212             :             // Force new selection block
    2213           0 :             mStartSelectedCell = nullptr;
    2214           0 :             mDomSelections[index]->RemoveAllRanges();
    2215             : 
    2216           0 :             if (startRowIndex == curRowIndex)
    2217           0 :               mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW;
    2218             :             else
    2219           0 :               mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN;
    2220             : 
    2221           0 :             return SelectRowOrColumn(childContent, mSelectingTableCellMode);
    2222             :           }
    2223             :         }
    2224             : 
    2225             :         // Reselect block of cells to new end location
    2226           0 :         return SelectBlockOfCells(mStartSelectedCell, childContent);
    2227             :       }
    2228             :     }
    2229             :     // Do nothing if dragging in table, but outside a cell
    2230           0 :     return NS_OK;
    2231             :   }
    2232             :   else
    2233             :   {
    2234             :     // Not dragging  -- mouse event is down or up
    2235           0 :     if (mDragState)
    2236             :     {
    2237             : #ifdef DEBUG_TABLE_SELECTION
    2238             : printf("HandleTableSelection: Mouse down event\n");
    2239             : #endif
    2240             :       // Clear cell we stored in mouse-down
    2241           0 :       mUnselectCellOnMouseUp = nullptr;
    2242             : 
    2243           0 :       if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL)
    2244             :       {
    2245           0 :         bool isSelected = false;
    2246             : 
    2247             :         // Check if we have other selected cells
    2248             :         nsIContent* previousCellNode =
    2249           0 :           GetFirstSelectedContent(GetFirstCellRange());
    2250           0 :         if (previousCellNode)
    2251             :         {
    2252             :           // We have at least 1 other selected cell
    2253             : 
    2254             :           // Check if new cell is already selected
    2255           0 :           nsIFrame  *cellFrame = childContent->GetPrimaryFrame();
    2256           0 :           if (!cellFrame) return NS_ERROR_NULL_POINTER;
    2257           0 :           isSelected = cellFrame->IsSelected();
    2258             :         }
    2259             :         else
    2260             :         {
    2261             :           // No cells selected -- remove non-cell selection
    2262           0 :           mDomSelections[index]->RemoveAllRanges();
    2263             :         }
    2264           0 :         mDragSelectingCells = true;    // Signal to start drag-cell-selection
    2265           0 :         mSelectingTableCellMode = aTarget;
    2266             :         // Set start for new drag-selection block (not appended)
    2267           0 :         mStartSelectedCell = childContent;
    2268             :         // The initial block end is same as the start
    2269           0 :         mEndSelectedCell = childContent;
    2270             : 
    2271           0 :         if (isSelected)
    2272             :         {
    2273             :           // Remember this cell to (possibly) unselect it on mouseup
    2274           0 :           mUnselectCellOnMouseUp = childContent;
    2275             : #ifdef DEBUG_TABLE_SELECTION
    2276             : printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
    2277             : #endif
    2278             :         }
    2279             :         else
    2280             :         {
    2281             :           // Select an unselected cell
    2282             :           // but first remove existing selection if not in same table
    2283           0 :           if (previousCellNode &&
    2284           0 :               !IsInSameTable(previousCellNode, childContent))
    2285             :           {
    2286           0 :             mDomSelections[index]->RemoveAllRanges();
    2287             :             // Reset selection mode that is cleared in RemoveAllRanges
    2288           0 :             mSelectingTableCellMode = aTarget;
    2289             :           }
    2290             : 
    2291           0 :           return SelectCellElement(childContent);
    2292             :         }
    2293             : 
    2294           0 :         return NS_OK;
    2295             :       }
    2296           0 :       else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE)
    2297             :       {
    2298             :         //TODO: We currently select entire table when clicked between cells,
    2299             :         //  should we restrict to only around border?
    2300             :         //  *** How do we get location data for cell and click?
    2301           0 :         mDragSelectingCells = false;
    2302           0 :         mStartSelectedCell = nullptr;
    2303           0 :         mEndSelectedCell = nullptr;
    2304             : 
    2305             :         // Remove existing selection and select the table
    2306           0 :         mDomSelections[index]->RemoveAllRanges();
    2307           0 :         return CreateAndAddRange(aParentContent, aContentOffset);
    2308             :       }
    2309           0 :       else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
    2310             :       {
    2311             : #ifdef DEBUG_TABLE_SELECTION
    2312             : printf("aTarget == %d\n", aTarget);
    2313             : #endif
    2314             : 
    2315             :         // Start drag-selecting mode so multiple rows/cols can be selected
    2316             :         // Note: Currently, nsFrame::GetDataForTableSelection
    2317             :         //       will never call us for row or column selection on mouse down
    2318           0 :         mDragSelectingCells = true;
    2319             : 
    2320             :         // Force new selection block
    2321           0 :         mStartSelectedCell = nullptr;
    2322           0 :         mDomSelections[index]->RemoveAllRanges();
    2323             :         // Always do this AFTER RemoveAllRanges
    2324           0 :         mSelectingTableCellMode = aTarget;
    2325           0 :         return SelectRowOrColumn(childContent, aTarget);
    2326             :       }
    2327             :     }
    2328             :     else
    2329             :     {
    2330             : #ifdef DEBUG_TABLE_SELECTION
    2331             :       printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%p\n",
    2332             :              mDragSelectingCells, mStartSelectedCell.get());
    2333             : #endif
    2334             :       // First check if we are extending a block selection
    2335             :       int32_t rangeCount;
    2336           0 :       result = mDomSelections[index]->GetRangeCount(&rangeCount);
    2337           0 :       if (NS_FAILED(result))
    2338           0 :         return result;
    2339             : 
    2340           0 :       if (rangeCount > 0 && aMouseEvent->IsShift() &&
    2341           0 :           mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
    2342             :       {
    2343             :         // Shift key is down: append a block selection
    2344           0 :         mDragSelectingCells = false;
    2345           0 :         return SelectBlockOfCells(mAppendStartSelectedCell, childContent);
    2346             :       }
    2347             : 
    2348           0 :       if (mDragSelectingCells)
    2349           0 :         mAppendStartSelectedCell = mStartSelectedCell;
    2350             : 
    2351           0 :       mDragSelectingCells = false;
    2352           0 :       mStartSelectedCell = nullptr;
    2353           0 :       mEndSelectedCell = nullptr;
    2354             : 
    2355             :       // Any other mouseup actions require that Ctrl or Cmd key is pressed
    2356             :       //  else stop table selection mode
    2357           0 :       bool doMouseUpAction = false;
    2358             : #ifdef XP_MACOSX
    2359             :       doMouseUpAction = aMouseEvent->IsMeta();
    2360             : #else
    2361           0 :       doMouseUpAction = aMouseEvent->IsControl();
    2362             : #endif
    2363           0 :       if (!doMouseUpAction)
    2364             :       {
    2365             : #ifdef DEBUG_TABLE_SELECTION
    2366             :         printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%p\n",
    2367             :                mAppendStartSelectedCell.get());
    2368             : #endif
    2369           0 :         return NS_OK;
    2370             :       }
    2371             :       // Unselect a cell only if it wasn't
    2372             :       //  just selected on mousedown
    2373           0 :       if( childContent == mUnselectCellOnMouseUp)
    2374             :       {
    2375             :         // Scan ranges to find the cell to unselect (the selection range to remove)
    2376             :         // XXXbz it's really weird that this lives outside the loop, so once we
    2377             :         // find one we keep looking at it even if we find no more cells...
    2378           0 :         nsINode* previousCellParent = nullptr;
    2379             : #ifdef DEBUG_TABLE_SELECTION
    2380             : printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
    2381             : #endif
    2382           0 :         for( int32_t i = 0; i < rangeCount; i++)
    2383             :         {
    2384             :           // Strong reference, because sometimes we want to remove
    2385             :           // this range, and then we might be the only owner.
    2386           0 :           RefPtr<nsRange> range = mDomSelections[index]->GetRangeAt(i);
    2387           0 :           if (!range) return NS_ERROR_NULL_POINTER;
    2388             : 
    2389           0 :           nsINode* container = range->GetStartContainer();
    2390           0 :           if (!container) {
    2391           0 :             return NS_ERROR_NULL_POINTER;
    2392             :           }
    2393             : 
    2394           0 :           int32_t offset = range->StartOffset();
    2395             :           // Be sure previous selection is a table cell
    2396           0 :           nsIContent* child = container->GetChildAt(offset);
    2397           0 :           if (child && IsCell(child)) {
    2398           0 :             previousCellParent = container;
    2399             :           }
    2400             : 
    2401             :           // We're done if we didn't find parent of a previously-selected cell
    2402           0 :           if (!previousCellParent) break;
    2403             : 
    2404           0 :           if (previousCellParent == aParentContent && offset == aContentOffset)
    2405             :           {
    2406             :             // Cell is already selected
    2407           0 :             if (rangeCount == 1)
    2408             :             {
    2409             : #ifdef DEBUG_TABLE_SELECTION
    2410             : printf("HandleTableSelection: Unselecting single selected cell\n");
    2411             : #endif
    2412             :               // This was the only cell selected.
    2413             :               // Collapse to "normal" selection inside the cell
    2414           0 :               mStartSelectedCell = nullptr;
    2415           0 :               mEndSelectedCell = nullptr;
    2416           0 :               mAppendStartSelectedCell = nullptr;
    2417             :               //TODO: We need a "Collapse to just before deepest child" routine
    2418             :               // Even better, should we collapse to just after the LAST deepest child
    2419             :               //  (i.e., at the end of the cell's contents)?
    2420           0 :               return mDomSelections[index]->Collapse(childContent, 0);
    2421             :             }
    2422             : #ifdef DEBUG_TABLE_SELECTION
    2423             : printf("HandleTableSelection: Removing cell from multi-cell selection\n");
    2424             : #endif
    2425             :             // Unselecting the start of previous block
    2426             :             // XXX What do we use now!
    2427           0 :             if (childContent == mAppendStartSelectedCell)
    2428           0 :                mAppendStartSelectedCell = nullptr;
    2429             : 
    2430             :             // Deselect cell by removing its range from selection
    2431           0 :             return mDomSelections[index]->RemoveRange(range);
    2432             :           }
    2433             :         }
    2434           0 :         mUnselectCellOnMouseUp = nullptr;
    2435             :       }
    2436             :     }
    2437             :   }
    2438           0 :   return result;
    2439             : }
    2440             : 
    2441             : nsresult
    2442           0 : nsFrameSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell)
    2443             : {
    2444           0 :   NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER);
    2445           0 :   NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER);
    2446           0 :   mEndSelectedCell = aEndCell;
    2447             : 
    2448           0 :   nsresult result = NS_OK;
    2449             : 
    2450             :   // If new end cell is in a different table, do nothing
    2451           0 :   nsIContent* table = IsInSameTable(aStartCell, aEndCell);
    2452           0 :   if (!table) {
    2453           0 :     return NS_OK;
    2454             :   }
    2455             : 
    2456             :   // Get starting and ending cells' location in the cellmap
    2457             :   int32_t startRowIndex, startColIndex, endRowIndex, endColIndex;
    2458           0 :   result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
    2459           0 :   if(NS_FAILED(result)) return result;
    2460           0 :   result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
    2461           0 :   if(NS_FAILED(result)) return result;
    2462             : 
    2463           0 :   if (mDragSelectingCells)
    2464             :   {
    2465             :     // Drag selecting: remove selected cells outside of new block limits
    2466             :     UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex,
    2467           0 :                   true);
    2468             :   }
    2469             : 
    2470             :   // Note that we select block in the direction of user's mouse dragging,
    2471             :   //  which means start cell may be after the end cell in either row or column
    2472           0 :   return AddCellsToSelection(table, startRowIndex, startColIndex,
    2473           0 :                              endRowIndex, endColIndex);
    2474             : }
    2475             : 
    2476             : nsresult
    2477           0 : nsFrameSelection::UnselectCells(nsIContent *aTableContent,
    2478             :                                 int32_t aStartRowIndex,
    2479             :                                 int32_t aStartColumnIndex,
    2480             :                                 int32_t aEndRowIndex,
    2481             :                                 int32_t aEndColumnIndex,
    2482             :                                 bool aRemoveOutsideOfCellRange)
    2483             : {
    2484           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2485           0 :   if (!mDomSelections[index])
    2486           0 :     return NS_ERROR_NULL_POINTER;
    2487             : 
    2488           0 :   nsTableWrapperFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
    2489           0 :   if (!tableFrame)
    2490           0 :     return NS_ERROR_FAILURE;
    2491             : 
    2492           0 :   int32_t minRowIndex = std::min(aStartRowIndex, aEndRowIndex);
    2493           0 :   int32_t maxRowIndex = std::max(aStartRowIndex, aEndRowIndex);
    2494           0 :   int32_t minColIndex = std::min(aStartColumnIndex, aEndColumnIndex);
    2495           0 :   int32_t maxColIndex = std::max(aStartColumnIndex, aEndColumnIndex);
    2496             : 
    2497             :   // Strong reference because we sometimes remove the range
    2498           0 :   RefPtr<nsRange> range = GetFirstCellRange();
    2499           0 :   nsIContent* cellNode = GetFirstSelectedContent(range);
    2500           0 :   NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
    2501             : 
    2502             :   int32_t curRowIndex, curColIndex;
    2503           0 :   while (cellNode)
    2504             :   {
    2505           0 :     nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
    2506           0 :     if (NS_FAILED(result))
    2507           0 :       return result;
    2508             : 
    2509             : #ifdef DEBUG_TABLE_SELECTION
    2510             :     if (!range)
    2511             :       printf("RemoveCellsToSelection -- range is null\n");
    2512             : #endif
    2513             : 
    2514           0 :     if (range) {
    2515           0 :       if (aRemoveOutsideOfCellRange) {
    2516           0 :         if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
    2517           0 :             curColIndex < minColIndex || curColIndex > maxColIndex) {
    2518             : 
    2519           0 :           mDomSelections[index]->RemoveRange(range);
    2520             :           // Since we've removed the range, decrement pointer to next range
    2521           0 :           mSelectedCellIndex--;
    2522             :         }
    2523             : 
    2524             :       } else {
    2525             :         // Remove cell from selection if it belongs to the given cells range or
    2526             :         // it is spanned onto the cells range.
    2527             :         nsTableCellFrame* cellFrame =
    2528           0 :           tableFrame->GetCellFrameAt(curRowIndex, curColIndex);
    2529             : 
    2530             :         int32_t origRowIndex, origColIndex;
    2531           0 :         cellFrame->GetRowIndex(origRowIndex);
    2532           0 :         cellFrame->GetColIndex(origColIndex);
    2533             :         uint32_t actualRowSpan =
    2534           0 :           tableFrame->GetEffectiveRowSpanAt(origRowIndex, origColIndex);
    2535             :         uint32_t actualColSpan =
    2536           0 :           tableFrame->GetEffectiveColSpanAt(curRowIndex, curColIndex);
    2537           0 :         if (origRowIndex <= maxRowIndex && maxRowIndex >= 0 &&
    2538           0 :             origRowIndex + actualRowSpan - 1 >= static_cast<uint32_t>(minRowIndex) &&
    2539           0 :             origColIndex <= maxColIndex && maxColIndex >= 0 &&
    2540           0 :             origColIndex + actualColSpan - 1 >= static_cast<uint32_t>(minColIndex)) {
    2541             : 
    2542           0 :           mDomSelections[index]->RemoveRange(range);
    2543             :           // Since we've removed the range, decrement pointer to next range
    2544           0 :           mSelectedCellIndex--;
    2545             :         }
    2546             :       }
    2547             :     }
    2548             : 
    2549           0 :     range = GetNextCellRange();
    2550           0 :     cellNode = GetFirstSelectedContent(range);
    2551           0 :     NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
    2552             :   }
    2553             : 
    2554           0 :   return NS_OK;
    2555             : }
    2556             : 
    2557             : nsresult
    2558           0 : nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
    2559             :                                       int32_t aStartRowIndex,
    2560             :                                       int32_t aStartColumnIndex,
    2561             :                                       int32_t aEndRowIndex,
    2562             :                                       int32_t aEndColumnIndex)
    2563             : {
    2564           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2565           0 :   if (!mDomSelections[index])
    2566           0 :     return NS_ERROR_NULL_POINTER;
    2567             : 
    2568           0 :   nsTableWrapperFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
    2569           0 :   if (!tableFrame) // Check that |table| is a table.
    2570           0 :     return NS_ERROR_FAILURE;
    2571             : 
    2572           0 :   nsresult result = NS_OK;
    2573           0 :   int32_t row = aStartRowIndex;
    2574             :   while(true)
    2575             :   {
    2576           0 :     int32_t col = aStartColumnIndex;
    2577             :     while(true)
    2578             :     {
    2579           0 :       nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(row, col);
    2580             : 
    2581             :       // Skip cells that are spanned from previous locations or are already selected
    2582           0 :       if (cellFrame) {
    2583             :         int32_t origRow, origCol;
    2584           0 :         cellFrame->GetRowIndex(origRow);
    2585           0 :         cellFrame->GetColIndex(origCol);
    2586           0 :         if (origRow == row && origCol == col && !cellFrame->IsSelected()) {
    2587           0 :           result = SelectCellElement(cellFrame->GetContent());
    2588           0 :           if (NS_FAILED(result)) return result;
    2589             :         }
    2590             :       }
    2591             :       // Done when we reach end column
    2592           0 :       if (col == aEndColumnIndex) break;
    2593             : 
    2594           0 :       if (aStartColumnIndex < aEndColumnIndex)
    2595           0 :         col ++;
    2596             :       else
    2597           0 :         col--;
    2598           0 :     }
    2599           0 :     if (row == aEndRowIndex) break;
    2600             : 
    2601           0 :     if (aStartRowIndex < aEndRowIndex)
    2602           0 :       row++;
    2603             :     else
    2604           0 :       row--;
    2605           0 :   }
    2606           0 :   return result;
    2607             : }
    2608             : 
    2609             : nsresult
    2610           0 : nsFrameSelection::RemoveCellsFromSelection(nsIContent *aTable,
    2611             :                                            int32_t aStartRowIndex,
    2612             :                                            int32_t aStartColumnIndex,
    2613             :                                            int32_t aEndRowIndex,
    2614             :                                            int32_t aEndColumnIndex)
    2615             : {
    2616             :   return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
    2617           0 :                        aEndRowIndex, aEndColumnIndex, false);
    2618             : }
    2619             : 
    2620             : nsresult
    2621           0 : nsFrameSelection::RestrictCellsToSelection(nsIContent *aTable,
    2622             :                                            int32_t aStartRowIndex,
    2623             :                                            int32_t aStartColumnIndex,
    2624             :                                            int32_t aEndRowIndex,
    2625             :                                            int32_t aEndColumnIndex)
    2626             : {
    2627             :   return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
    2628           0 :                        aEndRowIndex, aEndColumnIndex, true);
    2629             : }
    2630             : 
    2631             : nsresult
    2632           0 : nsFrameSelection::SelectRowOrColumn(nsIContent *aCellContent, uint32_t aTarget)
    2633             : {
    2634           0 :   if (!aCellContent) return NS_ERROR_NULL_POINTER;
    2635             : 
    2636           0 :   nsIContent* table = GetParentTable(aCellContent);
    2637           0 :   if (!table) return NS_ERROR_NULL_POINTER;
    2638             : 
    2639             :   // Get table and cell layout interfaces to access
    2640             :   // cell data based on cellmap location
    2641             :   // Frames are not ref counted, so don't use an nsCOMPtr
    2642           0 :   nsTableWrapperFrame* tableFrame = do_QueryFrame(table->GetPrimaryFrame());
    2643           0 :   if (!tableFrame) return NS_ERROR_FAILURE;
    2644           0 :   nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
    2645           0 :   if (!cellLayout) return NS_ERROR_FAILURE;
    2646             : 
    2647             :   // Get location of target cell:
    2648             :   int32_t rowIndex, colIndex;
    2649           0 :   nsresult result = cellLayout->GetCellIndexes(rowIndex, colIndex);
    2650           0 :   if (NS_FAILED(result)) return result;
    2651             : 
    2652             :   // Be sure we start at proper beginning
    2653             :   // (This allows us to select row or col given ANY cell!)
    2654           0 :   if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
    2655           0 :     colIndex = 0;
    2656           0 :   if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
    2657           0 :     rowIndex = 0;
    2658             : 
    2659           0 :   nsCOMPtr<nsIContent> firstCell, lastCell;
    2660             :   while (true) {
    2661             :     // Loop through all cells in column or row to find first and last
    2662             :     nsCOMPtr<nsIContent> curCellContent =
    2663           0 :       tableFrame->GetCellAt(rowIndex, colIndex);
    2664           0 :     if (!curCellContent)
    2665           0 :       break;
    2666             : 
    2667           0 :     if (!firstCell)
    2668           0 :       firstCell = curCellContent;
    2669             : 
    2670           0 :     lastCell = curCellContent.forget();
    2671             : 
    2672             :     // Move to next cell in cellmap, skipping spanned locations
    2673           0 :     if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
    2674           0 :       colIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
    2675             :     else
    2676           0 :       rowIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
    2677           0 :   }
    2678             : 
    2679             :   // Use SelectBlockOfCells:
    2680             :   // This will replace existing selection,
    2681             :   //  but allow unselecting by dragging out of selected region
    2682           0 :   if (firstCell && lastCell)
    2683             :   {
    2684           0 :     if (!mStartSelectedCell)
    2685             :     {
    2686             :       // We are starting a new block, so select the first cell
    2687           0 :       result = SelectCellElement(firstCell);
    2688           0 :       if (NS_FAILED(result)) return result;
    2689           0 :       mStartSelectedCell = firstCell;
    2690             :     }
    2691           0 :     nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
    2692           0 :     result = SelectBlockOfCells(mStartSelectedCell, lastCellContent);
    2693             : 
    2694             :     // This gets set to the cell at end of row/col,
    2695             :     //   but we need it to be the cell under cursor
    2696           0 :     mEndSelectedCell = aCellContent;
    2697           0 :     return result;
    2698             :   }
    2699             : 
    2700             : #if 0
    2701             : // This is a more efficient strategy that appends row to current selection,
    2702             : //  but doesn't allow dragging OFF of an existing selection to unselect!
    2703             :   do {
    2704             :     // Loop through all cells in column or row
    2705             :     result = tableLayout->GetCellDataAt(rowIndex, colIndex,
    2706             :                                         getter_AddRefs(cellElement),
    2707             :                                         curRowIndex, curColIndex,
    2708             :                                         rowSpan, colSpan,
    2709             :                                         actualRowSpan, actualColSpan,
    2710             :                                         isSelected);
    2711             :     if (NS_FAILED(result)) return result;
    2712             :     // We're done when cell is not found
    2713             :     if (!cellElement) break;
    2714             : 
    2715             : 
    2716             :     // Check spans else we infinitely loop
    2717             :     NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
    2718             :     NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
    2719             : 
    2720             :     // Skip cells that are already selected or span from outside our region
    2721             :     if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
    2722             :     {
    2723             :       result = SelectCellElement(cellElement);
    2724             :       if (NS_FAILED(result)) return result;
    2725             :     }
    2726             :     // Move to next row or column in cellmap, skipping spanned locations
    2727             :     if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
    2728             :       colIndex += actualColSpan;
    2729             :     else
    2730             :       rowIndex += actualRowSpan;
    2731             :   }
    2732             :   while (cellElement);
    2733             : #endif
    2734             : 
    2735           0 :   return NS_OK;
    2736             : }
    2737             : 
    2738             : nsIContent*
    2739           0 : nsFrameSelection::GetFirstCellNodeInRange(nsRange *aRange) const
    2740             : {
    2741           0 :   if (!aRange) return nullptr;
    2742             : 
    2743           0 :   nsINode* startContainer = aRange->GetStartContainer();
    2744           0 :   if (!startContainer) {
    2745           0 :     return nullptr;
    2746             :   }
    2747             : 
    2748           0 :   int32_t offset = aRange->StartOffset();
    2749             : 
    2750           0 :   nsIContent* childContent = startContainer->GetChildAt(offset);
    2751           0 :   if (!childContent)
    2752           0 :     return nullptr;
    2753             :   // Don't return node if not a cell
    2754           0 :   if (!IsCell(childContent))
    2755           0 :     return nullptr;
    2756             : 
    2757           0 :   return childContent;
    2758             : }
    2759             : 
    2760             : nsRange*
    2761           0 : nsFrameSelection::GetFirstCellRange()
    2762             : {
    2763           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2764           0 :   if (!mDomSelections[index])
    2765           0 :     return nullptr;
    2766             : 
    2767           0 :   nsRange* firstRange = mDomSelections[index]->GetRangeAt(0);
    2768           0 :   if (!GetFirstCellNodeInRange(firstRange)) {
    2769           0 :     return nullptr;
    2770             :   }
    2771             : 
    2772             :   // Setup for next cell
    2773           0 :   mSelectedCellIndex = 1;
    2774             : 
    2775           0 :   return firstRange;
    2776             : }
    2777             : 
    2778             : nsRange*
    2779           0 : nsFrameSelection::GetNextCellRange()
    2780             : {
    2781           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2782           0 :   if (!mDomSelections[index])
    2783           0 :     return nullptr;
    2784             : 
    2785           0 :   nsRange* range = mDomSelections[index]->GetRangeAt(mSelectedCellIndex);
    2786             : 
    2787             :   // Get first node in next range of selection - test if it's a cell
    2788           0 :   if (!GetFirstCellNodeInRange(range)) {
    2789           0 :     return nullptr;
    2790             :   }
    2791             : 
    2792             :   // Setup for next cell
    2793           0 :   mSelectedCellIndex++;
    2794             : 
    2795           0 :   return range;
    2796             : }
    2797             : 
    2798             : nsresult
    2799           0 : nsFrameSelection::GetCellIndexes(nsIContent *aCell,
    2800             :                                  int32_t    &aRowIndex,
    2801             :                                  int32_t    &aColIndex)
    2802             : {
    2803           0 :   if (!aCell) return NS_ERROR_NULL_POINTER;
    2804             : 
    2805           0 :   aColIndex=0; // initialize out params
    2806           0 :   aRowIndex=0;
    2807             : 
    2808           0 :   nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
    2809           0 :   if (!cellLayoutObject)  return NS_ERROR_FAILURE;
    2810           0 :   return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
    2811             : }
    2812             : 
    2813             : nsIContent*
    2814           0 : nsFrameSelection::IsInSameTable(nsIContent  *aContent1,
    2815             :                                 nsIContent  *aContent2) const
    2816             : {
    2817           0 :   if (!aContent1 || !aContent2) return nullptr;
    2818             : 
    2819           0 :   nsIContent* tableNode1 = GetParentTable(aContent1);
    2820           0 :   nsIContent* tableNode2 = GetParentTable(aContent2);
    2821             : 
    2822             :   // Must be in the same table.  Note that we want to return false for
    2823             :   // the test if both tables are null.
    2824           0 :   return (tableNode1 == tableNode2) ? tableNode1 : nullptr;
    2825             : }
    2826             : 
    2827             : nsIContent*
    2828           0 : nsFrameSelection::GetParentTable(nsIContent *aCell) const
    2829             : {
    2830           0 :   if (!aCell) {
    2831           0 :     return nullptr;
    2832             :   }
    2833             : 
    2834           0 :   for (nsIContent* parent = aCell->GetParent(); parent;
    2835           0 :        parent = parent->GetParent()) {
    2836           0 :     if (parent->IsHTMLElement(nsGkAtoms::table)) {
    2837           0 :       return parent;
    2838             :     }
    2839             :   }
    2840             : 
    2841           0 :   return nullptr;
    2842             : }
    2843             : 
    2844             : nsresult
    2845           0 : nsFrameSelection::SelectCellElement(nsIContent *aCellElement)
    2846             : {
    2847           0 :   nsIContent *parent = aCellElement->GetParent();
    2848             : 
    2849             :   // Get child offset
    2850           0 :   int32_t offset = parent->IndexOf(aCellElement);
    2851             : 
    2852           0 :   return CreateAndAddRange(parent, offset);
    2853             : }
    2854             : 
    2855             : nsresult
    2856           0 : nsFrameSelection::CreateAndAddRange(nsINode* aContainer, int32_t aOffset)
    2857             : {
    2858           0 :   if (!aContainer) {
    2859           0 :     return NS_ERROR_NULL_POINTER;
    2860             :   }
    2861             : 
    2862           0 :   RefPtr<nsRange> range = new nsRange(aContainer);
    2863             : 
    2864             :   // Set range around child at given offset
    2865           0 :   nsresult rv = range->SetStartAndEnd(aContainer, aOffset,
    2866           0 :                                       aContainer, aOffset + 1);
    2867           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2868           0 :     return rv;
    2869             :   }
    2870             : 
    2871           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2872           0 :   if (!mDomSelections[index])
    2873           0 :     return NS_ERROR_NULL_POINTER;
    2874             : 
    2875           0 :   return mDomSelections[index]->AddRange(range);
    2876             : }
    2877             : 
    2878             : // End of Table Selection
    2879             : 
    2880             : void
    2881           0 : nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter)
    2882             : {
    2883           0 :   if (mAncestorLimiter != aLimiter) {
    2884           0 :     mAncestorLimiter = aLimiter;
    2885           0 :     int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2886           0 :     if (!mDomSelections[index])
    2887           0 :       return;
    2888             : 
    2889           0 :     if (!IsValidSelectionPoint(this, mDomSelections[index]->GetFocusNode())) {
    2890           0 :       ClearNormalSelection();
    2891           0 :       if (mAncestorLimiter) {
    2892           0 :         PostReason(nsISelectionListener::NO_REASON);
    2893           0 :         TakeFocus(mAncestorLimiter, 0, 0, CARET_ASSOCIATE_BEFORE, false, false);
    2894             :       }
    2895             :     }
    2896             :   }
    2897             : }
    2898             : 
    2899             : nsresult
    2900           0 : nsFrameSelection::DeleteFromDocument()
    2901             : {
    2902             :   nsresult res;
    2903             : 
    2904             :   // If we're already collapsed, then we do nothing (bug 719503).
    2905             :   bool isCollapsed;
    2906           0 :   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2907           0 :   if (!mDomSelections[index])
    2908           0 :     return NS_ERROR_NULL_POINTER;
    2909             : 
    2910           0 :   mDomSelections[index]->GetIsCollapsed( &isCollapsed);
    2911           0 :   if (isCollapsed)
    2912             :   {
    2913           0 :     return NS_OK;
    2914             :   }
    2915             : 
    2916           0 :   RefPtr<Selection> selection = mDomSelections[index];
    2917           0 :   for (uint32_t rangeIdx = 0; rangeIdx < selection->RangeCount(); ++rangeIdx) {
    2918           0 :     RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
    2919           0 :     res = range->DeleteContents();
    2920           0 :     if (NS_FAILED(res))
    2921           0 :       return res;
    2922             :   }
    2923             : 
    2924             :   // Collapse to the new location.
    2925             :   // If we deleted one character, then we move back one element.
    2926             :   // FIXME  We don't know how to do this past frame boundaries yet.
    2927           0 :   if (isCollapsed)
    2928           0 :     mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset()-1);
    2929           0 :   else if (mDomSelections[index]->AnchorOffset() > 0)
    2930           0 :     mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset());
    2931             : #ifdef DEBUG
    2932             :   else
    2933           0 :     printf("Don't know how to set selection back past frame boundary\n");
    2934             : #endif
    2935             : 
    2936           0 :   return NS_OK;
    2937             : }
    2938             : 
    2939             : void
    2940           0 : nsFrameSelection::SetDelayedCaretData(WidgetMouseEvent* aMouseEvent)
    2941             : {
    2942           0 :   if (aMouseEvent) {
    2943           0 :     mDelayedMouseEventValid = true;
    2944           0 :     mDelayedMouseEventIsShift = aMouseEvent->IsShift();
    2945           0 :     mDelayedMouseEventClickCount = aMouseEvent->mClickCount;
    2946             :   } else {
    2947           0 :     mDelayedMouseEventValid = false;
    2948             :   }
    2949           0 : }
    2950             : 
    2951             : void
    2952           6 : nsFrameSelection::DisconnectFromPresShell()
    2953             : {
    2954          12 :   RefPtr<AccessibleCaretEventHub> eventHub = mShell->GetAccessibleCaretEventHub();
    2955           6 :   if (eventHub) {
    2956           0 :     int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
    2957           0 :     mDomSelections[index]->RemoveSelectionListener(eventHub);
    2958             :   }
    2959             : 
    2960           6 :   StopAutoScrollTimer();
    2961          66 :   for (size_t i = 0; i < kPresentSelectionTypeCount; i++) {
    2962          60 :     mDomSelections[i]->Clear(nullptr);
    2963             :   }
    2964           6 :   mShell = nullptr;
    2965           6 : }
    2966             : 
    2967             : /**
    2968             :  * See Bug 1288453.
    2969             :  *
    2970             :  * Update the selection cache on repaint to handle when a pre-existing
    2971             :  * selection becomes active aka the current selection.
    2972             :  *
    2973             :  * 1. Change the current selection by click n dragging another selection.
    2974             :  *   - Make a selection on content page. Make a selection in a text editor.
    2975             :  *   - You can click n drag the content selection to make it active again.
    2976             :  * 2. Change the current selection when switching to a tab with a selection.
    2977             :  *   - Make selection in tab.
    2978             :  *   - Switching tabs will make its respective selection active.
    2979             :  *
    2980             :  * Therefore, we only update the selection cache on a repaint
    2981             :  * if the current selection being repainted is not an empty selection.
    2982             :  *
    2983             :  * If the current selection is empty. The current selection cache
    2984             :  * would be cleared by nsAutoCopyListener::NotifySelectionChanged.
    2985             :  */
    2986             : nsresult
    2987           0 : nsFrameSelection::UpdateSelectionCacheOnRepaintSelection(Selection* aSel)
    2988             : {
    2989           0 :   nsIPresShell* ps = aSel->GetPresShell();
    2990           0 :   if (!ps) {
    2991           0 :     return NS_OK;
    2992             :   }
    2993           0 :   nsCOMPtr<nsIDocument> aDoc = ps->GetDocument();
    2994             : 
    2995             :   bool collapsed;
    2996           0 :   if (aDoc && aSel &&
    2997           0 :       NS_SUCCEEDED(aSel->GetIsCollapsed(&collapsed)) && !collapsed) {
    2998           0 :     return nsCopySupport::HTMLCopy(aSel, aDoc,
    2999           0 :                                    nsIClipboard::kSelectionCache, false);
    3000             :   }
    3001             : 
    3002           0 :   return NS_OK;
    3003             : }
    3004             : 
    3005             : // nsAutoCopyListener
    3006             : 
    3007             : nsAutoCopyListener* nsAutoCopyListener::sInstance = nullptr;
    3008             : 
    3009         176 : NS_IMPL_ISUPPORTS(nsAutoCopyListener, nsISelectionListener)
    3010             : 
    3011             : /*
    3012             :  * What we do now:
    3013             :  * On every selection change, we copy to the clipboard anew, creating a
    3014             :  * HTML buffer, a transferable, an nsISupportsString and
    3015             :  * a huge mess every time.  This is basically what nsPresShell::DoCopy does
    3016             :  * to move the selection into the clipboard for Edit->Copy.
    3017             :  *
    3018             :  * What we should do, to make our end of the deal faster:
    3019             :  * Create a singleton transferable with our own magic converter.  When selection
    3020             :  * changes (use a quick cache to detect ``real'' changes), we put the new
    3021             :  * nsISelection in the transferable.  Our magic converter will take care of
    3022             :  * transferable->whatever-other-format when the time comes to actually
    3023             :  * hand over the clipboard contents.
    3024             :  *
    3025             :  * Other issues:
    3026             :  * - which X clipboard should we populate?
    3027             :  * - should we use a different one than Edit->Copy, so that inadvertant
    3028             :  *   selections (or simple clicks, which currently cause a selection
    3029             :  *   notification, regardless of if they're in the document which currently has
    3030             :  *   selection!) don't lose the contents of the ``application''?  Or should we
    3031             :  *   just put some intelligence in the ``is this a real selection?'' code to
    3032             :  *   protect our selection against clicks in other documents that don't create
    3033             :  *   selections?
    3034             :  * - maybe we should just never clear the X clipboard?  That would make this
    3035             :  *   problem just go away, which is very tempting.
    3036             :  *
    3037             :  * On macOS,
    3038             :  * nsIClipboard::kSelectionCache is the flag for current selection cache.
    3039             :  * Set the current selection cache on the parent process in
    3040             :  * widget cocoa nsClipboard whenever selection changes.
    3041             :  */
    3042             : 
    3043             : NS_IMETHODIMP
    3044          23 : nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc,
    3045             :                                            nsISelection *aSel, int16_t aReason)
    3046             : {
    3047          23 :   if (mCachedClipboard == nsIClipboard::kSelectionCache) {
    3048           0 :     nsFocusManager* fm = nsFocusManager::GetFocusManager();
    3049             :     // If no active window, do nothing because a current selection changed
    3050             :     // cannot occur unless it is in the active window.
    3051           0 :     if (!fm->GetActiveWindow()) {
    3052           0 :       return NS_OK;
    3053             :     }
    3054             :   }
    3055             : 
    3056          69 :   if (!(aReason & nsISelectionListener::MOUSEUP_REASON   ||
    3057          23 :         aReason & nsISelectionListener::SELECTALL_REASON ||
    3058          23 :         aReason & nsISelectionListener::KEYPRESS_REASON))
    3059          23 :     return NS_OK; //dont care if we are still dragging
    3060             : 
    3061             :   bool collapsed;
    3062           0 :   if (!aDoc || !aSel ||
    3063           0 :       NS_FAILED(aSel->GetIsCollapsed(&collapsed)) || collapsed) {
    3064             : #ifdef DEBUG_CLIPBOARD
    3065             :     fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n");
    3066             : #endif
    3067             :     // If on macOS, clear the current selection transferable cached
    3068             :     // on the parent process (nsClipboard) when the selection is empty.
    3069           0 :     if (mCachedClipboard == nsIClipboard::kSelectionCache) {
    3070           0 :       return nsCopySupport::ClearSelectionCache();
    3071             :     }
    3072             :     /* clear X clipboard? */
    3073           0 :     return NS_OK;
    3074             :   }
    3075             : 
    3076           0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
    3077           0 :   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
    3078             : 
    3079             :   // call the copy code
    3080           0 :   return nsCopySupport::HTMLCopy(aSel, doc,
    3081           0 :                                  mCachedClipboard, false);
    3082             : }

Generated by: LCOV version 1.13