LCOV - code coverage report
Current view: directory - layout/xul/base/src/tree/src - nsTreeContentView.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 728 2 0.3 %
Date: 2012-06-02 Functions: 79 2 2.5 %

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

Generated by: LCOV version 1.7