michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nscore.h" michael@0: #include "nsError.h" michael@0: #include "nsIContent.h" michael@0: #include "nsINodeInfo.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsILocalStore.h" michael@0: #include "nsIBoxObject.h" michael@0: #include "nsITreeBoxObject.h" michael@0: #include "nsITreeSelection.h" michael@0: #include "nsITreeColumns.h" michael@0: #include "nsITreeView.h" michael@0: #include "nsTreeUtils.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsQuickSort.h" michael@0: #include "nsTreeRows.h" michael@0: #include "nsTemplateRule.h" michael@0: #include "nsTemplateMatch.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsXULContentUtils.h" michael@0: #include "nsXULTemplateBuilder.h" michael@0: #include "nsIXULSortService.h" michael@0: #include "nsTArray.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsDOMClassInfoID.h" michael@0: #include "nsWhitespaceTokenizer.h" michael@0: #include "nsTreeContentView.h" michael@0: michael@0: // For security check michael@0: #include "nsIDocument.h" michael@0: michael@0: /** michael@0: * A XUL template builder that serves as an tree view, allowing michael@0: * (pretty much) arbitrary RDF to be presented in an tree. michael@0: */ michael@0: class nsXULTreeBuilder : public nsXULTemplateBuilder, michael@0: public nsIXULTreeBuilder, michael@0: public nsINativeTreeView michael@0: { michael@0: public: michael@0: // nsISupports michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder) michael@0: michael@0: // nsIXULTreeBuilder michael@0: NS_DECL_NSIXULTREEBUILDER michael@0: michael@0: // nsITreeView michael@0: NS_DECL_NSITREEVIEW michael@0: // nsINativeTreeView: Untrusted code can use us michael@0: NS_IMETHOD EnsureNative() { return NS_OK; } michael@0: michael@0: // nsIMutationObserver michael@0: NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED michael@0: michael@0: protected: michael@0: friend nsresult michael@0: NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult); michael@0: michael@0: nsXULTreeBuilder(); michael@0: michael@0: /** michael@0: * Uninitialize the template builder michael@0: */ michael@0: virtual void Uninit(bool aIsFinal); michael@0: michael@0: /** michael@0: * Get sort variables from the active michael@0: */ michael@0: nsresult michael@0: EnsureSortVariables(); michael@0: michael@0: virtual nsresult michael@0: RebuildAll(); michael@0: michael@0: /** michael@0: * Given a row, use the row's match to figure out the appropriate michael@0: * in the rule's . michael@0: */ michael@0: nsresult michael@0: GetTemplateActionRowFor(int32_t aRow, nsIContent** aResult); michael@0: michael@0: /** michael@0: * Given a row and a column ID, use the row's match to figure out michael@0: * the appropriate in the rule's . michael@0: */ michael@0: nsresult michael@0: GetTemplateActionCellFor(int32_t aRow, nsITreeColumn* aCol, nsIContent** aResult); michael@0: michael@0: /** michael@0: * Return the resource corresponding to a row in the tree. michael@0: */ michael@0: nsresult michael@0: GetResourceFor(int32_t aRow, nsIRDFResource** aResource); michael@0: michael@0: /** michael@0: * Open a container row, inserting the container's children into michael@0: * the view. michael@0: */ michael@0: nsresult michael@0: OpenContainer(int32_t aIndex, nsIXULTemplateResult* aResult); michael@0: michael@0: /** michael@0: * Helper for OpenContainer, recursively open subtrees, remembering michael@0: * persisted ``open'' state michael@0: */ michael@0: nsresult michael@0: OpenSubtreeOf(nsTreeRows::Subtree* aSubtree, michael@0: int32_t aIndex, michael@0: nsIXULTemplateResult *aResult, michael@0: int32_t* aDelta); michael@0: michael@0: nsresult michael@0: OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree, michael@0: int32_t aIndex, michael@0: nsIXULTemplateResult *aResult, michael@0: nsTemplateQuerySet* aQuerySet, michael@0: int32_t* aDelta, michael@0: nsTArray& open); michael@0: michael@0: /** michael@0: * Close a container row, removing the container's childrem from michael@0: * the view. michael@0: */ michael@0: nsresult michael@0: CloseContainer(int32_t aIndex); michael@0: michael@0: /** michael@0: * Remove the matches for the rows in a subtree michael@0: */ michael@0: nsresult michael@0: RemoveMatchesFor(nsTreeRows::Subtree& subtree); michael@0: michael@0: /** michael@0: * Helper methods that determine if the specified container is open. michael@0: */ michael@0: nsresult michael@0: IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen); michael@0: michael@0: nsresult michael@0: IsContainerOpen(nsIRDFResource* aResource, bool* aOpen); michael@0: michael@0: /** michael@0: * A sorting callback for NS_QuickSort(). michael@0: */ michael@0: static int michael@0: Compare(const void* aLeft, const void* aRight, void* aClosure); michael@0: michael@0: /** michael@0: * The real sort routine michael@0: */ michael@0: int32_t michael@0: CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight); michael@0: michael@0: /** michael@0: * Sort the specified subtree, and recursively sort any subtrees michael@0: * beneath it. michael@0: */ michael@0: nsresult michael@0: SortSubtree(nsTreeRows::Subtree* aSubtree); michael@0: michael@0: NS_IMETHOD michael@0: HasGeneratedContent(nsIRDFResource* aResource, michael@0: nsIAtom* aTag, michael@0: bool* aGenerated); michael@0: michael@0: // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited michael@0: // from nsXULTemplateBuilder michael@0: michael@0: /** michael@0: * Return true if the result can be inserted into the template as a new michael@0: * row. michael@0: */ michael@0: bool michael@0: GetInsertionLocations(nsIXULTemplateResult* aResult, michael@0: nsCOMArray** aLocations); michael@0: michael@0: /** michael@0: * Implement result replacement michael@0: */ michael@0: virtual nsresult michael@0: ReplaceMatch(nsIXULTemplateResult* aOldResult, michael@0: nsTemplateMatch* aNewMatch, michael@0: nsTemplateRule* aNewMatchRule, michael@0: void *aContext); michael@0: michael@0: /** michael@0: * Implement match synchronization michael@0: */ michael@0: virtual nsresult michael@0: SynchronizeResult(nsIXULTemplateResult* aResult); michael@0: michael@0: /** michael@0: * The tree's box object, used to communicate with the front-end. michael@0: */ michael@0: nsCOMPtr mBoxObject; michael@0: michael@0: /** michael@0: * The tree's selection object. michael@0: */ michael@0: nsCOMPtr mSelection; michael@0: michael@0: /** michael@0: * The datasource that's used to persist open folder information michael@0: */ michael@0: nsCOMPtr mPersistStateStore; michael@0: michael@0: /** michael@0: * The rows in the view michael@0: */ michael@0: nsTreeRows mRows; michael@0: michael@0: /** michael@0: * The currently active sort variable michael@0: */ michael@0: nsCOMPtr mSortVariable; michael@0: michael@0: enum Direction { michael@0: eDirection_Descending = -1, michael@0: eDirection_Natural = 0, michael@0: eDirection_Ascending = +1 michael@0: }; michael@0: michael@0: /** michael@0: * The currently active sort order michael@0: */ michael@0: Direction mSortDirection; michael@0: michael@0: /* michael@0: * Sort hints (compare case, etc) michael@0: */ michael@0: uint32_t mSortHints; michael@0: michael@0: /** michael@0: * The builder observers. michael@0: */ michael@0: nsCOMArray mObservers; michael@0: }; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult) michael@0: { michael@0: *aResult = nullptr; michael@0: michael@0: NS_PRECONDITION(aOuter == nullptr, "no aggregation"); michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: nsresult rv; michael@0: nsXULTreeBuilder* result = new nsXULTreeBuilder(); michael@0: if (! result) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(result); // stabilize michael@0: michael@0: rv = result->InitGlobals(); michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = result->QueryInterface(aIID, aResult); michael@0: michael@0: NS_RELEASE(result); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder) michael@0: NS_IMPL_RELEASE_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder, michael@0: mBoxObject, michael@0: mSelection, michael@0: mPersistStateStore, michael@0: mObservers) michael@0: michael@0: DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder) michael@0: NS_INTERFACE_MAP_ENTRY(nsITreeView) michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTreeBuilder) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsXULTemplateBuilder) michael@0: michael@0: michael@0: nsXULTreeBuilder::nsXULTreeBuilder() michael@0: : mSortDirection(eDirection_Natural), mSortHints(0) michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsXULTreeBuilder::Uninit(bool aIsFinal) michael@0: { michael@0: int32_t count = mRows.Count(); michael@0: mRows.Clear(); michael@0: michael@0: if (mBoxObject) { michael@0: mBoxObject->BeginUpdateBatch(); michael@0: mBoxObject->RowCountChanged(0, -count); michael@0: if (mBoxObject) { michael@0: mBoxObject->EndUpdateBatch(); michael@0: } michael@0: } michael@0: michael@0: nsXULTemplateBuilder::Uninit(aIsFinal); michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsIXULTreeBuilder methods michael@0: // michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetResourceAtIndex(int32_t aRowIndex, nsIRDFResource** aResult) michael@0: { michael@0: if (aRowIndex < 0 || aRowIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: return GetResourceFor(aRowIndex, aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetIndexOfResource(nsIRDFResource* aResource, int32_t* aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResource); michael@0: nsTreeRows::iterator iter = mRows.FindByResource(aResource); michael@0: if (iter == mRows.Last()) michael@0: *aResult = -1; michael@0: else michael@0: *aResult = iter.GetRowIndex(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::AddObserver(nsIXULTreeBuilderObserver* aObserver) michael@0: { michael@0: return mObservers.AppendObject(aObserver) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::RemoveObserver(nsIXULTreeBuilderObserver* aObserver) michael@0: { michael@0: return mObservers.RemoveObject(aObserver) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::Sort(nsIDOMElement* aElement) michael@0: { michael@0: nsCOMPtr header = do_QueryInterface(aElement); michael@0: if (! header) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (header->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortLocked, michael@0: nsGkAtoms::_true, eCaseMatters)) michael@0: return NS_OK; michael@0: michael@0: nsAutoString sort; michael@0: header->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort); michael@0: michael@0: if (sort.IsEmpty()) michael@0: return NS_OK; michael@0: michael@0: // Grab the new sort variable michael@0: mSortVariable = do_GetAtom(sort); michael@0: michael@0: nsAutoString hints; michael@0: header->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints); michael@0: michael@0: bool hasNaturalState = true; michael@0: nsWhitespaceTokenizer tokenizer(hints); michael@0: while (tokenizer.hasMoreTokens()) { michael@0: const nsDependentSubstring& token(tokenizer.nextToken()); michael@0: if (token.EqualsLiteral("comparecase")) michael@0: mSortHints |= nsIXULSortService::SORT_COMPARECASE; michael@0: else if (token.EqualsLiteral("integer")) michael@0: mSortHints |= nsIXULSortService::SORT_INTEGER; michael@0: else if (token.EqualsLiteral("twostate")) michael@0: hasNaturalState = false; michael@0: } michael@0: michael@0: // Cycle the sort direction michael@0: nsAutoString dir; michael@0: header->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, dir); michael@0: michael@0: if (dir.EqualsLiteral("ascending")) { michael@0: dir.AssignLiteral("descending"); michael@0: mSortDirection = eDirection_Descending; michael@0: } michael@0: else if (hasNaturalState && dir.EqualsLiteral("descending")) { michael@0: dir.AssignLiteral("natural"); michael@0: mSortDirection = eDirection_Natural; michael@0: } michael@0: else { michael@0: dir.AssignLiteral("ascending"); michael@0: mSortDirection = eDirection_Ascending; michael@0: } michael@0: michael@0: // Sort it. michael@0: SortSubtree(mRows.GetRoot()); michael@0: mRows.InvalidateCachedRow(); michael@0: if (mBoxObject) michael@0: mBoxObject->Invalidate(); michael@0: michael@0: nsTreeUtils::UpdateSortIndicators(header, dir); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsITreeView methods michael@0: // michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetRowCount(int32_t* aRowCount) michael@0: { michael@0: *aRowCount = mRows.Count(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetSelection(nsITreeSelection** aSelection) michael@0: { michael@0: NS_IF_ADDREF(*aSelection = mSelection.get()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::SetSelection(nsITreeSelection* aSelection) michael@0: { michael@0: NS_ENSURE_TRUE(!aSelection || michael@0: nsTreeContentView::CanTrustTreeSelection(aSelection), michael@0: NS_ERROR_DOM_SECURITY_ERR); michael@0: mSelection = aSelection; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetRowProperties(int32_t aIndex, nsAString& aProps) michael@0: { michael@0: NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); michael@0: if (aIndex < 0 || aIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsCOMPtr row; michael@0: GetTemplateActionRowFor(aIndex, getter_AddRefs(row)); michael@0: if (row) { michael@0: nsAutoString raw; michael@0: row->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw); michael@0: michael@0: if (!raw.IsEmpty()) { michael@0: SubstituteText(mRows[aIndex]->mMatch->mResult, raw, aProps); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetCellProperties(int32_t aRow, nsITreeColumn* aCol, michael@0: nsAString& aProps) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row"); michael@0: if (aRow < 0 || aRow >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsCOMPtr cell; michael@0: GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); michael@0: if (cell) { michael@0: nsAutoString raw; michael@0: cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw); michael@0: michael@0: if (!raw.IsEmpty()) { michael@0: SubstituteText(mRows[aRow]->mMatch->mResult, raw, aProps); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetColumnProperties(nsITreeColumn* aCol, nsAString& aProps) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: // XXX sortactive fu michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::IsContainer(int32_t aIndex, bool* aResult) michael@0: { michael@0: NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); michael@0: if (aIndex < 0 || aIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsTreeRows::iterator iter = mRows[aIndex]; michael@0: michael@0: bool isContainer; michael@0: iter->mMatch->mResult->GetIsContainer(&isContainer); michael@0: michael@0: iter->mContainerType = isContainer michael@0: ? nsTreeRows::eContainerType_Container michael@0: : nsTreeRows::eContainerType_Noncontainer; michael@0: michael@0: *aResult = (iter->mContainerType == nsTreeRows::eContainerType_Container); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::IsContainerOpen(int32_t aIndex, bool* aOpen) michael@0: { michael@0: NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); michael@0: if (aIndex < 0 || aIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsTreeRows::iterator iter = mRows[aIndex]; michael@0: michael@0: if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) { michael@0: bool isOpen; michael@0: IsContainerOpen(iter->mMatch->mResult, &isOpen); michael@0: michael@0: iter->mContainerState = isOpen michael@0: ? nsTreeRows::eContainerState_Open michael@0: : nsTreeRows::eContainerState_Closed; michael@0: } michael@0: michael@0: *aOpen = (iter->mContainerState == nsTreeRows::eContainerState_Open); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::IsContainerEmpty(int32_t aIndex, bool* aResult) michael@0: { michael@0: NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); michael@0: if (aIndex < 0 || aIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsTreeRows::iterator iter = mRows[aIndex]; michael@0: NS_ASSERTION(iter->mContainerType == nsTreeRows::eContainerType_Container, michael@0: "asking for empty state on non-container"); michael@0: michael@0: // if recursion is disabled, pretend that the container is empty. This michael@0: // ensures that folders are still displayed as such, yet won't display michael@0: // their children michael@0: if ((mFlags & eDontRecurse) && (iter->mMatch->mResult != mRootResult)) { michael@0: *aResult = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (iter->mContainerFill == nsTreeRows::eContainerFill_Unknown) { michael@0: bool isEmpty; michael@0: iter->mMatch->mResult->GetIsEmpty(&isEmpty); michael@0: michael@0: iter->mContainerFill = isEmpty michael@0: ? nsTreeRows::eContainerFill_Empty michael@0: : nsTreeRows::eContainerFill_Nonempty; michael@0: } michael@0: michael@0: *aResult = (iter->mContainerFill == nsTreeRows::eContainerFill_Empty); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::IsSeparator(int32_t aIndex, bool* aResult) michael@0: { michael@0: NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); michael@0: if (aIndex < 0 || aIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsAutoString type; michael@0: nsTreeRows::Row& row = *(mRows[aIndex]); michael@0: row.mMatch->mResult->GetType(type); michael@0: michael@0: *aResult = type.EqualsLiteral("separator"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetParentIndex(int32_t aRowIndex, int32_t* aResult) michael@0: { michael@0: NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row"); michael@0: if (aRowIndex < 0 || aRowIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Construct a path to the row michael@0: nsTreeRows::iterator iter = mRows[aRowIndex]; michael@0: michael@0: // The parent of the row will be at the top of the path michael@0: nsTreeRows::Subtree* parent = iter.GetParent(); michael@0: michael@0: // Now walk through our previous siblings, subtracting off each michael@0: // one's subtree size michael@0: int32_t index = iter.GetChildIndex(); michael@0: while (--index >= 0) michael@0: aRowIndex -= mRows.GetSubtreeSizeFor(parent, index) + 1; michael@0: michael@0: // Now the parent's index will be the first row's index, less one. michael@0: *aResult = aRowIndex - 1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, bool* aResult) michael@0: { michael@0: NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row"); michael@0: if (aRowIndex < 0 || aRowIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Construct a path to the row michael@0: nsTreeRows::iterator iter = mRows[aRowIndex]; michael@0: michael@0: // The parent of the row will be at the top of the path michael@0: nsTreeRows::Subtree* parent = iter.GetParent(); michael@0: michael@0: // We have a next sibling if the child is not the last in the michael@0: // subtree. michael@0: *aResult = iter.GetChildIndex() != parent->Count() - 1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetLevel(int32_t aRowIndex, int32_t* aResult) michael@0: { michael@0: NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row"); michael@0: if (aRowIndex < 0 || aRowIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Construct a path to the row; the ``level'' is the path length michael@0: // less one. michael@0: nsTreeRows::iterator iter = mRows[aRowIndex]; michael@0: *aResult = iter.GetDepth() - 1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetImageSrc(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); michael@0: if (aRow < 0 || aRow >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Find the that corresponds to the column we want. michael@0: nsCOMPtr cell; michael@0: GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); michael@0: if (cell) { michael@0: nsAutoString raw; michael@0: cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, raw); michael@0: michael@0: SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult); michael@0: } michael@0: else michael@0: aResult.Truncate(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetProgressMode(int32_t aRow, nsITreeColumn* aCol, int32_t* aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); michael@0: if (aRow < 0 || aRow >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: *aResult = nsITreeView::PROGRESS_NONE; michael@0: michael@0: // Find the that corresponds to the column we want. michael@0: nsCOMPtr cell; michael@0: GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); michael@0: if (cell) { michael@0: nsAutoString raw; michael@0: cell->GetAttr(kNameSpaceID_None, nsGkAtoms::mode, raw); michael@0: michael@0: nsAutoString mode; michael@0: SubstituteText(mRows[aRow]->mMatch->mResult, raw, mode); michael@0: michael@0: if (mode.EqualsLiteral("normal")) michael@0: *aResult = nsITreeView::PROGRESS_NORMAL; michael@0: else if (mode.EqualsLiteral("undetermined")) michael@0: *aResult = nsITreeView::PROGRESS_UNDETERMINED; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetCellValue(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); michael@0: if (aRow < 0 || aRow >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Find the that corresponds to the column we want. michael@0: nsCOMPtr cell; michael@0: GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); michael@0: if (cell) { michael@0: nsAutoString raw; michael@0: cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, raw); michael@0: michael@0: SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult); michael@0: } michael@0: else michael@0: aResult.Truncate(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::GetCellText(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); michael@0: if (aRow < 0 || aRow >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Find the that corresponds to the column we want. michael@0: nsCOMPtr cell; michael@0: GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); michael@0: if (cell) { michael@0: nsAutoString raw; michael@0: cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, raw); michael@0: michael@0: SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult); michael@0: michael@0: } michael@0: else michael@0: aResult.Truncate(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::SetTree(nsITreeBoxObject* aTree) michael@0: { michael@0: mBoxObject = aTree; michael@0: michael@0: // If this is teardown time, then we're done. michael@0: if (!mBoxObject) { michael@0: Uninit(false); michael@0: return NS_OK; michael@0: } michael@0: NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: // Is our root's principal trusted? michael@0: bool isTrusted = false; michael@0: nsresult rv = IsSystemPrincipal(mRoot->NodePrincipal(), &isTrusted); michael@0: if (NS_SUCCEEDED(rv) && isTrusted) { michael@0: // Get the datasource we intend to use to remember open state. michael@0: nsAutoString datasourceStr; michael@0: mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::statedatasource, datasourceStr); michael@0: michael@0: // since we are trusted, use the user specified datasource michael@0: // if non specified, use localstore, which gives us michael@0: // persistence across sessions michael@0: if (! datasourceStr.IsEmpty()) { michael@0: gRDFService->GetDataSource(NS_ConvertUTF16toUTF8(datasourceStr).get(), michael@0: getter_AddRefs(mPersistStateStore)); michael@0: } michael@0: else { michael@0: gRDFService->GetDataSource("rdf:local-store", michael@0: getter_AddRefs(mPersistStateStore)); michael@0: } michael@0: } michael@0: michael@0: // Either no specific datasource was specified, or we failed michael@0: // to get one because we are not trusted. michael@0: // michael@0: // XXX if it were possible to ``write an arbitrary datasource michael@0: // back'', then we could also allow an untrusted document to michael@0: // use a statedatasource from the same codebase. michael@0: if (! mPersistStateStore) { michael@0: mPersistStateStore = michael@0: do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource"); michael@0: } michael@0: michael@0: NS_ASSERTION(mPersistStateStore, "failed to get a persistent state store"); michael@0: if (! mPersistStateStore) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: Rebuild(); michael@0: michael@0: EnsureSortVariables(); michael@0: if (mSortVariable) michael@0: SortSubtree(mRows.GetRoot()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::ToggleOpenState(int32_t aIndex) michael@0: { michael@0: if (aIndex < 0 || aIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsIXULTemplateResult* result = mRows[aIndex]->mMatch->mResult; michael@0: if (! result) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (mFlags & eDontRecurse) michael@0: return NS_OK; michael@0: michael@0: if (result && result != mRootResult) { michael@0: // don't open containers if child processing isn't allowed michael@0: bool mayProcessChildren; michael@0: nsresult rv = result->GetMayProcessChildren(&mayProcessChildren); michael@0: if (NS_FAILED(rv) || !mayProcessChildren) michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t count = mObservers.Count(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr observer = mObservers.SafeObjectAt(i); michael@0: if (observer) michael@0: observer->OnToggleOpenState(aIndex); michael@0: } michael@0: michael@0: if (mPersistStateStore) { michael@0: bool isOpen; michael@0: IsContainerOpen(aIndex, &isOpen); michael@0: michael@0: nsCOMPtr container; michael@0: GetResourceFor(aIndex, getter_AddRefs(container)); michael@0: if (! container) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: bool hasProperty; michael@0: IsContainerOpen(container, &hasProperty); michael@0: michael@0: if (isOpen) { michael@0: if (hasProperty) { michael@0: mPersistStateStore->Unassert(container, michael@0: nsXULContentUtils::NC_open, michael@0: nsXULContentUtils::true_); michael@0: } michael@0: michael@0: CloseContainer(aIndex); michael@0: } michael@0: else { michael@0: if (! hasProperty) { michael@0: mPersistStateStore->Assert(container, michael@0: nsXULContentUtils::NC_open, michael@0: nsXULContentUtils::true_, michael@0: true); michael@0: } michael@0: michael@0: OpenContainer(aIndex, result); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::CycleHeader(nsITreeColumn* aCol) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: nsCOMPtr element; michael@0: aCol->GetElement(getter_AddRefs(element)); michael@0: michael@0: nsAutoString id; michael@0: aCol->GetId(id); michael@0: michael@0: uint32_t count = mObservers.Count(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr observer = mObservers.SafeObjectAt(i); michael@0: if (observer) michael@0: observer->OnCycleHeader(id.get(), element); michael@0: } michael@0: michael@0: return Sort(element); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::SelectionChanged() michael@0: { michael@0: uint32_t count = mObservers.Count(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr observer = mObservers.SafeObjectAt(i); michael@0: if (observer) michael@0: observer->OnSelectionChanged(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::CycleCell(int32_t aRow, nsITreeColumn* aCol) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: michael@0: nsAutoString id; michael@0: aCol->GetId(id); michael@0: michael@0: uint32_t count = mObservers.Count(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr observer = mObservers.SafeObjectAt(i); michael@0: if (observer) michael@0: observer->OnCycleCell(aRow, id.get()); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::IsEditable(int32_t aRow, nsITreeColumn* aCol, bool* _retval) michael@0: { michael@0: *_retval = true; michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); michael@0: if (aRow < 0 || aRow >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Find the that corresponds to the column we want. michael@0: nsCOMPtr cell; michael@0: GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); michael@0: if (cell) { michael@0: nsAutoString raw; michael@0: cell->GetAttr(kNameSpaceID_None, nsGkAtoms::editable, raw); michael@0: michael@0: nsAutoString editable; michael@0: SubstituteText(mRows[aRow]->mMatch->mResult, raw, editable); michael@0: michael@0: if (editable.EqualsLiteral("false")) michael@0: *_retval = false; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::IsSelectable(int32_t aRow, nsITreeColumn* aCol, bool* _retval) michael@0: { michael@0: NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); michael@0: if (aRow < 0 || aRow >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: *_retval = true; michael@0: michael@0: // Find the that corresponds to the column we want. michael@0: nsCOMPtr cell; michael@0: GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); michael@0: if (cell) { michael@0: nsAutoString raw; michael@0: cell->GetAttr(kNameSpaceID_None, nsGkAtoms::selectable, raw); michael@0: michael@0: nsAutoString selectable; michael@0: SubstituteText(mRows[aRow]->mMatch->mResult, raw, selectable); michael@0: michael@0: if (selectable.EqualsLiteral("false")) michael@0: *_retval = false; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::SetCellValue(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::SetCellText(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::PerformAction(const char16_t* aAction) michael@0: { michael@0: uint32_t count = mObservers.Count(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr observer = mObservers.SafeObjectAt(i); michael@0: if (observer) michael@0: observer->OnPerformAction(aAction); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::PerformActionOnRow(const char16_t* aAction, int32_t aRow) michael@0: { michael@0: uint32_t count = mObservers.Count(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr observer = mObservers.SafeObjectAt(i); michael@0: if (observer) michael@0: observer->OnPerformActionOnRow(aAction, aRow); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::PerformActionOnCell(const char16_t* aAction, int32_t aRow, nsITreeColumn* aCol) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCol); michael@0: nsAutoString id; michael@0: aCol->GetId(id); michael@0: michael@0: uint32_t count = mObservers.Count(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr observer = mObservers.SafeObjectAt(i); michael@0: if (observer) michael@0: observer->OnPerformActionOnCell(aAction, aRow, id.get()); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: void michael@0: nsXULTreeBuilder::NodeWillBeDestroyed(const nsINode* aNode) michael@0: { michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: mObservers.Clear(); michael@0: michael@0: nsXULTemplateBuilder::NodeWillBeDestroyed(aNode); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::HasGeneratedContent(nsIRDFResource* aResource, michael@0: nsIAtom* aTag, michael@0: bool* aGenerated) michael@0: { michael@0: *aGenerated = false; michael@0: NS_ENSURE_ARG_POINTER(aResource); michael@0: michael@0: if (!mRootResult) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr rootresource; michael@0: nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (aResource == rootresource || michael@0: mRows.FindByResource(aResource) != mRows.Last()) michael@0: *aGenerated = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsXULTreeBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult, michael@0: nsCOMArray** aLocations) michael@0: { michael@0: *aLocations = nullptr; michael@0: michael@0: // Get the reference point and check if it is an open container. Rows michael@0: // should not be generated otherwise. michael@0: michael@0: nsAutoString ref; michael@0: nsresult rv = aResult->GetBindingFor(mRefVariable, ref); michael@0: if (NS_FAILED(rv) || ref.IsEmpty()) michael@0: return false; michael@0: michael@0: nsCOMPtr container; michael@0: rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container)); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: // Can always insert into the root resource michael@0: if (container == mRows.GetRootResource()) michael@0: return true; michael@0: michael@0: nsTreeRows::iterator iter = mRows.FindByResource(container); michael@0: if (iter == mRows.Last()) michael@0: return false; michael@0: michael@0: return (iter->mContainerState == nsTreeRows::eContainerState_Open); michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult, michael@0: nsTemplateMatch* aNewMatch, michael@0: nsTemplateRule* aNewMatchRule, michael@0: void *aLocation) michael@0: { michael@0: if (! mBoxObject) michael@0: return NS_OK; michael@0: michael@0: if (aOldResult) { michael@0: // Grovel through the rows looking for oldresult. michael@0: nsTreeRows::iterator iter = mRows.Find(aOldResult); michael@0: michael@0: NS_ASSERTION(iter != mRows.Last(), "couldn't find row"); michael@0: if (iter == mRows.Last()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Remove the rows from the view michael@0: int32_t row = iter.GetRowIndex(); michael@0: michael@0: // If the row contains children, remove the matches from the michael@0: // children so that they can be regenerated again if the element michael@0: // gets added back. michael@0: int32_t delta = mRows.GetSubtreeSizeFor(iter); michael@0: if (delta) michael@0: RemoveMatchesFor(*(iter->mSubtree)); michael@0: michael@0: if (mRows.RemoveRowAt(iter) == 0 && iter.GetRowIndex() >= 0) { michael@0: michael@0: // In this case iter now points to its parent michael@0: // Invalidate the row's cached fill state michael@0: iter->mContainerFill = nsTreeRows::eContainerFill_Unknown; michael@0: michael@0: nsCOMPtr cols; michael@0: mBoxObject->GetColumns(getter_AddRefs(cols)); michael@0: if (cols) { michael@0: nsCOMPtr primaryCol; michael@0: cols->GetPrimaryColumn(getter_AddRefs(primaryCol)); michael@0: if (primaryCol) michael@0: mBoxObject->InvalidateCell(iter.GetRowIndex(), primaryCol); michael@0: } michael@0: } michael@0: michael@0: // Notify the box object michael@0: mBoxObject->RowCountChanged(row, -delta - 1); michael@0: } michael@0: michael@0: if (aNewMatch && aNewMatch->mResult) { michael@0: // Insertion. michael@0: int32_t row = -1; michael@0: nsTreeRows::Subtree* parent = nullptr; michael@0: nsIXULTemplateResult* result = aNewMatch->mResult; michael@0: michael@0: nsAutoString ref; michael@0: nsresult rv = result->GetBindingFor(mRefVariable, ref); michael@0: if (NS_FAILED(rv) || ref.IsEmpty()) michael@0: return rv; michael@0: michael@0: nsCOMPtr container; michael@0: rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (container != mRows.GetRootResource()) { michael@0: nsTreeRows::iterator iter = mRows.FindByResource(container); michael@0: row = iter.GetRowIndex(); michael@0: michael@0: NS_ASSERTION(iter != mRows.Last(), "couldn't find container row"); michael@0: if (iter == mRows.Last()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Use the persist store to remember if the container michael@0: // is open or closed. michael@0: bool open = false; michael@0: IsContainerOpen(row, &open); michael@0: michael@0: // If it's open, make sure that we've got a subtree structure ready. michael@0: if (open) michael@0: parent = mRows.EnsureSubtreeFor(iter); michael@0: michael@0: // We know something has just been inserted into the michael@0: // container, so whether its open or closed, make sure michael@0: // that we've got our tree row's state correct. michael@0: if ((iter->mContainerType != nsTreeRows::eContainerType_Container) || michael@0: (iter->mContainerFill != nsTreeRows::eContainerFill_Nonempty)) { michael@0: iter->mContainerType = nsTreeRows::eContainerType_Container; michael@0: iter->mContainerFill = nsTreeRows::eContainerFill_Nonempty; michael@0: mBoxObject->InvalidateRow(iter.GetRowIndex()); michael@0: } michael@0: } michael@0: else { michael@0: parent = mRows.GetRoot(); michael@0: } michael@0: michael@0: if (parent) { michael@0: // If we get here, then we're inserting into an open michael@0: // container. By default, place the new element at the michael@0: // end of the container michael@0: int32_t index = parent->Count(); michael@0: michael@0: if (mSortVariable) { michael@0: // Figure out where to put the new element by doing an michael@0: // insertion sort. michael@0: int32_t left = 0; michael@0: int32_t right = index; michael@0: michael@0: while (left < right) { michael@0: index = (left + right) / 2; michael@0: int32_t cmp = CompareResults((*parent)[index].mMatch->mResult, result); michael@0: if (cmp < 0) michael@0: left = ++index; michael@0: else if (cmp > 0) michael@0: right = index; michael@0: else michael@0: break; michael@0: } michael@0: } michael@0: michael@0: nsTreeRows::iterator iter = michael@0: mRows.InsertRowAt(aNewMatch, parent, index); michael@0: michael@0: mBoxObject->RowCountChanged(iter.GetRowIndex(), +1); michael@0: michael@0: // See if this newly added row is open; in which case, michael@0: // recursively add its children to the tree, too. michael@0: michael@0: if (mFlags & eDontRecurse) michael@0: return NS_OK; michael@0: michael@0: if (result != mRootResult) { michael@0: // don't open containers if child processing isn't allowed michael@0: bool mayProcessChildren; michael@0: nsresult rv = result->GetMayProcessChildren(&mayProcessChildren); michael@0: if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK; michael@0: } michael@0: michael@0: bool open; michael@0: IsContainerOpen(result, &open); michael@0: if (open) michael@0: OpenContainer(iter.GetRowIndex(), result); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::SynchronizeResult(nsIXULTemplateResult* aResult) michael@0: { michael@0: if (mBoxObject) { michael@0: // XXX we could be more conservative and just invalidate the cells michael@0: // that got whacked... michael@0: michael@0: nsTreeRows::iterator iter = mRows.Find(aResult); michael@0: michael@0: NS_ASSERTION(iter != mRows.Last(), "couldn't find row"); michael@0: if (iter == mRows.Last()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: int32_t row = iter.GetRowIndex(); michael@0: if (row >= 0) michael@0: mBoxObject->InvalidateRow(row); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: ("xultemplate[%p] => row %d", this, row)); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::EnsureSortVariables() michael@0: { michael@0: // Grovel through kids to find the michael@0: // with the sort attributes. michael@0: nsCOMPtr treecols; michael@0: michael@0: nsXULContentUtils::FindChildByTag(mRoot, kNameSpaceID_XUL, michael@0: nsGkAtoms::treecols, michael@0: getter_AddRefs(treecols)); michael@0: michael@0: if (!treecols) michael@0: return NS_OK; michael@0: michael@0: for (nsIContent* child = treecols->GetFirstChild(); michael@0: child; michael@0: child = child->GetNextSibling()) { michael@0: michael@0: if (child->NodeInfo()->Equals(nsGkAtoms::treecol, michael@0: kNameSpaceID_XUL)) { michael@0: if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortActive, michael@0: nsGkAtoms::_true, eCaseMatters)) { michael@0: nsAutoString sort; michael@0: child->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort); michael@0: if (! sort.IsEmpty()) { michael@0: mSortVariable = do_GetAtom(sort); michael@0: michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::ascending, &nsGkAtoms::descending, nullptr}; michael@0: switch (child->FindAttrValueIn(kNameSpaceID_None, michael@0: nsGkAtoms::sortDirection, michael@0: strings, eCaseMatters)) { michael@0: case 0: mSortDirection = eDirection_Ascending; break; michael@0: case 1: mSortDirection = eDirection_Descending; break; michael@0: default: mSortDirection = eDirection_Natural; break; michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::RebuildAll() michael@0: { michael@0: NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsCOMPtr doc = mRoot->GetDocument(); michael@0: michael@0: // Bail out early if we are being torn down. michael@0: if (!doc) michael@0: return NS_OK; michael@0: michael@0: if (! mQueryProcessor) michael@0: return NS_OK; michael@0: michael@0: if (mBoxObject) { michael@0: mBoxObject->BeginUpdateBatch(); michael@0: } michael@0: michael@0: if (mQueriesCompiled) { michael@0: Uninit(false); michael@0: } michael@0: else if (mBoxObject) { michael@0: int32_t count = mRows.Count(); michael@0: mRows.Clear(); michael@0: mBoxObject->RowCountChanged(0, -count); michael@0: } michael@0: michael@0: nsresult rv = CompileQueries(); michael@0: if (NS_SUCCEEDED(rv) && mQuerySets.Length() > 0) { michael@0: // Seed the rule network with assignments for the tree row variable michael@0: nsAutoString ref; michael@0: mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref); michael@0: if (!ref.IsEmpty()) { michael@0: rv = mQueryProcessor->TranslateRef(mDataSource, ref, michael@0: getter_AddRefs(mRootResult)); michael@0: if (NS_SUCCEEDED(rv) && mRootResult) { michael@0: OpenContainer(-1, mRootResult); michael@0: michael@0: nsCOMPtr rootResource; michael@0: GetResultResource(mRootResult, getter_AddRefs(rootResource)); michael@0: michael@0: mRows.SetRootResource(rootResource); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (mBoxObject) { michael@0: mBoxObject->EndUpdateBatch(); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::GetTemplateActionRowFor(int32_t aRow, nsIContent** aResult) michael@0: { michael@0: // Get the template in the DOM from which we're supposed to michael@0: // generate text michael@0: nsTreeRows::Row& row = *(mRows[aRow]); michael@0: michael@0: // The match stores the indices of the rule and query to use. Use these michael@0: // to look up the right nsTemplateRule and use that rule's action to get michael@0: // the treerow in the template. michael@0: int16_t ruleindex = row.mMatch->RuleIndex(); michael@0: if (ruleindex >= 0) { michael@0: nsTemplateQuerySet* qs = mQuerySets[row.mMatch->QuerySetPriority()]; michael@0: nsTemplateRule* rule = qs->GetRuleAt(ruleindex); michael@0: if (rule) { michael@0: nsCOMPtr children; michael@0: nsXULContentUtils::FindChildByTag(rule->GetAction(), kNameSpaceID_XUL, michael@0: nsGkAtoms::treechildren, michael@0: getter_AddRefs(children)); michael@0: if (children) { michael@0: nsCOMPtr item; michael@0: nsXULContentUtils::FindChildByTag(children, kNameSpaceID_XUL, michael@0: nsGkAtoms::treeitem, michael@0: getter_AddRefs(item)); michael@0: if (item) michael@0: return nsXULContentUtils::FindChildByTag(item, michael@0: kNameSpaceID_XUL, michael@0: nsGkAtoms::treerow, michael@0: aResult); michael@0: } michael@0: } michael@0: } michael@0: michael@0: *aResult = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::GetTemplateActionCellFor(int32_t aRow, michael@0: nsITreeColumn* aCol, michael@0: nsIContent** aResult) michael@0: { michael@0: *aResult = nullptr; michael@0: michael@0: if (!aCol) return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsCOMPtr row; michael@0: GetTemplateActionRowFor(aRow, getter_AddRefs(row)); michael@0: if (row) { michael@0: nsCOMPtr colAtom; michael@0: int32_t colIndex; michael@0: aCol->GetAtom(getter_AddRefs(colAtom)); michael@0: aCol->GetIndex(&colIndex); michael@0: michael@0: uint32_t j = 0; michael@0: for (nsIContent* child = row->GetFirstChild(); michael@0: child; michael@0: child = child->GetNextSibling()) { michael@0: michael@0: if (child->NodeInfo()->Equals(nsGkAtoms::treecell, michael@0: kNameSpaceID_XUL)) { michael@0: if (colAtom && michael@0: child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref, michael@0: colAtom, eCaseMatters)) { michael@0: *aResult = child; michael@0: break; michael@0: } michael@0: else if (j == (uint32_t)colIndex) michael@0: *aResult = child; michael@0: j++; michael@0: } michael@0: } michael@0: } michael@0: NS_IF_ADDREF(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::GetResourceFor(int32_t aRow, nsIRDFResource** aResource) michael@0: { michael@0: nsTreeRows::Row& row = *(mRows[aRow]); michael@0: return GetResultResource(row.mMatch->mResult, aResource); michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::OpenContainer(int32_t aIndex, nsIXULTemplateResult* aResult) michael@0: { michael@0: // A row index of -1 in this case means ``open tree body'' michael@0: NS_ASSERTION(aIndex >= -1 && aIndex < mRows.Count(), "bad row"); michael@0: if (aIndex < -1 || aIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsTreeRows::Subtree* container; michael@0: michael@0: if (aIndex >= 0) { michael@0: nsTreeRows::iterator iter = mRows[aIndex]; michael@0: container = mRows.EnsureSubtreeFor(iter.GetParent(), michael@0: iter.GetChildIndex()); michael@0: michael@0: iter->mContainerState = nsTreeRows::eContainerState_Open; michael@0: } michael@0: else michael@0: container = mRows.GetRoot(); michael@0: michael@0: if (! container) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: int32_t count; michael@0: OpenSubtreeOf(container, aIndex, aResult, &count); michael@0: michael@0: // Notify the box object michael@0: if (mBoxObject) { michael@0: if (aIndex >= 0) michael@0: mBoxObject->InvalidateRow(aIndex); michael@0: michael@0: if (count) michael@0: mBoxObject->RowCountChanged(aIndex + 1, count); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::OpenSubtreeOf(nsTreeRows::Subtree* aSubtree, michael@0: int32_t aIndex, michael@0: nsIXULTemplateResult *aResult, michael@0: int32_t* aDelta) michael@0: { michael@0: nsAutoTArray open; michael@0: int32_t count = 0; michael@0: michael@0: int32_t rulecount = mQuerySets.Length(); michael@0: michael@0: for (int32_t r = 0; r < rulecount; r++) { michael@0: nsTemplateQuerySet* queryset = mQuerySets[r]; michael@0: OpenSubtreeForQuerySet(aSubtree, aIndex, aResult, queryset, &count, open); michael@0: } michael@0: michael@0: // Now recursively deal with any open sub-containers that just got michael@0: // inserted. We need to do this back-to-front to avoid skewing offsets. michael@0: for (int32_t i = open.Length() - 1; i >= 0; --i) { michael@0: int32_t index = open[i]; michael@0: michael@0: nsTreeRows::Subtree* child = michael@0: mRows.EnsureSubtreeFor(aSubtree, index); michael@0: michael@0: nsIXULTemplateResult* result = (*aSubtree)[index].mMatch->mResult; michael@0: michael@0: int32_t delta; michael@0: OpenSubtreeOf(child, aIndex + index, result, &delta); michael@0: count += delta; michael@0: } michael@0: michael@0: // Sort the container. michael@0: if (mSortVariable) { michael@0: NS_QuickSort(mRows.GetRowsFor(aSubtree), michael@0: aSubtree->Count(), michael@0: sizeof(nsTreeRows::Row), michael@0: Compare, michael@0: this); michael@0: } michael@0: michael@0: *aDelta = count; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree, michael@0: int32_t aIndex, michael@0: nsIXULTemplateResult* aResult, michael@0: nsTemplateQuerySet* aQuerySet, michael@0: int32_t* aDelta, michael@0: nsTArray& open) michael@0: { michael@0: int32_t count = *aDelta; michael@0: michael@0: nsCOMPtr results; michael@0: nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult, michael@0: aQuerySet->mCompiledQuery, michael@0: getter_AddRefs(results)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: bool hasMoreResults; michael@0: rv = results->HasMoreElements(&hasMoreResults); michael@0: michael@0: for (; NS_SUCCEEDED(rv) && hasMoreResults; michael@0: rv = results->HasMoreElements(&hasMoreResults)) { michael@0: nsCOMPtr nr; michael@0: rv = results->GetNext(getter_AddRefs(nr)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr nextresult = do_QueryInterface(nr); michael@0: if (!nextresult) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr resultid; michael@0: rv = GetResultResource(nextresult, getter_AddRefs(resultid)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (! resultid) michael@0: continue; michael@0: michael@0: // check if there is already an existing match. If so, a previous michael@0: // query already generated content so the match is just added to the michael@0: // end of the set of matches. michael@0: michael@0: bool generateContent = true; michael@0: michael@0: nsTemplateMatch* prevmatch = nullptr; michael@0: nsTemplateMatch* existingmatch = nullptr; michael@0: if (mMatchMap.Get(resultid, &existingmatch)){ michael@0: // check if there is an existing match that matched a rule michael@0: while (existingmatch) { michael@0: if (existingmatch->IsActive()) michael@0: generateContent = false; michael@0: prevmatch = existingmatch; michael@0: existingmatch = existingmatch->mNext; michael@0: } michael@0: } michael@0: michael@0: nsTemplateMatch *newmatch = michael@0: nsTemplateMatch::Create(aQuerySet->Priority(), nextresult, nullptr); michael@0: if (!newmatch) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: if (generateContent) { michael@0: // Don't allow cyclic graphs to get our knickers in a knot. michael@0: bool cyclic = false; michael@0: michael@0: if (aIndex >= 0) { michael@0: for (nsTreeRows::iterator iter = mRows[aIndex]; iter.GetDepth() > 0; iter.Pop()) { michael@0: nsCOMPtr parentid; michael@0: rv = GetResultResource(iter->mMatch->mResult, getter_AddRefs(parentid)); michael@0: if (NS_FAILED(rv)) { michael@0: nsTemplateMatch::Destroy(newmatch, false); michael@0: return rv; michael@0: } michael@0: michael@0: if (resultid == parentid) { michael@0: cyclic = true; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (cyclic) { michael@0: NS_WARNING("tree cannot handle cyclic graphs"); michael@0: nsTemplateMatch::Destroy(newmatch, false); michael@0: continue; michael@0: } michael@0: michael@0: int16_t ruleindex; michael@0: nsTemplateRule* matchedrule = nullptr; michael@0: rv = DetermineMatchedRule(nullptr, nextresult, aQuerySet, michael@0: &matchedrule, &ruleindex); michael@0: if (NS_FAILED(rv)) { michael@0: nsTemplateMatch::Destroy(newmatch, false); michael@0: return rv; michael@0: } michael@0: michael@0: if (matchedrule) { michael@0: rv = newmatch->RuleMatched(aQuerySet, matchedrule, ruleindex, michael@0: nextresult); michael@0: if (NS_FAILED(rv)) { michael@0: nsTemplateMatch::Destroy(newmatch, false); michael@0: return rv; michael@0: } michael@0: michael@0: // Remember that this match applied to this row michael@0: mRows.InsertRowAt(newmatch, aSubtree, count); michael@0: michael@0: // If this is open, then remember it so we can recursively add michael@0: // *its* rows to the tree. michael@0: bool isOpen = false; michael@0: IsContainerOpen(nextresult, &isOpen); michael@0: if (isOpen) { michael@0: if (open.AppendElement(count) == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: ++count; michael@0: } michael@0: michael@0: if (mFlags & eLoggingEnabled) michael@0: OutputMatchToLog(resultid, newmatch, true); michael@0: michael@0: } michael@0: michael@0: if (prevmatch) { michael@0: prevmatch->mNext = newmatch; michael@0: } michael@0: else { michael@0: mMatchMap.Put(resultid, newmatch); michael@0: } michael@0: } michael@0: michael@0: *aDelta = count; michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::CloseContainer(int32_t aIndex) michael@0: { michael@0: NS_ASSERTION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); michael@0: if (aIndex < 0 || aIndex >= mRows.Count()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsTreeRows::iterator iter = mRows[aIndex]; michael@0: michael@0: if (iter->mSubtree) michael@0: RemoveMatchesFor(*iter->mSubtree); michael@0: michael@0: michael@0: int32_t count = mRows.GetSubtreeSizeFor(iter); michael@0: mRows.RemoveSubtreeFor(iter); michael@0: michael@0: iter->mContainerState = nsTreeRows::eContainerState_Closed; michael@0: michael@0: if (mBoxObject) { michael@0: mBoxObject->InvalidateRow(aIndex); michael@0: michael@0: if (count) michael@0: mBoxObject->RowCountChanged(aIndex + 1, -count); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::RemoveMatchesFor(nsTreeRows::Subtree& subtree) michael@0: { michael@0: for (int32_t i = subtree.Count() - 1; i >= 0; --i) { michael@0: nsTreeRows::Row& row = subtree[i]; michael@0: michael@0: nsTemplateMatch* match = row.mMatch; michael@0: michael@0: nsCOMPtr id; michael@0: nsresult rv = GetResultResource(match->mResult, getter_AddRefs(id)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsTemplateMatch* existingmatch; michael@0: if (mMatchMap.Get(id, &existingmatch)) { michael@0: while (existingmatch) { michael@0: nsTemplateMatch* nextmatch = existingmatch->mNext; michael@0: nsTemplateMatch::Destroy(existingmatch, true); michael@0: existingmatch = nextmatch; michael@0: } michael@0: michael@0: mMatchMap.Remove(id); michael@0: } michael@0: michael@0: if ((row.mContainerState == nsTreeRows::eContainerState_Open) && row.mSubtree) michael@0: RemoveMatchesFor(*(row.mSubtree)); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen) michael@0: { michael@0: // items are never open if recursion is disabled michael@0: if ((mFlags & eDontRecurse) && aResult != mRootResult) { michael@0: *aOpen = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr id; michael@0: nsresult rv = GetResultResource(aResult, getter_AddRefs(id)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return IsContainerOpen(id, aOpen); michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::IsContainerOpen(nsIRDFResource* aResource, bool* aOpen) michael@0: { michael@0: if (mPersistStateStore) michael@0: mPersistStateStore->HasAssertion(aResource, michael@0: nsXULContentUtils::NC_open, michael@0: nsXULContentUtils::true_, michael@0: true, michael@0: aOpen); michael@0: else michael@0: *aOpen = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: int michael@0: nsXULTreeBuilder::Compare(const void* aLeft, const void* aRight, void* aClosure) michael@0: { michael@0: nsXULTreeBuilder* self = static_cast(aClosure); michael@0: michael@0: nsTreeRows::Row* left = static_cast michael@0: (const_cast(aLeft)); michael@0: michael@0: nsTreeRows::Row* right = static_cast michael@0: (const_cast(aRight)); michael@0: michael@0: return self->CompareResults(left->mMatch->mResult, right->mMatch->mResult); michael@0: } michael@0: michael@0: int32_t michael@0: nsXULTreeBuilder::CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight) michael@0: { michael@0: // this is an extra check done for RDF queries such that results appear in michael@0: // the order they appear in their containing Seq michael@0: if (mSortDirection == eDirection_Natural && mDB) { michael@0: // If the sort order is ``natural'', then see if the container michael@0: // is an RDF sequence. If so, we'll try to use the ordinal michael@0: // properties to determine order. michael@0: // michael@0: // XXX the problem with this is, it doesn't always get the michael@0: // *real* container; e.g., michael@0: // michael@0: // michael@0: // michael@0: // michael@0: // michael@0: // michael@0: // michael@0: // In this case mRefVariable is bound to ?uri, not michael@0: // ?subheadings. (The ``container'' in the template sense != michael@0: // container in the RDF sense.) michael@0: michael@0: nsCOMPtr ref; michael@0: nsresult rv = aLeft->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref)); michael@0: if (NS_FAILED(rv)) michael@0: return 0; michael@0: michael@0: nsCOMPtr container = do_QueryInterface(ref); michael@0: if (container) { michael@0: bool isSequence = false; michael@0: gRDFContainerUtils->IsSeq(mDB, container, &isSequence); michael@0: if (isSequence) { michael@0: // Determine the indices of the left and right elements michael@0: // in the container. michael@0: int32_t lindex = 0, rindex = 0; michael@0: michael@0: nsCOMPtr leftitem; michael@0: aLeft->GetResource(getter_AddRefs(leftitem)); michael@0: if (leftitem) { michael@0: gRDFContainerUtils->IndexOf(mDB, container, leftitem, &lindex); michael@0: if (lindex < 0) michael@0: return 0; michael@0: } michael@0: michael@0: nsCOMPtr rightitem; michael@0: aRight->GetResource(getter_AddRefs(rightitem)); michael@0: if (rightitem) { michael@0: gRDFContainerUtils->IndexOf(mDB, container, rightitem, &rindex); michael@0: if (rindex < 0) michael@0: return 0; michael@0: } michael@0: michael@0: return lindex - rindex; michael@0: } michael@0: } michael@0: } michael@0: michael@0: int32_t sortorder; michael@0: if (!mQueryProcessor) michael@0: return 0; michael@0: michael@0: mQueryProcessor->CompareResults(aLeft, aRight, mSortVariable, mSortHints, &sortorder); michael@0: michael@0: if (sortorder) michael@0: sortorder = sortorder * mSortDirection; michael@0: return sortorder; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTreeBuilder::SortSubtree(nsTreeRows::Subtree* aSubtree) michael@0: { michael@0: NS_QuickSort(mRows.GetRowsFor(aSubtree), michael@0: aSubtree->Count(), michael@0: sizeof(nsTreeRows::Row), michael@0: Compare, michael@0: this); michael@0: michael@0: for (int32_t i = aSubtree->Count() - 1; i >= 0; --i) { michael@0: nsTreeRows::Subtree* child = (*aSubtree)[i].mSubtree; michael@0: if (child) michael@0: SortSubtree(child); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /* boolean canDrop (in long index, in long orientation); */ michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::CanDrop(int32_t index, int32_t orientation, michael@0: nsIDOMDataTransfer* dataTransfer, bool *_retval) michael@0: { michael@0: *_retval = false; michael@0: uint32_t count = mObservers.Count(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr observer = mObservers.SafeObjectAt(i); michael@0: if (observer) { michael@0: observer->CanDrop(index, orientation, dataTransfer, _retval); michael@0: if (*_retval) michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::Drop(int32_t row, int32_t orient, nsIDOMDataTransfer* dataTransfer) michael@0: { michael@0: uint32_t count = mObservers.Count(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr observer = mObservers.SafeObjectAt(i); michael@0: if (observer) { michael@0: bool canDrop = false; michael@0: observer->CanDrop(row, orient, dataTransfer, &canDrop); michael@0: if (canDrop) michael@0: observer->OnDrop(row, orient, dataTransfer); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTreeBuilder::IsSorted(bool *_retval) michael@0: { michael@0: *_retval = (mSortVariable != nullptr); michael@0: return NS_OK; michael@0: } michael@0: