layout/xul/tree/nsTreeContentView.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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 "nsNameSpaceManager.h"
     7 #include "nsGkAtoms.h"
     8 #include "nsIBoxObject.h"
     9 #include "nsTreeUtils.h"
    10 #include "nsTreeContentView.h"
    11 #include "ChildIterator.h"
    12 #include "nsDOMClassInfoID.h"
    13 #include "nsError.h"
    14 #include "nsINodeInfo.h"
    15 #include "nsIXULSortService.h"
    16 #include "nsContentUtils.h"
    17 #include "nsTreeBodyFrame.h"
    18 #include "mozilla/dom/Element.h"
    19 #include "nsServiceManagerUtils.h"
    21 using namespace mozilla;
    23 #define NS_ENSURE_NATIVE_COLUMN(_col)                                \
    24   nsRefPtr<nsTreeColumn> col = nsTreeBodyFrame::GetColumnImpl(_col); \
    25   if (!col) {                                                        \
    26     return NS_ERROR_INVALID_ARG;                                     \
    27   }
    29 // A content model view implementation for the tree.
    31 #define ROW_FLAG_CONTAINER      0x01
    32 #define ROW_FLAG_OPEN           0x02
    33 #define ROW_FLAG_EMPTY          0x04
    34 #define ROW_FLAG_SEPARATOR      0x08
    36 class Row
    37 {
    38   public:
    39     Row(nsIContent* aContent, int32_t aParentIndex)
    40       : mContent(aContent), mParentIndex(aParentIndex),
    41         mSubtreeSize(0), mFlags(0) {
    42     }
    44     ~Row() {
    45     }
    47     void SetContainer(bool aContainer) {
    48       aContainer ? mFlags |= ROW_FLAG_CONTAINER : mFlags &= ~ROW_FLAG_CONTAINER;
    49     }
    50     bool IsContainer() { return mFlags & ROW_FLAG_CONTAINER; }
    52     void SetOpen(bool aOpen) {
    53       aOpen ? mFlags |= ROW_FLAG_OPEN : mFlags &= ~ROW_FLAG_OPEN;
    54     }
    55     bool IsOpen() { return !!(mFlags & ROW_FLAG_OPEN); }
    57     void SetEmpty(bool aEmpty) {
    58       aEmpty ? mFlags |= ROW_FLAG_EMPTY : mFlags &= ~ROW_FLAG_EMPTY;
    59     }
    60     bool IsEmpty() { return !!(mFlags & ROW_FLAG_EMPTY); }
    62     void SetSeparator(bool aSeparator) {
    63       aSeparator ? mFlags |= ROW_FLAG_SEPARATOR : mFlags &= ~ROW_FLAG_SEPARATOR;
    64     }
    65     bool IsSeparator() { return !!(mFlags & ROW_FLAG_SEPARATOR); }
    67     // Weak reference to a content item.
    68     nsIContent*         mContent;
    70     // The parent index of the item, set to -1 for the top level items.
    71     int32_t             mParentIndex;
    73     // Subtree size for this item.
    74     int32_t             mSubtreeSize;
    76   private:
    77     // State flags
    78     int8_t		mFlags;
    79 };
    82 // We don't reference count the reference to the document
    83 // If the document goes away first, we'll be informed and we
    84 // can drop our reference.
    85 // If we go away first, we'll get rid of ourselves from the
    86 // document's observer list.
    88 nsTreeContentView::nsTreeContentView(void) :
    89   mBoxObject(nullptr),
    90   mSelection(nullptr),
    91   mRoot(nullptr),
    92   mDocument(nullptr)
    93 {
    94 }
    96 nsTreeContentView::~nsTreeContentView(void)
    97 {
    98   // Remove ourselves from mDocument's observers.
    99   if (mDocument)
   100     mDocument->RemoveObserver(this);
   101 }
   103 nsresult
   104 NS_NewTreeContentView(nsITreeView** aResult)
   105 {
   106   *aResult = new nsTreeContentView;
   107   if (! *aResult)
   108     return NS_ERROR_OUT_OF_MEMORY;
   109   NS_ADDREF(*aResult);
   110   return NS_OK;
   111 }
   113 NS_IMPL_CYCLE_COLLECTION(nsTreeContentView,
   114                          mBoxObject,
   115                          mSelection,
   116                          mRoot,
   117                          mBody)
   119 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeContentView)
   120 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeContentView)
   122 DOMCI_DATA(TreeContentView, nsTreeContentView)
   124 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeContentView)
   125   NS_INTERFACE_MAP_ENTRY(nsITreeView)
   126   NS_INTERFACE_MAP_ENTRY(nsITreeContentView)
   127   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
   128   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   129   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITreeContentView)
   130   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeContentView)
   131 NS_INTERFACE_MAP_END
   133 NS_IMETHODIMP
   134 nsTreeContentView::GetRowCount(int32_t* aRowCount)
   135 {
   136   *aRowCount = mRows.Length();
   138   return NS_OK;
   139 }
   141 NS_IMETHODIMP
   142 nsTreeContentView::GetSelection(nsITreeSelection** aSelection)
   143 {
   144   NS_IF_ADDREF(*aSelection = mSelection);
   146   return NS_OK;
   147 }
   149 bool
   150 nsTreeContentView::CanTrustTreeSelection(nsISupports* aValue)
   151 {
   152   // Untrusted content is only allowed to specify known-good views
   153   if (nsContentUtils::IsCallerChrome())
   154     return true;
   155   nsCOMPtr<nsINativeTreeSelection> nativeTreeSel = do_QueryInterface(aValue);
   156   return nativeTreeSel && NS_SUCCEEDED(nativeTreeSel->EnsureNative());
   157 }
   159 NS_IMETHODIMP
   160 nsTreeContentView::SetSelection(nsITreeSelection* aSelection)
   161 {
   162   NS_ENSURE_TRUE(!aSelection || CanTrustTreeSelection(aSelection),
   163                  NS_ERROR_DOM_SECURITY_ERR);
   165   mSelection = aSelection;
   166   return NS_OK;
   167 }
   169 NS_IMETHODIMP
   170 nsTreeContentView::GetRowProperties(int32_t aIndex, nsAString& aProps)
   171 {
   172   NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
   173   if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
   174     return NS_ERROR_INVALID_ARG;   
   176   Row* row = mRows[aIndex];
   177   nsIContent* realRow;
   178   if (row->IsSeparator())
   179     realRow = row->mContent;
   180   else
   181     realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   183   if (realRow) {
   184     realRow->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, aProps);
   185   }
   187   return NS_OK;
   188 }
   190 NS_IMETHODIMP
   191 nsTreeContentView::GetCellProperties(int32_t aRow, nsITreeColumn* aCol,
   192                                      nsAString& aProps)
   193 {
   194   NS_ENSURE_NATIVE_COLUMN(aCol);
   195   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   196   if (aRow < 0 || aRow >= int32_t(mRows.Length()))
   197     return NS_ERROR_INVALID_ARG;   
   199   Row* row = mRows[aRow];
   200   nsIContent* realRow =
   201     nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   202   if (realRow) {
   203     nsIContent* cell = GetCell(realRow, aCol);
   204     if (cell) {
   205       cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, aProps);
   206     }
   207   }
   209   return NS_OK;
   210 }
   212 NS_IMETHODIMP
   213 nsTreeContentView::GetColumnProperties(nsITreeColumn* aCol, nsAString& aProps)
   214 {
   215   NS_ENSURE_NATIVE_COLUMN(aCol);
   216   nsCOMPtr<nsIDOMElement> element;
   217   aCol->GetElement(getter_AddRefs(element));
   219   element->GetAttribute(NS_LITERAL_STRING("properties"), aProps);
   220   return NS_OK;
   221 }
   223 NS_IMETHODIMP
   224 nsTreeContentView::IsContainer(int32_t aIndex, bool* _retval)
   225 {
   226   NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
   227   if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
   228     return NS_ERROR_INVALID_ARG;   
   230   *_retval = mRows[aIndex]->IsContainer();
   232   return NS_OK;
   233 }
   235 NS_IMETHODIMP
   236 nsTreeContentView::IsContainerOpen(int32_t aIndex, bool* _retval)
   237 {
   238   NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
   239   if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
   240     return NS_ERROR_INVALID_ARG;   
   242   *_retval = mRows[aIndex]->IsOpen();
   244   return NS_OK;
   245 }
   247 NS_IMETHODIMP
   248 nsTreeContentView::IsContainerEmpty(int32_t aIndex, bool* _retval)
   249 {
   250   NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
   251   if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
   252     return NS_ERROR_INVALID_ARG;   
   254   *_retval = mRows[aIndex]->IsEmpty();
   256   return NS_OK;
   257 }
   259 NS_IMETHODIMP
   260 nsTreeContentView::IsSeparator(int32_t aIndex, bool *_retval)
   261 {
   262   NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
   263   if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
   264     return NS_ERROR_INVALID_ARG;   
   266   *_retval = mRows[aIndex]->IsSeparator();
   268   return NS_OK;
   269 }
   271 NS_IMETHODIMP
   272 nsTreeContentView::IsSorted(bool *_retval)
   273 {
   274   *_retval = false;
   276   return NS_OK;
   277 }
   279 NS_IMETHODIMP
   280 nsTreeContentView::CanDrop(int32_t aIndex, int32_t aOrientation,
   281                            nsIDOMDataTransfer* aDataTransfer, bool *_retval)
   282 {
   283   NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
   284   if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
   285     return NS_ERROR_INVALID_ARG;   
   287   *_retval = false;
   289   return NS_OK;
   290 }
   292 NS_IMETHODIMP
   293 nsTreeContentView::Drop(int32_t aRow, int32_t aOrientation, nsIDOMDataTransfer* aDataTransfer)
   294 {
   295   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   296   if (aRow < 0 || aRow >= int32_t(mRows.Length()))
   297     return NS_ERROR_INVALID_ARG;   
   299   return NS_OK;
   300 }
   302 NS_IMETHODIMP
   303 nsTreeContentView::GetParentIndex(int32_t aRowIndex, int32_t* _retval)
   304 {
   305   NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < int32_t(mRows.Length()),
   306                   "bad row index");
   307   if (aRowIndex < 0 || aRowIndex >= int32_t(mRows.Length()))
   308     return NS_ERROR_INVALID_ARG;   
   310   *_retval = mRows[aRowIndex]->mParentIndex;
   312   return NS_OK;
   313 }
   315 NS_IMETHODIMP
   316 nsTreeContentView::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, bool* _retval)
   317 {
   318   NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < int32_t(mRows.Length()),
   319                   "bad row index");
   320   if (aRowIndex < 0 || aRowIndex >= int32_t(mRows.Length()))
   321     return NS_ERROR_INVALID_ARG;   
   323   // We have a next sibling if the row is not the last in the subtree.
   324   int32_t parentIndex = mRows[aRowIndex]->mParentIndex;
   325   if (parentIndex >= 0) {
   326     // Compute the last index in this subtree.
   327     int32_t lastIndex = parentIndex + (mRows[parentIndex])->mSubtreeSize;
   328     Row* row = mRows[lastIndex];
   329     while (row->mParentIndex != parentIndex) {
   330       lastIndex = row->mParentIndex;
   331       row = mRows[lastIndex];
   332     }
   334     *_retval = aRowIndex < lastIndex;
   335   }
   336   else {
   337     *_retval = uint32_t(aRowIndex) < mRows.Length() - 1;
   338   }
   340   return NS_OK;
   341 }
   343 NS_IMETHODIMP
   344 nsTreeContentView::GetLevel(int32_t aIndex, int32_t* _retval)
   345 {
   346   NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
   347   if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
   348     return NS_ERROR_INVALID_ARG;   
   350   int32_t level = 0;
   351   Row* row = mRows[aIndex];
   352   while (row->mParentIndex >= 0) {
   353     level++;
   354     row = mRows[row->mParentIndex];
   355   }
   356   *_retval = level;
   358   return NS_OK;
   359 }
   361  NS_IMETHODIMP
   362 nsTreeContentView::GetImageSrc(int32_t aRow, nsITreeColumn* aCol, nsAString& _retval)
   363 {
   364   _retval.Truncate();
   365   NS_ENSURE_NATIVE_COLUMN(aCol);
   366   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   367   if (aRow < 0 || aRow >= int32_t(mRows.Length()))
   368     return NS_ERROR_INVALID_ARG;   
   370   Row* row = mRows[aRow];
   372   nsIContent* realRow =
   373     nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   374   if (realRow) {
   375     nsIContent* cell = GetCell(realRow, aCol);
   376     if (cell)
   377       cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, _retval);
   378   }
   380   return NS_OK;
   381 }
   383 NS_IMETHODIMP
   384 nsTreeContentView::GetProgressMode(int32_t aRow, nsITreeColumn* aCol, int32_t* _retval)
   385 {
   386   NS_ENSURE_NATIVE_COLUMN(aCol);
   387   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   388   if (aRow < 0 || aRow >= int32_t(mRows.Length()))
   389     return NS_ERROR_INVALID_ARG;   
   391   *_retval = nsITreeView::PROGRESS_NONE;
   393   Row* row = mRows[aRow];
   395   nsIContent* realRow =
   396     nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   397   if (realRow) {
   398     nsIContent* cell = GetCell(realRow, aCol);
   399     if (cell) {
   400       static nsIContent::AttrValuesArray strings[] =
   401         {&nsGkAtoms::normal, &nsGkAtoms::undetermined, nullptr};
   402       switch (cell->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::mode,
   403                                     strings, eCaseMatters)) {
   404         case 0: *_retval = nsITreeView::PROGRESS_NORMAL; break;
   405         case 1: *_retval = nsITreeView::PROGRESS_UNDETERMINED; break;
   406       }
   407     }
   408   }
   410   return NS_OK;
   411 }
   413 NS_IMETHODIMP
   414 nsTreeContentView::GetCellValue(int32_t aRow, nsITreeColumn* aCol, nsAString& _retval)
   415 {
   416   _retval.Truncate();
   417   NS_ENSURE_NATIVE_COLUMN(aCol);
   418   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   419   if (aRow < 0 || aRow >= int32_t(mRows.Length()))
   420     return NS_ERROR_INVALID_ARG;   
   422   Row* row = mRows[aRow];
   424   nsIContent* realRow =
   425     nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   426   if (realRow) {
   427     nsIContent* cell = GetCell(realRow, aCol);
   428     if (cell)
   429       cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, _retval);
   430   }
   432   return NS_OK;
   433 }
   435 NS_IMETHODIMP
   436 nsTreeContentView::GetCellText(int32_t aRow, nsITreeColumn* aCol, nsAString& _retval)
   437 {
   438   _retval.Truncate();
   439   NS_ENSURE_NATIVE_COLUMN(aCol);
   440   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   441   NS_PRECONDITION(aCol, "bad column");
   443   if (aRow < 0 || aRow >= int32_t(mRows.Length()) || !aCol)
   444     return NS_ERROR_INVALID_ARG;
   446   Row* row = mRows[aRow];
   448   // Check for a "label" attribute - this is valid on an <treeitem>
   449   // with a single implied column.
   450   if (row->mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, _retval)
   451       && !_retval.IsEmpty())
   452     return NS_OK;
   454   nsIAtom *rowTag = row->mContent->Tag();
   455   if (rowTag == nsGkAtoms::treeitem && row->mContent->IsXUL()) {
   456     nsIContent* realRow =
   457       nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   458     if (realRow) {
   459       nsIContent* cell = GetCell(realRow, aCol);
   460       if (cell)
   461         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, _retval);
   462     }
   463   }
   465   return NS_OK;
   466 }
   468 NS_IMETHODIMP
   469 nsTreeContentView::SetTree(nsITreeBoxObject* aTree)
   470 {
   471   ClearRows();
   473   mBoxObject = aTree;
   475   MOZ_ASSERT(!mRoot, "mRoot should have been cleared out by ClearRows");
   477   if (aTree) {
   478     // Get our root element
   479     nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mBoxObject);
   480     if (!boxObject) {
   481       mBoxObject = nullptr;
   482       return NS_ERROR_INVALID_ARG;
   483     }
   484     nsCOMPtr<nsIDOMElement> element;
   485     boxObject->GetElement(getter_AddRefs(element));
   487     mRoot = do_QueryInterface(element);
   488     NS_ENSURE_STATE(mRoot);
   490     // Add ourselves to document's observers.
   491     nsIDocument* document = mRoot->GetDocument();
   492     if (document) {
   493       document->AddObserver(this);
   494       mDocument = document;
   495     }
   497     nsCOMPtr<nsIDOMElement> bodyElement;
   498     mBoxObject->GetTreeBody(getter_AddRefs(bodyElement));
   499     if (bodyElement) {
   500       mBody = do_QueryInterface(bodyElement);
   501       int32_t index = 0;
   502       Serialize(mBody, -1, &index, mRows);
   503     }
   504   }
   506   return NS_OK;
   507 }
   509 NS_IMETHODIMP
   510 nsTreeContentView::ToggleOpenState(int32_t aIndex)
   511 {
   512   NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
   513   if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
   514     return NS_ERROR_INVALID_ARG;   
   516   // We don't serialize content right here, since content might be generated
   517   // lazily.
   518   Row* row = mRows[aIndex];
   520   if (row->IsOpen())
   521     row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("false"), true);
   522   else
   523     row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("true"), true);
   525   return NS_OK;
   526 }
   528 NS_IMETHODIMP
   529 nsTreeContentView::CycleHeader(nsITreeColumn* aCol)
   530 {
   531   NS_ENSURE_NATIVE_COLUMN(aCol);
   533   if (!mRoot)
   534     return NS_OK;
   536   nsCOMPtr<nsIDOMElement> element;
   537   aCol->GetElement(getter_AddRefs(element));
   538   if (element) {
   539     nsCOMPtr<nsIContent> column = do_QueryInterface(element);
   540     nsAutoString sort;
   541     column->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
   542     if (!sort.IsEmpty()) {
   543       nsCOMPtr<nsIXULSortService> xs = do_GetService("@mozilla.org/xul/xul-sort-service;1");
   544       if (xs) {
   545         nsAutoString sortdirection;
   546         static nsIContent::AttrValuesArray strings[] =
   547           {&nsGkAtoms::ascending, &nsGkAtoms::descending, nullptr};
   548         switch (column->FindAttrValueIn(kNameSpaceID_None,
   549                                         nsGkAtoms::sortDirection,
   550                                         strings, eCaseMatters)) {
   551           case 0: sortdirection.AssignLiteral("descending"); break;
   552           case 1: sortdirection.AssignLiteral("natural"); break;
   553           default: sortdirection.AssignLiteral("ascending"); break;
   554         }
   556         nsAutoString hints;
   557         column->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints);
   558         sortdirection.AppendLiteral(" ");
   559         sortdirection += hints;
   561         nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot);
   562         xs->Sort(rootnode, sort, sortdirection);
   563       }
   564     }
   565   }
   567   return NS_OK;
   568 }
   570 NS_IMETHODIMP
   571 nsTreeContentView::SelectionChanged()
   572 {
   573   return NS_OK;
   574 }
   576 NS_IMETHODIMP
   577 nsTreeContentView::CycleCell(int32_t aRow, nsITreeColumn* aCol)
   578 {
   579   return NS_OK;
   580 }
   582 NS_IMETHODIMP
   583 nsTreeContentView::IsEditable(int32_t aRow, nsITreeColumn* aCol, bool* _retval)
   584 {
   585   *_retval = false;
   586   NS_ENSURE_NATIVE_COLUMN(aCol);
   587   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   588   if (aRow < 0 || aRow >= int32_t(mRows.Length()))
   589     return NS_ERROR_INVALID_ARG;   
   591   *_retval = true;
   593   Row* row = mRows[aRow];
   595   nsIContent* realRow =
   596     nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   597   if (realRow) {
   598     nsIContent* cell = GetCell(realRow, aCol);
   599     if (cell && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
   600                                   nsGkAtoms::_false, eCaseMatters)) {
   601       *_retval = false;
   602     }
   603   }
   605   return NS_OK;
   606 }
   608 NS_IMETHODIMP
   609 nsTreeContentView::IsSelectable(int32_t aRow, nsITreeColumn* aCol, bool* _retval)
   610 {
   611   NS_ENSURE_NATIVE_COLUMN(aCol);
   612   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   613   if (aRow < 0 || aRow >= int32_t(mRows.Length()))
   614     return NS_ERROR_INVALID_ARG;   
   616   *_retval = true;
   618   Row* row = mRows[aRow];
   620   nsIContent* realRow =
   621     nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   622   if (realRow) {
   623     nsIContent* cell = GetCell(realRow, aCol);
   624     if (cell && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::selectable,
   625                                   nsGkAtoms::_false, eCaseMatters)) {
   626       *_retval = false;
   627     }
   628   }
   630   return NS_OK;
   631 }
   633 NS_IMETHODIMP
   634 nsTreeContentView::SetCellValue(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue)
   635 {
   636   NS_ENSURE_NATIVE_COLUMN(aCol);
   637   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   638   if (aRow < 0 || aRow >= int32_t(mRows.Length()))
   639     return NS_ERROR_INVALID_ARG;   
   641   Row* row = mRows[aRow];
   643   nsIContent* realRow =
   644     nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   645   if (realRow) {
   646     nsIContent* cell = GetCell(realRow, aCol);
   647     if (cell)
   648       cell->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, true);
   649   }
   651   return NS_OK;
   652 }
   654 NS_IMETHODIMP
   655 nsTreeContentView::SetCellText(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue)
   656 {
   657   NS_ENSURE_NATIVE_COLUMN(aCol);
   658   NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
   659   if (aRow < 0 || aRow >= int32_t(mRows.Length()))
   660     return NS_ERROR_INVALID_ARG;   
   662   Row* row = mRows[aRow];
   664   nsIContent* realRow =
   665     nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
   666   if (realRow) {
   667     nsIContent* cell = GetCell(realRow, aCol);
   668     if (cell)
   669       cell->SetAttr(kNameSpaceID_None, nsGkAtoms::label, aValue, true);
   670   }
   672   return NS_OK;
   673 }
   675 NS_IMETHODIMP
   676 nsTreeContentView::PerformAction(const char16_t* aAction)
   677 {
   678   return NS_OK;
   679 }
   681 NS_IMETHODIMP
   682 nsTreeContentView::PerformActionOnRow(const char16_t* aAction, int32_t aRow)
   683 {
   684   return NS_OK;
   685 }
   687 NS_IMETHODIMP
   688 nsTreeContentView::PerformActionOnCell(const char16_t* aAction, int32_t aRow, nsITreeColumn* aCol)
   689 {
   690   return NS_OK;
   691 }
   694 NS_IMETHODIMP
   695 nsTreeContentView::GetItemAtIndex(int32_t aIndex, nsIDOMElement** _retval)
   696 {
   697   NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
   698   if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
   699     return NS_ERROR_INVALID_ARG;   
   701   Row* row = mRows[aIndex];
   702   row->mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)_retval);
   704   return NS_OK;
   705 }
   707 NS_IMETHODIMP
   708 nsTreeContentView::GetIndexOfItem(nsIDOMElement* aItem, int32_t* _retval)
   709 {
   710   nsCOMPtr<nsIContent> content = do_QueryInterface(aItem);
   711   *_retval = FindContent(content);
   713   return NS_OK;
   714 }
   716 void
   717 nsTreeContentView::AttributeChanged(nsIDocument*  aDocument,
   718                                     dom::Element* aElement,
   719                                     int32_t       aNameSpaceID,
   720                                     nsIAtom*      aAttribute,
   721                                     int32_t       aModType)
   722 {
   723   // Lots of codepaths under here that do all sorts of stuff, so be safe.
   724   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   726   // Make sure this notification concerns us.
   727   // First check the tag to see if it's one that we care about.
   728   nsIAtom* tag = aElement->Tag();
   730   if (mBoxObject && (aElement == mRoot || aElement == mBody)) {
   731     mBoxObject->ClearStyleAndImageCaches();
   732     mBoxObject->Invalidate();
   733   }
   735   // We don't consider non-XUL nodes.
   736   nsIContent* parent = nullptr;
   737   if (!aElement->IsXUL() ||
   738       ((parent = aElement->GetParent()) && !parent->IsXUL())) {
   739     return;
   740   }
   741   if (tag != nsGkAtoms::treecol &&
   742       tag != nsGkAtoms::treeitem &&
   743       tag != nsGkAtoms::treeseparator &&
   744       tag != nsGkAtoms::treerow &&
   745       tag != nsGkAtoms::treecell) {
   746     return;
   747   }
   749   // If we have a legal tag, go up to the tree/select and make sure
   750   // that it's ours.
   752   for (nsIContent* element = aElement; element != mBody; element = element->GetParent()) {
   753     if (!element)
   754       return; // this is not for us
   755     nsIAtom *parentTag = element->Tag();
   756     if (element->IsXUL() && parentTag == nsGkAtoms::tree)
   757       return; // this is not for us
   758   }
   760   // Handle changes of the hidden attribute.
   761   if (aAttribute == nsGkAtoms::hidden &&
   762      (tag == nsGkAtoms::treeitem || tag == nsGkAtoms::treeseparator)) {
   763     bool hidden = aElement->AttrValueIs(kNameSpaceID_None,
   764                                           nsGkAtoms::hidden,
   765                                           nsGkAtoms::_true, eCaseMatters);
   767     int32_t index = FindContent(aElement);
   768     if (hidden && index >= 0) {
   769       // Hide this row along with its children.
   770       int32_t count = RemoveRow(index);
   771       if (mBoxObject)
   772         mBoxObject->RowCountChanged(index, -count);
   773     }
   774     else if (!hidden && index < 0) {
   775       // Show this row along with its children.
   776       nsCOMPtr<nsIContent> parent = aElement->GetParent();
   777       if (parent) {
   778         InsertRowFor(parent, aElement);
   779       }
   780     }
   782     return;
   783   }
   785   if (tag == nsGkAtoms::treecol) {
   786     if (aAttribute == nsGkAtoms::properties) {
   787       if (mBoxObject) {
   788         nsCOMPtr<nsITreeColumns> cols;
   789         mBoxObject->GetColumns(getter_AddRefs(cols));
   790         if (cols) {
   791           nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aElement);
   792           nsCOMPtr<nsITreeColumn> col;
   793           cols->GetColumnFor(element, getter_AddRefs(col));
   794           mBoxObject->InvalidateColumn(col);
   795         }
   796       }
   797     }
   798   }
   799   else if (tag == nsGkAtoms::treeitem) {
   800     int32_t index = FindContent(aElement);
   801     if (index >= 0) {
   802       Row* row = mRows[index];
   803       if (aAttribute == nsGkAtoms::container) {
   804         bool isContainer =
   805           aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
   806                                 nsGkAtoms::_true, eCaseMatters);
   807         row->SetContainer(isContainer);
   808         if (mBoxObject)
   809           mBoxObject->InvalidateRow(index);
   810       }
   811       else if (aAttribute == nsGkAtoms::open) {
   812         bool isOpen =
   813           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 (mBoxObject)
   827           mBoxObject->InvalidateRow(index);
   828       }
   829     }
   830   }
   831   else if (tag == nsGkAtoms::treeseparator) {
   832     int32_t index = FindContent(aElement);
   833     if (index >= 0) {
   834       if (aAttribute == nsGkAtoms::properties && mBoxObject) {
   835         mBoxObject->InvalidateRow(index);
   836       }
   837     }
   838   }
   839   else if (tag == nsGkAtoms::treerow) {
   840     if (aAttribute == nsGkAtoms::properties) {
   841       nsCOMPtr<nsIContent> parent = aElement->GetParent();
   842       if (parent) {
   843         int32_t index = FindContent(parent);
   844         if (index >= 0 && mBoxObject) {
   845           mBoxObject->InvalidateRow(index);
   846         }
   847       }
   848     }
   849   }
   850   else if (tag == nsGkAtoms::treecell) {
   851     if (aAttribute == nsGkAtoms::ref ||
   852         aAttribute == nsGkAtoms::properties ||
   853         aAttribute == nsGkAtoms::mode ||
   854         aAttribute == nsGkAtoms::src ||
   855         aAttribute == nsGkAtoms::value ||
   856         aAttribute == nsGkAtoms::label) {
   857       nsIContent* parent = aElement->GetParent();
   858       if (parent) {
   859         nsCOMPtr<nsIContent> grandParent = parent->GetParent();
   860         if (grandParent && grandParent->IsXUL()) {
   861           int32_t index = FindContent(grandParent);
   862           if (index >= 0 && mBoxObject) {
   863             // XXX Should we make an effort to invalidate only cell ?
   864             mBoxObject->InvalidateRow(index);
   865           }
   866         }
   867       }
   868     }
   869   }
   870 }
   872 void
   873 nsTreeContentView::ContentAppended(nsIDocument *aDocument,
   874                                    nsIContent* aContainer,
   875                                    nsIContent* aFirstNewContent,
   876                                    int32_t     /* unused */)
   877 {
   878   for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
   879     // Our contentinserted doesn't use the index
   880     ContentInserted(aDocument, aContainer, cur, 0);
   881   }
   882 }
   884 void
   885 nsTreeContentView::ContentInserted(nsIDocument *aDocument,
   886                                    nsIContent* aContainer,
   887                                    nsIContent* aChild,
   888                                    int32_t /* unused */)
   889 {
   890   NS_ASSERTION(aChild, "null ptr");
   892   // Make sure this notification concerns us.
   893   // First check the tag to see if it's one that we care about.
   894   nsIAtom *childTag = aChild->Tag();
   896   // Don't allow non-XUL nodes.
   897   if (!aChild->IsXUL() || !aContainer->IsXUL())
   898     return;
   899   if (childTag != nsGkAtoms::treeitem &&
   900       childTag != nsGkAtoms::treeseparator &&
   901       childTag != nsGkAtoms::treechildren &&
   902       childTag != nsGkAtoms::treerow &&
   903       childTag != nsGkAtoms::treecell) {
   904     return;
   905   }
   907   // If we have a legal tag, go up to the tree/select and make sure
   908   // that it's ours.
   910   for (nsIContent* element = aContainer; element != mBody; element = element->GetParent()) {
   911     if (!element)
   912       return; // this is not for us
   913     nsIAtom *parentTag = element->Tag();
   914     if (element->IsXUL() && parentTag == nsGkAtoms::tree)
   915       return; // this is not for us
   916   }
   918   // Lots of codepaths under here that do all sorts of stuff, so be safe.
   919   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   921   if (childTag == nsGkAtoms::treechildren) {
   922     int32_t index = FindContent(aContainer);
   923     if (index >= 0) {
   924       Row* row = mRows[index];
   925       row->SetEmpty(false);
   926       if (mBoxObject)
   927         mBoxObject->InvalidateRow(index);
   928       if (row->IsContainer() && row->IsOpen()) {
   929         int32_t count = EnsureSubtree(index);
   930         if (mBoxObject)
   931           mBoxObject->RowCountChanged(index + 1, count);
   932       }
   933     }
   934   }
   935   else if (childTag == nsGkAtoms::treeitem ||
   936            childTag == nsGkAtoms::treeseparator) {
   937     InsertRowFor(aContainer, aChild);
   938   }
   939   else if (childTag == nsGkAtoms::treerow) {
   940     int32_t index = FindContent(aContainer);
   941     if (index >= 0 && mBoxObject)
   942       mBoxObject->InvalidateRow(index);
   943   }
   944   else if (childTag == nsGkAtoms::treecell) {
   945     nsCOMPtr<nsIContent> parent = aContainer->GetParent();
   946     if (parent) {
   947       int32_t index = FindContent(parent);
   948       if (index >= 0 && mBoxObject)
   949         mBoxObject->InvalidateRow(index);
   950     }
   951   }
   952 }
   954 void
   955 nsTreeContentView::ContentRemoved(nsIDocument *aDocument,
   956                                   nsIContent* aContainer,
   957                                   nsIContent* aChild,
   958                                   int32_t aIndexInContainer,
   959                                   nsIContent* aPreviousSibling)
   960 {
   961   NS_ASSERTION(aChild, "null ptr");
   963   // Make sure this notification concerns us.
   964   // First check the tag to see if it's one that we care about.
   965   nsIAtom *tag = aChild->Tag();
   967   // We don't consider non-XUL nodes.
   968   if (!aChild->IsXUL() || !aContainer->IsXUL())
   969     return;
   970   if (tag != nsGkAtoms::treeitem &&
   971       tag != nsGkAtoms::treeseparator &&
   972       tag != nsGkAtoms::treechildren &&
   973       tag != nsGkAtoms::treerow &&
   974       tag != nsGkAtoms::treecell) {
   975     return;
   976   }
   978   // If we have a legal tag, go up to the tree/select and make sure
   979   // that it's ours.
   981   for (nsIContent* element = aContainer; element != mBody; element = element->GetParent()) {
   982     if (!element)
   983       return; // this is not for us
   984     nsIAtom *parentTag = element->Tag();
   985     if (element->IsXUL() && parentTag == nsGkAtoms::tree)
   986       return; // this is not for us
   987   }
   989   // Lots of codepaths under here that do all sorts of stuff, so be safe.
   990   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   992   if (tag == nsGkAtoms::treechildren) {
   993     int32_t index = FindContent(aContainer);
   994     if (index >= 0) {
   995       Row* row = mRows[index];
   996       row->SetEmpty(true);
   997       int32_t count = RemoveSubtree(index);
   998       // Invalidate also the row to update twisty.
   999       if (mBoxObject) {
  1000         mBoxObject->InvalidateRow(index);
  1001         mBoxObject->RowCountChanged(index + 1, -count);
  1005   else if (tag == nsGkAtoms::treeitem ||
  1006            tag == nsGkAtoms::treeseparator
  1007           ) {
  1008     int32_t index = FindContent(aChild);
  1009     if (index >= 0) {
  1010       int32_t count = RemoveRow(index);
  1011       if (mBoxObject)
  1012         mBoxObject->RowCountChanged(index, -count);
  1015   else if (tag == nsGkAtoms::treerow) {
  1016     int32_t index = FindContent(aContainer);
  1017     if (index >= 0 && mBoxObject)
  1018       mBoxObject->InvalidateRow(index);
  1020   else if (tag == nsGkAtoms::treecell) {
  1021     nsCOMPtr<nsIContent> parent = aContainer->GetParent();
  1022     if (parent) {
  1023       int32_t index = FindContent(parent);
  1024       if (index >= 0 && mBoxObject)
  1025         mBoxObject->InvalidateRow(index);
  1030 void
  1031 nsTreeContentView::NodeWillBeDestroyed(const nsINode* aNode)
  1033   // XXXbz do we need this strong ref?  Do we drop refs to self in ClearRows?
  1034   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1035   ClearRows();
  1039 // Recursively serialize content, starting with aContent.
  1040 void
  1041 nsTreeContentView::Serialize(nsIContent* aContent, int32_t aParentIndex,
  1042                              int32_t* aIndex, nsTArray<nsAutoPtr<Row> >& aRows)
  1044   // Don't allow non-XUL nodes.
  1045   if (!aContent->IsXUL())
  1046     return;
  1048   dom::FlattenedChildIterator iter(aContent);
  1049   for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
  1050     nsIAtom *tag = content->Tag();
  1051     int32_t count = aRows.Length();
  1053     if (content->IsXUL()) {
  1054       if (tag == nsGkAtoms::treeitem)
  1055         SerializeItem(content, aParentIndex, aIndex, aRows);
  1056       else if (tag == nsGkAtoms::treeseparator)
  1057         SerializeSeparator(content, aParentIndex, aIndex, aRows);
  1059     *aIndex += aRows.Length() - count;
  1063 void
  1064 nsTreeContentView::SerializeItem(nsIContent* aContent, int32_t aParentIndex,
  1065                                  int32_t* aIndex, nsTArray<nsAutoPtr<Row> >& aRows)
  1067   if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
  1068                             nsGkAtoms::_true, eCaseMatters))
  1069     return;
  1071   Row* row = new Row(aContent, aParentIndex);
  1072   aRows.AppendElement(row);
  1074   if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
  1075                             nsGkAtoms::_true, eCaseMatters)) {
  1076     row->SetContainer(true);
  1077     if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
  1078                               nsGkAtoms::_true, eCaseMatters)) {
  1079       row->SetOpen(true);
  1080       nsIContent* child =
  1081         nsTreeUtils::GetImmediateChild(aContent, nsGkAtoms::treechildren);
  1082       if (child && child->IsXUL()) {
  1083         // Now, recursively serialize our child.
  1084         int32_t count = aRows.Length();
  1085         int32_t index = 0;
  1086         Serialize(child, aParentIndex + *aIndex + 1, &index, aRows);
  1087         row->mSubtreeSize += aRows.Length() - count;
  1089       else
  1090         row->SetEmpty(true);
  1091     } else if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty,
  1092                                      nsGkAtoms::_true, eCaseMatters)) {
  1093       row->SetEmpty(true);
  1098 void
  1099 nsTreeContentView::SerializeSeparator(nsIContent* aContent,
  1100                                       int32_t aParentIndex, int32_t* aIndex,
  1101                                       nsTArray<nsAutoPtr<Row> >& aRows)
  1103   if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
  1104                             nsGkAtoms::_true, eCaseMatters))
  1105     return;
  1107   Row* row = new Row(aContent, aParentIndex);
  1108   row->SetSeparator(true);
  1109   aRows.AppendElement(row);
  1112 void
  1113 nsTreeContentView::GetIndexInSubtree(nsIContent* aContainer,
  1114                                      nsIContent* aContent, int32_t* aIndex)
  1116   uint32_t childCount = aContainer->GetChildCount();
  1118   if (!aContainer->IsXUL())
  1119     return;
  1121   for (uint32_t i = 0; i < childCount; i++) {
  1122     nsIContent *content = aContainer->GetChildAt(i);
  1124     if (content == aContent)
  1125       break;
  1127     nsIAtom *tag = content->Tag();
  1129     if (content->IsXUL()) {
  1130       if (tag == nsGkAtoms::treeitem) {
  1131         if (! content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
  1132                                    nsGkAtoms::_true, eCaseMatters)) {
  1133           (*aIndex)++;
  1134           if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
  1135                                    nsGkAtoms::_true, eCaseMatters) &&
  1136               content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
  1137                                    nsGkAtoms::_true, eCaseMatters)) {
  1138             nsIContent* child =
  1139               nsTreeUtils::GetImmediateChild(content, nsGkAtoms::treechildren);
  1140             if (child && child->IsXUL())
  1141               GetIndexInSubtree(child, aContent, aIndex);
  1145       else if (tag == nsGkAtoms::treeseparator) {
  1146         if (! content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
  1147                                    nsGkAtoms::_true, eCaseMatters))
  1148           (*aIndex)++;
  1154 int32_t
  1155 nsTreeContentView::EnsureSubtree(int32_t aIndex)
  1157   Row* row = mRows[aIndex];
  1159   nsIContent* child;
  1160   child = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treechildren);
  1161   if (!child || !child->IsXUL()) {
  1162     return 0;
  1165   nsAutoTArray<nsAutoPtr<Row>, 8> rows;
  1166   int32_t index = 0;
  1167   Serialize(child, aIndex, &index, rows);
  1168   // We can't use InsertElementsAt since the destination can't steal
  1169   // ownership from its const source argument.
  1170   for (nsTArray<Row>::index_type i = 0; i < rows.Length(); i++) {
  1171     nsAutoPtr<Row>* newRow = mRows.InsertElementAt(aIndex + i + 1);
  1172     *newRow = rows[i];
  1174   int32_t count = rows.Length();
  1176   row->mSubtreeSize += count;
  1177   UpdateSubtreeSizes(row->mParentIndex, count);
  1179   // Update parent indexes, but skip newly added rows.
  1180   // They already have correct values.
  1181   UpdateParentIndexes(aIndex, count + 1, count);
  1183   return count;
  1186 int32_t
  1187 nsTreeContentView::RemoveSubtree(int32_t aIndex)
  1189   Row* row = mRows[aIndex];
  1190   int32_t count = row->mSubtreeSize;
  1192   mRows.RemoveElementsAt(aIndex + 1, count);
  1194   row->mSubtreeSize -= count;
  1195   UpdateSubtreeSizes(row->mParentIndex, -count);
  1197   UpdateParentIndexes(aIndex, 0, -count);
  1199   return count;
  1202 void
  1203 nsTreeContentView::InsertRowFor(nsIContent* aParent, nsIContent* aChild)
  1205   int32_t grandParentIndex = -1;
  1206   bool insertRow = false;
  1208   nsCOMPtr<nsIContent> grandParent = aParent->GetParent();
  1209   nsIAtom* grandParentTag = grandParent->Tag();
  1211   if (grandParent->IsXUL() && grandParentTag == nsGkAtoms::tree) {
  1212     // Allow insertion to the outermost container.
  1213     insertRow = true;
  1215   else {
  1216     // Test insertion to an inner container.
  1218     // First try to find this parent in our array of rows, if we find one
  1219     // we can be sure that all other parents are open too.
  1220     grandParentIndex = FindContent(grandParent);
  1221     if (grandParentIndex >= 0) {
  1222       // Got it, now test if it is open.
  1223       if (mRows[grandParentIndex]->IsOpen())
  1224         insertRow = true;
  1228   if (insertRow) {
  1229     int32_t index = 0;
  1230     GetIndexInSubtree(aParent, aChild, &index);
  1232     int32_t count = InsertRow(grandParentIndex, index, aChild);
  1233     if (mBoxObject)
  1234       mBoxObject->RowCountChanged(grandParentIndex + index + 1, count);
  1238 int32_t
  1239 nsTreeContentView::InsertRow(int32_t aParentIndex, int32_t aIndex, nsIContent* aContent)
  1241   nsAutoTArray<nsAutoPtr<Row>, 8> rows;
  1242   nsIAtom *tag = aContent->Tag();
  1243   if (aContent->IsXUL()) {
  1244     if (tag == nsGkAtoms::treeitem)
  1245       SerializeItem(aContent, aParentIndex, &aIndex, rows);
  1246     else if (tag == nsGkAtoms::treeseparator)
  1247       SerializeSeparator(aContent, aParentIndex, &aIndex, rows);
  1250   // We can't use InsertElementsAt since the destination can't steal
  1251   // ownership from its const source argument.
  1252   for (nsTArray<Row>::index_type i = 0; i < rows.Length(); i++) {
  1253     nsAutoPtr<Row>* newRow = mRows.InsertElementAt(aParentIndex + aIndex + i + 1);
  1254     *newRow = rows[i];
  1256   int32_t count = rows.Length();
  1258   UpdateSubtreeSizes(aParentIndex, count);
  1260   // Update parent indexes, but skip added rows.
  1261   // They already have correct values.
  1262   UpdateParentIndexes(aParentIndex + aIndex, count + 1, count);
  1264   return count;
  1267 int32_t
  1268 nsTreeContentView::RemoveRow(int32_t aIndex)
  1270   Row* row = mRows[aIndex];
  1271   int32_t count = row->mSubtreeSize + 1;
  1272   int32_t parentIndex = row->mParentIndex;
  1274   mRows.RemoveElementsAt(aIndex, count);
  1276   UpdateSubtreeSizes(parentIndex, -count);
  1278   UpdateParentIndexes(aIndex, 0, -count);
  1280   return count;
  1283 void
  1284 nsTreeContentView::ClearRows()
  1286   mRows.Clear();
  1287   mRoot = nullptr;
  1288   mBody = nullptr;
  1289   // Remove ourselves from mDocument's observers.
  1290   if (mDocument) {
  1291     mDocument->RemoveObserver(this);
  1292     mDocument = nullptr;
  1296 void
  1297 nsTreeContentView::OpenContainer(int32_t aIndex)
  1299   Row* row = mRows[aIndex];
  1300   row->SetOpen(true);
  1302   int32_t count = EnsureSubtree(aIndex);
  1303   if (mBoxObject) {
  1304     mBoxObject->InvalidateRow(aIndex);
  1305     mBoxObject->RowCountChanged(aIndex + 1, count);
  1309 void
  1310 nsTreeContentView::CloseContainer(int32_t aIndex)
  1312   Row* row = mRows[aIndex];
  1313   row->SetOpen(false);
  1315   int32_t count = RemoveSubtree(aIndex);
  1316   if (mBoxObject) {
  1317     mBoxObject->InvalidateRow(aIndex);
  1318     mBoxObject->RowCountChanged(aIndex + 1, -count);
  1322 int32_t
  1323 nsTreeContentView::FindContent(nsIContent* aContent)
  1325   for (uint32_t i = 0; i < mRows.Length(); i++) {
  1326     if (mRows[i]->mContent == aContent) {
  1327       return i;
  1331   return -1;
  1334 void
  1335 nsTreeContentView::UpdateSubtreeSizes(int32_t aParentIndex, int32_t count)
  1337   while (aParentIndex >= 0) {
  1338     Row* row = mRows[aParentIndex];
  1339     row->mSubtreeSize += count;
  1340     aParentIndex = row->mParentIndex;
  1344 void
  1345 nsTreeContentView::UpdateParentIndexes(int32_t aIndex, int32_t aSkip, int32_t aCount)
  1347   int32_t count = mRows.Length();
  1348   for (int32_t i = aIndex + aSkip; i < count; i++) {
  1349     Row* row = mRows[i];
  1350     if (row->mParentIndex > aIndex) {
  1351       row->mParentIndex += aCount;
  1356 nsIContent*
  1357 nsTreeContentView::GetCell(nsIContent* aContainer, nsITreeColumn* aCol)
  1359   nsCOMPtr<nsIAtom> colAtom;
  1360   int32_t colIndex;
  1361   aCol->GetAtom(getter_AddRefs(colAtom));
  1362   aCol->GetIndex(&colIndex);
  1364   // Traverse through cells, try to find the cell by "ref" attribute or by cell
  1365   // index in a row. "ref" attribute has higher priority.
  1366   nsIContent* result = nullptr;
  1367   int32_t j = 0;
  1368   dom::FlattenedChildIterator iter(aContainer);
  1369   for (nsIContent* cell = iter.GetNextChild(); cell; cell = iter.GetNextChild()) {
  1370     if (cell->Tag() == nsGkAtoms::treecell) {
  1371       if (colAtom && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref,
  1372                                        colAtom, eCaseMatters)) {
  1373         result = cell;
  1374         break;
  1376       else if (j == colIndex) {
  1377         result = cell;
  1379       j++;
  1383   return result;

mercurial