tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsTreeContentView.cpp (40146B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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 #include "nsTreeContentView.h"
      8 
      9 #include "ChildIterator.h"
     10 #include "mozilla/ErrorResult.h"
     11 #include "mozilla/dom/Document.h"
     12 #include "mozilla/dom/Element.h"
     13 #include "mozilla/dom/TreeContentViewBinding.h"
     14 #include "mozilla/dom/XULTreeElement.h"
     15 #include "nsError.h"
     16 #include "nsGkAtoms.h"
     17 #include "nsNameSpaceManager.h"
     18 #include "nsServiceManagerUtils.h"
     19 #include "nsTreeBodyFrame.h"
     20 #include "nsTreeColumns.h"
     21 #include "nsTreeUtils.h"
     22 #include "nsXULSortService.h"
     23 
     24 using namespace mozilla;
     25 using namespace mozilla::dom;
     26 
     27 // A content model view implementation for the tree.
     28 
     29 #define ROW_FLAG_CONTAINER 0x01
     30 #define ROW_FLAG_OPEN 0x02
     31 #define ROW_FLAG_EMPTY 0x04
     32 #define ROW_FLAG_SEPARATOR 0x08
     33 
     34 class Row {
     35 public:
     36  Row(Element* aContent, int32_t aParentIndex)
     37      : mContent(aContent),
     38        mParentIndex(aParentIndex),
     39        mSubtreeSize(0),
     40        mFlags(0) {}
     41 
     42  ~Row() = default;
     43 
     44  void SetContainer(bool aContainer) {
     45    aContainer ? mFlags |= ROW_FLAG_CONTAINER : mFlags &= ~ROW_FLAG_CONTAINER;
     46  }
     47  bool IsContainer() { return mFlags & ROW_FLAG_CONTAINER; }
     48 
     49  void SetOpen(bool aOpen) {
     50    aOpen ? mFlags |= ROW_FLAG_OPEN : mFlags &= ~ROW_FLAG_OPEN;
     51  }
     52  bool IsOpen() { return !!(mFlags & ROW_FLAG_OPEN); }
     53 
     54  void SetEmpty(bool aEmpty) {
     55    aEmpty ? mFlags |= ROW_FLAG_EMPTY : mFlags &= ~ROW_FLAG_EMPTY;
     56  }
     57  bool IsEmpty() { return !!(mFlags & ROW_FLAG_EMPTY); }
     58 
     59  void SetSeparator(bool aSeparator) {
     60    aSeparator ? mFlags |= ROW_FLAG_SEPARATOR : mFlags &= ~ROW_FLAG_SEPARATOR;
     61  }
     62  bool IsSeparator() { return !!(mFlags & ROW_FLAG_SEPARATOR); }
     63 
     64  // Weak reference to a content item.
     65  Element* mContent;
     66 
     67  // The parent index of the item, set to -1 for the top level items.
     68  int32_t mParentIndex;
     69 
     70  // Subtree size for this item.
     71  int32_t mSubtreeSize;
     72 
     73 private:
     74  // State flags
     75  int8_t mFlags;
     76 };
     77 
     78 // We don't reference count the reference to the document
     79 // If the document goes away first, we'll be informed and we
     80 // can drop our reference.
     81 // If we go away first, we'll get rid of ourselves from the
     82 // document's observer list.
     83 
     84 nsTreeContentView::nsTreeContentView(void)
     85    : mTree(nullptr), mSelection(nullptr), mDocument(nullptr) {}
     86 
     87 nsTreeContentView::~nsTreeContentView(void) {
     88  // Remove ourselves from mDocument's observers.
     89  if (mDocument) {
     90    mDocument->RemoveObserver(this);
     91  }
     92 }
     93 
     94 nsresult NS_NewTreeContentView(nsITreeView** aResult) {
     95  *aResult = new nsTreeContentView;
     96  if (!*aResult) {
     97    return NS_ERROR_OUT_OF_MEMORY;
     98  }
     99  NS_ADDREF(*aResult);
    100  return NS_OK;
    101 }
    102 
    103 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsTreeContentView, mTree, mSelection,
    104                                      mBody)
    105 
    106 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeContentView)
    107 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeContentView)
    108 
    109 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeContentView)
    110  NS_INTERFACE_MAP_ENTRY(nsITreeView)
    111  NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
    112  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
    113  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITreeView)
    114  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    115 NS_INTERFACE_MAP_END
    116 
    117 JSObject* nsTreeContentView::WrapObject(JSContext* aCx,
    118                                        JS::Handle<JSObject*> aGivenProto) {
    119  return TreeContentView_Binding::Wrap(aCx, this, aGivenProto);
    120 }
    121 
    122 nsISupports* nsTreeContentView::GetParentObject() { return mTree; }
    123 
    124 NS_IMETHODIMP
    125 nsTreeContentView::GetRowCount(int32_t* aRowCount) {
    126  *aRowCount = mRows.Length();
    127 
    128  return NS_OK;
    129 }
    130 
    131 NS_IMETHODIMP
    132 nsTreeContentView::GetSelection(nsITreeSelection** aSelection) {
    133  NS_IF_ADDREF(*aSelection = GetSelection());
    134 
    135  return NS_OK;
    136 }
    137 
    138 bool nsTreeContentView::CanTrustTreeSelection(nsISupports* aValue) {
    139  // Untrusted content is only allowed to specify known-good views
    140  if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
    141    return true;
    142  }
    143  nsCOMPtr<nsINativeTreeSelection> nativeTreeSel = do_QueryInterface(aValue);
    144  return nativeTreeSel && NS_SUCCEEDED(nativeTreeSel->EnsureNative());
    145 }
    146 
    147 NS_IMETHODIMP
    148 nsTreeContentView::SetSelection(nsITreeSelection* aSelection) {
    149  ErrorResult rv;
    150  SetSelection(aSelection, rv);
    151  return rv.StealNSResult();
    152 }
    153 
    154 void nsTreeContentView::SetSelection(nsITreeSelection* aSelection,
    155                                     ErrorResult& aError) {
    156  if (aSelection && !CanTrustTreeSelection(aSelection)) {
    157    aError.ThrowSecurityError("Not allowed to set tree selection");
    158    return;
    159  }
    160 
    161  mSelection = aSelection;
    162 }
    163 
    164 void nsTreeContentView::GetRowProperties(int32_t aRow, nsAString& aProperties,
    165                                         ErrorResult& aError) {
    166  aProperties.Truncate();
    167  if (!IsValidRowIndex(aRow)) {
    168    aError.Throw(NS_ERROR_INVALID_ARG);
    169    return;
    170  }
    171 
    172  Row* row = mRows[aRow].get();
    173  nsIContent* realRow;
    174  if (row->IsSeparator()) {
    175    realRow = row->mContent;
    176  } else {
    177    realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
    178  }
    179 
    180  if (realRow && realRow->IsElement()) {
    181    realRow->AsElement()->GetAttr(nsGkAtoms::properties, aProperties);
    182  }
    183 }
    184 
    185 NS_IMETHODIMP
    186 nsTreeContentView::GetRowProperties(int32_t aIndex, nsAString& aProps) {
    187  ErrorResult rv;
    188  GetRowProperties(aIndex, aProps, rv);
    189  return rv.StealNSResult();
    190 }
    191 
    192 void nsTreeContentView::GetCellProperties(int32_t aRow, nsTreeColumn& aColumn,
    193                                          nsAString& aProperties,
    194                                          ErrorResult& aError) {
    195  if (!IsValidRowIndex(aRow)) {
    196    aError.Throw(NS_ERROR_INVALID_ARG);
    197    return;
    198  }
    199 
    200  Row* row = mRows[aRow].get();
    201  nsIContent* realRow =
    202      nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
    203  if (realRow) {
    204    Element* cell = GetCell(realRow, aColumn);
    205    if (cell) {
    206      cell->GetAttr(nsGkAtoms::properties, aProperties);
    207    }
    208  }
    209 }
    210 
    211 NS_IMETHODIMP
    212 nsTreeContentView::GetCellProperties(int32_t aRow, nsTreeColumn* aCol,
    213                                     nsAString& aProps) {
    214  NS_ENSURE_ARG(aCol);
    215 
    216  ErrorResult rv;
    217  GetCellProperties(aRow, *aCol, aProps, rv);
    218  return rv.StealNSResult();
    219 }
    220 
    221 void nsTreeContentView::GetColumnProperties(nsTreeColumn& aColumn,
    222                                            nsAString& aProperties) {
    223  RefPtr<Element> element = aColumn.Element();
    224 
    225  if (element) {
    226    element->GetAttr(nsGkAtoms::properties, aProperties);
    227  }
    228 }
    229 
    230 NS_IMETHODIMP
    231 nsTreeContentView::GetColumnProperties(nsTreeColumn* aCol, nsAString& aProps) {
    232  NS_ENSURE_ARG(aCol);
    233 
    234  GetColumnProperties(*aCol, aProps);
    235  return NS_OK;
    236 }
    237 
    238 bool nsTreeContentView::IsContainer(int32_t aRow, ErrorResult& aError) {
    239  if (!IsValidRowIndex(aRow)) {
    240    aError.Throw(NS_ERROR_INVALID_ARG);
    241    return false;
    242  }
    243 
    244  return mRows[aRow]->IsContainer();
    245 }
    246 
    247 NS_IMETHODIMP
    248 nsTreeContentView::IsContainer(int32_t aIndex, bool* _retval) {
    249  ErrorResult rv;
    250  *_retval = IsContainer(aIndex, rv);
    251  return rv.StealNSResult();
    252 }
    253 
    254 bool nsTreeContentView::IsContainerOpen(int32_t aRow, ErrorResult& aError) {
    255  if (!IsValidRowIndex(aRow)) {
    256    aError.Throw(NS_ERROR_INVALID_ARG);
    257    return false;
    258  }
    259 
    260  return mRows[aRow]->IsOpen();
    261 }
    262 
    263 NS_IMETHODIMP
    264 nsTreeContentView::IsContainerOpen(int32_t aIndex, bool* _retval) {
    265  ErrorResult rv;
    266  *_retval = IsContainerOpen(aIndex, rv);
    267  return rv.StealNSResult();
    268 }
    269 
    270 bool nsTreeContentView::IsContainerEmpty(int32_t aRow, ErrorResult& aError) {
    271  if (!IsValidRowIndex(aRow)) {
    272    aError.Throw(NS_ERROR_INVALID_ARG);
    273    return false;
    274  }
    275 
    276  return mRows[aRow]->IsEmpty();
    277 }
    278 
    279 NS_IMETHODIMP
    280 nsTreeContentView::IsContainerEmpty(int32_t aIndex, bool* _retval) {
    281  ErrorResult rv;
    282  *_retval = IsContainerEmpty(aIndex, rv);
    283  return rv.StealNSResult();
    284 }
    285 
    286 bool nsTreeContentView::IsSeparator(int32_t aRow, ErrorResult& aError) {
    287  if (!IsValidRowIndex(aRow)) {
    288    aError.Throw(NS_ERROR_INVALID_ARG);
    289    return false;
    290  }
    291 
    292  return mRows[aRow]->IsSeparator();
    293 }
    294 
    295 NS_IMETHODIMP
    296 nsTreeContentView::IsSeparator(int32_t aIndex, bool* _retval) {
    297  ErrorResult rv;
    298  *_retval = IsSeparator(aIndex, rv);
    299  return rv.StealNSResult();
    300 }
    301 
    302 NS_IMETHODIMP
    303 nsTreeContentView::IsSorted(bool* _retval) {
    304  *_retval = IsSorted();
    305 
    306  return NS_OK;
    307 }
    308 
    309 bool nsTreeContentView::CanDrop(int32_t aRow, int32_t aOrientation,
    310                                ErrorResult& aError) {
    311  if (!IsValidRowIndex(aRow)) {
    312    aError.Throw(NS_ERROR_INVALID_ARG);
    313  }
    314  return false;
    315 }
    316 
    317 bool nsTreeContentView::CanDrop(int32_t aRow, int32_t aOrientation,
    318                                DataTransfer* aDataTransfer,
    319                                ErrorResult& aError) {
    320  return CanDrop(aRow, aOrientation, aError);
    321 }
    322 
    323 NS_IMETHODIMP
    324 nsTreeContentView::CanDrop(int32_t aIndex, int32_t aOrientation,
    325                           DataTransfer* aDataTransfer, bool* _retval) {
    326  ErrorResult rv;
    327  *_retval = CanDrop(aIndex, aOrientation, rv);
    328  return rv.StealNSResult();
    329 }
    330 
    331 void nsTreeContentView::Drop(int32_t aRow, int32_t aOrientation,
    332                             ErrorResult& aError) {
    333  if (!IsValidRowIndex(aRow)) {
    334    aError.Throw(NS_ERROR_INVALID_ARG);
    335  }
    336 }
    337 
    338 void nsTreeContentView::Drop(int32_t aRow, int32_t aOrientation,
    339                             DataTransfer* aDataTransfer, ErrorResult& aError) {
    340  Drop(aRow, aOrientation, aError);
    341 }
    342 
    343 NS_IMETHODIMP
    344 nsTreeContentView::Drop(int32_t aRow, int32_t aOrientation,
    345                        DataTransfer* aDataTransfer) {
    346  ErrorResult rv;
    347  Drop(aRow, aOrientation, rv);
    348  return rv.StealNSResult();
    349 }
    350 
    351 int32_t nsTreeContentView::GetParentIndex(int32_t aRow, ErrorResult& aError) {
    352  if (!IsValidRowIndex(aRow)) {
    353    aError.Throw(NS_ERROR_INVALID_ARG);
    354    return 0;
    355  }
    356 
    357  return mRows[aRow]->mParentIndex;
    358 }
    359 
    360 NS_IMETHODIMP
    361 nsTreeContentView::GetParentIndex(int32_t aRowIndex, int32_t* _retval) {
    362  ErrorResult rv;
    363  *_retval = GetParentIndex(aRowIndex, rv);
    364  return rv.StealNSResult();
    365 }
    366 
    367 bool nsTreeContentView::HasNextSibling(int32_t aRow, int32_t aAfterIndex,
    368                                       ErrorResult& aError) {
    369  if (!IsValidRowIndex(aRow)) {
    370    aError.Throw(NS_ERROR_INVALID_ARG);
    371    return false;
    372  }
    373 
    374  // We have a next sibling if the row is not the last in the subtree.
    375  int32_t parentIndex = mRows[aRow]->mParentIndex;
    376  if (parentIndex < 0) {
    377    return uint32_t(aRow) < mRows.Length() - 1;
    378  }
    379 
    380  // Compute the last index in this subtree.
    381  int32_t lastIndex = parentIndex + (mRows[parentIndex])->mSubtreeSize;
    382  Row* row = mRows[lastIndex].get();
    383  while (row->mParentIndex != parentIndex) {
    384    lastIndex = row->mParentIndex;
    385    row = mRows[lastIndex].get();
    386  }
    387 
    388  return aRow < lastIndex;
    389 }
    390 
    391 NS_IMETHODIMP
    392 nsTreeContentView::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex,
    393                                  bool* _retval) {
    394  ErrorResult rv;
    395  *_retval = HasNextSibling(aRowIndex, aAfterIndex, rv);
    396  return rv.StealNSResult();
    397 }
    398 
    399 int32_t nsTreeContentView::GetLevel(int32_t aRow, ErrorResult& aError) {
    400  if (!IsValidRowIndex(aRow)) {
    401    aError.Throw(NS_ERROR_INVALID_ARG);
    402    return 0;
    403  }
    404 
    405  int32_t level = 0;
    406  Row* row = mRows[aRow].get();
    407  while (row->mParentIndex >= 0) {
    408    level++;
    409    row = mRows[row->mParentIndex].get();
    410  }
    411  return level;
    412 }
    413 
    414 NS_IMETHODIMP
    415 nsTreeContentView::GetLevel(int32_t aIndex, int32_t* _retval) {
    416  ErrorResult rv;
    417  *_retval = GetLevel(aIndex, rv);
    418  return rv.StealNSResult();
    419 }
    420 
    421 void nsTreeContentView::GetImageSrc(int32_t aRow, nsTreeColumn& aColumn,
    422                                    nsAString& aSrc, ErrorResult& aError) {
    423  if (!IsValidRowIndex(aRow)) {
    424    aError.Throw(NS_ERROR_INVALID_ARG);
    425    return;
    426  }
    427 
    428  Row* row = mRows[aRow].get();
    429 
    430  nsIContent* realRow =
    431      nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
    432  if (realRow) {
    433    Element* cell = GetCell(realRow, aColumn);
    434    if (cell) {
    435      cell->GetAttr(nsGkAtoms::src, aSrc);
    436    }
    437  }
    438 }
    439 
    440 NS_IMETHODIMP
    441 nsTreeContentView::GetImageSrc(int32_t aRow, nsTreeColumn* aCol,
    442                               nsAString& _retval) {
    443  NS_ENSURE_ARG(aCol);
    444 
    445  ErrorResult rv;
    446  GetImageSrc(aRow, *aCol, _retval, rv);
    447  return rv.StealNSResult();
    448 }
    449 
    450 void nsTreeContentView::GetCellValue(int32_t aRow, nsTreeColumn& aColumn,
    451                                     nsAString& aValue, ErrorResult& aError) {
    452  if (!IsValidRowIndex(aRow)) {
    453    aError.Throw(NS_ERROR_INVALID_ARG);
    454    return;
    455  }
    456 
    457  Row* row = mRows[aRow].get();
    458 
    459  nsIContent* realRow =
    460      nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
    461  if (realRow) {
    462    Element* cell = GetCell(realRow, aColumn);
    463    if (cell) {
    464      cell->GetAttr(nsGkAtoms::value, aValue);
    465    }
    466  }
    467 }
    468 
    469 NS_IMETHODIMP
    470 nsTreeContentView::GetCellValue(int32_t aRow, nsTreeColumn* aCol,
    471                                nsAString& _retval) {
    472  NS_ENSURE_ARG(aCol);
    473 
    474  ErrorResult rv;
    475  GetCellValue(aRow, *aCol, _retval, rv);
    476  return rv.StealNSResult();
    477 }
    478 
    479 void nsTreeContentView::GetCellText(int32_t aRow, nsTreeColumn& aColumn,
    480                                    nsAString& aText, ErrorResult& aError) {
    481  if (!IsValidRowIndex(aRow)) {
    482    aError.Throw(NS_ERROR_INVALID_ARG);
    483    return;
    484  }
    485 
    486  Row* row = mRows[aRow].get();
    487 
    488  // Check for a "label" attribute - this is valid on an <treeitem>
    489  // with a single implied column.
    490  if (row->mContent->GetAttr(nsGkAtoms::label, aText) && !aText.IsEmpty()) {
    491    return;
    492  }
    493 
    494  if (row->mContent->IsXULElement(nsGkAtoms::treeitem)) {
    495    nsIContent* realRow =
    496        nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
    497    if (realRow) {
    498      Element* cell = GetCell(realRow, aColumn);
    499      if (cell) {
    500        cell->GetAttr(nsGkAtoms::label, aText);
    501      }
    502    }
    503  }
    504 }
    505 
    506 NS_IMETHODIMP
    507 nsTreeContentView::GetCellText(int32_t aRow, nsTreeColumn* aCol,
    508                               nsAString& _retval) {
    509  NS_ENSURE_ARG(aCol);
    510 
    511  ErrorResult rv;
    512  GetCellText(aRow, *aCol, _retval, rv);
    513  return rv.StealNSResult();
    514 }
    515 
    516 void nsTreeContentView::SetTree(XULTreeElement* aTree, ErrorResult& aError) {
    517  aError = SetTree(aTree);
    518 }
    519 
    520 NS_IMETHODIMP
    521 nsTreeContentView::SetTree(XULTreeElement* aTree) {
    522  ClearRows();
    523 
    524  mTree = aTree;
    525 
    526  if (aTree) {
    527    // Add ourselves to document's observers.
    528    Document* document = mTree->GetComposedDoc();
    529    if (document) {
    530      document->AddObserver(this);
    531      mDocument = document;
    532    }
    533 
    534    RefPtr<dom::Element> bodyElement = mTree->GetTreeBody();
    535    if (bodyElement) {
    536      mBody = std::move(bodyElement);
    537      int32_t index = 0;
    538      Serialize(mBody, -1, &index, mRows);
    539    }
    540  }
    541 
    542  return NS_OK;
    543 }
    544 
    545 void nsTreeContentView::ToggleOpenState(int32_t aRow, ErrorResult& aError) {
    546  if (!IsValidRowIndex(aRow)) {
    547    aError.Throw(NS_ERROR_INVALID_ARG);
    548    return;
    549  }
    550 
    551  // We don't serialize content right here, since content might be generated
    552  // lazily.
    553  Row* row = mRows[aRow].get();
    554 
    555  if (row->IsOpen()) {
    556    row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, u"false"_ns,
    557                           true);
    558  } else {
    559    row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, u"true"_ns,
    560                           true);
    561  }
    562 }
    563 
    564 NS_IMETHODIMP
    565 nsTreeContentView::ToggleOpenState(int32_t aIndex) {
    566  ErrorResult rv;
    567  ToggleOpenState(aIndex, rv);
    568  return rv.StealNSResult();
    569 }
    570 
    571 void nsTreeContentView::CycleHeader(nsTreeColumn& aColumn,
    572                                    ErrorResult& aError) {
    573  if (!mTree) {
    574    return;
    575  }
    576 
    577  RefPtr<Element> column = aColumn.Element();
    578  nsAutoString sort;
    579  column->GetAttr(nsGkAtoms::sort, sort);
    580  if (!sort.IsEmpty()) {
    581    nsAutoString sortdirection;
    582    static Element::AttrValuesArray strings[] = {
    583        nsGkAtoms::ascending, nsGkAtoms::descending, nullptr};
    584    switch (column->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::sortDirection,
    585                                    strings, eCaseMatters)) {
    586      case 0:
    587        sortdirection.AssignLiteral("descending");
    588        break;
    589      case 1:
    590        sortdirection.AssignLiteral("natural");
    591        break;
    592      default:
    593        sortdirection.AssignLiteral("ascending");
    594        break;
    595    }
    596 
    597    nsAutoString hints;
    598    column->GetAttr(nsGkAtoms::sorthints, hints);
    599    sortdirection.Append(' ');
    600    sortdirection += hints;
    601 
    602    XULWidgetSort(mTree, sort, sortdirection);
    603  }
    604 }
    605 
    606 NS_IMETHODIMP
    607 nsTreeContentView::CycleHeader(nsTreeColumn* aCol) {
    608  NS_ENSURE_ARG(aCol);
    609 
    610  ErrorResult rv;
    611  CycleHeader(*aCol, rv);
    612  return rv.StealNSResult();
    613 }
    614 
    615 NS_IMETHODIMP
    616 nsTreeContentView::SelectionChangedXPCOM() { return NS_OK; }
    617 
    618 NS_IMETHODIMP
    619 nsTreeContentView::CycleCell(int32_t aRow, nsTreeColumn* aCol) { return NS_OK; }
    620 
    621 bool nsTreeContentView::IsEditable(int32_t aRow, nsTreeColumn& aColumn,
    622                                   ErrorResult& aError) {
    623  if (!IsValidRowIndex(aRow)) {
    624    aError.Throw(NS_ERROR_INVALID_ARG);
    625    return false;
    626  }
    627 
    628  Row* row = mRows[aRow].get();
    629 
    630  nsIContent* realRow =
    631      nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
    632  if (realRow) {
    633    Element* cell = GetCell(realRow, aColumn);
    634    if (cell && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
    635                                  nsGkAtoms::_false, eCaseMatters)) {
    636      return false;
    637    }
    638  }
    639 
    640  return true;
    641 }
    642 
    643 NS_IMETHODIMP
    644 nsTreeContentView::IsEditable(int32_t aRow, nsTreeColumn* aCol, bool* _retval) {
    645  NS_ENSURE_ARG(aCol);
    646 
    647  ErrorResult rv;
    648  *_retval = IsEditable(aRow, *aCol, rv);
    649  return rv.StealNSResult();
    650 }
    651 
    652 void nsTreeContentView::SetCellValue(int32_t aRow, nsTreeColumn& aColumn,
    653                                     const nsAString& aValue,
    654                                     ErrorResult& aError) {
    655  if (!IsValidRowIndex(aRow)) {
    656    aError.Throw(NS_ERROR_INVALID_ARG);
    657    return;
    658  }
    659 
    660  Row* row = mRows[aRow].get();
    661 
    662  nsIContent* realRow =
    663      nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
    664  if (realRow) {
    665    Element* cell = GetCell(realRow, aColumn);
    666    if (cell) {
    667      cell->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, true);
    668    }
    669  }
    670 }
    671 
    672 NS_IMETHODIMP
    673 nsTreeContentView::SetCellValue(int32_t aRow, nsTreeColumn* aCol,
    674                                const nsAString& aValue) {
    675  NS_ENSURE_ARG(aCol);
    676 
    677  ErrorResult rv;
    678  SetCellValue(aRow, *aCol, aValue, rv);
    679  return rv.StealNSResult();
    680 }
    681 
    682 void nsTreeContentView::SetCellText(int32_t aRow, nsTreeColumn& aColumn,
    683                                    const nsAString& aValue,
    684                                    ErrorResult& aError) {
    685  if (!IsValidRowIndex(aRow)) {
    686    aError.Throw(NS_ERROR_INVALID_ARG);
    687    return;
    688  }
    689 
    690  Row* row = mRows[aRow].get();
    691 
    692  nsIContent* realRow =
    693      nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
    694  if (realRow) {
    695    Element* cell = GetCell(realRow, aColumn);
    696    if (cell) {
    697      cell->SetAttr(kNameSpaceID_None, nsGkAtoms::label, aValue, true);
    698    }
    699  }
    700 }
    701 
    702 NS_IMETHODIMP
    703 nsTreeContentView::SetCellText(int32_t aRow, nsTreeColumn* aCol,
    704                               const nsAString& aValue) {
    705  NS_ENSURE_ARG(aCol);
    706 
    707  ErrorResult rv;
    708  SetCellText(aRow, *aCol, aValue, rv);
    709  return rv.StealNSResult();
    710 }
    711 
    712 Element* nsTreeContentView::GetItemAtIndex(int32_t aIndex,
    713                                           ErrorResult& aError) {
    714  if (!IsValidRowIndex(aIndex)) {
    715    aError.Throw(NS_ERROR_INVALID_ARG);
    716    return nullptr;
    717  }
    718 
    719  return mRows[aIndex]->mContent;
    720 }
    721 
    722 int32_t nsTreeContentView::GetIndexOfItem(Element* aItem) {
    723  return FindContent(aItem);
    724 }
    725 
    726 void nsTreeContentView::AttributeChanged(dom::Element* aElement,
    727                                         int32_t aNameSpaceID,
    728                                         nsAtom* aAttribute, AttrModType,
    729                                         const nsAttrValue* aOldValue) {
    730  // Lots of codepaths under here that do all sorts of stuff, so be safe.
    731  nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
    732 
    733  // Make sure this notification concerns us.
    734  // First check the tag to see if it's one that we care about.
    735  if (aElement == mTree || aElement == mBody) {
    736    mTree->ClearStyleAndImageCaches();
    737    mTree->Invalidate();
    738  }
    739 
    740  // We don't consider non-XUL nodes.
    741  nsIContent* parent = nullptr;
    742  if (!aElement->IsXULElement() ||
    743      ((parent = aElement->GetParent()) && !parent->IsXULElement())) {
    744    return;
    745  }
    746  if (!aElement->IsAnyOfXULElements(nsGkAtoms::treecol, nsGkAtoms::treeitem,
    747                                    nsGkAtoms::treeseparator,
    748                                    nsGkAtoms::treerow, nsGkAtoms::treecell)) {
    749    return;
    750  }
    751 
    752  // If we have a legal tag, go up to the tree/select and make sure
    753  // that it's ours.
    754 
    755  for (nsIContent* element = aElement; element != mBody;
    756       element = element->GetParent()) {
    757    if (!element) {
    758      return;  // this is not for us
    759    }
    760    if (element->IsXULElement(nsGkAtoms::tree)) {
    761      return;  // this is not for us
    762    }
    763  }
    764 
    765  // Handle changes of the hidden attribute.
    766  if (aAttribute == nsGkAtoms::hidden &&
    767      aElement->IsAnyOfXULElements(nsGkAtoms::treeitem,
    768                                   nsGkAtoms::treeseparator)) {
    769    bool hidden = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
    770                                        nsGkAtoms::_true, eCaseMatters);
    771 
    772    int32_t index = FindContent(aElement);
    773    if (hidden && index >= 0) {
    774      // Hide this row along with its children.
    775      int32_t count = RemoveRow(index);
    776      if (mTree) {
    777        mTree->RowCountChanged(index, -count);
    778      }
    779    } else if (!hidden && index < 0) {
    780      // Show this row along with its children.
    781      nsCOMPtr<nsIContent> parent = aElement->GetParent();
    782      if (parent) {
    783        InsertRowFor(parent, aElement);
    784      }
    785    }
    786 
    787    return;
    788  }
    789 
    790  if (aElement->IsXULElement(nsGkAtoms::treecol)) {
    791    if (aAttribute == nsGkAtoms::properties) {
    792      if (mTree) {
    793        RefPtr<nsTreeColumns> cols = mTree->GetColumns();
    794        if (cols) {
    795          RefPtr<nsTreeColumn> col = cols->GetColumnFor(aElement);
    796          mTree->InvalidateColumn(col);
    797        }
    798      }
    799    }
    800  } else if (aElement->IsXULElement(nsGkAtoms::treeitem)) {
    801    int32_t index = FindContent(aElement);
    802    if (index >= 0) {
    803      Row* row = mRows[index].get();
    804      if (aAttribute == nsGkAtoms::container) {
    805        bool isContainer =
    806            aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
    807                                  nsGkAtoms::_true, eCaseMatters);
    808        row->SetContainer(isContainer);
    809        if (mTree) {
    810          mTree->InvalidateRow(index);
    811        }
    812      } else if (aAttribute == nsGkAtoms::open) {
    813        bool isOpen = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
    814                                            nsGkAtoms::_true, eCaseMatters);
    815        bool wasOpen = row->IsOpen();
    816        if (!isOpen && wasOpen) {
    817          CloseContainer(index);
    818        } else if (isOpen && !wasOpen) {
    819          OpenContainer(index);
    820        }
    821      } else if (aAttribute == nsGkAtoms::empty) {
    822        bool isEmpty =
    823            aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty,
    824                                  nsGkAtoms::_true, eCaseMatters);
    825        row->SetEmpty(isEmpty);
    826        if (mTree) {
    827          mTree->InvalidateRow(index);
    828        }
    829      }
    830    }
    831  } else if (aElement->IsXULElement(nsGkAtoms::treeseparator)) {
    832    int32_t index = FindContent(aElement);
    833    if (index >= 0) {
    834      if (aAttribute == nsGkAtoms::properties && mTree) {
    835        mTree->InvalidateRow(index);
    836      }
    837    }
    838  } else if (aElement->IsXULElement(nsGkAtoms::treerow)) {
    839    if (aAttribute == nsGkAtoms::properties) {
    840      nsCOMPtr<nsIContent> parent = aElement->GetParent();
    841      if (parent) {
    842        int32_t index = FindContent(parent);
    843        if (index >= 0 && mTree) {
    844          mTree->InvalidateRow(index);
    845        }
    846      }
    847    }
    848  } else if (aElement->IsXULElement(nsGkAtoms::treecell)) {
    849    if (aAttribute == nsGkAtoms::properties || aAttribute == nsGkAtoms::mode ||
    850        aAttribute == nsGkAtoms::src || aAttribute == nsGkAtoms::value ||
    851        aAttribute == nsGkAtoms::label) {
    852      nsIContent* parent = aElement->GetParent();
    853      if (parent) {
    854        nsCOMPtr<nsIContent> grandParent = parent->GetParent();
    855        if (grandParent && grandParent->IsXULElement()) {
    856          int32_t index = FindContent(grandParent);
    857          if (index >= 0 && mTree) {
    858            // XXX Should we make an effort to invalidate only cell ?
    859            mTree->InvalidateRow(index);
    860          }
    861        }
    862      }
    863    }
    864  }
    865 }
    866 
    867 void nsTreeContentView::ContentAppended(nsIContent* aFirstNewContent,
    868                                        const ContentAppendInfo& aInfo) {
    869  for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
    870    // Our contentinserted doesn't use the index
    871    ContentInserted(cur, aInfo);
    872  }
    873 }
    874 
    875 void nsTreeContentView::ContentInserted(nsIContent* aChild,
    876                                        const ContentInsertInfo&) {
    877  NS_ASSERTION(aChild, "null ptr");
    878  nsIContent* container = aChild->GetParent();
    879 
    880  // Make sure this notification concerns us.
    881  // First check the tag to see if it's one that we care about.
    882 
    883  // Don't allow non-XUL nodes.
    884  if (!aChild->IsXULElement() || !container->IsXULElement()) {
    885    return;
    886  }
    887 
    888  if (!aChild->IsAnyOfXULElements(nsGkAtoms::treeitem, nsGkAtoms::treeseparator,
    889                                  nsGkAtoms::treechildren, nsGkAtoms::treerow,
    890                                  nsGkAtoms::treecell)) {
    891    return;
    892  }
    893 
    894  // If we have a legal tag, go up to the tree/select and make sure
    895  // that it's ours.
    896 
    897  for (nsIContent* element = container; element != mBody;
    898       element = element->GetParent()) {
    899    if (!element) {
    900      return;  // this is not for us
    901    }
    902    if (element->IsXULElement(nsGkAtoms::tree)) {
    903      return;  // this is not for us
    904    }
    905  }
    906 
    907  // Lots of codepaths under here that do all sorts of stuff, so be safe.
    908  nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
    909 
    910  if (aChild->IsXULElement(nsGkAtoms::treechildren)) {
    911    int32_t index = FindContent(container);
    912    if (index >= 0) {
    913      Row* row = mRows[index].get();
    914      row->SetEmpty(false);
    915      if (mTree) {
    916        mTree->InvalidateRow(index);
    917      }
    918      if (row->IsContainer() && row->IsOpen()) {
    919        int32_t count = EnsureSubtree(index);
    920        if (mTree) {
    921          mTree->RowCountChanged(index + 1, count);
    922        }
    923      }
    924    }
    925  } else if (aChild->IsAnyOfXULElements(nsGkAtoms::treeitem,
    926                                        nsGkAtoms::treeseparator)) {
    927    InsertRowFor(container, aChild);
    928  } else if (aChild->IsXULElement(nsGkAtoms::treerow)) {
    929    int32_t index = FindContent(container);
    930    if (index >= 0 && mTree) {
    931      mTree->InvalidateRow(index);
    932    }
    933  } else if (aChild->IsXULElement(nsGkAtoms::treecell)) {
    934    nsCOMPtr<nsIContent> parent = container->GetParent();
    935    if (parent) {
    936      int32_t index = FindContent(parent);
    937      if (index >= 0 && mTree) {
    938        mTree->InvalidateRow(index);
    939      }
    940    }
    941  }
    942 }
    943 
    944 void nsTreeContentView::ContentWillBeRemoved(nsIContent* aChild,
    945                                             const ContentRemoveInfo&) {
    946  NS_ASSERTION(aChild, "null ptr");
    947 
    948  nsIContent* container = aChild->GetParent();
    949  // Make sure this notification concerns us.
    950  // First check the tag to see if it's one that we care about.
    951 
    952  // We don't consider non-XUL nodes.
    953  if (!aChild->IsXULElement() || !container->IsXULElement()) {
    954    return;
    955  }
    956 
    957  if (!aChild->IsAnyOfXULElements(nsGkAtoms::treeitem, nsGkAtoms::treeseparator,
    958                                  nsGkAtoms::treechildren, nsGkAtoms::treerow,
    959                                  nsGkAtoms::treecell)) {
    960    return;
    961  }
    962 
    963  // If we have a legal tag, go up to the tree/select and make sure
    964  // that it's ours.
    965 
    966  for (nsIContent* element = container; element != mBody;
    967       element = element->GetParent()) {
    968    if (!element) {
    969      return;  // this is not for us
    970    }
    971    if (element->IsXULElement(nsGkAtoms::tree)) {
    972      return;  // this is not for us
    973    }
    974  }
    975 
    976  // Lots of codepaths under here that do all sorts of stuff, so be safe.
    977  nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
    978 
    979  if (aChild->IsXULElement(nsGkAtoms::treechildren)) {
    980    int32_t index = FindContent(container);
    981    if (index >= 0) {
    982      Row* row = mRows[index].get();
    983      row->SetEmpty(true);
    984      int32_t count = RemoveSubtree(index);
    985      // Invalidate also the row to update twisty.
    986      if (mTree) {
    987        mTree->InvalidateRow(index);
    988        mTree->RowCountChanged(index + 1, -count);
    989      }
    990    }
    991  } else if (aChild->IsAnyOfXULElements(nsGkAtoms::treeitem,
    992                                        nsGkAtoms::treeseparator)) {
    993    int32_t index = FindContent(aChild);
    994    if (index >= 0) {
    995      int32_t count = RemoveRow(index);
    996      if (mTree) {
    997        mTree->RowCountChanged(index, -count);
    998      }
    999    }
   1000  } else if (aChild->IsXULElement(nsGkAtoms::treerow)) {
   1001    int32_t index = FindContent(container);
   1002    if (index >= 0 && mTree) {
   1003      mTree->InvalidateRow(index);
   1004    }
   1005  } else if (aChild->IsXULElement(nsGkAtoms::treecell)) {
   1006    nsCOMPtr<nsIContent> parent = container->GetParent();
   1007    if (parent) {
   1008      int32_t index = FindContent(parent);
   1009      if (index >= 0 && mTree) {
   1010        mTree->InvalidateRow(index);
   1011      }
   1012    }
   1013  }
   1014 }
   1015 
   1016 void nsTreeContentView::NodeWillBeDestroyed(nsINode* aNode) {
   1017  // XXXbz do we need this strong ref?  Do we drop refs to self in ClearRows?
   1018  nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   1019  ClearRows();
   1020 }
   1021 
   1022 // Recursively serialize content, starting with aContent.
   1023 void nsTreeContentView::Serialize(nsIContent* aContent, int32_t aParentIndex,
   1024                                  int32_t* aIndex,
   1025                                  nsTArray<UniquePtr<Row>>& aRows) {
   1026  // Don't allow non-XUL nodes.
   1027  if (!aContent->IsXULElement()) {
   1028    return;
   1029  }
   1030 
   1031  dom::FlattenedChildIterator iter(aContent);
   1032  for (nsIContent* content = iter.GetNextChild(); content;
   1033       content = iter.GetNextChild()) {
   1034    int32_t count = aRows.Length();
   1035 
   1036    if (content->IsXULElement(nsGkAtoms::treeitem)) {
   1037      SerializeItem(content->AsElement(), aParentIndex, aIndex, aRows);
   1038    } else if (content->IsXULElement(nsGkAtoms::treeseparator)) {
   1039      SerializeSeparator(content->AsElement(), aParentIndex, aIndex, aRows);
   1040    }
   1041 
   1042    *aIndex += aRows.Length() - count;
   1043  }
   1044 }
   1045 
   1046 void nsTreeContentView::SerializeItem(Element* aContent, int32_t aParentIndex,
   1047                                      int32_t* aIndex,
   1048                                      nsTArray<UniquePtr<Row>>& aRows) {
   1049  if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
   1050                            nsGkAtoms::_true, eCaseMatters)) {
   1051    return;
   1052  }
   1053 
   1054  aRows.AppendElement(MakeUnique<Row>(aContent, aParentIndex));
   1055  Row* row = aRows.LastElement().get();
   1056 
   1057  if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
   1058                            nsGkAtoms::_true, eCaseMatters)) {
   1059    row->SetContainer(true);
   1060    if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
   1061                              nsGkAtoms::_true, eCaseMatters)) {
   1062      row->SetOpen(true);
   1063      nsIContent* child =
   1064          nsTreeUtils::GetImmediateChild(aContent, nsGkAtoms::treechildren);
   1065      if (child && child->IsXULElement()) {
   1066        // Now, recursively serialize our child.
   1067        int32_t count = aRows.Length();
   1068        int32_t index = 0;
   1069        Serialize(child, aParentIndex + *aIndex + 1, &index, aRows);
   1070        row->mSubtreeSize += aRows.Length() - count;
   1071      } else {
   1072        row->SetEmpty(true);
   1073      }
   1074    } else if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty,
   1075                                     nsGkAtoms::_true, eCaseMatters)) {
   1076      row->SetEmpty(true);
   1077    }
   1078  }
   1079 }
   1080 
   1081 void nsTreeContentView::SerializeSeparator(Element* aContent,
   1082                                           int32_t aParentIndex,
   1083                                           int32_t* aIndex,
   1084                                           nsTArray<UniquePtr<Row>>& aRows) {
   1085  if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
   1086                            nsGkAtoms::_true, eCaseMatters)) {
   1087    return;
   1088  }
   1089 
   1090  auto row = MakeUnique<Row>(aContent, aParentIndex);
   1091  row->SetSeparator(true);
   1092  aRows.AppendElement(std::move(row));
   1093 }
   1094 
   1095 void nsTreeContentView::GetIndexInSubtree(nsIContent* aContainer,
   1096                                          nsIContent* aContent,
   1097                                          int32_t* aIndex) {
   1098  if (!aContainer->IsXULElement()) {
   1099    return;
   1100  }
   1101 
   1102  for (nsIContent* content = aContainer->GetFirstChild(); content;
   1103       content = content->GetNextSibling()) {
   1104    if (content == aContent) {
   1105      break;
   1106    }
   1107 
   1108    if (content->IsXULElement(nsGkAtoms::treeitem)) {
   1109      if (!content->AsElement()->AttrValueIs(kNameSpaceID_None,
   1110                                             nsGkAtoms::hidden,
   1111                                             nsGkAtoms::_true, eCaseMatters)) {
   1112        (*aIndex)++;
   1113        if (content->AsElement()->AttrValueIs(kNameSpaceID_None,
   1114                                              nsGkAtoms::container,
   1115                                              nsGkAtoms::_true, eCaseMatters) &&
   1116            content->AsElement()->AttrValueIs(kNameSpaceID_None,
   1117                                              nsGkAtoms::open, nsGkAtoms::_true,
   1118                                              eCaseMatters)) {
   1119          nsIContent* child =
   1120              nsTreeUtils::GetImmediateChild(content, nsGkAtoms::treechildren);
   1121          if (child && child->IsXULElement()) {
   1122            GetIndexInSubtree(child, aContent, aIndex);
   1123          }
   1124        }
   1125      }
   1126    } else if (content->IsXULElement(nsGkAtoms::treeseparator)) {
   1127      if (!content->AsElement()->AttrValueIs(kNameSpaceID_None,
   1128                                             nsGkAtoms::hidden,
   1129                                             nsGkAtoms::_true, eCaseMatters)) {
   1130        (*aIndex)++;
   1131      }
   1132    }
   1133  }
   1134 }
   1135 
   1136 int32_t nsTreeContentView::EnsureSubtree(int32_t aIndex) {
   1137  Row* row = mRows[aIndex].get();
   1138 
   1139  nsIContent* child;
   1140  child =
   1141      nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treechildren);
   1142  if (!child || !child->IsXULElement()) {
   1143    return 0;
   1144  }
   1145 
   1146  AutoTArray<UniquePtr<Row>, 8> rows;
   1147  int32_t index = 0;
   1148  Serialize(child, aIndex, &index, rows);
   1149  // Insert |rows| into |mRows| at position |aIndex|, by first creating empty
   1150  // UniquePtr entries and then Move'ing |rows|'s entries into them. (Note
   1151  // that we can't simply use InsertElementsAt with an array argument, since
   1152  // the destination can't steal ownership from its const source argument.)
   1153  UniquePtr<Row>* newRows = mRows.InsertElementsAt(aIndex + 1, rows.Length());
   1154  for (nsTArray<Row>::index_type i = 0; i < rows.Length(); i++) {
   1155    newRows[i] = std::move(rows[i]);
   1156  }
   1157  int32_t count = rows.Length();
   1158 
   1159  row->mSubtreeSize += count;
   1160  UpdateSubtreeSizes(row->mParentIndex, count);
   1161 
   1162  // Update parent indexes, but skip newly added rows.
   1163  // They already have correct values.
   1164  UpdateParentIndexes(aIndex, count + 1, count);
   1165 
   1166  return count;
   1167 }
   1168 
   1169 int32_t nsTreeContentView::RemoveSubtree(int32_t aIndex) {
   1170  Row* row = mRows[aIndex].get();
   1171  int32_t count = row->mSubtreeSize;
   1172 
   1173  mRows.RemoveElementsAt(aIndex + 1, count);
   1174 
   1175  row->mSubtreeSize -= count;
   1176  UpdateSubtreeSizes(row->mParentIndex, -count);
   1177 
   1178  UpdateParentIndexes(aIndex, 0, -count);
   1179 
   1180  return count;
   1181 }
   1182 
   1183 void nsTreeContentView::InsertRowFor(nsIContent* aParent, nsIContent* aChild) {
   1184  int32_t grandParentIndex = -1;
   1185  bool insertRow = false;
   1186 
   1187  nsCOMPtr<nsIContent> grandParent = aParent->GetParent();
   1188 
   1189  if (grandParent->IsXULElement(nsGkAtoms::tree)) {
   1190    // Allow insertion to the outermost container.
   1191    insertRow = true;
   1192  } else {
   1193    // Test insertion to an inner container.
   1194 
   1195    // First try to find this parent in our array of rows, if we find one
   1196    // we can be sure that all other parents are open too.
   1197    grandParentIndex = FindContent(grandParent);
   1198    if (grandParentIndex >= 0) {
   1199      // Got it, now test if it is open.
   1200      if (mRows[grandParentIndex]->IsOpen()) {
   1201        insertRow = true;
   1202      }
   1203    }
   1204  }
   1205 
   1206  if (insertRow) {
   1207    int32_t index = 0;
   1208    GetIndexInSubtree(aParent, aChild, &index);
   1209 
   1210    int32_t count = InsertRow(grandParentIndex, index, aChild);
   1211    if (mTree) {
   1212      mTree->RowCountChanged(grandParentIndex + index + 1, count);
   1213    }
   1214  }
   1215 }
   1216 
   1217 int32_t nsTreeContentView::InsertRow(int32_t aParentIndex, int32_t aIndex,
   1218                                     nsIContent* aContent) {
   1219  AutoTArray<UniquePtr<Row>, 8> rows;
   1220  if (aContent->IsXULElement(nsGkAtoms::treeitem)) {
   1221    SerializeItem(aContent->AsElement(), aParentIndex, &aIndex, rows);
   1222  } else if (aContent->IsXULElement(nsGkAtoms::treeseparator)) {
   1223    SerializeSeparator(aContent->AsElement(), aParentIndex, &aIndex, rows);
   1224  }
   1225 
   1226  // We can't use InsertElementsAt since the destination can't steal
   1227  // ownership from its const source argument.
   1228  int32_t count = rows.Length();
   1229  for (nsTArray<Row>::index_type i = 0; i < size_t(count); i++) {
   1230    mRows.InsertElementAt(aParentIndex + aIndex + i + 1, std::move(rows[i]));
   1231  }
   1232 
   1233  UpdateSubtreeSizes(aParentIndex, count);
   1234 
   1235  // Update parent indexes, but skip added rows.
   1236  // They already have correct values.
   1237  UpdateParentIndexes(aParentIndex + aIndex, count + 1, count);
   1238 
   1239  return count;
   1240 }
   1241 
   1242 int32_t nsTreeContentView::RemoveRow(int32_t aIndex) {
   1243  Row* row = mRows[aIndex].get();
   1244  int32_t count = row->mSubtreeSize + 1;
   1245  int32_t parentIndex = row->mParentIndex;
   1246 
   1247  mRows.RemoveElementsAt(aIndex, count);
   1248 
   1249  UpdateSubtreeSizes(parentIndex, -count);
   1250 
   1251  UpdateParentIndexes(aIndex, 0, -count);
   1252 
   1253  return count;
   1254 }
   1255 
   1256 void nsTreeContentView::ClearRows() {
   1257  mRows.Clear();
   1258  mBody = nullptr;
   1259  // Remove ourselves from mDocument's observers.
   1260  if (mDocument) {
   1261    mDocument->RemoveObserver(this);
   1262    mDocument = nullptr;
   1263  }
   1264 }
   1265 
   1266 void nsTreeContentView::OpenContainer(int32_t aIndex) {
   1267  Row* row = mRows[aIndex].get();
   1268  row->SetOpen(true);
   1269 
   1270  int32_t count = EnsureSubtree(aIndex);
   1271  if (mTree) {
   1272    mTree->InvalidateRow(aIndex);
   1273    mTree->RowCountChanged(aIndex + 1, count);
   1274  }
   1275 }
   1276 
   1277 void nsTreeContentView::CloseContainer(int32_t aIndex) {
   1278  Row* row = mRows[aIndex].get();
   1279  row->SetOpen(false);
   1280 
   1281  int32_t count = RemoveSubtree(aIndex);
   1282  if (mTree) {
   1283    mTree->InvalidateRow(aIndex);
   1284    mTree->RowCountChanged(aIndex + 1, -count);
   1285  }
   1286 }
   1287 
   1288 int32_t nsTreeContentView::FindContent(nsIContent* aContent) {
   1289  for (uint32_t i = 0; i < mRows.Length(); i++) {
   1290    if (mRows[i]->mContent == aContent) {
   1291      return i;
   1292    }
   1293  }
   1294 
   1295  return -1;
   1296 }
   1297 
   1298 void nsTreeContentView::UpdateSubtreeSizes(int32_t aParentIndex,
   1299                                           int32_t count) {
   1300  while (aParentIndex >= 0) {
   1301    Row* row = mRows[aParentIndex].get();
   1302    row->mSubtreeSize += count;
   1303    aParentIndex = row->mParentIndex;
   1304  }
   1305 }
   1306 
   1307 void nsTreeContentView::UpdateParentIndexes(int32_t aIndex, int32_t aSkip,
   1308                                            int32_t aCount) {
   1309  int32_t count = mRows.Length();
   1310  for (int32_t i = aIndex + aSkip; i < count; i++) {
   1311    Row* row = mRows[i].get();
   1312    if (row->mParentIndex > aIndex) {
   1313      row->mParentIndex += aCount;
   1314    }
   1315  }
   1316 }
   1317 
   1318 Element* nsTreeContentView::GetCell(nsIContent* aContainer,
   1319                                    nsTreeColumn& aCol) {
   1320  int32_t colIndex(aCol.GetIndex());
   1321 
   1322  // Traverse through cells, try to find the cell by index in a row.
   1323  Element* result = nullptr;
   1324  int32_t j = 0;
   1325  dom::FlattenedChildIterator iter(aContainer);
   1326  for (nsIContent* cell = iter.GetNextChild(); cell;
   1327       cell = iter.GetNextChild()) {
   1328    if (cell->IsXULElement(nsGkAtoms::treecell)) {
   1329      if (j == colIndex) {
   1330        result = cell->AsElement();
   1331        break;
   1332      }
   1333      j++;
   1334    }
   1335  }
   1336 
   1337  return result;
   1338 }
   1339 
   1340 bool nsTreeContentView::IsValidRowIndex(int32_t aRowIndex) {
   1341  return aRowIndex >= 0 && aRowIndex < int32_t(mRows.Length());
   1342 }