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