content/xul/templates/src/nsXULTreeBuilder.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nscore.h"
     7 #include "nsError.h"
     8 #include "nsIContent.h"
     9 #include "nsINodeInfo.h"
    10 #include "nsIDOMElement.h"
    11 #include "nsILocalStore.h"
    12 #include "nsIBoxObject.h"
    13 #include "nsITreeBoxObject.h"
    14 #include "nsITreeSelection.h"
    15 #include "nsITreeColumns.h"
    16 #include "nsITreeView.h"
    17 #include "nsTreeUtils.h"
    18 #include "nsIServiceManager.h"
    19 #include "nsReadableUtils.h"
    20 #include "nsQuickSort.h"
    21 #include "nsTreeRows.h"
    22 #include "nsTemplateRule.h"
    23 #include "nsTemplateMatch.h"
    24 #include "nsGkAtoms.h"
    25 #include "nsXULContentUtils.h"
    26 #include "nsXULTemplateBuilder.h"
    27 #include "nsIXULSortService.h"
    28 #include "nsTArray.h"
    29 #include "nsUnicharUtils.h"
    30 #include "nsNameSpaceManager.h"
    31 #include "nsDOMClassInfoID.h"
    32 #include "nsWhitespaceTokenizer.h"
    33 #include "nsTreeContentView.h"
    35 // For security check
    36 #include "nsIDocument.h"
    38 /**
    39  * A XUL template builder that serves as an tree view, allowing
    40  * (pretty much) arbitrary RDF to be presented in an tree.
    41  */
    42 class nsXULTreeBuilder : public nsXULTemplateBuilder,
    43                          public nsIXULTreeBuilder,
    44                          public nsINativeTreeView
    45 {
    46 public:
    47     // nsISupports
    48     NS_DECL_ISUPPORTS_INHERITED
    49     NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
    51     // nsIXULTreeBuilder
    52     NS_DECL_NSIXULTREEBUILDER
    54     // nsITreeView
    55     NS_DECL_NSITREEVIEW
    56     // nsINativeTreeView: Untrusted code can use us
    57     NS_IMETHOD EnsureNative() { return NS_OK; }
    59     // nsIMutationObserver
    60     NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
    62 protected:
    63     friend nsresult
    64     NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
    66     nsXULTreeBuilder();
    68     /**
    69      * Uninitialize the template builder
    70      */
    71     virtual void Uninit(bool aIsFinal);
    73     /**
    74      * Get sort variables from the active <treecol>
    75      */
    76     nsresult
    77     EnsureSortVariables();
    79     virtual nsresult
    80     RebuildAll();
    82     /**
    83      * Given a row, use the row's match to figure out the appropriate
    84      * <treerow> in the rule's <action>.
    85      */
    86     nsresult
    87     GetTemplateActionRowFor(int32_t aRow, nsIContent** aResult);
    89     /**
    90      * Given a row and a column ID, use the row's match to figure out
    91      * the appropriate <treecell> in the rule's <action>.
    92      */
    93     nsresult
    94     GetTemplateActionCellFor(int32_t aRow, nsITreeColumn* aCol, nsIContent** aResult);
    96     /**
    97      * Return the resource corresponding to a row in the tree.
    98      */
    99     nsresult
   100     GetResourceFor(int32_t aRow, nsIRDFResource** aResource);
   102     /**
   103      * Open a container row, inserting the container's children into
   104      * the view.
   105      */
   106     nsresult
   107     OpenContainer(int32_t aIndex, nsIXULTemplateResult* aResult);
   109     /**
   110      * Helper for OpenContainer, recursively open subtrees, remembering
   111      * persisted ``open'' state
   112      */
   113     nsresult
   114     OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
   115                   int32_t aIndex,
   116                   nsIXULTemplateResult *aResult,
   117                   int32_t* aDelta);
   119     nsresult
   120     OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
   121                            int32_t aIndex,
   122                            nsIXULTemplateResult *aResult,
   123                            nsTemplateQuerySet* aQuerySet,
   124                            int32_t* aDelta,
   125                            nsTArray<int32_t>& open);
   127     /**
   128      * Close a container row, removing the container's childrem from
   129      * the view.
   130      */
   131     nsresult
   132     CloseContainer(int32_t aIndex);
   134     /**
   135      * Remove the matches for the rows in a subtree
   136      */
   137     nsresult
   138     RemoveMatchesFor(nsTreeRows::Subtree& subtree);
   140     /**
   141      * Helper methods that determine if the specified container is open.
   142      */
   143     nsresult
   144     IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen);
   146     nsresult
   147     IsContainerOpen(nsIRDFResource* aResource, bool* aOpen);
   149     /**
   150      * A sorting callback for NS_QuickSort().
   151      */
   152     static int
   153     Compare(const void* aLeft, const void* aRight, void* aClosure);
   155     /**
   156      * The real sort routine
   157      */
   158     int32_t
   159     CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight);
   161     /**
   162      * Sort the specified subtree, and recursively sort any subtrees
   163      * beneath it.
   164      */
   165     nsresult
   166     SortSubtree(nsTreeRows::Subtree* aSubtree);
   168     NS_IMETHOD
   169     HasGeneratedContent(nsIRDFResource* aResource,
   170                         nsIAtom* aTag,
   171                         bool* aGenerated);
   173     // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited
   174     // from nsXULTemplateBuilder
   176     /**
   177      * Return true if the result can be inserted into the template as a new
   178      * row.
   179      */
   180     bool
   181     GetInsertionLocations(nsIXULTemplateResult* aResult,
   182                           nsCOMArray<nsIContent>** aLocations);
   184     /**
   185      * Implement result replacement
   186      */
   187     virtual nsresult
   188     ReplaceMatch(nsIXULTemplateResult* aOldResult,
   189                  nsTemplateMatch* aNewMatch,
   190                  nsTemplateRule* aNewMatchRule,
   191                  void *aContext);
   193     /**
   194      * Implement match synchronization
   195      */
   196     virtual nsresult
   197     SynchronizeResult(nsIXULTemplateResult* aResult);
   199     /**
   200      * The tree's box object, used to communicate with the front-end.
   201      */
   202     nsCOMPtr<nsITreeBoxObject> mBoxObject;
   204     /**
   205      * The tree's selection object.
   206      */
   207     nsCOMPtr<nsITreeSelection> mSelection;
   209     /**
   210      * The datasource that's used to persist open folder information
   211      */
   212     nsCOMPtr<nsIRDFDataSource> mPersistStateStore;
   214     /**
   215      * The rows in the view
   216      */
   217     nsTreeRows mRows;
   219     /**
   220      * The currently active sort variable
   221      */
   222     nsCOMPtr<nsIAtom> mSortVariable;
   224     enum Direction {
   225         eDirection_Descending = -1,
   226         eDirection_Natural    =  0,
   227         eDirection_Ascending  = +1
   228     };
   230     /**
   231      * The currently active sort order
   232      */
   233     Direction mSortDirection;
   235     /*
   236      * Sort hints (compare case, etc)
   237      */
   238     uint32_t mSortHints;
   240     /** 
   241      * The builder observers.
   242      */
   243     nsCOMArray<nsIXULTreeBuilderObserver> mObservers;
   244 };
   246 //----------------------------------------------------------------------
   248 nsresult
   249 NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
   250 {
   251     *aResult = nullptr;
   253     NS_PRECONDITION(aOuter == nullptr, "no aggregation");
   254     if (aOuter)
   255         return NS_ERROR_NO_AGGREGATION;
   257     nsresult rv;
   258     nsXULTreeBuilder* result = new nsXULTreeBuilder();
   259     if (! result)
   260         return NS_ERROR_OUT_OF_MEMORY;
   262     NS_ADDREF(result); // stabilize
   264     rv = result->InitGlobals();
   266     if (NS_SUCCEEDED(rv))
   267         rv = result->QueryInterface(aIID, aResult);
   269     NS_RELEASE(result);
   270     return rv;
   271 }
   273 NS_IMPL_ADDREF_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
   274 NS_IMPL_RELEASE_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
   276 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder,
   277                                    mBoxObject,
   278                                    mSelection,
   279                                    mPersistStateStore,
   280                                    mObservers)
   282 DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder)
   284 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder)
   285     NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder)
   286     NS_INTERFACE_MAP_ENTRY(nsITreeView)
   287     NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTreeBuilder)
   288 NS_INTERFACE_MAP_END_INHERITING(nsXULTemplateBuilder)
   291 nsXULTreeBuilder::nsXULTreeBuilder()
   292     : mSortDirection(eDirection_Natural), mSortHints(0)
   293 {
   294 }
   296 void
   297 nsXULTreeBuilder::Uninit(bool aIsFinal)
   298 {
   299     int32_t count = mRows.Count();
   300     mRows.Clear();
   302     if (mBoxObject) {
   303         mBoxObject->BeginUpdateBatch();
   304         mBoxObject->RowCountChanged(0, -count);
   305         if (mBoxObject) {
   306             mBoxObject->EndUpdateBatch();
   307         }
   308     }
   310     nsXULTemplateBuilder::Uninit(aIsFinal);
   311 }
   314 //----------------------------------------------------------------------
   315 //
   316 // nsIXULTreeBuilder methods
   317 //
   319 NS_IMETHODIMP
   320 nsXULTreeBuilder::GetResourceAtIndex(int32_t aRowIndex, nsIRDFResource** aResult)
   321 {
   322     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
   323         return NS_ERROR_INVALID_ARG;
   325     return GetResourceFor(aRowIndex, aResult);
   326 }
   328 NS_IMETHODIMP
   329 nsXULTreeBuilder::GetIndexOfResource(nsIRDFResource* aResource, int32_t* aResult)
   330 {
   331     NS_ENSURE_ARG_POINTER(aResource);
   332     nsTreeRows::iterator iter = mRows.FindByResource(aResource);
   333     if (iter == mRows.Last())
   334         *aResult = -1;
   335     else
   336         *aResult = iter.GetRowIndex();
   337     return NS_OK;
   338 }
   340 NS_IMETHODIMP
   341 nsXULTreeBuilder::AddObserver(nsIXULTreeBuilderObserver* aObserver)
   342 {
   343     return mObservers.AppendObject(aObserver) ? NS_OK : NS_ERROR_FAILURE;
   344 }
   346 NS_IMETHODIMP
   347 nsXULTreeBuilder::RemoveObserver(nsIXULTreeBuilderObserver* aObserver)
   348 {
   349     return mObservers.RemoveObject(aObserver) ? NS_OK : NS_ERROR_FAILURE;
   350 }
   352 NS_IMETHODIMP
   353 nsXULTreeBuilder::Sort(nsIDOMElement* aElement)
   354 {
   355     nsCOMPtr<nsIContent> header = do_QueryInterface(aElement);
   356     if (! header)
   357         return NS_ERROR_FAILURE;
   359     if (header->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortLocked,
   360                             nsGkAtoms::_true, eCaseMatters))
   361         return NS_OK;
   363     nsAutoString sort;
   364     header->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
   366     if (sort.IsEmpty())
   367         return NS_OK;
   369     // Grab the new sort variable
   370     mSortVariable = do_GetAtom(sort);
   372     nsAutoString hints;
   373     header->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints);
   375     bool hasNaturalState = true;
   376     nsWhitespaceTokenizer tokenizer(hints);
   377     while (tokenizer.hasMoreTokens()) {
   378       const nsDependentSubstring& token(tokenizer.nextToken());
   379       if (token.EqualsLiteral("comparecase"))
   380         mSortHints |= nsIXULSortService::SORT_COMPARECASE;
   381       else if (token.EqualsLiteral("integer"))
   382         mSortHints |= nsIXULSortService::SORT_INTEGER;
   383       else if (token.EqualsLiteral("twostate"))
   384         hasNaturalState = false;
   385     }
   387     // Cycle the sort direction
   388     nsAutoString dir;
   389     header->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, dir);
   391     if (dir.EqualsLiteral("ascending")) {
   392         dir.AssignLiteral("descending");
   393         mSortDirection = eDirection_Descending;
   394     }
   395     else if (hasNaturalState && dir.EqualsLiteral("descending")) {
   396         dir.AssignLiteral("natural");
   397         mSortDirection = eDirection_Natural;
   398     }
   399     else {
   400         dir.AssignLiteral("ascending");
   401         mSortDirection = eDirection_Ascending;
   402     }
   404     // Sort it.
   405     SortSubtree(mRows.GetRoot());
   406     mRows.InvalidateCachedRow();
   407     if (mBoxObject) 
   408         mBoxObject->Invalidate();
   410     nsTreeUtils::UpdateSortIndicators(header, dir);
   412     return NS_OK;
   413 }
   415 //----------------------------------------------------------------------
   416 //
   417 // nsITreeView methods
   418 //
   420 NS_IMETHODIMP
   421 nsXULTreeBuilder::GetRowCount(int32_t* aRowCount)
   422 {
   423     *aRowCount = mRows.Count();
   424     return NS_OK;
   425 }
   427 NS_IMETHODIMP
   428 nsXULTreeBuilder::GetSelection(nsITreeSelection** aSelection)
   429 {
   430     NS_IF_ADDREF(*aSelection = mSelection.get());
   431     return NS_OK;
   432 }
   434 NS_IMETHODIMP
   435 nsXULTreeBuilder::SetSelection(nsITreeSelection* aSelection)
   436 {
   437     NS_ENSURE_TRUE(!aSelection ||
   438                    nsTreeContentView::CanTrustTreeSelection(aSelection),
   439                    NS_ERROR_DOM_SECURITY_ERR);
   440     mSelection = aSelection;
   441     return NS_OK;
   442 }
   444 NS_IMETHODIMP
   445 nsXULTreeBuilder::GetRowProperties(int32_t aIndex, nsAString& aProps)
   446 {
   447     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
   448     if (aIndex < 0 || aIndex >= mRows.Count())
   449         return NS_ERROR_INVALID_ARG;
   451     nsCOMPtr<nsIContent> row;
   452     GetTemplateActionRowFor(aIndex, getter_AddRefs(row));
   453     if (row) {
   454         nsAutoString raw;
   455         row->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
   457         if (!raw.IsEmpty()) {
   458             SubstituteText(mRows[aIndex]->mMatch->mResult, raw, aProps);
   459         }
   460     }
   462     return NS_OK;
   463 }
   465 NS_IMETHODIMP
   466 nsXULTreeBuilder::GetCellProperties(int32_t aRow, nsITreeColumn* aCol,
   467                                     nsAString& aProps)
   468 {
   469     NS_ENSURE_ARG_POINTER(aCol);
   470     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
   471     if (aRow < 0 || aRow >= mRows.Count())
   472         return NS_ERROR_INVALID_ARG;
   474     nsCOMPtr<nsIContent> cell;
   475     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
   476     if (cell) {
   477         nsAutoString raw;
   478         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
   480         if (!raw.IsEmpty()) {
   481             SubstituteText(mRows[aRow]->mMatch->mResult, raw, aProps);
   482         }
   483     }
   485     return NS_OK;
   486 }
   488 NS_IMETHODIMP
   489 nsXULTreeBuilder::GetColumnProperties(nsITreeColumn* aCol, nsAString& aProps)
   490 {
   491     NS_ENSURE_ARG_POINTER(aCol);
   492     // XXX sortactive fu
   493     return NS_OK;
   494 }
   496 NS_IMETHODIMP
   497 nsXULTreeBuilder::IsContainer(int32_t aIndex, bool* aResult)
   498 {
   499     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
   500     if (aIndex < 0 || aIndex >= mRows.Count())
   501         return NS_ERROR_INVALID_ARG;
   503     nsTreeRows::iterator iter = mRows[aIndex];
   505     bool isContainer;
   506     iter->mMatch->mResult->GetIsContainer(&isContainer);
   508     iter->mContainerType = isContainer
   509         ? nsTreeRows::eContainerType_Container
   510         : nsTreeRows::eContainerType_Noncontainer;
   512     *aResult = (iter->mContainerType == nsTreeRows::eContainerType_Container);
   513     return NS_OK;
   514 }
   516 NS_IMETHODIMP
   517 nsXULTreeBuilder::IsContainerOpen(int32_t aIndex, bool* aOpen)
   518 {
   519     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
   520     if (aIndex < 0 || aIndex >= mRows.Count())
   521         return NS_ERROR_INVALID_ARG;
   523     nsTreeRows::iterator iter = mRows[aIndex];
   525     if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) {
   526         bool isOpen;
   527         IsContainerOpen(iter->mMatch->mResult, &isOpen);
   529         iter->mContainerState = isOpen
   530             ? nsTreeRows::eContainerState_Open
   531             : nsTreeRows::eContainerState_Closed;
   532     }
   534     *aOpen = (iter->mContainerState == nsTreeRows::eContainerState_Open);
   535     return NS_OK;
   536 }
   538 NS_IMETHODIMP
   539 nsXULTreeBuilder::IsContainerEmpty(int32_t aIndex, bool* aResult)
   540 {
   541     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
   542     if (aIndex < 0 || aIndex >= mRows.Count())
   543         return NS_ERROR_INVALID_ARG;
   545     nsTreeRows::iterator iter = mRows[aIndex];
   546     NS_ASSERTION(iter->mContainerType == nsTreeRows::eContainerType_Container,
   547                  "asking for empty state on non-container");
   549     // if recursion is disabled, pretend that the container is empty. This
   550     // ensures that folders are still displayed as such, yet won't display
   551     // their children
   552     if ((mFlags & eDontRecurse) && (iter->mMatch->mResult != mRootResult)) {
   553         *aResult = true;
   554         return NS_OK;
   555     }
   557     if (iter->mContainerFill == nsTreeRows::eContainerFill_Unknown) {
   558         bool isEmpty;
   559         iter->mMatch->mResult->GetIsEmpty(&isEmpty);
   561         iter->mContainerFill = isEmpty
   562             ? nsTreeRows::eContainerFill_Empty
   563             : nsTreeRows::eContainerFill_Nonempty;
   564     }
   566     *aResult = (iter->mContainerFill == nsTreeRows::eContainerFill_Empty);
   567     return NS_OK;
   568 }
   570 NS_IMETHODIMP
   571 nsXULTreeBuilder::IsSeparator(int32_t aIndex, bool* aResult)
   572 {
   573     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
   574     if (aIndex < 0 || aIndex >= mRows.Count())
   575         return NS_ERROR_INVALID_ARG;
   577     nsAutoString type;
   578     nsTreeRows::Row& row = *(mRows[aIndex]);
   579     row.mMatch->mResult->GetType(type);
   581     *aResult = type.EqualsLiteral("separator");
   583     return NS_OK;
   584 }
   586 NS_IMETHODIMP
   587 nsXULTreeBuilder::GetParentIndex(int32_t aRowIndex, int32_t* aResult)
   588 {
   589     NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
   590     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
   591         return NS_ERROR_INVALID_ARG;
   593     // Construct a path to the row
   594     nsTreeRows::iterator iter = mRows[aRowIndex];
   596     // The parent of the row will be at the top of the path
   597     nsTreeRows::Subtree* parent = iter.GetParent();
   599     // Now walk through our previous siblings, subtracting off each
   600     // one's subtree size
   601     int32_t index = iter.GetChildIndex();
   602     while (--index >= 0)
   603         aRowIndex -= mRows.GetSubtreeSizeFor(parent, index) + 1;
   605     // Now the parent's index will be the first row's index, less one.
   606     *aResult = aRowIndex - 1;
   607     return NS_OK;
   608 }
   610 NS_IMETHODIMP
   611 nsXULTreeBuilder::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, bool* aResult)
   612 {
   613     NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
   614     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
   615         return NS_ERROR_INVALID_ARG;
   617     // Construct a path to the row
   618     nsTreeRows::iterator iter = mRows[aRowIndex];
   620     // The parent of the row will be at the top of the path
   621     nsTreeRows::Subtree* parent = iter.GetParent();
   623     // We have a next sibling if the child is not the last in the
   624     // subtree.
   625     *aResult = iter.GetChildIndex() != parent->Count() - 1;
   626     return NS_OK;
   627 }
   629 NS_IMETHODIMP
   630 nsXULTreeBuilder::GetLevel(int32_t aRowIndex, int32_t* aResult)
   631 {
   632     NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
   633     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
   634         return NS_ERROR_INVALID_ARG;
   636     // Construct a path to the row; the ``level'' is the path length
   637     // less one.
   638     nsTreeRows::iterator iter = mRows[aRowIndex];
   639     *aResult = iter.GetDepth() - 1;
   640     return NS_OK;
   641 }
   643 NS_IMETHODIMP
   644 nsXULTreeBuilder::GetImageSrc(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult)
   645 {
   646     NS_ENSURE_ARG_POINTER(aCol);
   647     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
   648     if (aRow < 0 || aRow >= mRows.Count())
   649         return NS_ERROR_INVALID_ARG;
   651     // Find the <cell> that corresponds to the column we want.
   652     nsCOMPtr<nsIContent> cell;
   653     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
   654     if (cell) {
   655         nsAutoString raw;
   656         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, raw);
   658         SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
   659     }
   660     else
   661         aResult.Truncate();
   663     return NS_OK;
   664 }
   667 NS_IMETHODIMP
   668 nsXULTreeBuilder::GetProgressMode(int32_t aRow, nsITreeColumn* aCol, int32_t* aResult)
   669 {
   670     NS_ENSURE_ARG_POINTER(aCol);
   671     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
   672     if (aRow < 0 || aRow >= mRows.Count())
   673         return NS_ERROR_INVALID_ARG;
   675     *aResult = nsITreeView::PROGRESS_NONE;
   677     // Find the <cell> that corresponds to the column we want.
   678     nsCOMPtr<nsIContent> cell;
   679     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
   680     if (cell) {
   681         nsAutoString raw;
   682         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::mode, raw);
   684         nsAutoString mode;
   685         SubstituteText(mRows[aRow]->mMatch->mResult, raw, mode);
   687         if (mode.EqualsLiteral("normal"))
   688             *aResult = nsITreeView::PROGRESS_NORMAL;
   689         else if (mode.EqualsLiteral("undetermined"))
   690             *aResult = nsITreeView::PROGRESS_UNDETERMINED;
   691     }
   693     return NS_OK;
   694 }
   696 NS_IMETHODIMP
   697 nsXULTreeBuilder::GetCellValue(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult)
   698 {
   699     NS_ENSURE_ARG_POINTER(aCol);
   700     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
   701     if (aRow < 0 || aRow >= mRows.Count())
   702         return NS_ERROR_INVALID_ARG;
   704     // Find the <cell> that corresponds to the column we want.
   705     nsCOMPtr<nsIContent> cell;
   706     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
   707     if (cell) {
   708         nsAutoString raw;
   709         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, raw);
   711         SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
   712     }
   713     else
   714         aResult.Truncate();
   716     return NS_OK;
   717 }
   719 NS_IMETHODIMP
   720 nsXULTreeBuilder::GetCellText(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult)
   721 {
   722     NS_ENSURE_ARG_POINTER(aCol);
   723     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
   724     if (aRow < 0 || aRow >= mRows.Count())
   725         return NS_ERROR_INVALID_ARG;
   727     // Find the <cell> that corresponds to the column we want.
   728     nsCOMPtr<nsIContent> cell;
   729     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
   730     if (cell) {
   731         nsAutoString raw;
   732         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, raw);
   734         SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
   736     }
   737     else
   738         aResult.Truncate();
   740     return NS_OK;
   741 }
   743 NS_IMETHODIMP
   744 nsXULTreeBuilder::SetTree(nsITreeBoxObject* aTree)
   745 {
   746     mBoxObject = aTree;
   748     // If this is teardown time, then we're done.
   749     if (!mBoxObject) {
   750         Uninit(false);
   751         return NS_OK;
   752     }
   753     NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
   755     // Is our root's principal trusted?
   756     bool isTrusted = false;
   757     nsresult rv = IsSystemPrincipal(mRoot->NodePrincipal(), &isTrusted);
   758     if (NS_SUCCEEDED(rv) && isTrusted) {
   759         // Get the datasource we intend to use to remember open state.
   760         nsAutoString datasourceStr;
   761         mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::statedatasource, datasourceStr);
   763         // since we are trusted, use the user specified datasource
   764         // if non specified, use localstore, which gives us
   765         // persistence across sessions
   766         if (! datasourceStr.IsEmpty()) {
   767             gRDFService->GetDataSource(NS_ConvertUTF16toUTF8(datasourceStr).get(),
   768                                        getter_AddRefs(mPersistStateStore));
   769         }
   770         else {
   771             gRDFService->GetDataSource("rdf:local-store",
   772                                        getter_AddRefs(mPersistStateStore));
   773         }
   774     }
   776     // Either no specific datasource was specified, or we failed
   777     // to get one because we are not trusted.
   778     //
   779     // XXX if it were possible to ``write an arbitrary datasource
   780     // back'', then we could also allow an untrusted document to
   781     // use a statedatasource from the same codebase.
   782     if (! mPersistStateStore) {
   783         mPersistStateStore =
   784             do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource");
   785     }
   787     NS_ASSERTION(mPersistStateStore, "failed to get a persistent state store");
   788     if (! mPersistStateStore)
   789         return NS_ERROR_FAILURE;
   791     Rebuild();
   793     EnsureSortVariables();
   794     if (mSortVariable)
   795         SortSubtree(mRows.GetRoot());
   797     return NS_OK;
   798 }
   800 NS_IMETHODIMP
   801 nsXULTreeBuilder::ToggleOpenState(int32_t aIndex)
   802 {
   803     if (aIndex < 0 || aIndex >= mRows.Count())
   804         return NS_ERROR_INVALID_ARG;
   806     nsIXULTemplateResult* result = mRows[aIndex]->mMatch->mResult;
   807     if (! result)
   808         return NS_ERROR_FAILURE;
   810     if (mFlags & eDontRecurse)
   811         return NS_OK;
   813     if (result && result != mRootResult) {
   814         // don't open containers if child processing isn't allowed
   815         bool mayProcessChildren;
   816         nsresult rv = result->GetMayProcessChildren(&mayProcessChildren);
   817         if (NS_FAILED(rv) || !mayProcessChildren)
   818             return rv;
   819     }
   821     uint32_t count = mObservers.Count();
   822     for (uint32_t i = 0; i < count; ++i) {
   823         nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
   824         if (observer)
   825             observer->OnToggleOpenState(aIndex);
   826     }
   828     if (mPersistStateStore) {
   829         bool isOpen;
   830         IsContainerOpen(aIndex, &isOpen);
   832         nsCOMPtr<nsIRDFResource> container;
   833         GetResourceFor(aIndex, getter_AddRefs(container));
   834         if (! container)
   835             return NS_ERROR_FAILURE;
   837         bool hasProperty;
   838         IsContainerOpen(container, &hasProperty);
   840         if (isOpen) {
   841             if (hasProperty) {
   842                 mPersistStateStore->Unassert(container,
   843                                              nsXULContentUtils::NC_open,
   844                                              nsXULContentUtils::true_);
   845             }
   847             CloseContainer(aIndex);
   848         }
   849         else {
   850             if (! hasProperty) {
   851                 mPersistStateStore->Assert(container,
   852                                            nsXULContentUtils::NC_open,
   853                                            nsXULContentUtils::true_,
   854                                            true);
   855             }
   857             OpenContainer(aIndex, result);
   858         }
   859     }
   861     return NS_OK;
   862 }
   864 NS_IMETHODIMP
   865 nsXULTreeBuilder::CycleHeader(nsITreeColumn* aCol)
   866 {
   867     NS_ENSURE_ARG_POINTER(aCol);
   868     nsCOMPtr<nsIDOMElement> element;
   869     aCol->GetElement(getter_AddRefs(element));
   871     nsAutoString id;
   872     aCol->GetId(id);
   874     uint32_t count = mObservers.Count();
   875     for (uint32_t i = 0; i < count; ++i) {
   876         nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
   877         if (observer)
   878             observer->OnCycleHeader(id.get(), element);
   879     }
   881     return Sort(element);
   882 }
   884 NS_IMETHODIMP
   885 nsXULTreeBuilder::SelectionChanged()
   886 {
   887     uint32_t count = mObservers.Count();
   888     for (uint32_t i = 0; i < count; ++i) {
   889         nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
   890         if (observer)
   891             observer->OnSelectionChanged();
   892     }
   894     return NS_OK;
   895 }
   897 NS_IMETHODIMP
   898 nsXULTreeBuilder::CycleCell(int32_t aRow, nsITreeColumn* aCol)
   899 {
   900     NS_ENSURE_ARG_POINTER(aCol);
   902     nsAutoString id;
   903     aCol->GetId(id);
   905     uint32_t count = mObservers.Count();
   906     for (uint32_t i = 0; i < count; ++i) {
   907         nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
   908         if (observer)
   909             observer->OnCycleCell(aRow, id.get());
   910     }
   912     return NS_OK;
   913 }
   915 NS_IMETHODIMP
   916 nsXULTreeBuilder::IsEditable(int32_t aRow, nsITreeColumn* aCol, bool* _retval)
   917 {
   918     *_retval = true;
   919     NS_ENSURE_ARG_POINTER(aCol);
   920     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
   921     if (aRow < 0 || aRow >= mRows.Count())
   922         return NS_ERROR_INVALID_ARG;
   924     // Find the <cell> that corresponds to the column we want.
   925     nsCOMPtr<nsIContent> cell;
   926     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
   927     if (cell) {
   928         nsAutoString raw;
   929         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::editable, raw);
   931         nsAutoString editable;
   932         SubstituteText(mRows[aRow]->mMatch->mResult, raw, editable);
   934         if (editable.EqualsLiteral("false"))
   935             *_retval = false;
   936     }
   938     return NS_OK;
   939 }
   941 NS_IMETHODIMP
   942 nsXULTreeBuilder::IsSelectable(int32_t aRow, nsITreeColumn* aCol, bool* _retval)
   943 {
   944     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
   945     if (aRow < 0 || aRow >= mRows.Count())
   946         return NS_ERROR_INVALID_ARG;
   948     *_retval = true;
   950     // Find the <cell> that corresponds to the column we want.
   951     nsCOMPtr<nsIContent> cell;
   952     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
   953     if (cell) {
   954         nsAutoString raw;
   955         cell->GetAttr(kNameSpaceID_None, nsGkAtoms::selectable, raw);
   957         nsAutoString selectable;
   958         SubstituteText(mRows[aRow]->mMatch->mResult, raw, selectable);
   960         if (selectable.EqualsLiteral("false"))
   961             *_retval = false;
   962     }
   964     return NS_OK;
   965 }
   967 NS_IMETHODIMP
   968 nsXULTreeBuilder::SetCellValue(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue)
   969 {
   970     NS_ENSURE_ARG_POINTER(aCol);
   971     return NS_OK;
   972 }
   974 NS_IMETHODIMP
   975 nsXULTreeBuilder::SetCellText(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue)
   976 {
   977     NS_ENSURE_ARG_POINTER(aCol);
   978     return NS_OK;
   979 }
   981 NS_IMETHODIMP
   982 nsXULTreeBuilder::PerformAction(const char16_t* aAction)
   983 {
   984     uint32_t count = mObservers.Count();
   985     for (uint32_t i = 0; i < count; ++i) {
   986         nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
   987         if (observer)
   988             observer->OnPerformAction(aAction);
   989     }
   991     return NS_OK;
   992 }
   994 NS_IMETHODIMP
   995 nsXULTreeBuilder::PerformActionOnRow(const char16_t* aAction, int32_t aRow)
   996 {
   997     uint32_t count = mObservers.Count();
   998     for (uint32_t i = 0; i < count; ++i) {
   999         nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
  1000         if (observer)
  1001             observer->OnPerformActionOnRow(aAction, aRow);
  1004     return NS_OK;
  1007 NS_IMETHODIMP
  1008 nsXULTreeBuilder::PerformActionOnCell(const char16_t* aAction, int32_t aRow, nsITreeColumn* aCol)
  1010     NS_ENSURE_ARG_POINTER(aCol);
  1011     nsAutoString id;
  1012     aCol->GetId(id);
  1014     uint32_t count = mObservers.Count();
  1015     for (uint32_t i = 0; i < count; ++i) {
  1016         nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
  1017         if (observer)
  1018             observer->OnPerformActionOnCell(aAction, aRow, id.get());
  1021     return NS_OK;
  1025 void
  1026 nsXULTreeBuilder::NodeWillBeDestroyed(const nsINode* aNode)
  1028     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1029     mObservers.Clear();
  1031     nsXULTemplateBuilder::NodeWillBeDestroyed(aNode);
  1034 NS_IMETHODIMP
  1035 nsXULTreeBuilder::HasGeneratedContent(nsIRDFResource* aResource,
  1036                                       nsIAtom* aTag,
  1037                                       bool* aGenerated)
  1039     *aGenerated = false;
  1040     NS_ENSURE_ARG_POINTER(aResource);
  1042     if (!mRootResult)
  1043         return NS_OK;
  1045     nsCOMPtr<nsIRDFResource> rootresource;
  1046     nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource));
  1047     if (NS_FAILED(rv))
  1048         return rv;
  1050     if (aResource == rootresource ||
  1051         mRows.FindByResource(aResource) != mRows.Last())
  1052         *aGenerated = true;
  1054     return NS_OK;
  1057 bool
  1058 nsXULTreeBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult,
  1059                                         nsCOMArray<nsIContent>** aLocations)
  1061     *aLocations = nullptr;
  1063     // Get the reference point and check if it is an open container. Rows
  1064     // should not be generated otherwise.
  1066     nsAutoString ref;
  1067     nsresult rv = aResult->GetBindingFor(mRefVariable, ref);
  1068     if (NS_FAILED(rv) || ref.IsEmpty())
  1069         return false;
  1071     nsCOMPtr<nsIRDFResource> container;
  1072     rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
  1073     if (NS_FAILED(rv))
  1074         return false;
  1076     // Can always insert into the root resource
  1077     if (container == mRows.GetRootResource())
  1078         return true;
  1080     nsTreeRows::iterator iter = mRows.FindByResource(container);
  1081     if (iter == mRows.Last())
  1082         return false;
  1084     return (iter->mContainerState == nsTreeRows::eContainerState_Open);
  1087 nsresult
  1088 nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult,
  1089                                nsTemplateMatch* aNewMatch,
  1090                                nsTemplateRule* aNewMatchRule,
  1091                                void *aLocation)
  1093     if (! mBoxObject)
  1094         return NS_OK;
  1096     if (aOldResult) {
  1097         // Grovel through the rows looking for oldresult.
  1098         nsTreeRows::iterator iter = mRows.Find(aOldResult);
  1100         NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
  1101         if (iter == mRows.Last())
  1102             return NS_ERROR_FAILURE;
  1104         // Remove the rows from the view
  1105         int32_t row = iter.GetRowIndex();
  1107         // If the row contains children, remove the matches from the
  1108         // children so that they can be regenerated again if the element
  1109         // gets added back.
  1110         int32_t delta = mRows.GetSubtreeSizeFor(iter);
  1111         if (delta)
  1112             RemoveMatchesFor(*(iter->mSubtree));
  1114         if (mRows.RemoveRowAt(iter) == 0 && iter.GetRowIndex() >= 0) {
  1116             // In this case iter now points to its parent
  1117             // Invalidate the row's cached fill state
  1118             iter->mContainerFill = nsTreeRows::eContainerFill_Unknown;
  1120             nsCOMPtr<nsITreeColumns> cols;
  1121             mBoxObject->GetColumns(getter_AddRefs(cols));
  1122             if (cols) {
  1123                 nsCOMPtr<nsITreeColumn> primaryCol;
  1124                 cols->GetPrimaryColumn(getter_AddRefs(primaryCol));
  1125                 if (primaryCol)
  1126                     mBoxObject->InvalidateCell(iter.GetRowIndex(), primaryCol);
  1130         // Notify the box object
  1131         mBoxObject->RowCountChanged(row, -delta - 1);
  1134     if (aNewMatch && aNewMatch->mResult) {
  1135         // Insertion.
  1136         int32_t row = -1;
  1137         nsTreeRows::Subtree* parent = nullptr;
  1138         nsIXULTemplateResult* result = aNewMatch->mResult;
  1140         nsAutoString ref;
  1141         nsresult rv = result->GetBindingFor(mRefVariable, ref);
  1142         if (NS_FAILED(rv) || ref.IsEmpty())
  1143             return rv;
  1145         nsCOMPtr<nsIRDFResource> container;
  1146         rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
  1147         if (NS_FAILED(rv))
  1148             return rv;
  1150         if (container != mRows.GetRootResource()) {
  1151             nsTreeRows::iterator iter = mRows.FindByResource(container);
  1152             row = iter.GetRowIndex();
  1154             NS_ASSERTION(iter != mRows.Last(), "couldn't find container row");
  1155             if (iter == mRows.Last())
  1156                 return NS_ERROR_FAILURE;
  1158             // Use the persist store to remember if the container
  1159             // is open or closed.
  1160             bool open = false;
  1161             IsContainerOpen(row, &open);
  1163             // If it's open, make sure that we've got a subtree structure ready.
  1164             if (open)
  1165                 parent = mRows.EnsureSubtreeFor(iter);
  1167             // We know something has just been inserted into the
  1168             // container, so whether its open or closed, make sure
  1169             // that we've got our tree row's state correct.
  1170             if ((iter->mContainerType != nsTreeRows::eContainerType_Container) ||
  1171                 (iter->mContainerFill != nsTreeRows::eContainerFill_Nonempty)) {
  1172                 iter->mContainerType  = nsTreeRows::eContainerType_Container;
  1173                 iter->mContainerFill = nsTreeRows::eContainerFill_Nonempty;
  1174                 mBoxObject->InvalidateRow(iter.GetRowIndex());
  1177         else {
  1178             parent = mRows.GetRoot();
  1181         if (parent) {
  1182             // If we get here, then we're inserting into an open
  1183             // container. By default, place the new element at the
  1184             // end of the container
  1185             int32_t index = parent->Count();
  1187             if (mSortVariable) {
  1188                 // Figure out where to put the new element by doing an
  1189                 // insertion sort.
  1190                 int32_t left = 0;
  1191                 int32_t right = index;
  1193                 while (left < right) {
  1194                     index = (left + right) / 2;
  1195                     int32_t cmp = CompareResults((*parent)[index].mMatch->mResult, result);
  1196                     if (cmp < 0)
  1197                         left = ++index;
  1198                     else if (cmp > 0)
  1199                         right = index;
  1200                     else
  1201                         break;
  1205             nsTreeRows::iterator iter =
  1206                 mRows.InsertRowAt(aNewMatch, parent, index);
  1208             mBoxObject->RowCountChanged(iter.GetRowIndex(), +1);
  1210             // See if this newly added row is open; in which case,
  1211             // recursively add its children to the tree, too.
  1213             if (mFlags & eDontRecurse)
  1214                 return NS_OK;
  1216             if (result != mRootResult) {
  1217                 // don't open containers if child processing isn't allowed
  1218                 bool mayProcessChildren;
  1219                 nsresult rv = result->GetMayProcessChildren(&mayProcessChildren);
  1220                 if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK;
  1223             bool open;
  1224             IsContainerOpen(result, &open);
  1225             if (open)
  1226                 OpenContainer(iter.GetRowIndex(), result);
  1230     return NS_OK;
  1233 nsresult
  1234 nsXULTreeBuilder::SynchronizeResult(nsIXULTemplateResult* aResult)
  1236     if (mBoxObject) {
  1237         // XXX we could be more conservative and just invalidate the cells
  1238         // that got whacked...
  1240         nsTreeRows::iterator iter = mRows.Find(aResult);
  1242         NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
  1243         if (iter == mRows.Last())
  1244             return NS_ERROR_FAILURE;
  1246         int32_t row = iter.GetRowIndex();
  1247         if (row >= 0)
  1248             mBoxObject->InvalidateRow(row);
  1250         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  1251                ("xultemplate[%p]   => row %d", this, row));
  1254     return NS_OK;
  1257 //----------------------------------------------------------------------
  1259 nsresult
  1260 nsXULTreeBuilder::EnsureSortVariables()
  1262     // Grovel through <treecols> kids to find the <treecol>
  1263     // with the sort attributes.
  1264     nsCOMPtr<nsIContent> treecols;
  1266     nsXULContentUtils::FindChildByTag(mRoot, kNameSpaceID_XUL,
  1267                                       nsGkAtoms::treecols,
  1268                                       getter_AddRefs(treecols));
  1270     if (!treecols)
  1271         return NS_OK;
  1273     for (nsIContent* child = treecols->GetFirstChild();
  1274          child;
  1275          child = child->GetNextSibling()) {
  1277         if (child->NodeInfo()->Equals(nsGkAtoms::treecol,
  1278                                       kNameSpaceID_XUL)) {
  1279             if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortActive,
  1280                                    nsGkAtoms::_true, eCaseMatters)) {
  1281                 nsAutoString sort;
  1282                 child->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
  1283                 if (! sort.IsEmpty()) {
  1284                     mSortVariable = do_GetAtom(sort);
  1286                     static nsIContent::AttrValuesArray strings[] =
  1287                       {&nsGkAtoms::ascending, &nsGkAtoms::descending, nullptr};
  1288                     switch (child->FindAttrValueIn(kNameSpaceID_None,
  1289                                                    nsGkAtoms::sortDirection,
  1290                                                    strings, eCaseMatters)) {
  1291                        case 0: mSortDirection = eDirection_Ascending; break;
  1292                        case 1: mSortDirection = eDirection_Descending; break;
  1293                        default: mSortDirection = eDirection_Natural; break;
  1296                 break;
  1301     return NS_OK;
  1304 nsresult
  1305 nsXULTreeBuilder::RebuildAll()
  1307     NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
  1309     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
  1311     // Bail out early if we are being torn down.
  1312     if (!doc)
  1313         return NS_OK;
  1315     if (! mQueryProcessor)
  1316         return NS_OK;
  1318     if (mBoxObject) {
  1319         mBoxObject->BeginUpdateBatch();
  1322     if (mQueriesCompiled) {
  1323         Uninit(false);
  1325     else if (mBoxObject) {
  1326         int32_t count = mRows.Count();
  1327         mRows.Clear();
  1328         mBoxObject->RowCountChanged(0, -count);
  1331     nsresult rv = CompileQueries();
  1332     if (NS_SUCCEEDED(rv) && mQuerySets.Length() > 0) {
  1333         // Seed the rule network with assignments for the tree row variable
  1334         nsAutoString ref;
  1335         mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref);
  1336         if (!ref.IsEmpty()) {
  1337             rv = mQueryProcessor->TranslateRef(mDataSource, ref,
  1338                                                getter_AddRefs(mRootResult));
  1339             if (NS_SUCCEEDED(rv) && mRootResult) {
  1340                 OpenContainer(-1, mRootResult);
  1342                 nsCOMPtr<nsIRDFResource> rootResource;
  1343                 GetResultResource(mRootResult, getter_AddRefs(rootResource));
  1345                 mRows.SetRootResource(rootResource);
  1350     if (mBoxObject) {
  1351         mBoxObject->EndUpdateBatch();
  1354     return rv;
  1357 nsresult
  1358 nsXULTreeBuilder::GetTemplateActionRowFor(int32_t aRow, nsIContent** aResult)
  1360     // Get the template in the DOM from which we're supposed to
  1361     // generate text
  1362     nsTreeRows::Row& row = *(mRows[aRow]);
  1364     // The match stores the indices of the rule and query to use. Use these
  1365     // to look up the right nsTemplateRule and use that rule's action to get
  1366     // the treerow in the template.
  1367     int16_t ruleindex = row.mMatch->RuleIndex();
  1368     if (ruleindex >= 0) {
  1369         nsTemplateQuerySet* qs = mQuerySets[row.mMatch->QuerySetPriority()];
  1370         nsTemplateRule* rule = qs->GetRuleAt(ruleindex);
  1371         if (rule) {
  1372             nsCOMPtr<nsIContent> children;
  1373             nsXULContentUtils::FindChildByTag(rule->GetAction(), kNameSpaceID_XUL,
  1374                                               nsGkAtoms::treechildren,
  1375                                               getter_AddRefs(children));
  1376             if (children) {
  1377                 nsCOMPtr<nsIContent> item;
  1378                 nsXULContentUtils::FindChildByTag(children, kNameSpaceID_XUL,
  1379                                                   nsGkAtoms::treeitem,
  1380                                                   getter_AddRefs(item));
  1381                 if (item)
  1382                     return nsXULContentUtils::FindChildByTag(item,
  1383                                                              kNameSpaceID_XUL,
  1384                                                              nsGkAtoms::treerow,
  1385                                                              aResult);
  1390     *aResult = nullptr;
  1391     return NS_OK;
  1394 nsresult
  1395 nsXULTreeBuilder::GetTemplateActionCellFor(int32_t aRow,
  1396                                            nsITreeColumn* aCol,
  1397                                            nsIContent** aResult)
  1399     *aResult = nullptr;
  1401     if (!aCol) return NS_ERROR_INVALID_ARG;
  1403     nsCOMPtr<nsIContent> row;
  1404     GetTemplateActionRowFor(aRow, getter_AddRefs(row));
  1405     if (row) {
  1406         nsCOMPtr<nsIAtom> colAtom;
  1407         int32_t colIndex;
  1408         aCol->GetAtom(getter_AddRefs(colAtom));
  1409         aCol->GetIndex(&colIndex);
  1411         uint32_t j = 0;
  1412         for (nsIContent* child = row->GetFirstChild();
  1413              child;
  1414              child = child->GetNextSibling()) {
  1416             if (child->NodeInfo()->Equals(nsGkAtoms::treecell,
  1417                                           kNameSpaceID_XUL)) {
  1418                 if (colAtom &&
  1419                     child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref,
  1420                                        colAtom, eCaseMatters)) {
  1421                     *aResult = child;
  1422                     break;
  1424                 else if (j == (uint32_t)colIndex)
  1425                     *aResult = child;
  1426                 j++;
  1430     NS_IF_ADDREF(*aResult);
  1432     return NS_OK;
  1435 nsresult
  1436 nsXULTreeBuilder::GetResourceFor(int32_t aRow, nsIRDFResource** aResource)
  1438     nsTreeRows::Row& row = *(mRows[aRow]);
  1439     return GetResultResource(row.mMatch->mResult, aResource);
  1442 nsresult
  1443 nsXULTreeBuilder::OpenContainer(int32_t aIndex, nsIXULTemplateResult* aResult)
  1445     // A row index of -1 in this case means ``open tree body''
  1446     NS_ASSERTION(aIndex >= -1 && aIndex < mRows.Count(), "bad row");
  1447     if (aIndex < -1 || aIndex >= mRows.Count())
  1448         return NS_ERROR_INVALID_ARG;
  1450     nsTreeRows::Subtree* container;
  1452     if (aIndex >= 0) {
  1453         nsTreeRows::iterator iter = mRows[aIndex];
  1454         container = mRows.EnsureSubtreeFor(iter.GetParent(),
  1455                                            iter.GetChildIndex());
  1457         iter->mContainerState = nsTreeRows::eContainerState_Open;
  1459     else
  1460         container = mRows.GetRoot();
  1462     if (! container)
  1463         return NS_ERROR_OUT_OF_MEMORY;
  1465     int32_t count;
  1466     OpenSubtreeOf(container, aIndex, aResult, &count);
  1468     // Notify the box object
  1469     if (mBoxObject) {
  1470         if (aIndex >= 0)
  1471             mBoxObject->InvalidateRow(aIndex);
  1473         if (count)
  1474             mBoxObject->RowCountChanged(aIndex + 1, count);
  1477     return NS_OK;
  1480 nsresult
  1481 nsXULTreeBuilder::OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
  1482                                 int32_t aIndex,
  1483                                 nsIXULTemplateResult *aResult,
  1484                                 int32_t* aDelta)
  1486     nsAutoTArray<int32_t, 8> open;
  1487     int32_t count = 0;
  1489     int32_t rulecount = mQuerySets.Length();
  1491     for (int32_t r = 0; r < rulecount; r++) {
  1492         nsTemplateQuerySet* queryset = mQuerySets[r];
  1493         OpenSubtreeForQuerySet(aSubtree, aIndex, aResult, queryset, &count, open);
  1496     // Now recursively deal with any open sub-containers that just got
  1497     // inserted. We need to do this back-to-front to avoid skewing offsets.
  1498     for (int32_t i = open.Length() - 1; i >= 0; --i) {
  1499         int32_t index = open[i];
  1501         nsTreeRows::Subtree* child =
  1502             mRows.EnsureSubtreeFor(aSubtree, index);
  1504         nsIXULTemplateResult* result = (*aSubtree)[index].mMatch->mResult;
  1506         int32_t delta;
  1507         OpenSubtreeOf(child, aIndex + index, result, &delta);
  1508         count += delta;
  1511     // Sort the container.
  1512     if (mSortVariable) {
  1513         NS_QuickSort(mRows.GetRowsFor(aSubtree),
  1514                      aSubtree->Count(),
  1515                      sizeof(nsTreeRows::Row),
  1516                      Compare,
  1517                      this);
  1520     *aDelta = count;
  1521     return NS_OK;
  1524 nsresult
  1525 nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
  1526                                          int32_t aIndex,
  1527                                          nsIXULTemplateResult* aResult,
  1528                                          nsTemplateQuerySet* aQuerySet,
  1529                                          int32_t* aDelta,
  1530                                          nsTArray<int32_t>& open)
  1532     int32_t count = *aDelta;
  1534     nsCOMPtr<nsISimpleEnumerator> results;
  1535     nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult,
  1536                                                    aQuerySet->mCompiledQuery,
  1537                                                    getter_AddRefs(results));
  1538     if (NS_FAILED(rv))
  1539         return rv;
  1541     bool hasMoreResults;
  1542     rv = results->HasMoreElements(&hasMoreResults);
  1544     for (; NS_SUCCEEDED(rv) && hasMoreResults;
  1545            rv = results->HasMoreElements(&hasMoreResults)) {
  1546         nsCOMPtr<nsISupports> nr;
  1547         rv = results->GetNext(getter_AddRefs(nr));
  1548         if (NS_FAILED(rv))
  1549             return rv;
  1551         nsCOMPtr<nsIXULTemplateResult> nextresult = do_QueryInterface(nr);
  1552         if (!nextresult)
  1553             return NS_ERROR_UNEXPECTED;
  1555         nsCOMPtr<nsIRDFResource> resultid;
  1556         rv = GetResultResource(nextresult, getter_AddRefs(resultid));
  1557         if (NS_FAILED(rv))
  1558             return rv;
  1560         if (! resultid)
  1561             continue;
  1563         // check if there is already an existing match. If so, a previous
  1564         // query already generated content so the match is just added to the
  1565         // end of the set of matches.
  1567         bool generateContent = true;
  1569         nsTemplateMatch* prevmatch = nullptr;
  1570         nsTemplateMatch* existingmatch = nullptr;
  1571         if (mMatchMap.Get(resultid, &existingmatch)){
  1572             // check if there is an existing match that matched a rule
  1573             while (existingmatch) {
  1574                 if (existingmatch->IsActive())
  1575                     generateContent = false;
  1576                 prevmatch = existingmatch;
  1577                 existingmatch = existingmatch->mNext;
  1581         nsTemplateMatch *newmatch =
  1582             nsTemplateMatch::Create(aQuerySet->Priority(), nextresult, nullptr);
  1583         if (!newmatch)
  1584             return NS_ERROR_OUT_OF_MEMORY;
  1586         if (generateContent) {
  1587             // Don't allow cyclic graphs to get our knickers in a knot.
  1588             bool cyclic = false;
  1590             if (aIndex >= 0) {
  1591                 for (nsTreeRows::iterator iter = mRows[aIndex]; iter.GetDepth() > 0; iter.Pop()) {
  1592                     nsCOMPtr<nsIRDFResource> parentid;
  1593                     rv = GetResultResource(iter->mMatch->mResult, getter_AddRefs(parentid));
  1594                     if (NS_FAILED(rv)) {
  1595                         nsTemplateMatch::Destroy(newmatch, false);
  1596                         return rv;
  1599                     if (resultid == parentid) {
  1600                         cyclic = true;
  1601                         break;
  1606             if (cyclic) {
  1607                 NS_WARNING("tree cannot handle cyclic graphs");
  1608                 nsTemplateMatch::Destroy(newmatch, false);
  1609                 continue;
  1612             int16_t ruleindex;
  1613             nsTemplateRule* matchedrule = nullptr;
  1614             rv = DetermineMatchedRule(nullptr, nextresult, aQuerySet,
  1615                                       &matchedrule, &ruleindex);
  1616             if (NS_FAILED(rv)) {
  1617                 nsTemplateMatch::Destroy(newmatch, false);
  1618                 return rv;
  1621             if (matchedrule) {
  1622                 rv = newmatch->RuleMatched(aQuerySet, matchedrule, ruleindex,
  1623                                            nextresult);
  1624                 if (NS_FAILED(rv)) {
  1625                     nsTemplateMatch::Destroy(newmatch, false);
  1626                     return rv;
  1629                 // Remember that this match applied to this row
  1630                 mRows.InsertRowAt(newmatch, aSubtree, count);
  1632                 // If this is open, then remember it so we can recursively add
  1633                 // *its* rows to the tree.
  1634                 bool isOpen = false;
  1635                 IsContainerOpen(nextresult, &isOpen);
  1636                 if (isOpen) {
  1637                     if (open.AppendElement(count) == nullptr)
  1638                         return NS_ERROR_OUT_OF_MEMORY;
  1641                 ++count;
  1644             if (mFlags & eLoggingEnabled)
  1645                 OutputMatchToLog(resultid, newmatch, true);
  1649         if (prevmatch) {
  1650             prevmatch->mNext = newmatch;
  1652         else {
  1653             mMatchMap.Put(resultid, newmatch);
  1657     *aDelta = count;
  1658     return rv;
  1661 nsresult
  1662 nsXULTreeBuilder::CloseContainer(int32_t aIndex)
  1664     NS_ASSERTION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
  1665     if (aIndex < 0 || aIndex >= mRows.Count())
  1666         return NS_ERROR_INVALID_ARG;
  1668     nsTreeRows::iterator iter = mRows[aIndex];
  1670     if (iter->mSubtree)
  1671         RemoveMatchesFor(*iter->mSubtree);
  1674     int32_t count = mRows.GetSubtreeSizeFor(iter);
  1675     mRows.RemoveSubtreeFor(iter);
  1677     iter->mContainerState = nsTreeRows::eContainerState_Closed;
  1679     if (mBoxObject) {
  1680         mBoxObject->InvalidateRow(aIndex);
  1682         if (count)
  1683             mBoxObject->RowCountChanged(aIndex + 1, -count);
  1686     return NS_OK;
  1689 nsresult
  1690 nsXULTreeBuilder::RemoveMatchesFor(nsTreeRows::Subtree& subtree)
  1692     for (int32_t i = subtree.Count() - 1; i >= 0; --i) {
  1693         nsTreeRows::Row& row = subtree[i];
  1695         nsTemplateMatch* match = row.mMatch;
  1697         nsCOMPtr<nsIRDFResource> id;
  1698         nsresult rv = GetResultResource(match->mResult, getter_AddRefs(id));
  1699         if (NS_FAILED(rv))
  1700             return rv;
  1702         nsTemplateMatch* existingmatch;
  1703         if (mMatchMap.Get(id, &existingmatch)) {
  1704             while (existingmatch) {
  1705                 nsTemplateMatch* nextmatch = existingmatch->mNext;
  1706                 nsTemplateMatch::Destroy(existingmatch, true);
  1707                 existingmatch = nextmatch;
  1710             mMatchMap.Remove(id);
  1713         if ((row.mContainerState == nsTreeRows::eContainerState_Open) && row.mSubtree)
  1714             RemoveMatchesFor(*(row.mSubtree));
  1717     return NS_OK;
  1720 nsresult
  1721 nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen)
  1723     // items are never open if recursion is disabled
  1724     if ((mFlags & eDontRecurse) && aResult != mRootResult) {
  1725         *aOpen = false;
  1726         return NS_OK;
  1729     nsCOMPtr<nsIRDFResource> id;
  1730     nsresult rv = GetResultResource(aResult, getter_AddRefs(id));
  1731     if (NS_FAILED(rv))
  1732         return rv;
  1734     return IsContainerOpen(id, aOpen);
  1737 nsresult
  1738 nsXULTreeBuilder::IsContainerOpen(nsIRDFResource* aResource, bool* aOpen)
  1740     if (mPersistStateStore)
  1741         mPersistStateStore->HasAssertion(aResource,
  1742                                          nsXULContentUtils::NC_open,
  1743                                          nsXULContentUtils::true_,
  1744                                          true,
  1745                                          aOpen);
  1746     else
  1747         *aOpen = false;
  1749     return NS_OK;
  1752 int
  1753 nsXULTreeBuilder::Compare(const void* aLeft, const void* aRight, void* aClosure)
  1755     nsXULTreeBuilder* self = static_cast<nsXULTreeBuilder*>(aClosure);
  1757     nsTreeRows::Row* left = static_cast<nsTreeRows::Row*>
  1758                                        (const_cast<void*>(aLeft));
  1760     nsTreeRows::Row* right = static_cast<nsTreeRows::Row*>
  1761                                         (const_cast<void*>(aRight));
  1763     return self->CompareResults(left->mMatch->mResult, right->mMatch->mResult);
  1766 int32_t
  1767 nsXULTreeBuilder::CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight)
  1769     // this is an extra check done for RDF queries such that results appear in
  1770     // the order they appear in their containing Seq
  1771     if (mSortDirection == eDirection_Natural && mDB) {
  1772         // If the sort order is ``natural'', then see if the container
  1773         // is an RDF sequence. If so, we'll try to use the ordinal
  1774         // properties to determine order.
  1775         //
  1776         // XXX the problem with this is, it doesn't always get the
  1777         // *real* container; e.g.,
  1778         //
  1779         //  <treerow uri="?uri" />
  1780         //
  1781         //  <triple subject="?uri"
  1782         //          predicate="http://home.netscape.com/NC-rdf#subheadings"
  1783         //          object="?subheadings" />
  1784         //
  1785         //  <member container="?subheadings" child="?subheading" />
  1786         //
  1787         // In this case mRefVariable is bound to ?uri, not
  1788         // ?subheadings. (The ``container'' in the template sense !=
  1789         // container in the RDF sense.)
  1791         nsCOMPtr<nsISupports> ref;
  1792         nsresult rv = aLeft->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref));
  1793         if (NS_FAILED(rv))
  1794             return 0;
  1796         nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
  1797         if (container) {
  1798             bool isSequence = false;
  1799             gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
  1800             if (isSequence) {
  1801                 // Determine the indices of the left and right elements
  1802                 // in the container.
  1803                 int32_t lindex = 0, rindex = 0;
  1805                 nsCOMPtr<nsIRDFResource> leftitem;
  1806                 aLeft->GetResource(getter_AddRefs(leftitem));
  1807                 if (leftitem) {
  1808                     gRDFContainerUtils->IndexOf(mDB, container, leftitem, &lindex);
  1809                     if (lindex < 0)
  1810                         return 0;
  1813                 nsCOMPtr<nsIRDFResource> rightitem;
  1814                 aRight->GetResource(getter_AddRefs(rightitem));
  1815                 if (rightitem) {
  1816                     gRDFContainerUtils->IndexOf(mDB, container, rightitem, &rindex);
  1817                     if (rindex < 0)
  1818                         return 0;
  1821                 return lindex - rindex;
  1826     int32_t sortorder;
  1827     if (!mQueryProcessor)
  1828         return 0;
  1830     mQueryProcessor->CompareResults(aLeft, aRight, mSortVariable, mSortHints, &sortorder);
  1832     if (sortorder)
  1833         sortorder = sortorder * mSortDirection;
  1834     return sortorder;
  1837 nsresult
  1838 nsXULTreeBuilder::SortSubtree(nsTreeRows::Subtree* aSubtree)
  1840     NS_QuickSort(mRows.GetRowsFor(aSubtree),
  1841                  aSubtree->Count(),
  1842                  sizeof(nsTreeRows::Row),
  1843                  Compare,
  1844                  this);
  1846     for (int32_t i = aSubtree->Count() - 1; i >= 0; --i) {
  1847         nsTreeRows::Subtree* child = (*aSubtree)[i].mSubtree;
  1848         if (child)
  1849             SortSubtree(child);
  1852     return NS_OK;
  1856 /* boolean canDrop (in long index, in long orientation); */
  1857 NS_IMETHODIMP
  1858 nsXULTreeBuilder::CanDrop(int32_t index, int32_t orientation,
  1859                           nsIDOMDataTransfer* dataTransfer, bool *_retval)
  1861     *_retval = false;
  1862     uint32_t count = mObservers.Count();
  1863     for (uint32_t i = 0; i < count; ++i) {
  1864         nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
  1865         if (observer) {
  1866             observer->CanDrop(index, orientation, dataTransfer, _retval);
  1867             if (*_retval)
  1868                 break;
  1872     return NS_OK;
  1875 NS_IMETHODIMP
  1876 nsXULTreeBuilder::Drop(int32_t row, int32_t orient, nsIDOMDataTransfer* dataTransfer)
  1878     uint32_t count = mObservers.Count();
  1879     for (uint32_t i = 0; i < count; ++i) {
  1880         nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
  1881         if (observer) {
  1882             bool canDrop = false;
  1883             observer->CanDrop(row, orient, dataTransfer, &canDrop);
  1884             if (canDrop)
  1885                 observer->OnDrop(row, orient, dataTransfer);
  1889     return NS_OK;
  1892 NS_IMETHODIMP
  1893 nsXULTreeBuilder::IsSorted(bool *_retval)
  1895   *_retval = (mSortVariable != nullptr);
  1896   return NS_OK;

mercurial