layout/xul/tree/nsTreeSelection.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/AsyncEventDispatcher.h"
     7 #include "nsCOMPtr.h"
     8 #include "nsTreeSelection.h"
     9 #include "nsIBoxObject.h"
    10 #include "nsITreeBoxObject.h"
    11 #include "nsITreeView.h"
    12 #include "nsString.h"
    13 #include "nsIDOMElement.h"
    14 #include "nsDOMClassInfoID.h"
    15 #include "nsIContent.h"
    16 #include "nsNameSpaceManager.h"
    17 #include "nsGkAtoms.h"
    18 #include "nsAutoPtr.h"
    19 #include "nsComponentManagerUtils.h"
    21 using namespace mozilla;
    23 // A helper class for managing our ranges of selection.
    24 struct nsTreeRange
    25 {
    26   nsTreeSelection* mSelection;
    28   nsTreeRange* mPrev;
    29   nsTreeRange* mNext;
    31   int32_t mMin;
    32   int32_t mMax;
    34   nsTreeRange(nsTreeSelection* aSel, int32_t aSingleVal)
    35     :mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aSingleVal), mMax(aSingleVal) {}
    36   nsTreeRange(nsTreeSelection* aSel, int32_t aMin, int32_t aMax) 
    37     :mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aMin), mMax(aMax) {}
    39   ~nsTreeRange() { delete mNext; }
    41   void Connect(nsTreeRange* aPrev = nullptr, nsTreeRange* aNext = nullptr) {
    42     if (aPrev)
    43       aPrev->mNext = this;
    44     else
    45       mSelection->mFirstRange = this;
    47     if (aNext)
    48       aNext->mPrev = this;
    50     mPrev = aPrev;
    51     mNext = aNext;
    52   }
    54   nsresult RemoveRange(int32_t aStart, int32_t aEnd) {
    55     // This should so be a loop... sigh...
    56     // We start past the range to remove, so no more to remove
    57     if (aEnd < mMin)
    58       return NS_OK;
    59     // We are the last range to be affected
    60     if (aEnd < mMax) {
    61       if (aStart <= mMin) {
    62         // Just chop the start of the range off
    63         mMin = aEnd + 1;
    64       } else {
    65         // We need to split the range
    66         nsTreeRange* range = new nsTreeRange(mSelection, aEnd + 1, mMax);
    67         if (!range)
    68           return NS_ERROR_OUT_OF_MEMORY;
    70         mMax = aStart - 1;
    71         range->Connect(this, mNext);
    72       }
    73       return NS_OK;
    74     }
    75     nsTreeRange* next = mNext;
    76     if (aStart <= mMin) {
    77       // The remove includes us, remove ourselves from the list
    78       if (mPrev)
    79         mPrev->mNext = next;
    80       else
    81         mSelection->mFirstRange = next;
    83       if (next)
    84         next->mPrev = mPrev;
    85       mPrev = mNext = nullptr;
    86       delete this;
    87     } else if (aStart <= mMax) {
    88       // Just chop the end of the range off
    89       mMax = aStart - 1;
    90     }
    91     return next ? next->RemoveRange(aStart, aEnd) : NS_OK;
    92   }
    94   nsresult Remove(int32_t aIndex) {
    95     if (aIndex >= mMin && aIndex <= mMax) {
    96       // We have found the range that contains us.
    97       if (mMin == mMax) {
    98         // Delete the whole range.
    99         if (mPrev)
   100           mPrev->mNext = mNext;
   101         if (mNext)
   102           mNext->mPrev = mPrev;
   103         nsTreeRange* first = mSelection->mFirstRange;
   104         if (first == this)
   105           mSelection->mFirstRange = mNext;
   106         mNext = mPrev = nullptr;
   107         delete this;
   108       }
   109       else if (aIndex == mMin)
   110         mMin++;
   111       else if (aIndex == mMax)
   112         mMax--;
   113       else {
   114         // We have to break this range.
   115         nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex + 1, mMax);
   116         if (!newRange)
   117           return NS_ERROR_OUT_OF_MEMORY;
   119         newRange->Connect(this, mNext);
   120         mMax = aIndex - 1;
   121       }
   122     }
   123     else if (mNext)
   124       return mNext->Remove(aIndex);
   126     return NS_OK;
   127   }
   129   nsresult Add(int32_t aIndex) {
   130     if (aIndex < mMin) {
   131       // We have found a spot to insert.
   132       if (aIndex + 1 == mMin)
   133         mMin = aIndex;
   134       else if (mPrev && mPrev->mMax+1 == aIndex)
   135         mPrev->mMax = aIndex;
   136       else {
   137         // We have to create a new range.
   138         nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
   139         if (!newRange)
   140           return NS_ERROR_OUT_OF_MEMORY;
   142         newRange->Connect(mPrev, this);
   143       }
   144     }
   145     else if (mNext)
   146       mNext->Add(aIndex);
   147     else {
   148       // Insert on to the end.
   149       if (mMax+1 == aIndex)
   150         mMax = aIndex;
   151       else {
   152         // We have to create a new range.
   153         nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
   154         if (!newRange)
   155           return NS_ERROR_OUT_OF_MEMORY;
   157         newRange->Connect(this, nullptr);
   158       }
   159     }
   160     return NS_OK;
   161   }
   163   bool Contains(int32_t aIndex) {
   164     if (aIndex >= mMin && aIndex <= mMax)
   165       return true;
   167     if (mNext)
   168       return mNext->Contains(aIndex);
   170     return false;
   171   }
   173   int32_t Count() {
   174     int32_t total = mMax - mMin + 1;
   175     if (mNext)
   176       total += mNext->Count();
   177     return total;
   178   }
   180   static void CollectRanges(nsTreeRange* aRange, nsTArray<int32_t>& aRanges)
   181   {
   182     nsTreeRange* cur = aRange;
   183     while (cur) {
   184       aRanges.AppendElement(cur->mMin);
   185       aRanges.AppendElement(cur->mMax);
   186       cur = cur->mNext;
   187     }
   188   }
   190   static void InvalidateRanges(nsITreeBoxObject* aTree,
   191                                nsTArray<int32_t>& aRanges)
   192   {
   193     if (aTree) {
   194       nsCOMPtr<nsITreeBoxObject> tree = aTree;
   195       for (uint32_t i = 0; i < aRanges.Length(); i += 2) {
   196         aTree->InvalidateRange(aRanges[i], aRanges[i + 1]);
   197       }
   198     }
   199   }
   201   void Invalidate() {
   202     nsTArray<int32_t> ranges;
   203     CollectRanges(this, ranges);
   204     InvalidateRanges(mSelection->mTree, ranges);
   206   }
   208   void RemoveAllBut(int32_t aIndex) {
   209     if (aIndex >= mMin && aIndex <= mMax) {
   211       // Invalidate everything in this list.
   212       nsTArray<int32_t> ranges;
   213       CollectRanges(mSelection->mFirstRange, ranges);
   215       mMin = aIndex;
   216       mMax = aIndex;
   218       nsTreeRange* first = mSelection->mFirstRange;
   219       if (mPrev)
   220         mPrev->mNext = mNext;
   221       if (mNext)
   222         mNext->mPrev = mPrev;
   223       mNext = mPrev = nullptr;
   225       if (first != this) {
   226         delete mSelection->mFirstRange;
   227         mSelection->mFirstRange = this;
   228       }
   229       InvalidateRanges(mSelection->mTree, ranges);
   230     }
   231     else if (mNext)
   232       mNext->RemoveAllBut(aIndex);
   233   }
   235   void Insert(nsTreeRange* aRange) {
   236     if (mMin >= aRange->mMax)
   237       aRange->Connect(mPrev, this);
   238     else if (mNext)
   239       mNext->Insert(aRange);
   240     else 
   241       aRange->Connect(this, nullptr);
   242   }
   243 };
   245 nsTreeSelection::nsTreeSelection(nsITreeBoxObject* aTree)
   246   : mTree(aTree),
   247     mSuppressed(false),
   248     mCurrentIndex(-1),
   249     mShiftSelectPivot(-1),
   250     mFirstRange(nullptr)
   251 {
   252 }
   254 nsTreeSelection::~nsTreeSelection()
   255 {
   256   delete mFirstRange;
   257   if (mSelectTimer)
   258     mSelectTimer->Cancel();
   259 }
   261 NS_IMPL_CYCLE_COLLECTION(nsTreeSelection, mTree, mCurrentColumn)
   263 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeSelection)
   264 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeSelection)
   266 DOMCI_DATA(TreeSelection, nsTreeSelection)
   268 // QueryInterface implementation for nsBoxObject
   269 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeSelection)
   270   NS_INTERFACE_MAP_ENTRY(nsITreeSelection)
   271   NS_INTERFACE_MAP_ENTRY(nsINativeTreeSelection)
   272   NS_INTERFACE_MAP_ENTRY(nsISupports)
   273   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeSelection)
   274 NS_INTERFACE_MAP_END
   276 NS_IMETHODIMP nsTreeSelection::GetTree(nsITreeBoxObject * *aTree)
   277 {
   278   NS_IF_ADDREF(*aTree = mTree);
   279   return NS_OK;
   280 }
   282 NS_IMETHODIMP nsTreeSelection::SetTree(nsITreeBoxObject * aTree)
   283 {
   284   if (mSelectTimer) {
   285     mSelectTimer->Cancel();
   286     mSelectTimer = nullptr;
   287   }
   289   // Make sure aTree really implements nsITreeBoxObject and nsIBoxObject!
   290   nsCOMPtr<nsIBoxObject> bo = do_QueryInterface(aTree);
   291   mTree = do_QueryInterface(bo);
   292   NS_ENSURE_STATE(mTree == aTree);
   293   return NS_OK;
   294 }
   296 NS_IMETHODIMP nsTreeSelection::GetSingle(bool* aSingle)
   297 {
   298   if (!mTree)
   299     return NS_ERROR_NULL_POINTER;
   301   nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
   303   nsCOMPtr<nsIDOMElement> element;
   304   boxObject->GetElement(getter_AddRefs(element));
   306   nsCOMPtr<nsIContent> content = do_QueryInterface(element);
   308   static nsIContent::AttrValuesArray strings[] =
   309     {&nsGkAtoms::single, &nsGkAtoms::cell, &nsGkAtoms::text, nullptr};
   311   *aSingle = content->FindAttrValueIn(kNameSpaceID_None,
   312                                       nsGkAtoms::seltype,
   313                                       strings, eCaseMatters) >= 0;
   315   return NS_OK;
   316 }
   318 NS_IMETHODIMP nsTreeSelection::IsSelected(int32_t aIndex, bool* aResult)
   319 {
   320   if (mFirstRange)
   321     *aResult = mFirstRange->Contains(aIndex);
   322   else
   323     *aResult = false;
   324   return NS_OK;
   325 }
   327 NS_IMETHODIMP nsTreeSelection::TimedSelect(int32_t aIndex, int32_t aMsec)
   328 {
   329   bool suppressSelect = mSuppressed;
   331   if (aMsec != -1)
   332     mSuppressed = true;
   334   nsresult rv = Select(aIndex);
   335   if (NS_FAILED(rv))
   336     return rv;
   338   if (aMsec != -1) {
   339     mSuppressed = suppressSelect;
   340     if (!mSuppressed) {
   341       if (mSelectTimer)
   342         mSelectTimer->Cancel();
   344       mSelectTimer = do_CreateInstance("@mozilla.org/timer;1");
   345       mSelectTimer->InitWithFuncCallback(SelectCallback, this, aMsec, 
   346                                          nsITimer::TYPE_ONE_SHOT);
   347     }
   348   }
   350   return NS_OK;
   351 }
   353 NS_IMETHODIMP nsTreeSelection::Select(int32_t aIndex)
   354 {
   355   mShiftSelectPivot = -1;
   357   nsresult rv = SetCurrentIndex(aIndex);
   358   if (NS_FAILED(rv))
   359     return rv;
   361   if (mFirstRange) {
   362     bool alreadySelected = mFirstRange->Contains(aIndex);
   364     if (alreadySelected) {
   365       int32_t count = mFirstRange->Count();
   366       if (count > 1) {
   367         // We need to deselect everything but our item.
   368         mFirstRange->RemoveAllBut(aIndex);
   369         FireOnSelectHandler();
   370       }
   371       return NS_OK;
   372     }
   373     else {
   374       // Clear out our selection.
   375       mFirstRange->Invalidate();
   376       delete mFirstRange;
   377     }
   378   }
   380   // Create our new selection.
   381   mFirstRange = new nsTreeRange(this, aIndex);
   382   if (!mFirstRange)
   383     return NS_ERROR_OUT_OF_MEMORY;
   385   mFirstRange->Invalidate();
   387   // Fire the select event
   388   FireOnSelectHandler();
   389   return NS_OK;
   390 }
   392 NS_IMETHODIMP nsTreeSelection::ToggleSelect(int32_t aIndex)
   393 {
   394   // There are six cases that can occur on a ToggleSelect with our
   395   // range code.
   396   // (1) A new range should be made for a selection.
   397   // (2) A single range is removed from the selection.
   398   // (3) The item is added to an existing range.
   399   // (4) The item is removed from an existing range.
   400   // (5) The addition of the item causes two ranges to be merged.
   401   // (6) The removal of the item causes two ranges to be split.
   402   mShiftSelectPivot = -1;
   403   nsresult rv = SetCurrentIndex(aIndex);
   404   if (NS_FAILED(rv))
   405     return rv;
   407   if (!mFirstRange)
   408     Select(aIndex);
   409   else {
   410     if (!mFirstRange->Contains(aIndex)) {
   411       bool single;
   412       rv = GetSingle(&single);
   413       if (NS_SUCCEEDED(rv) && !single)
   414         rv = mFirstRange->Add(aIndex);
   415     }
   416     else
   417       rv = mFirstRange->Remove(aIndex);
   418     if (NS_SUCCEEDED(rv)) {
   419       if (mTree)
   420         mTree->InvalidateRow(aIndex);
   422       FireOnSelectHandler();
   423     }
   424   }
   426   return rv;
   427 }
   429 NS_IMETHODIMP nsTreeSelection::RangedSelect(int32_t aStartIndex, int32_t aEndIndex, bool aAugment)
   430 {
   431   bool single;
   432   nsresult rv = GetSingle(&single);
   433   if (NS_FAILED(rv))
   434     return rv;
   436   if ((mFirstRange || (aStartIndex != aEndIndex)) && single)
   437     return NS_OK;
   439   if (!aAugment) {
   440     // Clear our selection.
   441     if (mFirstRange) {
   442         mFirstRange->Invalidate();
   443         delete mFirstRange;
   444         mFirstRange = nullptr;
   445     }
   446   }
   448   if (aStartIndex == -1) {
   449     if (mShiftSelectPivot != -1)
   450       aStartIndex = mShiftSelectPivot;
   451     else if (mCurrentIndex != -1)
   452       aStartIndex = mCurrentIndex;
   453     else
   454       aStartIndex = aEndIndex;
   455   }
   457   mShiftSelectPivot = aStartIndex;
   458   rv = SetCurrentIndex(aEndIndex);
   459   if (NS_FAILED(rv))
   460     return rv;
   462   int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
   463   int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
   465   if (aAugment && mFirstRange) {
   466     // We need to remove all the items within our selected range from the selection,
   467     // and then we insert our new range into the list.
   468     nsresult rv = mFirstRange->RemoveRange(start, end);
   469     if (NS_FAILED(rv))
   470       return rv;
   471   }
   473   nsTreeRange* range = new nsTreeRange(this, start, end);
   474   if (!range)
   475     return NS_ERROR_OUT_OF_MEMORY;
   477   range->Invalidate();
   479   if (aAugment && mFirstRange)
   480     mFirstRange->Insert(range);
   481   else
   482     mFirstRange = range;
   484   FireOnSelectHandler();
   486   return NS_OK;
   487 }
   489 NS_IMETHODIMP nsTreeSelection::ClearRange(int32_t aStartIndex, int32_t aEndIndex)
   490 {
   491   nsresult rv = SetCurrentIndex(aEndIndex);
   492   if (NS_FAILED(rv))
   493     return rv;
   495   if (mFirstRange) {
   496     int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
   497     int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
   499     mFirstRange->RemoveRange(start, end);
   501     if (mTree)
   502       mTree->InvalidateRange(start, end);
   503   }
   505   return NS_OK;
   506 }
   508 NS_IMETHODIMP nsTreeSelection::ClearSelection()
   509 {
   510   if (mFirstRange) {
   511     mFirstRange->Invalidate();
   512     delete mFirstRange;
   513     mFirstRange = nullptr;
   514   }
   515   mShiftSelectPivot = -1;
   517   FireOnSelectHandler();
   519   return NS_OK;
   520 }
   522 NS_IMETHODIMP nsTreeSelection::InvertSelection()
   523 {
   524   return NS_ERROR_NOT_IMPLEMENTED;
   525 }
   527 NS_IMETHODIMP nsTreeSelection::SelectAll()
   528 {
   529   if (!mTree)
   530     return NS_OK;
   532   nsCOMPtr<nsITreeView> view;
   533   mTree->GetView(getter_AddRefs(view));
   534   if (!view)
   535     return NS_OK;
   537   int32_t rowCount;
   538   view->GetRowCount(&rowCount);
   539   bool single;
   540   nsresult rv = GetSingle(&single);
   541   if (NS_FAILED(rv))
   542     return rv;
   544   if (rowCount == 0 || (rowCount > 1 && single))
   545     return NS_OK;
   547   mShiftSelectPivot = -1;
   549   // Invalidate not necessary when clearing selection, since 
   550   // we're going to invalidate the world on the SelectAll.
   551   delete mFirstRange;
   553   mFirstRange = new nsTreeRange(this, 0, rowCount-1);
   554   mFirstRange->Invalidate();
   556   FireOnSelectHandler();
   558   return NS_OK;
   559 }
   561 NS_IMETHODIMP nsTreeSelection::GetRangeCount(int32_t* aResult)
   562 {
   563   int32_t count = 0;
   564   nsTreeRange* curr = mFirstRange;
   565   while (curr) {
   566     count++;
   567     curr = curr->mNext;
   568   }
   570   *aResult = count;
   571   return NS_OK;
   572 }
   574 NS_IMETHODIMP nsTreeSelection::GetRangeAt(int32_t aIndex, int32_t* aMin, int32_t* aMax)
   575 {
   576   *aMin = *aMax = -1;
   577   int32_t i = -1;
   578   nsTreeRange* curr = mFirstRange;
   579   while (curr) {
   580     i++;
   581     if (i == aIndex) {
   582       *aMin = curr->mMin;
   583       *aMax = curr->mMax;
   584       break;
   585     }
   586     curr = curr->mNext;
   587   }
   589   return NS_OK;
   590 }
   592 NS_IMETHODIMP nsTreeSelection::GetCount(int32_t *count)
   593 {
   594   if (mFirstRange)
   595     *count = mFirstRange->Count();
   596   else // No range available, so there's no selected row.
   597     *count = 0;
   599   return NS_OK;
   600 }
   602 NS_IMETHODIMP nsTreeSelection::GetSelectEventsSuppressed(bool *aSelectEventsSuppressed)
   603 {
   604   *aSelectEventsSuppressed = mSuppressed;
   605   return NS_OK;
   606 }
   608 NS_IMETHODIMP nsTreeSelection::SetSelectEventsSuppressed(bool aSelectEventsSuppressed)
   609 {
   610   mSuppressed = aSelectEventsSuppressed;
   611   if (!mSuppressed)
   612     FireOnSelectHandler();
   613   return NS_OK;
   614 }
   616 NS_IMETHODIMP nsTreeSelection::GetCurrentIndex(int32_t *aCurrentIndex)
   617 {
   618   *aCurrentIndex = mCurrentIndex;
   619   return NS_OK;
   620 }
   622 NS_IMETHODIMP nsTreeSelection::SetCurrentIndex(int32_t aIndex)
   623 {
   624   if (!mTree) {
   625     return NS_ERROR_UNEXPECTED;
   626   }
   627   if (mCurrentIndex == aIndex) {
   628     return NS_OK;
   629   }
   630   if (mCurrentIndex != -1 && mTree)
   631     mTree->InvalidateRow(mCurrentIndex);
   633   mCurrentIndex = aIndex;
   634   if (!mTree)
   635     return NS_OK;
   637   if (aIndex != -1)
   638     mTree->InvalidateRow(aIndex);
   640   // Fire DOMMenuItemActive or DOMMenuItemInactive event for tree.
   641   nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
   642   NS_ASSERTION(boxObject, "no box object!");
   643   if (!boxObject)
   644     return NS_ERROR_UNEXPECTED;
   645   nsCOMPtr<nsIDOMElement> treeElt;
   646   boxObject->GetElement(getter_AddRefs(treeElt));
   648   nsCOMPtr<nsINode> treeDOMNode(do_QueryInterface(treeElt));
   649   NS_ENSURE_STATE(treeDOMNode);
   651   NS_NAMED_LITERAL_STRING(DOMMenuItemActive, "DOMMenuItemActive");
   652   NS_NAMED_LITERAL_STRING(DOMMenuItemInactive, "DOMMenuItemInactive");
   654   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
   655     new AsyncEventDispatcher(treeDOMNode,
   656                              (aIndex != -1 ? DOMMenuItemActive :
   657                                              DOMMenuItemInactive),
   658                              true, false);
   659   return asyncDispatcher->PostDOMEvent();
   660 }
   662 NS_IMETHODIMP nsTreeSelection::GetCurrentColumn(nsITreeColumn** aCurrentColumn)
   663 {
   664   NS_IF_ADDREF(*aCurrentColumn = mCurrentColumn);
   665   return NS_OK;
   666 }
   668 NS_IMETHODIMP nsTreeSelection::SetCurrentColumn(nsITreeColumn* aCurrentColumn)
   669 {
   670   if (!mTree) {
   671     return NS_ERROR_UNEXPECTED;
   672   }
   673   if (mCurrentColumn == aCurrentColumn) {
   674     return NS_OK;
   675   }
   677   if (mCurrentColumn) {
   678     if (mFirstRange)
   679       mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn);
   680     if (mCurrentIndex != -1)
   681       mTree->InvalidateCell(mCurrentIndex, mCurrentColumn);
   682   }
   684   mCurrentColumn = aCurrentColumn;
   686   if (mCurrentColumn) {
   687     if (mFirstRange)
   688       mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn);
   689     if (mCurrentIndex != -1)
   690       mTree->InvalidateCell(mCurrentIndex, mCurrentColumn);
   691   }
   693   return NS_OK;
   694 }
   696 #define ADD_NEW_RANGE(macro_range, macro_selection, macro_start, macro_end) \
   697   { \
   698     int32_t start = macro_start; \
   699     int32_t end = macro_end; \
   700     if (start > end) { \
   701       end = start; \
   702     } \
   703     nsTreeRange* macro_new_range = new nsTreeRange(macro_selection, start, end); \
   704     if (macro_range) \
   705       macro_range->Insert(macro_new_range); \
   706     else \
   707       macro_range = macro_new_range; \
   708   }
   710 NS_IMETHODIMP
   711 nsTreeSelection::AdjustSelection(int32_t aIndex, int32_t aCount)
   712 {
   713   NS_ASSERTION(aCount != 0, "adjusting by zero");
   714   if (!aCount) return NS_OK;
   716   // adjust mShiftSelectPivot, if necessary
   717   if ((mShiftSelectPivot != 1) && (aIndex <= mShiftSelectPivot)) {
   718     // if we are deleting and the delete includes the shift select pivot, reset it
   719     if (aCount < 0 && (mShiftSelectPivot <= (aIndex -aCount -1))) {
   720         mShiftSelectPivot = -1;
   721     }
   722     else {
   723         mShiftSelectPivot += aCount;
   724     }
   725   }
   727   // adjust mCurrentIndex, if necessary
   728   if ((mCurrentIndex != -1) && (aIndex <= mCurrentIndex)) {
   729     // if we are deleting and the delete includes the current index, reset it
   730     if (aCount < 0 && (mCurrentIndex <= (aIndex -aCount -1))) {
   731         mCurrentIndex = -1;
   732     }
   733     else {
   734         mCurrentIndex += aCount;
   735     }
   736   }
   738   // no selection, so nothing to do.
   739   if (!mFirstRange) return NS_OK;
   741   bool selChanged = false;
   742   nsTreeRange* oldFirstRange = mFirstRange;
   743   nsTreeRange* curr = mFirstRange;
   744   mFirstRange = nullptr;
   745   while (curr) {
   746     if (aCount > 0) {
   747       // inserting
   748       if (aIndex > curr->mMax) {
   749         // adjustment happens after the range, so no change
   750         ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
   751       }
   752       else if (aIndex <= curr->mMin) {  
   753         // adjustment happens before the start of the range, so shift down
   754         ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount);
   755         selChanged = true;
   756       }
   757       else {
   758         // adjustment happen inside the range.
   759         // break apart the range and create two ranges
   760         ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1);
   761         ADD_NEW_RANGE(mFirstRange, this, aIndex + aCount, curr->mMax + aCount);
   762         selChanged = true;
   763       }
   764     }
   765     else {
   766       // deleting
   767       if (aIndex > curr->mMax) {
   768         // adjustment happens after the range, so no change
   769         ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
   770       }
   771       else {
   772         // remember, aCount is negative
   773         selChanged = true;
   774         int32_t lastIndexOfAdjustment = aIndex - aCount - 1;
   775         if (aIndex <= curr->mMin) {
   776           if (lastIndexOfAdjustment < curr->mMin) {
   777             // adjustment happens before the start of the range, so shift up
   778             ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount);
   779           }
   780           else if (lastIndexOfAdjustment >= curr->mMax) {
   781             // adjustment contains the range.  remove the range by not adding it to the newRange
   782           }
   783           else {
   784             // adjustment starts before the range, and ends in the middle of it, so trim the range
   785             ADD_NEW_RANGE(mFirstRange, this, aIndex, curr->mMax + aCount)
   786           }
   787         }
   788         else if (lastIndexOfAdjustment >= curr->mMax) {
   789          // adjustment starts in the middle of the current range, and contains the end of the range, so trim the range
   790          ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1)
   791         }
   792         else {
   793           // range contains the adjustment, so shorten the range
   794           ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax + aCount)
   795         }
   796       }
   797     }
   798     curr = curr->mNext;
   799   }
   801   delete oldFirstRange;
   803   // Fire the select event
   804   if (selChanged)
   805     FireOnSelectHandler();
   807   return NS_OK;
   808 }
   810 NS_IMETHODIMP
   811 nsTreeSelection::InvalidateSelection()
   812 {
   813   if (mFirstRange)
   814     mFirstRange->Invalidate();
   815   return NS_OK;
   816 }
   818 NS_IMETHODIMP
   819 nsTreeSelection::GetShiftSelectPivot(int32_t* aIndex)
   820 {
   821   *aIndex = mShiftSelectPivot;
   822   return NS_OK;
   823 }
   826 nsresult
   827 nsTreeSelection::FireOnSelectHandler()
   828 {
   829   if (mSuppressed || !mTree)
   830     return NS_OK;
   832   nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
   833   NS_ASSERTION(boxObject, "no box object!");
   834   if (!boxObject)
   835      return NS_ERROR_UNEXPECTED;
   836   nsCOMPtr<nsIDOMElement> elt;
   837   boxObject->GetElement(getter_AddRefs(elt));
   838   NS_ENSURE_STATE(elt);
   840   nsCOMPtr<nsINode> node(do_QueryInterface(elt));
   841   NS_ENSURE_STATE(node);
   843   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
   844     new AsyncEventDispatcher(node, NS_LITERAL_STRING("select"), true, false);
   845   asyncDispatcher->RunDOMEventWhenSafe();
   846   return NS_OK;
   847 }
   849 void
   850 nsTreeSelection::SelectCallback(nsITimer *aTimer, void *aClosure)
   851 {
   852   nsRefPtr<nsTreeSelection> self = static_cast<nsTreeSelection*>(aClosure);
   853   if (self) {
   854     self->FireOnSelectHandler();
   855     aTimer->Cancel();
   856     self->mSelectTimer = nullptr;
   857   }
   858 }
   860 ///////////////////////////////////////////////////////////////////////////////////
   862 nsresult
   863 NS_NewTreeSelection(nsITreeBoxObject* aTree, nsITreeSelection** aResult)
   864 {
   865   *aResult = new nsTreeSelection(aTree);
   866   if (!*aResult)
   867     return NS_ERROR_OUT_OF_MEMORY;
   868   NS_ADDREF(*aResult);
   869   return NS_OK;
   870 }

mercurial