content/xul/templates/src/nsXULTreeBuilder.cpp

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

mercurial