LCOV - code coverage report
Current view: directory - content/xul/templates/src - nsXULTreeBuilder.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 886 3 0.3 %
Date: 2012-06-02 Functions: 74 3 4.1 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       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 Communicator client code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Chris Waterson <waterson@netscape.com>
      24                 :  *   Ben Goodger <ben@netscape.com>
      25                 :  *   Jan Varga <varga@ku.sk>
      26                 :  *   Neil Deakin <enndeakin@sympatico.ca>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      30                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : #include "nscore.h"
      43                 : #include "nsIContent.h"
      44                 : #include "nsINodeInfo.h"
      45                 : #include "nsIDOMElement.h"
      46                 : #include "nsILocalStore.h"
      47                 : #include "nsIBoxObject.h"
      48                 : #include "nsITreeBoxObject.h"
      49                 : #include "nsITreeSelection.h"
      50                 : #include "nsITreeColumns.h"
      51                 : #include "nsITreeView.h"
      52                 : #include "nsTreeUtils.h"
      53                 : #include "nsIServiceManager.h"
      54                 : #include "nsReadableUtils.h"
      55                 : #include "nsQuickSort.h"
      56                 : #include "nsTreeRows.h"
      57                 : #include "nsTemplateRule.h"
      58                 : #include "nsTemplateMatch.h"
      59                 : #include "nsGkAtoms.h"
      60                 : #include "nsXULContentUtils.h"
      61                 : #include "nsXULTemplateBuilder.h"
      62                 : #include "nsIXULSortService.h"
      63                 : #include "nsTArray.h"
      64                 : #include "nsUnicharUtils.h"
      65                 : #include "nsINameSpaceManager.h"
      66                 : #include "nsDOMClassInfoID.h"
      67                 : #include "nsWhitespaceTokenizer.h"
      68                 : #include "nsTreeContentView.h"
      69                 : 
      70                 : // For security check
      71                 : #include "nsIDocument.h"
      72                 : 
      73                 : /**
      74                 :  * A XUL template builder that serves as an tree view, allowing
      75                 :  * (pretty much) arbitrary RDF to be presented in an tree.
      76                 :  */
      77                 : class nsXULTreeBuilder : public nsXULTemplateBuilder,
      78                 :                          public nsIXULTreeBuilder,
      79                 :                          public nsINativeTreeView
      80               0 : {
      81                 : public:
      82                 :     // nsISupports
      83                 :     NS_DECL_ISUPPORTS_INHERITED
      84            1464 :     NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
      85                 : 
      86                 :     // nsIXULTreeBuilder
      87                 :     NS_DECL_NSIXULTREEBUILDER
      88                 : 
      89                 :     // nsITreeView
      90                 :     NS_DECL_NSITREEVIEW
      91                 :     // nsINativeTreeView: Untrusted code can use us
      92               0 :     NS_IMETHOD EnsureNative() { return NS_OK; }
      93                 : 
      94                 :     // nsIMutationObserver
      95                 :     NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
      96                 : 
      97                 : protected:
      98                 :     friend nsresult
      99                 :     NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
     100                 : 
     101                 :     nsXULTreeBuilder();
     102                 : 
     103                 :     /**
     104                 :      * Uninitialize the template builder
     105                 :      */
     106                 :     virtual void Uninit(bool aIsFinal);
     107                 : 
     108                 :     /**
     109                 :      * Get sort variables from the active <treecol>
     110                 :      */
     111                 :     nsresult
     112                 :     EnsureSortVariables();
     113                 : 
     114                 :     virtual nsresult
     115                 :     RebuildAll();
     116                 : 
     117                 :     /**
     118                 :      * Given a row, use the row's match to figure out the appropriate
     119                 :      * <treerow> in the rule's <action>.
     120                 :      */
     121                 :     nsresult
     122                 :     GetTemplateActionRowFor(PRInt32 aRow, nsIContent** aResult);
     123                 : 
     124                 :     /**
     125                 :      * Given a row and a column ID, use the row's match to figure out
     126                 :      * the appropriate <treecell> in the rule's <action>.
     127                 :      */
     128                 :     nsresult
     129                 :     GetTemplateActionCellFor(PRInt32 aRow, nsITreeColumn* aCol, nsIContent** aResult);
     130                 : 
     131                 :     /**
     132                 :      * Return the resource corresponding to a row in the tree.
     133                 :      */
     134                 :     nsresult
     135                 :     GetResourceFor(PRInt32 aRow, nsIRDFResource** aResource);
     136                 : 
     137                 :     /**
     138                 :      * Open a container row, inserting the container's children into
     139                 :      * the view.
     140                 :      */
     141                 :     nsresult
     142                 :     OpenContainer(PRInt32 aIndex, nsIXULTemplateResult* aResult);
     143                 : 
     144                 :     /**
     145                 :      * Helper for OpenContainer, recursively open subtrees, remembering
     146                 :      * persisted ``open'' state
     147                 :      */
     148                 :     nsresult
     149                 :     OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
     150                 :                   PRInt32 aIndex,
     151                 :                   nsIXULTemplateResult *aResult,
     152                 :                   PRInt32* aDelta);
     153                 : 
     154                 :     nsresult
     155                 :     OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
     156                 :                            PRInt32 aIndex,
     157                 :                            nsIXULTemplateResult *aResult,
     158                 :                            nsTemplateQuerySet* aQuerySet,
     159                 :                            PRInt32* aDelta,
     160                 :                            nsTArray<PRInt32>& open);
     161                 : 
     162                 :     /**
     163                 :      * Close a container row, removing the container's childrem from
     164                 :      * the view.
     165                 :      */
     166                 :     nsresult
     167                 :     CloseContainer(PRInt32 aIndex);
     168                 : 
     169                 :     /**
     170                 :      * Remove the matches for the rows in a subtree
     171                 :      */
     172                 :     nsresult
     173                 :     RemoveMatchesFor(nsTreeRows::Subtree& subtree);
     174                 : 
     175                 :     /**
     176                 :      * Helper methods that determine if the specified container is open.
     177                 :      */
     178                 :     nsresult
     179                 :     IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen);
     180                 : 
     181                 :     nsresult
     182                 :     IsContainerOpen(nsIRDFResource* aResource, bool* aOpen);
     183                 : 
     184                 :     /**
     185                 :      * A sorting callback for NS_QuickSort().
     186                 :      */
     187                 :     static int
     188                 :     Compare(const void* aLeft, const void* aRight, void* aClosure);
     189                 : 
     190                 :     /**
     191                 :      * The real sort routine
     192                 :      */
     193                 :     PRInt32
     194                 :     CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight);
     195                 : 
     196                 :     /**
     197                 :      * Sort the specified subtree, and recursively sort any subtrees
     198                 :      * beneath it.
     199                 :      */
     200                 :     nsresult
     201                 :     SortSubtree(nsTreeRows::Subtree* aSubtree);
     202                 : 
     203                 :     NS_IMETHOD
     204                 :     HasGeneratedContent(nsIRDFResource* aResource,
     205                 :                         nsIAtom* aTag,
     206                 :                         bool* aGenerated);
     207                 : 
     208                 :     // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited
     209                 :     // from nsXULTemplateBuilder
     210                 : 
     211                 :     /**
     212                 :      * Return true if the result can be inserted into the template as a new
     213                 :      * row.
     214                 :      */
     215                 :     bool
     216                 :     GetInsertionLocations(nsIXULTemplateResult* aResult,
     217                 :                           nsCOMArray<nsIContent>** aLocations);
     218                 : 
     219                 :     /**
     220                 :      * Implement result replacement
     221                 :      */
     222                 :     virtual nsresult
     223                 :     ReplaceMatch(nsIXULTemplateResult* aOldResult,
     224                 :                  nsTemplateMatch* aNewMatch,
     225                 :                  nsTemplateRule* aNewMatchRule,
     226                 :                  void *aContext);
     227                 : 
     228                 :     /**
     229                 :      * Implement match synchronization
     230                 :      */
     231                 :     virtual nsresult
     232                 :     SynchronizeResult(nsIXULTemplateResult* aResult);
     233                 : 
     234                 :     /**
     235                 :      * The tree's box object, used to communicate with the front-end.
     236                 :      */
     237                 :     nsCOMPtr<nsITreeBoxObject> mBoxObject;
     238                 : 
     239                 :     /**
     240                 :      * The tree's selection object.
     241                 :      */
     242                 :     nsCOMPtr<nsITreeSelection> mSelection;
     243                 : 
     244                 :     /**
     245                 :      * The datasource that's used to persist open folder information
     246                 :      */
     247                 :     nsCOMPtr<nsIRDFDataSource> mPersistStateStore;
     248                 : 
     249                 :     /**
     250                 :      * The rows in the view
     251                 :      */
     252                 :     nsTreeRows mRows;
     253                 : 
     254                 :     /**
     255                 :      * The currently active sort variable
     256                 :      */
     257                 :     nsCOMPtr<nsIAtom> mSortVariable;
     258                 : 
     259                 :     enum Direction {
     260                 :         eDirection_Descending = -1,
     261                 :         eDirection_Natural    =  0,
     262                 :         eDirection_Ascending  = +1
     263                 :     };
     264                 : 
     265                 :     /**
     266                 :      * The currently active sort order
     267                 :      */
     268                 :     Direction mSortDirection;
     269                 : 
     270                 :     /*
     271                 :      * Sort hints (compare case, etc)
     272                 :      */
     273                 :     PRUint32 mSortHints;
     274                 : 
     275                 :     /** 
     276                 :      * The builder observers.
     277                 :      */
     278                 :     nsCOMPtr<nsISupportsArray> mObservers;
     279                 : };
     280                 : 
     281                 : //----------------------------------------------------------------------
     282                 : 
     283                 : nsresult
     284               0 : NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
     285                 : {
     286               0 :     *aResult = nsnull;
     287                 : 
     288               0 :     NS_PRECONDITION(aOuter == nsnull, "no aggregation");
     289               0 :     if (aOuter)
     290               0 :         return NS_ERROR_NO_AGGREGATION;
     291                 : 
     292                 :     nsresult rv;
     293               0 :     nsXULTreeBuilder* result = new nsXULTreeBuilder();
     294               0 :     if (! result)
     295               0 :         return NS_ERROR_OUT_OF_MEMORY;
     296                 : 
     297               0 :     NS_ADDREF(result); // stabilize
     298                 : 
     299               0 :     rv = result->InitGlobals();
     300                 : 
     301               0 :     if (NS_SUCCEEDED(rv))
     302               0 :         rv = result->QueryInterface(aIID, aResult);
     303                 : 
     304               0 :     NS_RELEASE(result);
     305               0 :     return rv;
     306                 : }
     307                 : 
     308            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTreeBuilder)
     309                 : 
     310               0 : NS_IMPL_ADDREF_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
     311               0 : NS_IMPL_RELEASE_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
     312                 : 
     313               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
     314               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mBoxObject)
     315               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSelection)
     316               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPersistStateStore)
     317               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mObservers)
     318               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     319                 : 
     320               0 : static bool TraverseObservers(nsISupports* aElement, void *aData)
     321                 : {
     322                 :     nsCycleCollectionTraversalCallback *cb =
     323               0 :         static_cast<nsCycleCollectionTraversalCallback*>(aData);
     324               0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mObservers[i]");
     325               0 :     cb->NoteXPCOMChild(aElement);
     326               0 :     return true;
     327                 : }
     328                 : 
     329               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
     330               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mBoxObject)
     331               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSelection)
     332               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPersistStateStore)
     333               0 :     if (tmp->mObservers) {
     334               0 :         tmp->mObservers->EnumerateForwards(TraverseObservers, &cb);
     335                 :     }
     336               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     337                 : 
     338                 : DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder)
     339                 : 
     340               0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder)
     341               0 :     NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder)
     342               0 :     NS_INTERFACE_MAP_ENTRY(nsITreeView)
     343               0 :     NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTreeBuilder)
     344               0 : NS_INTERFACE_MAP_END_INHERITING(nsXULTemplateBuilder)
     345                 : 
     346                 : 
     347               0 : nsXULTreeBuilder::nsXULTreeBuilder()
     348               0 :     : mSortDirection(eDirection_Natural), mSortHints(0)
     349                 : {
     350               0 : }
     351                 : 
     352                 : void
     353               0 : nsXULTreeBuilder::Uninit(bool aIsFinal)
     354                 : {
     355               0 :     PRInt32 count = mRows.Count();
     356               0 :     mRows.Clear();
     357                 : 
     358               0 :     if (mBoxObject) {
     359               0 :         mBoxObject->BeginUpdateBatch();
     360               0 :         mBoxObject->RowCountChanged(0, -count);
     361               0 :         if (mBoxObject) {
     362               0 :             mBoxObject->EndUpdateBatch();
     363                 :         }
     364                 :     }
     365                 : 
     366               0 :     nsXULTemplateBuilder::Uninit(aIsFinal);
     367               0 : }
     368                 : 
     369                 : 
     370                 : //----------------------------------------------------------------------
     371                 : //
     372                 : // nsIXULTreeBuilder methods
     373                 : //
     374                 : 
     375                 : NS_IMETHODIMP
     376               0 : nsXULTreeBuilder::GetResourceAtIndex(PRInt32 aRowIndex, nsIRDFResource** aResult)
     377                 : {
     378               0 :     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
     379               0 :         return NS_ERROR_INVALID_ARG;
     380                 : 
     381               0 :     return GetResourceFor(aRowIndex, aResult);
     382                 : }
     383                 : 
     384                 : NS_IMETHODIMP
     385               0 : nsXULTreeBuilder::GetIndexOfResource(nsIRDFResource* aResource, PRInt32* aResult)
     386                 : {
     387               0 :     NS_ENSURE_ARG_POINTER(aResource);
     388               0 :     nsTreeRows::iterator iter = mRows.FindByResource(aResource);
     389               0 :     if (iter == mRows.Last())
     390               0 :         *aResult = -1;
     391                 :     else
     392               0 :         *aResult = iter.GetRowIndex();
     393               0 :     return NS_OK;
     394                 : }
     395                 : 
     396                 : NS_IMETHODIMP
     397               0 : nsXULTreeBuilder::AddObserver(nsIXULTreeBuilderObserver* aObserver)
     398                 : {
     399                 :     nsresult rv;  
     400               0 :     if (!mObservers) {
     401               0 :         rv = NS_NewISupportsArray(getter_AddRefs(mObservers));
     402               0 :         if (NS_FAILED(rv))
     403               0 :             return rv;
     404                 :     }
     405                 : 
     406               0 :     return mObservers->AppendElement(aObserver);
     407                 : }
     408                 : 
     409                 : NS_IMETHODIMP
     410               0 : nsXULTreeBuilder::RemoveObserver(nsIXULTreeBuilderObserver* aObserver)
     411                 : {
     412               0 :     return mObservers ? mObservers->RemoveElement(aObserver) : NS_ERROR_FAILURE;
     413                 : }
     414                 : 
     415                 : NS_IMETHODIMP
     416               0 : nsXULTreeBuilder::Sort(nsIDOMElement* aElement)
     417                 : {
     418               0 :     nsCOMPtr<nsIContent> header = do_QueryInterface(aElement);
     419               0 :     if (! header)
     420               0 :         return NS_ERROR_FAILURE;
     421                 : 
     422               0 :     if (header->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortLocked,
     423               0 :                             nsGkAtoms::_true, eCaseMatters))
     424               0 :         return NS_OK;
     425                 : 
     426               0 :     nsAutoString sort;
     427               0 :     header->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
     428                 : 
     429               0 :     if (sort.IsEmpty())
     430               0 :         return NS_OK;
     431                 : 
     432                 :     // Grab the new sort variable
     433               0 :     mSortVariable = do_GetAtom(sort);
     434                 : 
     435               0 :     nsAutoString hints;
     436               0 :     header->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints);
     437                 : 
     438               0 :     bool hasNaturalState = true;
     439               0 :     nsWhitespaceTokenizer tokenizer(hints);
     440               0 :     while (tokenizer.hasMoreTokens()) {
     441               0 :       const nsDependentSubstring& token(tokenizer.nextToken());
     442               0 :       if (token.EqualsLiteral("comparecase"))
     443               0 :         mSortHints |= nsIXULSortService::SORT_COMPARECASE;
     444               0 :       else if (token.EqualsLiteral("integer"))
     445               0 :         mSortHints |= nsIXULSortService::SORT_INTEGER;
     446               0 :       else if (token.EqualsLiteral("twostate"))
     447               0 :         hasNaturalState = false;
     448                 :     }
     449                 : 
     450                 :     // Cycle the sort direction
     451               0 :     nsAutoString dir;
     452               0 :     header->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, dir);
     453                 : 
     454               0 :     if (dir.EqualsLiteral("ascending")) {
     455               0 :         dir.AssignLiteral("descending");
     456               0 :         mSortDirection = eDirection_Descending;
     457                 :     }
     458               0 :     else if (hasNaturalState && dir.EqualsLiteral("descending")) {
     459               0 :         dir.AssignLiteral("natural");
     460               0 :         mSortDirection = eDirection_Natural;
     461                 :     }
     462                 :     else {
     463               0 :         dir.AssignLiteral("ascending");
     464               0 :         mSortDirection = eDirection_Ascending;
     465                 :     }
     466                 : 
     467                 :     // Sort it.
     468               0 :     SortSubtree(mRows.GetRoot());
     469               0 :     mRows.InvalidateCachedRow();
     470               0 :     if (mBoxObject) 
     471               0 :         mBoxObject->Invalidate();
     472                 : 
     473               0 :     nsTreeUtils::UpdateSortIndicators(header, dir);
     474                 : 
     475               0 :     return NS_OK;
     476                 : }
     477                 : 
     478                 : //----------------------------------------------------------------------
     479                 : //
     480                 : // nsITreeView methods
     481                 : //
     482                 : 
     483                 : NS_IMETHODIMP
     484               0 : nsXULTreeBuilder::GetRowCount(PRInt32* aRowCount)
     485                 : {
     486               0 :     *aRowCount = mRows.Count();
     487               0 :     return NS_OK;
     488                 : }
     489                 : 
     490                 : NS_IMETHODIMP
     491               0 : nsXULTreeBuilder::GetSelection(nsITreeSelection** aSelection)
     492                 : {
     493               0 :     NS_IF_ADDREF(*aSelection = mSelection.get());
     494               0 :     return NS_OK;
     495                 : }
     496                 : 
     497                 : NS_IMETHODIMP
     498               0 : nsXULTreeBuilder::SetSelection(nsITreeSelection* aSelection)
     499                 : {
     500               0 :     NS_ENSURE_TRUE(!aSelection ||
     501                 :                    nsTreeContentView::CanTrustTreeSelection(aSelection),
     502                 :                    NS_ERROR_DOM_SECURITY_ERR);
     503               0 :     mSelection = aSelection;
     504               0 :     return NS_OK;
     505                 : }
     506                 : 
     507                 : NS_IMETHODIMP
     508               0 : nsXULTreeBuilder::GetRowProperties(PRInt32 aIndex, nsISupportsArray* aProperties)
     509                 : {
     510               0 :     NS_ENSURE_ARG_POINTER(aProperties);
     511               0 :     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
     512               0 :     if (aIndex < 0 || aIndex >= mRows.Count())
     513               0 :         return NS_ERROR_INVALID_ARG;
     514                 : 
     515               0 :     nsCOMPtr<nsIContent> row;
     516               0 :     GetTemplateActionRowFor(aIndex, getter_AddRefs(row));
     517               0 :     if (row) {
     518               0 :         nsAutoString raw;
     519               0 :         row->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
     520                 : 
     521               0 :         if (!raw.IsEmpty()) {
     522               0 :             nsAutoString cooked;
     523               0 :             SubstituteText(mRows[aIndex]->mMatch->mResult, raw, cooked);
     524                 : 
     525               0 :             nsTreeUtils::TokenizeProperties(cooked, aProperties);
     526                 :         }
     527                 :     }
     528                 : 
     529               0 :     return NS_OK;
     530                 : }
     531                 : 
     532                 : NS_IMETHODIMP
     533               0 : nsXULTreeBuilder::GetCellProperties(PRInt32 aRow, nsITreeColumn* aCol, nsISupportsArray* aProperties)
     534                 : {
     535               0 :     NS_ENSURE_ARG_POINTER(aCol);
     536               0 :     NS_ENSURE_ARG_POINTER(aProperties);
     537               0 :     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
     538               0 :     if (aRow < 0 || aRow >= mRows.Count())
     539               0 :         return NS_ERROR_INVALID_ARG;
     540                 : 
     541               0 :     nsCOMPtr<nsIContent> cell;
     542               0 :     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
     543               0 :     if (cell) {
     544               0 :         nsAutoString raw;
     545               0 :         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
     546                 : 
     547               0 :         if (!raw.IsEmpty()) {
     548               0 :             nsAutoString cooked;
     549               0 :             SubstituteText(mRows[aRow]->mMatch->mResult, raw, cooked);
     550                 : 
     551               0 :             nsTreeUtils::TokenizeProperties(cooked, aProperties);
     552                 :         }
     553                 :     }
     554                 : 
     555               0 :     return NS_OK;
     556                 : }
     557                 : 
     558                 : NS_IMETHODIMP
     559               0 : nsXULTreeBuilder::GetColumnProperties(nsITreeColumn* aCol,
     560                 :                                       nsISupportsArray* aProperties)
     561                 : {
     562               0 :     NS_ENSURE_ARG_POINTER(aCol);
     563               0 :     NS_ENSURE_ARG_POINTER(aProperties);
     564                 :     // XXX sortactive fu
     565               0 :     return NS_OK;
     566                 : }
     567                 : 
     568                 : NS_IMETHODIMP
     569               0 : nsXULTreeBuilder::IsContainer(PRInt32 aIndex, bool* aResult)
     570                 : {
     571               0 :     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
     572               0 :     if (aIndex < 0 || aIndex >= mRows.Count())
     573               0 :         return NS_ERROR_INVALID_ARG;
     574                 : 
     575               0 :     nsTreeRows::iterator iter = mRows[aIndex];
     576                 : 
     577                 :     bool isContainer;
     578               0 :     iter->mMatch->mResult->GetIsContainer(&isContainer);
     579                 : 
     580               0 :     iter->mContainerType = isContainer
     581                 :         ? nsTreeRows::eContainerType_Container
     582               0 :         : nsTreeRows::eContainerType_Noncontainer;
     583                 : 
     584               0 :     *aResult = (iter->mContainerType == nsTreeRows::eContainerType_Container);
     585               0 :     return NS_OK;
     586                 : }
     587                 : 
     588                 : NS_IMETHODIMP
     589               0 : nsXULTreeBuilder::IsContainerOpen(PRInt32 aIndex, bool* aOpen)
     590                 : {
     591               0 :     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
     592               0 :     if (aIndex < 0 || aIndex >= mRows.Count())
     593               0 :         return NS_ERROR_INVALID_ARG;
     594                 : 
     595               0 :     nsTreeRows::iterator iter = mRows[aIndex];
     596                 : 
     597               0 :     if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) {
     598                 :         bool isOpen;
     599               0 :         IsContainerOpen(iter->mMatch->mResult, &isOpen);
     600                 : 
     601               0 :         iter->mContainerState = isOpen
     602                 :             ? nsTreeRows::eContainerState_Open
     603               0 :             : nsTreeRows::eContainerState_Closed;
     604                 :     }
     605                 : 
     606               0 :     *aOpen = (iter->mContainerState == nsTreeRows::eContainerState_Open);
     607               0 :     return NS_OK;
     608                 : }
     609                 : 
     610                 : NS_IMETHODIMP
     611               0 : nsXULTreeBuilder::IsContainerEmpty(PRInt32 aIndex, bool* aResult)
     612                 : {
     613               0 :     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
     614               0 :     if (aIndex < 0 || aIndex >= mRows.Count())
     615               0 :         return NS_ERROR_INVALID_ARG;
     616                 : 
     617               0 :     nsTreeRows::iterator iter = mRows[aIndex];
     618               0 :     NS_ASSERTION(iter->mContainerType == nsTreeRows::eContainerType_Container,
     619                 :                  "asking for empty state on non-container");
     620                 : 
     621                 :     // if recursion is disabled, pretend that the container is empty. This
     622                 :     // ensures that folders are still displayed as such, yet won't display
     623                 :     // their children
     624               0 :     if ((mFlags & eDontRecurse) && (iter->mMatch->mResult != mRootResult)) {
     625               0 :         *aResult = true;
     626               0 :         return NS_OK;
     627                 :     }
     628                 : 
     629               0 :     if (iter->mContainerFill == nsTreeRows::eContainerFill_Unknown) {
     630                 :         bool isEmpty;
     631               0 :         iter->mMatch->mResult->GetIsEmpty(&isEmpty);
     632                 : 
     633               0 :         iter->mContainerFill = isEmpty
     634                 :             ? nsTreeRows::eContainerFill_Empty
     635               0 :             : nsTreeRows::eContainerFill_Nonempty;
     636                 :     }
     637                 : 
     638               0 :     *aResult = (iter->mContainerFill == nsTreeRows::eContainerFill_Empty);
     639               0 :     return NS_OK;
     640                 : }
     641                 : 
     642                 : NS_IMETHODIMP
     643               0 : nsXULTreeBuilder::IsSeparator(PRInt32 aIndex, bool* aResult)
     644                 : {
     645               0 :     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
     646               0 :     if (aIndex < 0 || aIndex >= mRows.Count())
     647               0 :         return NS_ERROR_INVALID_ARG;
     648                 : 
     649               0 :     nsAutoString type;
     650               0 :     nsTreeRows::Row& row = *(mRows[aIndex]);
     651               0 :     row.mMatch->mResult->GetType(type);
     652                 : 
     653               0 :     *aResult = type.EqualsLiteral("separator");
     654                 : 
     655               0 :     return NS_OK;
     656                 : }
     657                 : 
     658                 : NS_IMETHODIMP
     659               0 : nsXULTreeBuilder::GetParentIndex(PRInt32 aRowIndex, PRInt32* aResult)
     660                 : {
     661               0 :     NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
     662               0 :     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
     663               0 :         return NS_ERROR_INVALID_ARG;
     664                 : 
     665                 :     // Construct a path to the row
     666               0 :     nsTreeRows::iterator iter = mRows[aRowIndex];
     667                 : 
     668                 :     // The parent of the row will be at the top of the path
     669               0 :     nsTreeRows::Subtree* parent = iter.GetParent();
     670                 : 
     671                 :     // Now walk through our previous siblings, subtracting off each
     672                 :     // one's subtree size
     673               0 :     PRInt32 index = iter.GetChildIndex();
     674               0 :     while (--index >= 0)
     675               0 :         aRowIndex -= mRows.GetSubtreeSizeFor(parent, index) + 1;
     676                 : 
     677                 :     // Now the parent's index will be the first row's index, less one.
     678               0 :     *aResult = aRowIndex - 1;
     679               0 :     return NS_OK;
     680                 : }
     681                 : 
     682                 : NS_IMETHODIMP
     683               0 : nsXULTreeBuilder::HasNextSibling(PRInt32 aRowIndex, PRInt32 aAfterIndex, bool* aResult)
     684                 : {
     685               0 :     NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
     686               0 :     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
     687               0 :         return NS_ERROR_INVALID_ARG;
     688                 : 
     689                 :     // Construct a path to the row
     690               0 :     nsTreeRows::iterator iter = mRows[aRowIndex];
     691                 : 
     692                 :     // The parent of the row will be at the top of the path
     693               0 :     nsTreeRows::Subtree* parent = iter.GetParent();
     694                 : 
     695                 :     // We have a next sibling if the child is not the last in the
     696                 :     // subtree.
     697               0 :     *aResult = iter.GetChildIndex() != parent->Count() - 1;
     698               0 :     return NS_OK;
     699                 : }
     700                 : 
     701                 : NS_IMETHODIMP
     702               0 : nsXULTreeBuilder::GetLevel(PRInt32 aRowIndex, PRInt32* aResult)
     703                 : {
     704               0 :     NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
     705               0 :     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
     706               0 :         return NS_ERROR_INVALID_ARG;
     707                 : 
     708                 :     // Construct a path to the row; the ``level'' is the path length
     709                 :     // less one.
     710               0 :     nsTreeRows::iterator iter = mRows[aRowIndex];
     711               0 :     *aResult = iter.GetDepth() - 1;
     712               0 :     return NS_OK;
     713                 : }
     714                 : 
     715                 : NS_IMETHODIMP
     716               0 : nsXULTreeBuilder::GetImageSrc(PRInt32 aRow, nsITreeColumn* aCol, nsAString& aResult)
     717                 : {
     718               0 :     NS_ENSURE_ARG_POINTER(aCol);
     719               0 :     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
     720               0 :     if (aRow < 0 || aRow >= mRows.Count())
     721               0 :         return NS_ERROR_INVALID_ARG;
     722                 : 
     723                 :     // Find the <cell> that corresponds to the column we want.
     724               0 :     nsCOMPtr<nsIContent> cell;
     725               0 :     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
     726               0 :     if (cell) {
     727               0 :         nsAutoString raw;
     728               0 :         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, raw);
     729                 : 
     730               0 :         SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
     731                 :     }
     732                 :     else
     733               0 :         aResult.Truncate();
     734                 : 
     735               0 :     return NS_OK;
     736                 : }
     737                 : 
     738                 : 
     739                 : NS_IMETHODIMP
     740               0 : nsXULTreeBuilder::GetProgressMode(PRInt32 aRow, nsITreeColumn* aCol, PRInt32* aResult)
     741                 : {
     742               0 :     NS_ENSURE_ARG_POINTER(aCol);
     743               0 :     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
     744               0 :     if (aRow < 0 || aRow >= mRows.Count())
     745               0 :         return NS_ERROR_INVALID_ARG;
     746                 : 
     747               0 :     *aResult = nsITreeView::PROGRESS_NONE;
     748                 : 
     749                 :     // Find the <cell> that corresponds to the column we want.
     750               0 :     nsCOMPtr<nsIContent> cell;
     751               0 :     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
     752               0 :     if (cell) {
     753               0 :         nsAutoString raw;
     754               0 :         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::mode, raw);
     755                 : 
     756               0 :         nsAutoString mode;
     757               0 :         SubstituteText(mRows[aRow]->mMatch->mResult, raw, mode);
     758                 : 
     759               0 :         if (mode.EqualsLiteral("normal"))
     760               0 :             *aResult = nsITreeView::PROGRESS_NORMAL;
     761               0 :         else if (mode.EqualsLiteral("undetermined"))
     762               0 :             *aResult = nsITreeView::PROGRESS_UNDETERMINED;
     763                 :     }
     764                 : 
     765               0 :     return NS_OK;
     766                 : }
     767                 : 
     768                 : NS_IMETHODIMP
     769               0 : nsXULTreeBuilder::GetCellValue(PRInt32 aRow, nsITreeColumn* aCol, nsAString& aResult)
     770                 : {
     771               0 :     NS_ENSURE_ARG_POINTER(aCol);
     772               0 :     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
     773               0 :     if (aRow < 0 || aRow >= mRows.Count())
     774               0 :         return NS_ERROR_INVALID_ARG;
     775                 : 
     776                 :     // Find the <cell> that corresponds to the column we want.
     777               0 :     nsCOMPtr<nsIContent> cell;
     778               0 :     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
     779               0 :     if (cell) {
     780               0 :         nsAutoString raw;
     781               0 :         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, raw);
     782                 : 
     783               0 :         SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
     784                 :     }
     785                 :     else
     786               0 :         aResult.Truncate();
     787                 : 
     788               0 :     return NS_OK;
     789                 : }
     790                 : 
     791                 : NS_IMETHODIMP
     792               0 : nsXULTreeBuilder::GetCellText(PRInt32 aRow, nsITreeColumn* aCol, nsAString& aResult)
     793                 : {
     794               0 :     NS_ENSURE_ARG_POINTER(aCol);
     795               0 :     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
     796               0 :     if (aRow < 0 || aRow >= mRows.Count())
     797               0 :         return NS_ERROR_INVALID_ARG;
     798                 : 
     799                 :     // Find the <cell> that corresponds to the column we want.
     800               0 :     nsCOMPtr<nsIContent> cell;
     801               0 :     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
     802               0 :     if (cell) {
     803               0 :         nsAutoString raw;
     804               0 :         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, raw);
     805                 : 
     806               0 :         SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
     807                 : 
     808                 :     }
     809                 :     else
     810               0 :         aResult.Truncate();
     811                 : 
     812               0 :     return NS_OK;
     813                 : }
     814                 : 
     815                 : NS_IMETHODIMP
     816               0 : nsXULTreeBuilder::SetTree(nsITreeBoxObject* aTree)
     817                 : {
     818               0 :     mBoxObject = aTree;
     819                 : 
     820                 :     // If this is teardown time, then we're done.
     821               0 :     if (!mBoxObject) {
     822               0 :         Uninit(false);
     823               0 :         return NS_OK;
     824                 :     }
     825               0 :     NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
     826                 : 
     827                 :     // Is our root's principal trusted?
     828               0 :     bool isTrusted = false;
     829               0 :     nsresult rv = IsSystemPrincipal(mRoot->NodePrincipal(), &isTrusted);
     830               0 :     if (NS_SUCCEEDED(rv) && isTrusted) {
     831                 :         // Get the datasource we intend to use to remember open state.
     832               0 :         nsAutoString datasourceStr;
     833               0 :         mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::statedatasource, datasourceStr);
     834                 : 
     835                 :         // since we are trusted, use the user specified datasource
     836                 :         // if non specified, use localstore, which gives us
     837                 :         // persistence across sessions
     838               0 :         if (! datasourceStr.IsEmpty()) {
     839               0 :             gRDFService->GetDataSource(NS_ConvertUTF16toUTF8(datasourceStr).get(),
     840               0 :                                        getter_AddRefs(mPersistStateStore));
     841                 :         }
     842                 :         else {
     843                 :             gRDFService->GetDataSource("rdf:local-store",
     844               0 :                                        getter_AddRefs(mPersistStateStore));
     845                 :         }
     846                 :     }
     847                 : 
     848                 :     // Either no specific datasource was specified, or we failed
     849                 :     // to get one because we are not trusted.
     850                 :     //
     851                 :     // XXX if it were possible to ``write an arbitrary datasource
     852                 :     // back'', then we could also allow an untrusted document to
     853                 :     // use a statedatasource from the same codebase.
     854               0 :     if (! mPersistStateStore) {
     855                 :         mPersistStateStore =
     856               0 :             do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource");
     857                 :     }
     858                 : 
     859               0 :     NS_ASSERTION(mPersistStateStore, "failed to get a persistent state store");
     860               0 :     if (! mPersistStateStore)
     861               0 :         return NS_ERROR_FAILURE;
     862                 : 
     863               0 :     Rebuild();
     864                 : 
     865               0 :     EnsureSortVariables();
     866               0 :     if (mSortVariable)
     867               0 :         SortSubtree(mRows.GetRoot());
     868                 : 
     869               0 :     return NS_OK;
     870                 : }
     871                 : 
     872                 : NS_IMETHODIMP
     873               0 : nsXULTreeBuilder::ToggleOpenState(PRInt32 aIndex)
     874                 : {
     875               0 :     if (aIndex < 0 || aIndex >= mRows.Count())
     876               0 :         return NS_ERROR_INVALID_ARG;
     877                 : 
     878               0 :     nsIXULTemplateResult* result = mRows[aIndex]->mMatch->mResult;
     879               0 :     if (! result)
     880               0 :         return NS_ERROR_FAILURE;
     881                 : 
     882               0 :     if (mFlags & eDontRecurse)
     883               0 :         return NS_OK;
     884                 : 
     885               0 :     if (result && result != mRootResult) {
     886                 :         // don't open containers if child processing isn't allowed
     887                 :         bool mayProcessChildren;
     888               0 :         nsresult rv = result->GetMayProcessChildren(&mayProcessChildren);
     889               0 :         if (NS_FAILED(rv) || !mayProcessChildren)
     890               0 :             return rv;
     891                 :     }
     892                 : 
     893               0 :     if (mObservers) {
     894                 :         PRUint32 count;
     895               0 :         mObservers->Count(&count);
     896               0 :         for (PRUint32 i = 0; i < count; ++i) {
     897               0 :             nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
     898               0 :             if (observer)
     899               0 :                 observer->OnToggleOpenState(aIndex);
     900                 :         }
     901                 :     }
     902                 : 
     903               0 :     if (mPersistStateStore) {
     904                 :         bool isOpen;
     905               0 :         IsContainerOpen(aIndex, &isOpen);
     906                 : 
     907               0 :         nsCOMPtr<nsIRDFResource> container;
     908               0 :         GetResourceFor(aIndex, getter_AddRefs(container));
     909               0 :         if (! container)
     910               0 :             return NS_ERROR_FAILURE;
     911                 : 
     912                 :         bool hasProperty;
     913               0 :         IsContainerOpen(container, &hasProperty);
     914                 : 
     915               0 :         if (isOpen) {
     916               0 :             if (hasProperty) {
     917               0 :                 mPersistStateStore->Unassert(container,
     918                 :                                              nsXULContentUtils::NC_open,
     919               0 :                                              nsXULContentUtils::true_);
     920                 :             }
     921                 : 
     922               0 :             CloseContainer(aIndex);
     923                 :         }
     924                 :         else {
     925               0 :             if (! hasProperty) {
     926               0 :                 mPersistStateStore->Assert(container,
     927                 :                                            nsXULContentUtils::NC_open,
     928                 :                                            nsXULContentUtils::true_,
     929               0 :                                            true);
     930                 :             }
     931                 : 
     932               0 :             OpenContainer(aIndex, result);
     933                 :         }
     934                 :     }
     935                 : 
     936               0 :     return NS_OK;
     937                 : }
     938                 : 
     939                 : NS_IMETHODIMP
     940               0 : nsXULTreeBuilder::CycleHeader(nsITreeColumn* aCol)
     941                 : {
     942               0 :     NS_ENSURE_ARG_POINTER(aCol);
     943               0 :     nsCOMPtr<nsIDOMElement> element;
     944               0 :     aCol->GetElement(getter_AddRefs(element));
     945                 : 
     946               0 :     if (mObservers) {
     947               0 :         nsAutoString id;
     948               0 :         aCol->GetId(id);
     949                 : 
     950                 :         PRUint32 count;
     951               0 :         mObservers->Count(&count);
     952               0 :         for (PRUint32 i = 0; i < count; ++i) {
     953               0 :             nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
     954               0 :             if (observer)
     955               0 :                 observer->OnCycleHeader(id.get(), element);
     956                 :         }
     957                 :     }
     958                 : 
     959               0 :     return Sort(element);
     960                 : }
     961                 : 
     962                 : NS_IMETHODIMP
     963               0 : nsXULTreeBuilder::SelectionChanged()
     964                 : {
     965               0 :     if (mObservers) {
     966                 :         PRUint32 count;
     967               0 :         mObservers->Count(&count);
     968               0 :         for (PRUint32 i = 0; i < count; ++i) {
     969               0 :             nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
     970               0 :             if (observer)
     971               0 :                 observer->OnSelectionChanged();
     972                 :         }
     973                 :     }
     974                 : 
     975               0 :     return NS_OK;
     976                 : }
     977                 : 
     978                 : NS_IMETHODIMP
     979               0 : nsXULTreeBuilder::CycleCell(PRInt32 aRow, nsITreeColumn* aCol)
     980                 : {
     981               0 :     NS_ENSURE_ARG_POINTER(aCol);
     982               0 :     if (mObservers) {
     983               0 :         nsAutoString id;
     984               0 :         aCol->GetId(id);
     985                 : 
     986                 :         PRUint32 count;
     987               0 :         mObservers->Count(&count);
     988               0 :         for (PRUint32 i = 0; i < count; ++i) {
     989               0 :             nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
     990               0 :             if (observer)
     991               0 :                 observer->OnCycleCell(aRow, id.get());
     992                 :         }
     993                 :     }
     994                 : 
     995               0 :     return NS_OK;
     996                 : }
     997                 : 
     998                 : NS_IMETHODIMP
     999               0 : nsXULTreeBuilder::IsEditable(PRInt32 aRow, nsITreeColumn* aCol, bool* _retval)
    1000                 : {
    1001               0 :     *_retval = true;
    1002               0 :     NS_ENSURE_ARG_POINTER(aCol);
    1003               0 :     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
    1004               0 :     if (aRow < 0 || aRow >= mRows.Count())
    1005               0 :         return NS_ERROR_INVALID_ARG;
    1006                 : 
    1007                 :     // Find the <cell> that corresponds to the column we want.
    1008               0 :     nsCOMPtr<nsIContent> cell;
    1009               0 :     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
    1010               0 :     if (cell) {
    1011               0 :         nsAutoString raw;
    1012               0 :         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::editable, raw);
    1013                 : 
    1014               0 :         nsAutoString editable;
    1015               0 :         SubstituteText(mRows[aRow]->mMatch->mResult, raw, editable);
    1016                 : 
    1017               0 :         if (editable.EqualsLiteral("false"))
    1018               0 :             *_retval = false;
    1019                 :     }
    1020                 : 
    1021               0 :     return NS_OK;
    1022                 : }
    1023                 : 
    1024                 : NS_IMETHODIMP
    1025               0 : nsXULTreeBuilder::IsSelectable(PRInt32 aRow, nsITreeColumn* aCol, bool* _retval)
    1026                 : {
    1027               0 :     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
    1028               0 :     if (aRow < 0 || aRow >= mRows.Count())
    1029               0 :         return NS_ERROR_INVALID_ARG;
    1030                 : 
    1031               0 :     *_retval = true;
    1032                 : 
    1033                 :     // Find the <cell> that corresponds to the column we want.
    1034               0 :     nsCOMPtr<nsIContent> cell;
    1035               0 :     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
    1036               0 :     if (cell) {
    1037               0 :         nsAutoString raw;
    1038               0 :         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::selectable, raw);
    1039                 : 
    1040               0 :         nsAutoString selectable;
    1041               0 :         SubstituteText(mRows[aRow]->mMatch->mResult, raw, selectable);
    1042                 : 
    1043               0 :         if (selectable.EqualsLiteral("false"))
    1044               0 :             *_retval = false;
    1045                 :     }
    1046                 : 
    1047               0 :     return NS_OK;
    1048                 : }
    1049                 : 
    1050                 : NS_IMETHODIMP
    1051               0 : nsXULTreeBuilder::SetCellValue(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
    1052                 : {
    1053               0 :     NS_ENSURE_ARG_POINTER(aCol);
    1054               0 :     return NS_OK;
    1055                 : }
    1056                 : 
    1057                 : NS_IMETHODIMP
    1058               0 : nsXULTreeBuilder::SetCellText(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
    1059                 : {
    1060               0 :     NS_ENSURE_ARG_POINTER(aCol);
    1061               0 :     return NS_OK;
    1062                 : }
    1063                 : 
    1064                 : NS_IMETHODIMP
    1065               0 : nsXULTreeBuilder::PerformAction(const PRUnichar* aAction)
    1066                 : {
    1067               0 :     if (mObservers) {  
    1068                 :         PRUint32 count;
    1069               0 :         mObservers->Count(&count);
    1070               0 :         for (PRUint32 i = 0; i < count; ++i) {
    1071               0 :             nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
    1072               0 :             if (observer)
    1073               0 :                 observer->OnPerformAction(aAction);
    1074                 :         }
    1075                 :     }
    1076                 : 
    1077               0 :     return NS_OK;
    1078                 : }
    1079                 : 
    1080                 : NS_IMETHODIMP
    1081               0 : nsXULTreeBuilder::PerformActionOnRow(const PRUnichar* aAction, PRInt32 aRow)
    1082                 : {
    1083               0 :     if (mObservers) {  
    1084                 :         PRUint32 count;
    1085               0 :         mObservers->Count(&count);
    1086               0 :         for (PRUint32 i = 0; i < count; ++i) {
    1087               0 :             nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
    1088               0 :             if (observer)
    1089               0 :                 observer->OnPerformActionOnRow(aAction, aRow);
    1090                 :         }
    1091                 :     }
    1092                 : 
    1093               0 :     return NS_OK;
    1094                 : }
    1095                 : 
    1096                 : NS_IMETHODIMP
    1097               0 : nsXULTreeBuilder::PerformActionOnCell(const PRUnichar* aAction, PRInt32 aRow, nsITreeColumn* aCol)
    1098                 : {
    1099               0 :     NS_ENSURE_ARG_POINTER(aCol);
    1100               0 :     if (mObservers) {  
    1101               0 :         nsAutoString id;
    1102               0 :         aCol->GetId(id);
    1103                 : 
    1104                 :         PRUint32 count;
    1105               0 :         mObservers->Count(&count);
    1106               0 :         for (PRUint32 i = 0; i < count; ++i) {
    1107               0 :             nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
    1108               0 :             if (observer)
    1109               0 :                 observer->OnPerformActionOnCell(aAction, aRow, id.get());
    1110                 :         }
    1111                 :     }
    1112                 : 
    1113               0 :     return NS_OK;
    1114                 : }
    1115                 : 
    1116                 : 
    1117                 : void
    1118               0 : nsXULTreeBuilder::NodeWillBeDestroyed(const nsINode* aNode)
    1119                 : {
    1120               0 :     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
    1121               0 :     if (mObservers)
    1122               0 :         mObservers->Clear();
    1123                 : 
    1124               0 :     nsXULTemplateBuilder::NodeWillBeDestroyed(aNode);
    1125               0 : }
    1126                 : 
    1127                 : NS_IMETHODIMP
    1128               0 : nsXULTreeBuilder::HasGeneratedContent(nsIRDFResource* aResource,
    1129                 :                                       nsIAtom* aTag,
    1130                 :                                       bool* aGenerated)
    1131                 : {
    1132               0 :     *aGenerated = false;
    1133               0 :     NS_ENSURE_ARG_POINTER(aResource);
    1134                 : 
    1135               0 :     if (!mRootResult)
    1136               0 :         return NS_OK;
    1137                 : 
    1138               0 :     nsCOMPtr<nsIRDFResource> rootresource;
    1139               0 :     nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource));
    1140               0 :     if (NS_FAILED(rv))
    1141               0 :         return rv;
    1142                 : 
    1143               0 :     if (aResource == rootresource ||
    1144               0 :         mRows.FindByResource(aResource) != mRows.Last())
    1145               0 :         *aGenerated = true;
    1146                 : 
    1147               0 :     return NS_OK;
    1148                 : }
    1149                 : 
    1150                 : bool
    1151               0 : nsXULTreeBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult,
    1152                 :                                         nsCOMArray<nsIContent>** aLocations)
    1153                 : {
    1154               0 :     *aLocations = nsnull;
    1155                 : 
    1156                 :     // Get the reference point and check if it is an open container. Rows
    1157                 :     // should not be generated otherwise.
    1158                 : 
    1159               0 :     nsAutoString ref;
    1160               0 :     nsresult rv = aResult->GetBindingFor(mRefVariable, ref);
    1161               0 :     if (NS_FAILED(rv) || ref.IsEmpty())
    1162               0 :         return false;
    1163                 : 
    1164               0 :     nsCOMPtr<nsIRDFResource> container;
    1165               0 :     rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
    1166               0 :     if (NS_FAILED(rv))
    1167               0 :         return false;
    1168                 : 
    1169                 :     // Can always insert into the root resource
    1170               0 :     if (container == mRows.GetRootResource())
    1171               0 :         return true;
    1172                 : 
    1173               0 :     nsTreeRows::iterator iter = mRows.FindByResource(container);
    1174               0 :     if (iter == mRows.Last())
    1175               0 :         return false;
    1176                 : 
    1177               0 :     return (iter->mContainerState == nsTreeRows::eContainerState_Open);
    1178                 : }
    1179                 : 
    1180                 : nsresult
    1181               0 : nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult,
    1182                 :                                nsTemplateMatch* aNewMatch,
    1183                 :                                nsTemplateRule* aNewMatchRule,
    1184                 :                                void *aLocation)
    1185                 : {
    1186               0 :     if (! mBoxObject)
    1187               0 :         return NS_OK;
    1188                 : 
    1189               0 :     if (aOldResult) {
    1190                 :         // Grovel through the rows looking for oldresult.
    1191               0 :         nsTreeRows::iterator iter = mRows.Find(aOldResult);
    1192                 : 
    1193               0 :         NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
    1194               0 :         if (iter == mRows.Last())
    1195               0 :             return NS_ERROR_FAILURE;
    1196                 : 
    1197                 :         // Remove the rows from the view
    1198               0 :         PRInt32 row = iter.GetRowIndex();
    1199                 : 
    1200                 :         // If the row contains children, remove the matches from the
    1201                 :         // children so that they can be regenerated again if the element
    1202                 :         // gets added back.
    1203               0 :         PRInt32 delta = mRows.GetSubtreeSizeFor(iter);
    1204               0 :         if (delta)
    1205               0 :             RemoveMatchesFor(*(iter->mSubtree));
    1206                 : 
    1207               0 :         if (mRows.RemoveRowAt(iter) == 0 && iter.GetRowIndex() >= 0) {
    1208                 : 
    1209                 :             // In this case iter now points to its parent
    1210                 :             // Invalidate the row's cached fill state
    1211               0 :             iter->mContainerFill = nsTreeRows::eContainerFill_Unknown;
    1212                 : 
    1213               0 :             nsCOMPtr<nsITreeColumns> cols;
    1214               0 :             mBoxObject->GetColumns(getter_AddRefs(cols));
    1215               0 :             if (cols) {
    1216               0 :                 nsCOMPtr<nsITreeColumn> primaryCol;
    1217               0 :                 cols->GetPrimaryColumn(getter_AddRefs(primaryCol));
    1218               0 :                 if (primaryCol)
    1219               0 :                     mBoxObject->InvalidateCell(iter.GetRowIndex(), primaryCol);
    1220                 :             }
    1221                 :         }
    1222                 : 
    1223                 :         // Notify the box object
    1224               0 :         mBoxObject->RowCountChanged(row, -delta - 1);
    1225                 :     }
    1226                 : 
    1227               0 :     if (aNewMatch && aNewMatch->mResult) {
    1228                 :         // Insertion.
    1229               0 :         PRInt32 row = -1;
    1230               0 :         nsTreeRows::Subtree* parent = nsnull;
    1231               0 :         nsIXULTemplateResult* result = aNewMatch->mResult;
    1232                 : 
    1233               0 :         nsAutoString ref;
    1234               0 :         nsresult rv = result->GetBindingFor(mRefVariable, ref);
    1235               0 :         if (NS_FAILED(rv) || ref.IsEmpty())
    1236               0 :             return rv;
    1237                 : 
    1238               0 :         nsCOMPtr<nsIRDFResource> container;
    1239               0 :         rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
    1240               0 :         if (NS_FAILED(rv))
    1241               0 :             return rv;
    1242                 : 
    1243               0 :         if (container != mRows.GetRootResource()) {
    1244               0 :             nsTreeRows::iterator iter = mRows.FindByResource(container);
    1245               0 :             row = iter.GetRowIndex();
    1246                 : 
    1247               0 :             NS_ASSERTION(iter != mRows.Last(), "couldn't find container row");
    1248               0 :             if (iter == mRows.Last())
    1249               0 :                 return NS_ERROR_FAILURE;
    1250                 : 
    1251                 :             // Use the persist store to remember if the container
    1252                 :             // is open or closed.
    1253               0 :             bool open = false;
    1254               0 :             IsContainerOpen(row, &open);
    1255                 : 
    1256                 :             // If it's open, make sure that we've got a subtree structure ready.
    1257               0 :             if (open)
    1258               0 :                 parent = mRows.EnsureSubtreeFor(iter);
    1259                 : 
    1260                 :             // We know something has just been inserted into the
    1261                 :             // container, so whether its open or closed, make sure
    1262                 :             // that we've got our tree row's state correct.
    1263               0 :             if ((iter->mContainerType != nsTreeRows::eContainerType_Container) ||
    1264               0 :                 (iter->mContainerFill != nsTreeRows::eContainerFill_Nonempty)) {
    1265               0 :                 iter->mContainerType  = nsTreeRows::eContainerType_Container;
    1266               0 :                 iter->mContainerFill = nsTreeRows::eContainerFill_Nonempty;
    1267               0 :                 mBoxObject->InvalidateRow(iter.GetRowIndex());
    1268                 :             }
    1269                 :         }
    1270                 :         else {
    1271               0 :             parent = mRows.GetRoot();
    1272                 :         }
    1273                 : 
    1274               0 :         if (parent) {
    1275                 :             // If we get here, then we're inserting into an open
    1276                 :             // container. By default, place the new element at the
    1277                 :             // end of the container
    1278               0 :             PRInt32 index = parent->Count();
    1279                 : 
    1280               0 :             if (mSortVariable) {
    1281                 :                 // Figure out where to put the new element by doing an
    1282                 :                 // insertion sort.
    1283               0 :                 PRInt32 left = 0;
    1284               0 :                 PRInt32 right = index;
    1285                 : 
    1286               0 :                 while (left < right) {
    1287               0 :                     index = (left + right) / 2;
    1288               0 :                     PRInt32 cmp = CompareResults((*parent)[index].mMatch->mResult, result);
    1289               0 :                     if (cmp < 0)
    1290               0 :                         left = ++index;
    1291               0 :                     else if (cmp > 0)
    1292               0 :                         right = index;
    1293                 :                     else
    1294               0 :                         break;
    1295                 :                 }
    1296                 :             }
    1297                 : 
    1298                 :             nsTreeRows::iterator iter =
    1299               0 :                 mRows.InsertRowAt(aNewMatch, parent, index);
    1300                 : 
    1301               0 :             mBoxObject->RowCountChanged(iter.GetRowIndex(), +1);
    1302                 : 
    1303                 :             // See if this newly added row is open; in which case,
    1304                 :             // recursively add its children to the tree, too.
    1305                 : 
    1306               0 :             if (mFlags & eDontRecurse)
    1307               0 :                 return NS_OK;
    1308                 : 
    1309               0 :             if (result != mRootResult) {
    1310                 :                 // don't open containers if child processing isn't allowed
    1311                 :                 bool mayProcessChildren;
    1312               0 :                 nsresult rv = result->GetMayProcessChildren(&mayProcessChildren);
    1313               0 :                 if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK;
    1314                 :             }
    1315                 : 
    1316                 :             bool open;
    1317               0 :             IsContainerOpen(result, &open);
    1318               0 :             if (open)
    1319               0 :                 OpenContainer(iter.GetRowIndex(), result);
    1320                 :         }
    1321                 :     }
    1322                 : 
    1323               0 :     return NS_OK;
    1324                 : }
    1325                 : 
    1326                 : nsresult
    1327               0 : nsXULTreeBuilder::SynchronizeResult(nsIXULTemplateResult* aResult)
    1328                 : {
    1329               0 :     if (mBoxObject) {
    1330                 :         // XXX we could be more conservative and just invalidate the cells
    1331                 :         // that got whacked...
    1332                 : 
    1333               0 :         nsTreeRows::iterator iter = mRows.Find(aResult);
    1334                 : 
    1335               0 :         NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
    1336               0 :         if (iter == mRows.Last())
    1337               0 :             return NS_ERROR_FAILURE;
    1338                 : 
    1339               0 :         PRInt32 row = iter.GetRowIndex();
    1340               0 :         if (row >= 0)
    1341               0 :             mBoxObject->InvalidateRow(row);
    1342                 : 
    1343               0 :         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
    1344                 :                ("xultemplate[%p]   => row %d", this, row));
    1345                 :     }
    1346                 : 
    1347               0 :     return NS_OK;
    1348                 : }
    1349                 : 
    1350                 : //----------------------------------------------------------------------
    1351                 : 
    1352                 : nsresult
    1353               0 : nsXULTreeBuilder::EnsureSortVariables()
    1354                 : {
    1355                 :     // Grovel through <treecols> kids to find the <treecol>
    1356                 :     // with the sort attributes.
    1357               0 :     nsCOMPtr<nsIContent> treecols;
    1358                 :  
    1359                 :     nsXULContentUtils::FindChildByTag(mRoot, kNameSpaceID_XUL,
    1360                 :                                       nsGkAtoms::treecols,
    1361               0 :                                       getter_AddRefs(treecols));
    1362                 : 
    1363               0 :     if (!treecols)
    1364               0 :         return NS_OK;
    1365                 :         
    1366               0 :     for (nsIContent* child = treecols->GetFirstChild();
    1367                 :          child;
    1368               0 :          child = child->GetNextSibling()) {
    1369                 : 
    1370               0 :         if (child->NodeInfo()->Equals(nsGkAtoms::treecol,
    1371               0 :                                       kNameSpaceID_XUL)) {
    1372               0 :             if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortActive,
    1373               0 :                                    nsGkAtoms::_true, eCaseMatters)) {
    1374               0 :                 nsAutoString sort;
    1375               0 :                 child->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
    1376               0 :                 if (! sort.IsEmpty()) {
    1377               0 :                     mSortVariable = do_GetAtom(sort);
    1378                 : 
    1379                 :                     static nsIContent::AttrValuesArray strings[] =
    1380                 :                       {&nsGkAtoms::ascending, &nsGkAtoms::descending, nsnull};
    1381               0 :                     switch (child->FindAttrValueIn(kNameSpaceID_None,
    1382                 :                                                    nsGkAtoms::sortDirection,
    1383               0 :                                                    strings, eCaseMatters)) {
    1384               0 :                        case 0: mSortDirection = eDirection_Ascending; break;
    1385               0 :                        case 1: mSortDirection = eDirection_Descending; break;
    1386               0 :                        default: mSortDirection = eDirection_Natural; break;
    1387                 :                     }
    1388                 :                 }
    1389                 :                 break;
    1390                 :             }
    1391                 :         }
    1392                 :     }
    1393                 : 
    1394               0 :     return NS_OK;
    1395                 : }
    1396                 : 
    1397                 : nsresult
    1398               0 : nsXULTreeBuilder::RebuildAll()
    1399                 : {
    1400               0 :     NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
    1401                 : 
    1402               0 :     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
    1403                 : 
    1404                 :     // Bail out early if we are being torn down.
    1405               0 :     if (!doc)
    1406               0 :         return NS_OK;
    1407                 : 
    1408               0 :     if (! mQueryProcessor)
    1409               0 :         return NS_OK;
    1410                 : 
    1411               0 :     if (mBoxObject) {
    1412               0 :         mBoxObject->BeginUpdateBatch();
    1413                 :     }
    1414                 : 
    1415               0 :     if (mQueriesCompiled) {
    1416               0 :         Uninit(false);
    1417                 :     }
    1418               0 :     else if (mBoxObject) {
    1419               0 :         PRInt32 count = mRows.Count();
    1420               0 :         mRows.Clear();
    1421               0 :         mBoxObject->RowCountChanged(0, -count);
    1422                 :     }
    1423                 : 
    1424               0 :     nsresult rv = CompileQueries();
    1425               0 :     if (NS_SUCCEEDED(rv) && mQuerySets.Length() > 0) {
    1426                 :         // Seed the rule network with assignments for the tree row variable
    1427               0 :         nsAutoString ref;
    1428               0 :         mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref);
    1429               0 :         if (!ref.IsEmpty()) {
    1430               0 :             rv = mQueryProcessor->TranslateRef(mDataSource, ref,
    1431               0 :                                                getter_AddRefs(mRootResult));
    1432               0 :             if (NS_SUCCEEDED(rv) && mRootResult) {
    1433               0 :                 OpenContainer(-1, mRootResult);
    1434                 : 
    1435               0 :                 nsCOMPtr<nsIRDFResource> rootResource;
    1436               0 :                 GetResultResource(mRootResult, getter_AddRefs(rootResource));
    1437                 : 
    1438               0 :                 mRows.SetRootResource(rootResource);
    1439                 :             }
    1440                 :         }
    1441                 :     }
    1442                 : 
    1443               0 :     if (mBoxObject) {
    1444               0 :         mBoxObject->EndUpdateBatch();
    1445                 :     }
    1446                 : 
    1447               0 :     return rv;
    1448                 : }
    1449                 : 
    1450                 : nsresult
    1451               0 : nsXULTreeBuilder::GetTemplateActionRowFor(PRInt32 aRow, nsIContent** aResult)
    1452                 : {
    1453                 :     // Get the template in the DOM from which we're supposed to
    1454                 :     // generate text
    1455               0 :     nsTreeRows::Row& row = *(mRows[aRow]);
    1456                 : 
    1457                 :     // The match stores the indices of the rule and query to use. Use these
    1458                 :     // to look up the right nsTemplateRule and use that rule's action to get
    1459                 :     // the treerow in the template.
    1460               0 :     PRInt16 ruleindex = row.mMatch->RuleIndex();
    1461               0 :     if (ruleindex >= 0) {
    1462               0 :         nsTemplateQuerySet* qs = mQuerySets[row.mMatch->QuerySetPriority()];
    1463               0 :         nsTemplateRule* rule = qs->GetRuleAt(ruleindex);
    1464               0 :         if (rule) {
    1465               0 :             nsCOMPtr<nsIContent> children;
    1466                 :             nsXULContentUtils::FindChildByTag(rule->GetAction(), kNameSpaceID_XUL,
    1467                 :                                               nsGkAtoms::treechildren,
    1468               0 :                                               getter_AddRefs(children));
    1469               0 :             if (children) {
    1470               0 :                 nsCOMPtr<nsIContent> item;
    1471                 :                 nsXULContentUtils::FindChildByTag(children, kNameSpaceID_XUL,
    1472                 :                                                   nsGkAtoms::treeitem,
    1473               0 :                                                   getter_AddRefs(item));
    1474               0 :                 if (item)
    1475                 :                     return nsXULContentUtils::FindChildByTag(item,
    1476                 :                                                              kNameSpaceID_XUL,
    1477                 :                                                              nsGkAtoms::treerow,
    1478               0 :                                                              aResult);
    1479                 :             }
    1480                 :         }
    1481                 :     }
    1482                 : 
    1483               0 :     *aResult = nsnull;
    1484               0 :     return NS_OK;
    1485                 : }
    1486                 : 
    1487                 : nsresult
    1488               0 : nsXULTreeBuilder::GetTemplateActionCellFor(PRInt32 aRow,
    1489                 :                                            nsITreeColumn* aCol,
    1490                 :                                            nsIContent** aResult)
    1491                 : {
    1492               0 :     *aResult = nsnull;
    1493                 : 
    1494               0 :     if (!aCol) return NS_ERROR_INVALID_ARG;
    1495                 : 
    1496               0 :     nsCOMPtr<nsIContent> row;
    1497               0 :     GetTemplateActionRowFor(aRow, getter_AddRefs(row));
    1498               0 :     if (row) {
    1499               0 :         nsCOMPtr<nsIAtom> colAtom;
    1500                 :         PRInt32 colIndex;
    1501               0 :         aCol->GetAtom(getter_AddRefs(colAtom));
    1502               0 :         aCol->GetIndex(&colIndex);
    1503                 : 
    1504               0 :         PRUint32 j = 0;
    1505               0 :         for (nsIContent* child = row->GetFirstChild();
    1506                 :              child;
    1507               0 :              child = child->GetNextSibling()) {
    1508                 :             
    1509               0 :             if (child->NodeInfo()->Equals(nsGkAtoms::treecell,
    1510               0 :                                           kNameSpaceID_XUL)) {
    1511               0 :                 if (colAtom &&
    1512                 :                     child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref,
    1513               0 :                                        colAtom, eCaseMatters)) {
    1514               0 :                     *aResult = child;
    1515               0 :                     break;
    1516                 :                 }
    1517               0 :                 else if (j == (PRUint32)colIndex)
    1518               0 :                     *aResult = child;
    1519               0 :                 j++;
    1520                 :             }
    1521                 :         }
    1522                 :     }
    1523               0 :     NS_IF_ADDREF(*aResult);
    1524                 : 
    1525               0 :     return NS_OK;
    1526                 : }
    1527                 : 
    1528                 : nsresult
    1529               0 : nsXULTreeBuilder::GetResourceFor(PRInt32 aRow, nsIRDFResource** aResource)
    1530                 : {
    1531               0 :     nsTreeRows::Row& row = *(mRows[aRow]);
    1532               0 :     return GetResultResource(row.mMatch->mResult, aResource);
    1533                 : }
    1534                 : 
    1535                 : nsresult
    1536               0 : nsXULTreeBuilder::OpenContainer(PRInt32 aIndex, nsIXULTemplateResult* aResult)
    1537                 : {
    1538                 :     // A row index of -1 in this case means ``open tree body''
    1539               0 :     NS_ASSERTION(aIndex >= -1 && aIndex < mRows.Count(), "bad row");
    1540               0 :     if (aIndex < -1 || aIndex >= mRows.Count())
    1541               0 :         return NS_ERROR_INVALID_ARG;
    1542                 : 
    1543                 :     nsTreeRows::Subtree* container;
    1544                 : 
    1545               0 :     if (aIndex >= 0) {
    1546               0 :         nsTreeRows::iterator iter = mRows[aIndex];
    1547                 :         container = mRows.EnsureSubtreeFor(iter.GetParent(),
    1548               0 :                                            iter.GetChildIndex());
    1549                 : 
    1550               0 :         iter->mContainerState = nsTreeRows::eContainerState_Open;
    1551                 :     }
    1552                 :     else
    1553               0 :         container = mRows.GetRoot();
    1554                 : 
    1555               0 :     if (! container)
    1556               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1557                 : 
    1558                 :     PRInt32 count;
    1559               0 :     OpenSubtreeOf(container, aIndex, aResult, &count);
    1560                 : 
    1561                 :     // Notify the box object
    1562               0 :     if (mBoxObject) {
    1563               0 :         if (aIndex >= 0)
    1564               0 :             mBoxObject->InvalidateRow(aIndex);
    1565                 : 
    1566               0 :         if (count)
    1567               0 :             mBoxObject->RowCountChanged(aIndex + 1, count);
    1568                 :     }
    1569                 : 
    1570               0 :     return NS_OK;
    1571                 : }
    1572                 : 
    1573                 : nsresult
    1574               0 : nsXULTreeBuilder::OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
    1575                 :                                 PRInt32 aIndex,
    1576                 :                                 nsIXULTemplateResult *aResult,
    1577                 :                                 PRInt32* aDelta)
    1578                 : {
    1579               0 :     nsAutoTArray<PRInt32, 8> open;
    1580               0 :     PRInt32 count = 0;
    1581                 : 
    1582               0 :     PRInt32 rulecount = mQuerySets.Length();
    1583                 : 
    1584               0 :     for (PRInt32 r = 0; r < rulecount; r++) {
    1585               0 :         nsTemplateQuerySet* queryset = mQuerySets[r];
    1586               0 :         OpenSubtreeForQuerySet(aSubtree, aIndex, aResult, queryset, &count, open);
    1587                 :     }
    1588                 : 
    1589                 :     // Now recursively deal with any open sub-containers that just got
    1590                 :     // inserted. We need to do this back-to-front to avoid skewing offsets.
    1591               0 :     for (PRInt32 i = open.Length() - 1; i >= 0; --i) {
    1592               0 :         PRInt32 index = open[i];
    1593                 : 
    1594                 :         nsTreeRows::Subtree* child =
    1595               0 :             mRows.EnsureSubtreeFor(aSubtree, index);
    1596                 : 
    1597               0 :         nsIXULTemplateResult* result = (*aSubtree)[index].mMatch->mResult;
    1598                 : 
    1599                 :         PRInt32 delta;
    1600               0 :         OpenSubtreeOf(child, aIndex + index, result, &delta);
    1601               0 :         count += delta;
    1602                 :     }
    1603                 : 
    1604                 :     // Sort the container.
    1605               0 :     if (mSortVariable) {
    1606               0 :         NS_QuickSort(mRows.GetRowsFor(aSubtree),
    1607               0 :                      aSubtree->Count(),
    1608                 :                      sizeof(nsTreeRows::Row),
    1609                 :                      Compare,
    1610               0 :                      this);
    1611                 :     }
    1612                 : 
    1613               0 :     *aDelta = count;
    1614               0 :     return NS_OK;
    1615                 : }
    1616                 : 
    1617                 : nsresult
    1618               0 : nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
    1619                 :                                          PRInt32 aIndex,
    1620                 :                                          nsIXULTemplateResult* aResult,
    1621                 :                                          nsTemplateQuerySet* aQuerySet,
    1622                 :                                          PRInt32* aDelta,
    1623                 :                                          nsTArray<PRInt32>& open)
    1624                 : {
    1625               0 :     PRInt32 count = *aDelta;
    1626                 :     
    1627               0 :     nsCOMPtr<nsISimpleEnumerator> results;
    1628               0 :     nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult,
    1629                 :                                                    aQuerySet->mCompiledQuery,
    1630               0 :                                                    getter_AddRefs(results));
    1631               0 :     if (NS_FAILED(rv))
    1632               0 :         return rv;
    1633                 : 
    1634                 :     bool hasMoreResults;
    1635               0 :     rv = results->HasMoreElements(&hasMoreResults);
    1636                 : 
    1637               0 :     for (; NS_SUCCEEDED(rv) && hasMoreResults;
    1638               0 :            rv = results->HasMoreElements(&hasMoreResults)) {
    1639               0 :         nsCOMPtr<nsISupports> nr;
    1640               0 :         rv = results->GetNext(getter_AddRefs(nr));
    1641               0 :         if (NS_FAILED(rv))
    1642               0 :             return rv;
    1643                 : 
    1644               0 :         nsCOMPtr<nsIXULTemplateResult> nextresult = do_QueryInterface(nr);
    1645               0 :         if (!nextresult)
    1646               0 :             return NS_ERROR_UNEXPECTED;
    1647                 : 
    1648               0 :         nsCOMPtr<nsIRDFResource> resultid;
    1649               0 :         rv = GetResultResource(nextresult, getter_AddRefs(resultid));
    1650               0 :         if (NS_FAILED(rv))
    1651               0 :             return rv;
    1652                 : 
    1653               0 :         if (! resultid)
    1654               0 :             continue;
    1655                 : 
    1656                 :         // check if there is already an existing match. If so, a previous
    1657                 :         // query already generated content so the match is just added to the
    1658                 :         // end of the set of matches.
    1659                 : 
    1660               0 :         bool generateContent = true;
    1661                 : 
    1662               0 :         nsTemplateMatch* prevmatch = nsnull;
    1663               0 :         nsTemplateMatch* existingmatch = nsnull;
    1664               0 :         if (mMatchMap.Get(resultid, &existingmatch)){
    1665                 :             // check if there is an existing match that matched a rule
    1666               0 :             while (existingmatch) {
    1667               0 :                 if (existingmatch->IsActive())
    1668               0 :                     generateContent = false;
    1669               0 :                 prevmatch = existingmatch;
    1670               0 :                 existingmatch = existingmatch->mNext;
    1671                 :             }
    1672                 :         }
    1673                 : 
    1674                 :         nsTemplateMatch *newmatch =
    1675               0 :             nsTemplateMatch::Create(mPool, aQuerySet->Priority(),
    1676               0 :                                     nextresult, nsnull);
    1677               0 :         if (!newmatch)
    1678               0 :             return NS_ERROR_OUT_OF_MEMORY;
    1679                 : 
    1680               0 :         if (generateContent) {
    1681                 :             // Don't allow cyclic graphs to get our knickers in a knot.
    1682               0 :             bool cyclic = false;
    1683                 : 
    1684               0 :             if (aIndex >= 0) {
    1685               0 :                 for (nsTreeRows::iterator iter = mRows[aIndex]; iter.GetDepth() > 0; iter.Pop()) {
    1686               0 :                     nsCOMPtr<nsIRDFResource> parentid;
    1687               0 :                     rv = GetResultResource(iter->mMatch->mResult, getter_AddRefs(parentid));
    1688               0 :                     if (NS_FAILED(rv)) {
    1689               0 :                         nsTemplateMatch::Destroy(mPool, newmatch, false);
    1690               0 :                         return rv;
    1691                 :                     }
    1692                 : 
    1693               0 :                     if (resultid == parentid) {
    1694               0 :                         cyclic = true;
    1695                 :                         break;
    1696                 :                     }
    1697                 :                 }
    1698                 :             }
    1699                 : 
    1700               0 :             if (cyclic) {
    1701               0 :                 NS_WARNING("tree cannot handle cyclic graphs");
    1702               0 :                 nsTemplateMatch::Destroy(mPool, newmatch, false);
    1703               0 :                 continue;
    1704                 :             }
    1705                 : 
    1706                 :             PRInt16 ruleindex;
    1707               0 :             nsTemplateRule* matchedrule = nsnull;
    1708                 :             rv = DetermineMatchedRule(nsnull, nextresult, aQuerySet,
    1709               0 :                                       &matchedrule, &ruleindex);
    1710               0 :             if (NS_FAILED(rv)) {
    1711               0 :                 nsTemplateMatch::Destroy(mPool, newmatch, false);
    1712               0 :                 return rv;
    1713                 :             }
    1714                 : 
    1715               0 :             if (matchedrule) {
    1716                 :                 rv = newmatch->RuleMatched(aQuerySet, matchedrule, ruleindex,
    1717               0 :                                            nextresult);
    1718               0 :                 if (NS_FAILED(rv)) {
    1719               0 :                     nsTemplateMatch::Destroy(mPool, newmatch, false);
    1720               0 :                     return rv;
    1721                 :                 }
    1722                 : 
    1723                 :                 // Remember that this match applied to this row
    1724               0 :                 mRows.InsertRowAt(newmatch, aSubtree, count);
    1725                 : 
    1726                 :                 // If this is open, then remember it so we can recursively add
    1727                 :                 // *its* rows to the tree.
    1728               0 :                 bool isOpen = false;
    1729               0 :                 IsContainerOpen(nextresult, &isOpen);
    1730               0 :                 if (isOpen) {
    1731               0 :                     if (open.AppendElement(count) == nsnull)
    1732               0 :                         return NS_ERROR_OUT_OF_MEMORY;
    1733                 :                 }
    1734                 : 
    1735               0 :                 ++count;
    1736                 :             }
    1737                 : 
    1738               0 :             if (mFlags & eLoggingEnabled)
    1739               0 :                 OutputMatchToLog(resultid, newmatch, true);
    1740                 : 
    1741                 :         }
    1742                 : 
    1743               0 :         if (prevmatch) {
    1744               0 :             prevmatch->mNext = newmatch;
    1745                 :         }
    1746               0 :         else if (!mMatchMap.Put(resultid, newmatch)) {
    1747               0 :             nsTemplateMatch::Destroy(mPool, newmatch, true);
    1748               0 :             return NS_ERROR_OUT_OF_MEMORY;
    1749                 :         }
    1750                 :     }
    1751                 : 
    1752               0 :     *aDelta = count;
    1753               0 :     return rv;
    1754                 : }
    1755                 : 
    1756                 : nsresult
    1757               0 : nsXULTreeBuilder::CloseContainer(PRInt32 aIndex)
    1758                 : {
    1759               0 :     NS_ASSERTION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
    1760               0 :     if (aIndex < 0 || aIndex >= mRows.Count())
    1761               0 :         return NS_ERROR_INVALID_ARG;
    1762                 : 
    1763               0 :     nsTreeRows::iterator iter = mRows[aIndex];
    1764                 : 
    1765               0 :     if (iter->mSubtree)
    1766               0 :         RemoveMatchesFor(*iter->mSubtree);
    1767                 : 
    1768                 : 
    1769               0 :     PRInt32 count = mRows.GetSubtreeSizeFor(iter);
    1770               0 :     mRows.RemoveSubtreeFor(iter);
    1771                 : 
    1772               0 :     iter->mContainerState = nsTreeRows::eContainerState_Closed;
    1773                 : 
    1774               0 :     if (mBoxObject) {
    1775               0 :         mBoxObject->InvalidateRow(aIndex);
    1776                 : 
    1777               0 :         if (count)
    1778               0 :             mBoxObject->RowCountChanged(aIndex + 1, -count);
    1779                 :     }
    1780                 : 
    1781               0 :     return NS_OK;
    1782                 : }
    1783                 : 
    1784                 : nsresult
    1785               0 : nsXULTreeBuilder::RemoveMatchesFor(nsTreeRows::Subtree& subtree)
    1786                 : {
    1787               0 :     for (PRInt32 i = subtree.Count() - 1; i >= 0; --i) {
    1788               0 :         nsTreeRows::Row& row = subtree[i];
    1789                 : 
    1790               0 :         nsTemplateMatch* match = row.mMatch;
    1791                 : 
    1792               0 :         nsCOMPtr<nsIRDFResource> id;
    1793               0 :         nsresult rv = GetResultResource(match->mResult, getter_AddRefs(id));
    1794               0 :         if (NS_FAILED(rv))
    1795               0 :             return rv;
    1796                 : 
    1797                 :         nsTemplateMatch* existingmatch;
    1798               0 :         if (mMatchMap.Get(id, &existingmatch)) {
    1799               0 :             while (existingmatch) {
    1800               0 :                 nsTemplateMatch* nextmatch = existingmatch->mNext;
    1801               0 :                 nsTemplateMatch::Destroy(mPool, existingmatch, true);
    1802               0 :                 existingmatch = nextmatch;
    1803                 :             }
    1804                 : 
    1805               0 :             mMatchMap.Remove(id);
    1806                 :         }
    1807                 : 
    1808               0 :         if ((row.mContainerState == nsTreeRows::eContainerState_Open) && row.mSubtree)
    1809               0 :             RemoveMatchesFor(*(row.mSubtree));
    1810                 :     }
    1811                 : 
    1812               0 :     return NS_OK;
    1813                 : }
    1814                 : 
    1815                 : nsresult
    1816               0 : nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen)
    1817                 : {
    1818                 :     // items are never open if recursion is disabled
    1819               0 :     if ((mFlags & eDontRecurse) && aResult != mRootResult) {
    1820               0 :         *aOpen = false;
    1821               0 :         return NS_OK;
    1822                 :     }
    1823                 : 
    1824               0 :     nsCOMPtr<nsIRDFResource> id;
    1825               0 :     nsresult rv = GetResultResource(aResult, getter_AddRefs(id));
    1826               0 :     if (NS_FAILED(rv))
    1827               0 :         return rv;
    1828                 : 
    1829               0 :     return IsContainerOpen(id, aOpen);
    1830                 : }
    1831                 : 
    1832                 : nsresult
    1833               0 : nsXULTreeBuilder::IsContainerOpen(nsIRDFResource* aResource, bool* aOpen)
    1834                 : {
    1835               0 :     if (mPersistStateStore)
    1836               0 :         mPersistStateStore->HasAssertion(aResource,
    1837                 :                                          nsXULContentUtils::NC_open,
    1838                 :                                          nsXULContentUtils::true_,
    1839                 :                                          true,
    1840               0 :                                          aOpen);
    1841                 :     else
    1842               0 :         *aOpen = false;
    1843                 : 
    1844               0 :     return NS_OK;
    1845                 : }
    1846                 : 
    1847                 : int
    1848               0 : nsXULTreeBuilder::Compare(const void* aLeft, const void* aRight, void* aClosure)
    1849                 : {
    1850               0 :     nsXULTreeBuilder* self = static_cast<nsXULTreeBuilder*>(aClosure);
    1851                 : 
    1852                 :     nsTreeRows::Row* left = static_cast<nsTreeRows::Row*>
    1853               0 :                                        (const_cast<void*>(aLeft));
    1854                 : 
    1855                 :     nsTreeRows::Row* right = static_cast<nsTreeRows::Row*>
    1856               0 :                                         (const_cast<void*>(aRight));
    1857                 : 
    1858               0 :     return self->CompareResults(left->mMatch->mResult, right->mMatch->mResult);
    1859                 : }
    1860                 : 
    1861                 : PRInt32
    1862               0 : nsXULTreeBuilder::CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight)
    1863                 : {
    1864                 :     // this is an extra check done for RDF queries such that results appear in
    1865                 :     // the order they appear in their containing Seq
    1866               0 :     if (mSortDirection == eDirection_Natural && mDB) {
    1867                 :         // If the sort order is ``natural'', then see if the container
    1868                 :         // is an RDF sequence. If so, we'll try to use the ordinal
    1869                 :         // properties to determine order.
    1870                 :         //
    1871                 :         // XXX the problem with this is, it doesn't always get the
    1872                 :         // *real* container; e.g.,
    1873                 :         //
    1874                 :         //  <treerow uri="?uri" />
    1875                 :         //
    1876                 :         //  <triple subject="?uri"
    1877                 :         //          predicate="http://home.netscape.com/NC-rdf#subheadings"
    1878                 :         //          object="?subheadings" />
    1879                 :         //
    1880                 :         //  <member container="?subheadings" child="?subheading" />
    1881                 :         //
    1882                 :         // In this case mRefVariable is bound to ?uri, not
    1883                 :         // ?subheadings. (The ``container'' in the template sense !=
    1884                 :         // container in the RDF sense.)
    1885                 : 
    1886               0 :         nsCOMPtr<nsISupports> ref;
    1887               0 :         nsresult rv = aLeft->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref));
    1888               0 :         if (NS_FAILED(rv))
    1889               0 :             return rv;
    1890                 : 
    1891               0 :         nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
    1892               0 :         if (container) {
    1893               0 :             bool isSequence = false;
    1894               0 :             gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
    1895               0 :             if (isSequence) {
    1896                 :                 // Determine the indices of the left and right elements
    1897                 :                 // in the container.
    1898               0 :                 PRInt32 lindex = 0, rindex = 0;
    1899                 : 
    1900               0 :                 nsCOMPtr<nsIRDFResource> leftitem;
    1901               0 :                 aLeft->GetResource(getter_AddRefs(leftitem));
    1902               0 :                 if (leftitem) {
    1903               0 :                     gRDFContainerUtils->IndexOf(mDB, container, leftitem, &lindex);
    1904               0 :                     if (lindex < 0)
    1905               0 :                         return 0;
    1906                 :                 }
    1907                 : 
    1908               0 :                 nsCOMPtr<nsIRDFResource> rightitem;
    1909               0 :                 aRight->GetResource(getter_AddRefs(rightitem));
    1910               0 :                 if (rightitem) {
    1911               0 :                     gRDFContainerUtils->IndexOf(mDB, container, rightitem, &rindex);
    1912               0 :                     if (rindex < 0)
    1913               0 :                         return 0;
    1914                 :                 }
    1915                 : 
    1916               0 :                 return lindex - rindex;
    1917                 :             }
    1918                 :         }
    1919                 :     }
    1920                 : 
    1921                 :     PRInt32 sortorder;
    1922               0 :     mQueryProcessor->CompareResults(aLeft, aRight, mSortVariable, mSortHints, &sortorder);
    1923                 : 
    1924               0 :     if (sortorder)
    1925               0 :         sortorder = sortorder * mSortDirection;
    1926               0 :     return sortorder;
    1927                 : }
    1928                 : 
    1929                 : nsresult
    1930               0 : nsXULTreeBuilder::SortSubtree(nsTreeRows::Subtree* aSubtree)
    1931                 : {
    1932               0 :     NS_QuickSort(mRows.GetRowsFor(aSubtree),
    1933               0 :                  aSubtree->Count(),
    1934                 :                  sizeof(nsTreeRows::Row),
    1935                 :                  Compare,
    1936               0 :                  this);
    1937                 : 
    1938               0 :     for (PRInt32 i = aSubtree->Count() - 1; i >= 0; --i) {
    1939               0 :         nsTreeRows::Subtree* child = (*aSubtree)[i].mSubtree;
    1940               0 :         if (child)
    1941               0 :             SortSubtree(child);
    1942                 :     }
    1943                 : 
    1944               0 :     return NS_OK;
    1945                 : }
    1946                 : 
    1947                 : 
    1948                 : /* boolean canDrop (in long index, in long orientation); */
    1949                 : NS_IMETHODIMP
    1950               0 : nsXULTreeBuilder::CanDrop(PRInt32 index, PRInt32 orientation,
    1951                 :                           nsIDOMDataTransfer* dataTransfer, bool *_retval)
    1952                 : {
    1953               0 :     *_retval = false;
    1954               0 :     if (mObservers) {
    1955                 :         PRUint32 count;
    1956               0 :         mObservers->Count(&count);
    1957               0 :         for (PRUint32 i = 0; i < count; ++i) {
    1958               0 :             nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
    1959               0 :             if (observer) {
    1960               0 :                 observer->CanDrop(index, orientation, dataTransfer, _retval);
    1961               0 :                 if (*_retval)
    1962                 :                     break;
    1963                 :             }
    1964                 :         }
    1965                 :     }
    1966                 : 
    1967               0 :     return NS_OK;
    1968                 : }
    1969                 : 
    1970                 : NS_IMETHODIMP
    1971               0 : nsXULTreeBuilder::Drop(PRInt32 row, PRInt32 orient, nsIDOMDataTransfer* dataTransfer)
    1972                 : {
    1973               0 :     if (mObservers) {
    1974                 :         PRUint32 count;
    1975               0 :         mObservers->Count(&count);
    1976               0 :         for (PRUint32 i = 0; i < count; ++i) {
    1977               0 :             nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
    1978               0 :             if (observer) {
    1979               0 :                 bool canDrop = false;
    1980               0 :                 observer->CanDrop(row, orient, dataTransfer, &canDrop);
    1981               0 :                 if (canDrop)
    1982               0 :                     observer->OnDrop(row, orient, dataTransfer);
    1983                 :             }
    1984                 :         }
    1985                 :     }
    1986                 : 
    1987               0 :     return NS_OK;
    1988                 : }
    1989                 : 
    1990                 : NS_IMETHODIMP
    1991               0 : nsXULTreeBuilder::IsSorted(bool *_retval)
    1992                 : {
    1993               0 :   *_retval = (mSortVariable != nsnull);
    1994               0 :   return NS_OK;
    1995            4392 : }
    1996                 : 

Generated by: LCOV version 1.7