layout/xul/tree/nsTreeContentView.cpp

branch
TOR_BUG_9701
changeset 3
141e0f1194b1
equal deleted inserted replaced
-1:000000000000 0:f6b3fa049c44
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 "nsNameSpaceManager.h"
7 #include "nsGkAtoms.h"
8 #include "nsIBoxObject.h"
9 #include "nsTreeUtils.h"
10 #include "nsTreeContentView.h"
11 #include "ChildIterator.h"
12 #include "nsDOMClassInfoID.h"
13 #include "nsError.h"
14 #include "nsINodeInfo.h"
15 #include "nsIXULSortService.h"
16 #include "nsContentUtils.h"
17 #include "nsTreeBodyFrame.h"
18 #include "mozilla/dom/Element.h"
19 #include "nsServiceManagerUtils.h"
20
21 using namespace mozilla;
22
23 #define NS_ENSURE_NATIVE_COLUMN(_col) \
24 nsRefPtr<nsTreeColumn> col = nsTreeBodyFrame::GetColumnImpl(_col); \
25 if (!col) { \
26 return NS_ERROR_INVALID_ARG; \
27 }
28
29 // A content model view implementation for the tree.
30
31 #define ROW_FLAG_CONTAINER 0x01
32 #define ROW_FLAG_OPEN 0x02
33 #define ROW_FLAG_EMPTY 0x04
34 #define ROW_FLAG_SEPARATOR 0x08
35
36 class Row
37 {
38 public:
39 Row(nsIContent* aContent, int32_t aParentIndex)
40 : mContent(aContent), mParentIndex(aParentIndex),
41 mSubtreeSize(0), mFlags(0) {
42 }
43
44 ~Row() {
45 }
46
47 void SetContainer(bool aContainer) {
48 aContainer ? mFlags |= ROW_FLAG_CONTAINER : mFlags &= ~ROW_FLAG_CONTAINER;
49 }
50 bool IsContainer() { return mFlags & ROW_FLAG_CONTAINER; }
51
52 void SetOpen(bool aOpen) {
53 aOpen ? mFlags |= ROW_FLAG_OPEN : mFlags &= ~ROW_FLAG_OPEN;
54 }
55 bool IsOpen() { return !!(mFlags & ROW_FLAG_OPEN); }
56
57 void SetEmpty(bool aEmpty) {
58 aEmpty ? mFlags |= ROW_FLAG_EMPTY : mFlags &= ~ROW_FLAG_EMPTY;
59 }
60 bool IsEmpty() { return !!(mFlags & ROW_FLAG_EMPTY); }
61
62 void SetSeparator(bool aSeparator) {
63 aSeparator ? mFlags |= ROW_FLAG_SEPARATOR : mFlags &= ~ROW_FLAG_SEPARATOR;
64 }
65 bool IsSeparator() { return !!(mFlags & ROW_FLAG_SEPARATOR); }
66
67 // Weak reference to a content item.
68 nsIContent* mContent;
69
70 // The parent index of the item, set to -1 for the top level items.
71 int32_t mParentIndex;
72
73 // Subtree size for this item.
74 int32_t mSubtreeSize;
75
76 private:
77 // State flags
78 int8_t mFlags;
79 };
80
81
82 // We don't reference count the reference to the document
83 // If the document goes away first, we'll be informed and we
84 // can drop our reference.
85 // If we go away first, we'll get rid of ourselves from the
86 // document's observer list.
87
88 nsTreeContentView::nsTreeContentView(void) :
89 mBoxObject(nullptr),
90 mSelection(nullptr),
91 mRoot(nullptr),
92 mDocument(nullptr)
93 {
94 }
95
96 nsTreeContentView::~nsTreeContentView(void)
97 {
98 // Remove ourselves from mDocument's observers.
99 if (mDocument)
100 mDocument->RemoveObserver(this);
101 }
102
103 nsresult
104 NS_NewTreeContentView(nsITreeView** aResult)
105 {
106 *aResult = new nsTreeContentView;
107 if (! *aResult)
108 return NS_ERROR_OUT_OF_MEMORY;
109 NS_ADDREF(*aResult);
110 return NS_OK;
111 }
112
113 NS_IMPL_CYCLE_COLLECTION(nsTreeContentView,
114 mBoxObject,
115 mSelection,
116 mRoot,
117 mBody)
118
119 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeContentView)
120 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeContentView)
121
122 DOMCI_DATA(TreeContentView, nsTreeContentView)
123
124 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeContentView)
125 NS_INTERFACE_MAP_ENTRY(nsITreeView)
126 NS_INTERFACE_MAP_ENTRY(nsITreeContentView)
127 NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
128 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
129 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITreeContentView)
130 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeContentView)
131 NS_INTERFACE_MAP_END
132
133 NS_IMETHODIMP
134 nsTreeContentView::GetRowCount(int32_t* aRowCount)
135 {
136 *aRowCount = mRows.Length();
137
138 return NS_OK;
139 }
140
141 NS_IMETHODIMP
142 nsTreeContentView::GetSelection(nsITreeSelection** aSelection)
143 {
144 NS_IF_ADDREF(*aSelection = mSelection);
145
146 return NS_OK;
147 }
148
149 bool
150 nsTreeContentView::CanTrustTreeSelection(nsISupports* aValue)
151 {
152 // Untrusted content is only allowed to specify known-good views
153 if (nsContentUtils::IsCallerChrome())
154 return true;
155 nsCOMPtr<nsINativeTreeSelection> nativeTreeSel = do_QueryInterface(aValue);
156 return nativeTreeSel && NS_SUCCEEDED(nativeTreeSel->EnsureNative());
157 }
158
159 NS_IMETHODIMP
160 nsTreeContentView::SetSelection(nsITreeSelection* aSelection)
161 {
162 NS_ENSURE_TRUE(!aSelection || CanTrustTreeSelection(aSelection),
163 NS_ERROR_DOM_SECURITY_ERR);
164
165 mSelection = aSelection;
166 return NS_OK;
167 }
168
169 NS_IMETHODIMP
170 nsTreeContentView::GetRowProperties(int32_t aIndex, nsAString& aProps)
171 {
172 NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
173 if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
174 return NS_ERROR_INVALID_ARG;
175
176 Row* row = mRows[aIndex];
177 nsIContent* realRow;
178 if (row->IsSeparator())
179 realRow = row->mContent;
180 else
181 realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
182
183 if (realRow) {
184 realRow->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, aProps);
185 }
186
187 return NS_OK;
188 }
189
190 NS_IMETHODIMP
191 nsTreeContentView::GetCellProperties(int32_t aRow, nsITreeColumn* aCol,
192 nsAString& aProps)
193 {
194 NS_ENSURE_NATIVE_COLUMN(aCol);
195 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
196 if (aRow < 0 || aRow >= int32_t(mRows.Length()))
197 return NS_ERROR_INVALID_ARG;
198
199 Row* row = mRows[aRow];
200 nsIContent* realRow =
201 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
202 if (realRow) {
203 nsIContent* cell = GetCell(realRow, aCol);
204 if (cell) {
205 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, aProps);
206 }
207 }
208
209 return NS_OK;
210 }
211
212 NS_IMETHODIMP
213 nsTreeContentView::GetColumnProperties(nsITreeColumn* aCol, nsAString& aProps)
214 {
215 NS_ENSURE_NATIVE_COLUMN(aCol);
216 nsCOMPtr<nsIDOMElement> element;
217 aCol->GetElement(getter_AddRefs(element));
218
219 element->GetAttribute(NS_LITERAL_STRING("properties"), aProps);
220 return NS_OK;
221 }
222
223 NS_IMETHODIMP
224 nsTreeContentView::IsContainer(int32_t aIndex, bool* _retval)
225 {
226 NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
227 if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
228 return NS_ERROR_INVALID_ARG;
229
230 *_retval = mRows[aIndex]->IsContainer();
231
232 return NS_OK;
233 }
234
235 NS_IMETHODIMP
236 nsTreeContentView::IsContainerOpen(int32_t aIndex, bool* _retval)
237 {
238 NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
239 if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
240 return NS_ERROR_INVALID_ARG;
241
242 *_retval = mRows[aIndex]->IsOpen();
243
244 return NS_OK;
245 }
246
247 NS_IMETHODIMP
248 nsTreeContentView::IsContainerEmpty(int32_t aIndex, bool* _retval)
249 {
250 NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
251 if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
252 return NS_ERROR_INVALID_ARG;
253
254 *_retval = mRows[aIndex]->IsEmpty();
255
256 return NS_OK;
257 }
258
259 NS_IMETHODIMP
260 nsTreeContentView::IsSeparator(int32_t aIndex, bool *_retval)
261 {
262 NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
263 if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
264 return NS_ERROR_INVALID_ARG;
265
266 *_retval = mRows[aIndex]->IsSeparator();
267
268 return NS_OK;
269 }
270
271 NS_IMETHODIMP
272 nsTreeContentView::IsSorted(bool *_retval)
273 {
274 *_retval = false;
275
276 return NS_OK;
277 }
278
279 NS_IMETHODIMP
280 nsTreeContentView::CanDrop(int32_t aIndex, int32_t aOrientation,
281 nsIDOMDataTransfer* aDataTransfer, bool *_retval)
282 {
283 NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
284 if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
285 return NS_ERROR_INVALID_ARG;
286
287 *_retval = false;
288
289 return NS_OK;
290 }
291
292 NS_IMETHODIMP
293 nsTreeContentView::Drop(int32_t aRow, int32_t aOrientation, nsIDOMDataTransfer* aDataTransfer)
294 {
295 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
296 if (aRow < 0 || aRow >= int32_t(mRows.Length()))
297 return NS_ERROR_INVALID_ARG;
298
299 return NS_OK;
300 }
301
302 NS_IMETHODIMP
303 nsTreeContentView::GetParentIndex(int32_t aRowIndex, int32_t* _retval)
304 {
305 NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < int32_t(mRows.Length()),
306 "bad row index");
307 if (aRowIndex < 0 || aRowIndex >= int32_t(mRows.Length()))
308 return NS_ERROR_INVALID_ARG;
309
310 *_retval = mRows[aRowIndex]->mParentIndex;
311
312 return NS_OK;
313 }
314
315 NS_IMETHODIMP
316 nsTreeContentView::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, bool* _retval)
317 {
318 NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < int32_t(mRows.Length()),
319 "bad row index");
320 if (aRowIndex < 0 || aRowIndex >= int32_t(mRows.Length()))
321 return NS_ERROR_INVALID_ARG;
322
323 // We have a next sibling if the row is not the last in the subtree.
324 int32_t parentIndex = mRows[aRowIndex]->mParentIndex;
325 if (parentIndex >= 0) {
326 // Compute the last index in this subtree.
327 int32_t lastIndex = parentIndex + (mRows[parentIndex])->mSubtreeSize;
328 Row* row = mRows[lastIndex];
329 while (row->mParentIndex != parentIndex) {
330 lastIndex = row->mParentIndex;
331 row = mRows[lastIndex];
332 }
333
334 *_retval = aRowIndex < lastIndex;
335 }
336 else {
337 *_retval = uint32_t(aRowIndex) < mRows.Length() - 1;
338 }
339
340 return NS_OK;
341 }
342
343 NS_IMETHODIMP
344 nsTreeContentView::GetLevel(int32_t aIndex, int32_t* _retval)
345 {
346 NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
347 if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
348 return NS_ERROR_INVALID_ARG;
349
350 int32_t level = 0;
351 Row* row = mRows[aIndex];
352 while (row->mParentIndex >= 0) {
353 level++;
354 row = mRows[row->mParentIndex];
355 }
356 *_retval = level;
357
358 return NS_OK;
359 }
360
361 NS_IMETHODIMP
362 nsTreeContentView::GetImageSrc(int32_t aRow, nsITreeColumn* aCol, nsAString& _retval)
363 {
364 _retval.Truncate();
365 NS_ENSURE_NATIVE_COLUMN(aCol);
366 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
367 if (aRow < 0 || aRow >= int32_t(mRows.Length()))
368 return NS_ERROR_INVALID_ARG;
369
370 Row* row = mRows[aRow];
371
372 nsIContent* realRow =
373 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
374 if (realRow) {
375 nsIContent* cell = GetCell(realRow, aCol);
376 if (cell)
377 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, _retval);
378 }
379
380 return NS_OK;
381 }
382
383 NS_IMETHODIMP
384 nsTreeContentView::GetProgressMode(int32_t aRow, nsITreeColumn* aCol, int32_t* _retval)
385 {
386 NS_ENSURE_NATIVE_COLUMN(aCol);
387 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
388 if (aRow < 0 || aRow >= int32_t(mRows.Length()))
389 return NS_ERROR_INVALID_ARG;
390
391 *_retval = nsITreeView::PROGRESS_NONE;
392
393 Row* row = mRows[aRow];
394
395 nsIContent* realRow =
396 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
397 if (realRow) {
398 nsIContent* cell = GetCell(realRow, aCol);
399 if (cell) {
400 static nsIContent::AttrValuesArray strings[] =
401 {&nsGkAtoms::normal, &nsGkAtoms::undetermined, nullptr};
402 switch (cell->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::mode,
403 strings, eCaseMatters)) {
404 case 0: *_retval = nsITreeView::PROGRESS_NORMAL; break;
405 case 1: *_retval = nsITreeView::PROGRESS_UNDETERMINED; break;
406 }
407 }
408 }
409
410 return NS_OK;
411 }
412
413 NS_IMETHODIMP
414 nsTreeContentView::GetCellValue(int32_t aRow, nsITreeColumn* aCol, nsAString& _retval)
415 {
416 _retval.Truncate();
417 NS_ENSURE_NATIVE_COLUMN(aCol);
418 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
419 if (aRow < 0 || aRow >= int32_t(mRows.Length()))
420 return NS_ERROR_INVALID_ARG;
421
422 Row* row = mRows[aRow];
423
424 nsIContent* realRow =
425 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
426 if (realRow) {
427 nsIContent* cell = GetCell(realRow, aCol);
428 if (cell)
429 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, _retval);
430 }
431
432 return NS_OK;
433 }
434
435 NS_IMETHODIMP
436 nsTreeContentView::GetCellText(int32_t aRow, nsITreeColumn* aCol, nsAString& _retval)
437 {
438 _retval.Truncate();
439 NS_ENSURE_NATIVE_COLUMN(aCol);
440 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
441 NS_PRECONDITION(aCol, "bad column");
442
443 if (aRow < 0 || aRow >= int32_t(mRows.Length()) || !aCol)
444 return NS_ERROR_INVALID_ARG;
445
446 Row* row = mRows[aRow];
447
448 // Check for a "label" attribute - this is valid on an <treeitem>
449 // with a single implied column.
450 if (row->mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, _retval)
451 && !_retval.IsEmpty())
452 return NS_OK;
453
454 nsIAtom *rowTag = row->mContent->Tag();
455 if (rowTag == nsGkAtoms::treeitem && row->mContent->IsXUL()) {
456 nsIContent* realRow =
457 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
458 if (realRow) {
459 nsIContent* cell = GetCell(realRow, aCol);
460 if (cell)
461 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, _retval);
462 }
463 }
464
465 return NS_OK;
466 }
467
468 NS_IMETHODIMP
469 nsTreeContentView::SetTree(nsITreeBoxObject* aTree)
470 {
471 ClearRows();
472
473 mBoxObject = aTree;
474
475 MOZ_ASSERT(!mRoot, "mRoot should have been cleared out by ClearRows");
476
477 if (aTree) {
478 // Get our root element
479 nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mBoxObject);
480 if (!boxObject) {
481 mBoxObject = nullptr;
482 return NS_ERROR_INVALID_ARG;
483 }
484 nsCOMPtr<nsIDOMElement> element;
485 boxObject->GetElement(getter_AddRefs(element));
486
487 mRoot = do_QueryInterface(element);
488 NS_ENSURE_STATE(mRoot);
489
490 // Add ourselves to document's observers.
491 nsIDocument* document = mRoot->GetDocument();
492 if (document) {
493 document->AddObserver(this);
494 mDocument = document;
495 }
496
497 nsCOMPtr<nsIDOMElement> bodyElement;
498 mBoxObject->GetTreeBody(getter_AddRefs(bodyElement));
499 if (bodyElement) {
500 mBody = do_QueryInterface(bodyElement);
501 int32_t index = 0;
502 Serialize(mBody, -1, &index, mRows);
503 }
504 }
505
506 return NS_OK;
507 }
508
509 NS_IMETHODIMP
510 nsTreeContentView::ToggleOpenState(int32_t aIndex)
511 {
512 NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
513 if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
514 return NS_ERROR_INVALID_ARG;
515
516 // We don't serialize content right here, since content might be generated
517 // lazily.
518 Row* row = mRows[aIndex];
519
520 if (row->IsOpen())
521 row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("false"), true);
522 else
523 row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("true"), true);
524
525 return NS_OK;
526 }
527
528 NS_IMETHODIMP
529 nsTreeContentView::CycleHeader(nsITreeColumn* aCol)
530 {
531 NS_ENSURE_NATIVE_COLUMN(aCol);
532
533 if (!mRoot)
534 return NS_OK;
535
536 nsCOMPtr<nsIDOMElement> element;
537 aCol->GetElement(getter_AddRefs(element));
538 if (element) {
539 nsCOMPtr<nsIContent> column = do_QueryInterface(element);
540 nsAutoString sort;
541 column->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
542 if (!sort.IsEmpty()) {
543 nsCOMPtr<nsIXULSortService> xs = do_GetService("@mozilla.org/xul/xul-sort-service;1");
544 if (xs) {
545 nsAutoString sortdirection;
546 static nsIContent::AttrValuesArray strings[] =
547 {&nsGkAtoms::ascending, &nsGkAtoms::descending, nullptr};
548 switch (column->FindAttrValueIn(kNameSpaceID_None,
549 nsGkAtoms::sortDirection,
550 strings, eCaseMatters)) {
551 case 0: sortdirection.AssignLiteral("descending"); break;
552 case 1: sortdirection.AssignLiteral("natural"); break;
553 default: sortdirection.AssignLiteral("ascending"); break;
554 }
555
556 nsAutoString hints;
557 column->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints);
558 sortdirection.AppendLiteral(" ");
559 sortdirection += hints;
560
561 nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot);
562 xs->Sort(rootnode, sort, sortdirection);
563 }
564 }
565 }
566
567 return NS_OK;
568 }
569
570 NS_IMETHODIMP
571 nsTreeContentView::SelectionChanged()
572 {
573 return NS_OK;
574 }
575
576 NS_IMETHODIMP
577 nsTreeContentView::CycleCell(int32_t aRow, nsITreeColumn* aCol)
578 {
579 return NS_OK;
580 }
581
582 NS_IMETHODIMP
583 nsTreeContentView::IsEditable(int32_t aRow, nsITreeColumn* aCol, bool* _retval)
584 {
585 *_retval = false;
586 NS_ENSURE_NATIVE_COLUMN(aCol);
587 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
588 if (aRow < 0 || aRow >= int32_t(mRows.Length()))
589 return NS_ERROR_INVALID_ARG;
590
591 *_retval = true;
592
593 Row* row = mRows[aRow];
594
595 nsIContent* realRow =
596 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
597 if (realRow) {
598 nsIContent* cell = GetCell(realRow, aCol);
599 if (cell && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
600 nsGkAtoms::_false, eCaseMatters)) {
601 *_retval = false;
602 }
603 }
604
605 return NS_OK;
606 }
607
608 NS_IMETHODIMP
609 nsTreeContentView::IsSelectable(int32_t aRow, nsITreeColumn* aCol, bool* _retval)
610 {
611 NS_ENSURE_NATIVE_COLUMN(aCol);
612 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
613 if (aRow < 0 || aRow >= int32_t(mRows.Length()))
614 return NS_ERROR_INVALID_ARG;
615
616 *_retval = true;
617
618 Row* row = mRows[aRow];
619
620 nsIContent* realRow =
621 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
622 if (realRow) {
623 nsIContent* cell = GetCell(realRow, aCol);
624 if (cell && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::selectable,
625 nsGkAtoms::_false, eCaseMatters)) {
626 *_retval = false;
627 }
628 }
629
630 return NS_OK;
631 }
632
633 NS_IMETHODIMP
634 nsTreeContentView::SetCellValue(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue)
635 {
636 NS_ENSURE_NATIVE_COLUMN(aCol);
637 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
638 if (aRow < 0 || aRow >= int32_t(mRows.Length()))
639 return NS_ERROR_INVALID_ARG;
640
641 Row* row = mRows[aRow];
642
643 nsIContent* realRow =
644 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
645 if (realRow) {
646 nsIContent* cell = GetCell(realRow, aCol);
647 if (cell)
648 cell->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, true);
649 }
650
651 return NS_OK;
652 }
653
654 NS_IMETHODIMP
655 nsTreeContentView::SetCellText(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue)
656 {
657 NS_ENSURE_NATIVE_COLUMN(aCol);
658 NS_PRECONDITION(aRow >= 0 && aRow < int32_t(mRows.Length()), "bad row");
659 if (aRow < 0 || aRow >= int32_t(mRows.Length()))
660 return NS_ERROR_INVALID_ARG;
661
662 Row* row = mRows[aRow];
663
664 nsIContent* realRow =
665 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
666 if (realRow) {
667 nsIContent* cell = GetCell(realRow, aCol);
668 if (cell)
669 cell->SetAttr(kNameSpaceID_None, nsGkAtoms::label, aValue, true);
670 }
671
672 return NS_OK;
673 }
674
675 NS_IMETHODIMP
676 nsTreeContentView::PerformAction(const char16_t* aAction)
677 {
678 return NS_OK;
679 }
680
681 NS_IMETHODIMP
682 nsTreeContentView::PerformActionOnRow(const char16_t* aAction, int32_t aRow)
683 {
684 return NS_OK;
685 }
686
687 NS_IMETHODIMP
688 nsTreeContentView::PerformActionOnCell(const char16_t* aAction, int32_t aRow, nsITreeColumn* aCol)
689 {
690 return NS_OK;
691 }
692
693
694 NS_IMETHODIMP
695 nsTreeContentView::GetItemAtIndex(int32_t aIndex, nsIDOMElement** _retval)
696 {
697 NS_PRECONDITION(aIndex >= 0 && aIndex < int32_t(mRows.Length()), "bad index");
698 if (aIndex < 0 || aIndex >= int32_t(mRows.Length()))
699 return NS_ERROR_INVALID_ARG;
700
701 Row* row = mRows[aIndex];
702 row->mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)_retval);
703
704 return NS_OK;
705 }
706
707 NS_IMETHODIMP
708 nsTreeContentView::GetIndexOfItem(nsIDOMElement* aItem, int32_t* _retval)
709 {
710 nsCOMPtr<nsIContent> content = do_QueryInterface(aItem);
711 *_retval = FindContent(content);
712
713 return NS_OK;
714 }
715
716 void
717 nsTreeContentView::AttributeChanged(nsIDocument* aDocument,
718 dom::Element* aElement,
719 int32_t aNameSpaceID,
720 nsIAtom* aAttribute,
721 int32_t aModType)
722 {
723 // Lots of codepaths under here that do all sorts of stuff, so be safe.
724 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
725
726 // Make sure this notification concerns us.
727 // First check the tag to see if it's one that we care about.
728 nsIAtom* tag = aElement->Tag();
729
730 if (mBoxObject && (aElement == mRoot || aElement == mBody)) {
731 mBoxObject->ClearStyleAndImageCaches();
732 mBoxObject->Invalidate();
733 }
734
735 // We don't consider non-XUL nodes.
736 nsIContent* parent = nullptr;
737 if (!aElement->IsXUL() ||
738 ((parent = aElement->GetParent()) && !parent->IsXUL())) {
739 return;
740 }
741 if (tag != nsGkAtoms::treecol &&
742 tag != nsGkAtoms::treeitem &&
743 tag != nsGkAtoms::treeseparator &&
744 tag != nsGkAtoms::treerow &&
745 tag != nsGkAtoms::treecell) {
746 return;
747 }
748
749 // If we have a legal tag, go up to the tree/select and make sure
750 // that it's ours.
751
752 for (nsIContent* element = aElement; element != mBody; element = element->GetParent()) {
753 if (!element)
754 return; // this is not for us
755 nsIAtom *parentTag = element->Tag();
756 if (element->IsXUL() && parentTag == nsGkAtoms::tree)
757 return; // this is not for us
758 }
759
760 // Handle changes of the hidden attribute.
761 if (aAttribute == nsGkAtoms::hidden &&
762 (tag == nsGkAtoms::treeitem || tag == nsGkAtoms::treeseparator)) {
763 bool hidden = aElement->AttrValueIs(kNameSpaceID_None,
764 nsGkAtoms::hidden,
765 nsGkAtoms::_true, eCaseMatters);
766
767 int32_t index = FindContent(aElement);
768 if (hidden && index >= 0) {
769 // Hide this row along with its children.
770 int32_t count = RemoveRow(index);
771 if (mBoxObject)
772 mBoxObject->RowCountChanged(index, -count);
773 }
774 else if (!hidden && index < 0) {
775 // Show this row along with its children.
776 nsCOMPtr<nsIContent> parent = aElement->GetParent();
777 if (parent) {
778 InsertRowFor(parent, aElement);
779 }
780 }
781
782 return;
783 }
784
785 if (tag == nsGkAtoms::treecol) {
786 if (aAttribute == nsGkAtoms::properties) {
787 if (mBoxObject) {
788 nsCOMPtr<nsITreeColumns> cols;
789 mBoxObject->GetColumns(getter_AddRefs(cols));
790 if (cols) {
791 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aElement);
792 nsCOMPtr<nsITreeColumn> col;
793 cols->GetColumnFor(element, getter_AddRefs(col));
794 mBoxObject->InvalidateColumn(col);
795 }
796 }
797 }
798 }
799 else if (tag == nsGkAtoms::treeitem) {
800 int32_t index = FindContent(aElement);
801 if (index >= 0) {
802 Row* row = mRows[index];
803 if (aAttribute == nsGkAtoms::container) {
804 bool isContainer =
805 aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
806 nsGkAtoms::_true, eCaseMatters);
807 row->SetContainer(isContainer);
808 if (mBoxObject)
809 mBoxObject->InvalidateRow(index);
810 }
811 else if (aAttribute == nsGkAtoms::open) {
812 bool isOpen =
813 aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
814 nsGkAtoms::_true, eCaseMatters);
815 bool wasOpen = row->IsOpen();
816 if (! isOpen && wasOpen)
817 CloseContainer(index);
818 else if (isOpen && ! wasOpen)
819 OpenContainer(index);
820 }
821 else if (aAttribute == nsGkAtoms::empty) {
822 bool isEmpty =
823 aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty,
824 nsGkAtoms::_true, eCaseMatters);
825 row->SetEmpty(isEmpty);
826 if (mBoxObject)
827 mBoxObject->InvalidateRow(index);
828 }
829 }
830 }
831 else if (tag == nsGkAtoms::treeseparator) {
832 int32_t index = FindContent(aElement);
833 if (index >= 0) {
834 if (aAttribute == nsGkAtoms::properties && mBoxObject) {
835 mBoxObject->InvalidateRow(index);
836 }
837 }
838 }
839 else if (tag == nsGkAtoms::treerow) {
840 if (aAttribute == nsGkAtoms::properties) {
841 nsCOMPtr<nsIContent> parent = aElement->GetParent();
842 if (parent) {
843 int32_t index = FindContent(parent);
844 if (index >= 0 && mBoxObject) {
845 mBoxObject->InvalidateRow(index);
846 }
847 }
848 }
849 }
850 else if (tag == nsGkAtoms::treecell) {
851 if (aAttribute == nsGkAtoms::ref ||
852 aAttribute == nsGkAtoms::properties ||
853 aAttribute == nsGkAtoms::mode ||
854 aAttribute == nsGkAtoms::src ||
855 aAttribute == nsGkAtoms::value ||
856 aAttribute == nsGkAtoms::label) {
857 nsIContent* parent = aElement->GetParent();
858 if (parent) {
859 nsCOMPtr<nsIContent> grandParent = parent->GetParent();
860 if (grandParent && grandParent->IsXUL()) {
861 int32_t index = FindContent(grandParent);
862 if (index >= 0 && mBoxObject) {
863 // XXX Should we make an effort to invalidate only cell ?
864 mBoxObject->InvalidateRow(index);
865 }
866 }
867 }
868 }
869 }
870 }
871
872 void
873 nsTreeContentView::ContentAppended(nsIDocument *aDocument,
874 nsIContent* aContainer,
875 nsIContent* aFirstNewContent,
876 int32_t /* unused */)
877 {
878 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
879 // Our contentinserted doesn't use the index
880 ContentInserted(aDocument, aContainer, cur, 0);
881 }
882 }
883
884 void
885 nsTreeContentView::ContentInserted(nsIDocument *aDocument,
886 nsIContent* aContainer,
887 nsIContent* aChild,
888 int32_t /* unused */)
889 {
890 NS_ASSERTION(aChild, "null ptr");
891
892 // Make sure this notification concerns us.
893 // First check the tag to see if it's one that we care about.
894 nsIAtom *childTag = aChild->Tag();
895
896 // Don't allow non-XUL nodes.
897 if (!aChild->IsXUL() || !aContainer->IsXUL())
898 return;
899 if (childTag != nsGkAtoms::treeitem &&
900 childTag != nsGkAtoms::treeseparator &&
901 childTag != nsGkAtoms::treechildren &&
902 childTag != nsGkAtoms::treerow &&
903 childTag != nsGkAtoms::treecell) {
904 return;
905 }
906
907 // If we have a legal tag, go up to the tree/select and make sure
908 // that it's ours.
909
910 for (nsIContent* element = aContainer; element != mBody; element = element->GetParent()) {
911 if (!element)
912 return; // this is not for us
913 nsIAtom *parentTag = element->Tag();
914 if (element->IsXUL() && parentTag == nsGkAtoms::tree)
915 return; // this is not for us
916 }
917
918 // Lots of codepaths under here that do all sorts of stuff, so be safe.
919 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
920
921 if (childTag == nsGkAtoms::treechildren) {
922 int32_t index = FindContent(aContainer);
923 if (index >= 0) {
924 Row* row = mRows[index];
925 row->SetEmpty(false);
926 if (mBoxObject)
927 mBoxObject->InvalidateRow(index);
928 if (row->IsContainer() && row->IsOpen()) {
929 int32_t count = EnsureSubtree(index);
930 if (mBoxObject)
931 mBoxObject->RowCountChanged(index + 1, count);
932 }
933 }
934 }
935 else if (childTag == nsGkAtoms::treeitem ||
936 childTag == nsGkAtoms::treeseparator) {
937 InsertRowFor(aContainer, aChild);
938 }
939 else if (childTag == nsGkAtoms::treerow) {
940 int32_t index = FindContent(aContainer);
941 if (index >= 0 && mBoxObject)
942 mBoxObject->InvalidateRow(index);
943 }
944 else if (childTag == nsGkAtoms::treecell) {
945 nsCOMPtr<nsIContent> parent = aContainer->GetParent();
946 if (parent) {
947 int32_t index = FindContent(parent);
948 if (index >= 0 && mBoxObject)
949 mBoxObject->InvalidateRow(index);
950 }
951 }
952 }
953
954 void
955 nsTreeContentView::ContentRemoved(nsIDocument *aDocument,
956 nsIContent* aContainer,
957 nsIContent* aChild,
958 int32_t aIndexInContainer,
959 nsIContent* aPreviousSibling)
960 {
961 NS_ASSERTION(aChild, "null ptr");
962
963 // Make sure this notification concerns us.
964 // First check the tag to see if it's one that we care about.
965 nsIAtom *tag = aChild->Tag();
966
967 // We don't consider non-XUL nodes.
968 if (!aChild->IsXUL() || !aContainer->IsXUL())
969 return;
970 if (tag != nsGkAtoms::treeitem &&
971 tag != nsGkAtoms::treeseparator &&
972 tag != nsGkAtoms::treechildren &&
973 tag != nsGkAtoms::treerow &&
974 tag != nsGkAtoms::treecell) {
975 return;
976 }
977
978 // If we have a legal tag, go up to the tree/select and make sure
979 // that it's ours.
980
981 for (nsIContent* element = aContainer; element != mBody; element = element->GetParent()) {
982 if (!element)
983 return; // this is not for us
984 nsIAtom *parentTag = element->Tag();
985 if (element->IsXUL() && parentTag == nsGkAtoms::tree)
986 return; // this is not for us
987 }
988
989 // Lots of codepaths under here that do all sorts of stuff, so be safe.
990 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
991
992 if (tag == nsGkAtoms::treechildren) {
993 int32_t index = FindContent(aContainer);
994 if (index >= 0) {
995 Row* row = mRows[index];
996 row->SetEmpty(true);
997 int32_t count = RemoveSubtree(index);
998 // Invalidate also the row to update twisty.
999 if (mBoxObject) {
1000 mBoxObject->InvalidateRow(index);
1001 mBoxObject->RowCountChanged(index + 1, -count);
1002 }
1003 }
1004 }
1005 else if (tag == nsGkAtoms::treeitem ||
1006 tag == nsGkAtoms::treeseparator
1007 ) {
1008 int32_t index = FindContent(aChild);
1009 if (index >= 0) {
1010 int32_t count = RemoveRow(index);
1011 if (mBoxObject)
1012 mBoxObject->RowCountChanged(index, -count);
1013 }
1014 }
1015 else if (tag == nsGkAtoms::treerow) {
1016 int32_t index = FindContent(aContainer);
1017 if (index >= 0 && mBoxObject)
1018 mBoxObject->InvalidateRow(index);
1019 }
1020 else if (tag == nsGkAtoms::treecell) {
1021 nsCOMPtr<nsIContent> parent = aContainer->GetParent();
1022 if (parent) {
1023 int32_t index = FindContent(parent);
1024 if (index >= 0 && mBoxObject)
1025 mBoxObject->InvalidateRow(index);
1026 }
1027 }
1028 }
1029
1030 void
1031 nsTreeContentView::NodeWillBeDestroyed(const nsINode* aNode)
1032 {
1033 // XXXbz do we need this strong ref? Do we drop refs to self in ClearRows?
1034 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
1035 ClearRows();
1036 }
1037
1038
1039 // Recursively serialize content, starting with aContent.
1040 void
1041 nsTreeContentView::Serialize(nsIContent* aContent, int32_t aParentIndex,
1042 int32_t* aIndex, nsTArray<nsAutoPtr<Row> >& aRows)
1043 {
1044 // Don't allow non-XUL nodes.
1045 if (!aContent->IsXUL())
1046 return;
1047
1048 dom::FlattenedChildIterator iter(aContent);
1049 for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
1050 nsIAtom *tag = content->Tag();
1051 int32_t count = aRows.Length();
1052
1053 if (content->IsXUL()) {
1054 if (tag == nsGkAtoms::treeitem)
1055 SerializeItem(content, aParentIndex, aIndex, aRows);
1056 else if (tag == nsGkAtoms::treeseparator)
1057 SerializeSeparator(content, aParentIndex, aIndex, aRows);
1058 }
1059 *aIndex += aRows.Length() - count;
1060 }
1061 }
1062
1063 void
1064 nsTreeContentView::SerializeItem(nsIContent* aContent, int32_t aParentIndex,
1065 int32_t* aIndex, nsTArray<nsAutoPtr<Row> >& aRows)
1066 {
1067 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
1068 nsGkAtoms::_true, eCaseMatters))
1069 return;
1070
1071 Row* row = new Row(aContent, aParentIndex);
1072 aRows.AppendElement(row);
1073
1074 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
1075 nsGkAtoms::_true, eCaseMatters)) {
1076 row->SetContainer(true);
1077 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
1078 nsGkAtoms::_true, eCaseMatters)) {
1079 row->SetOpen(true);
1080 nsIContent* child =
1081 nsTreeUtils::GetImmediateChild(aContent, nsGkAtoms::treechildren);
1082 if (child && child->IsXUL()) {
1083 // Now, recursively serialize our child.
1084 int32_t count = aRows.Length();
1085 int32_t index = 0;
1086 Serialize(child, aParentIndex + *aIndex + 1, &index, aRows);
1087 row->mSubtreeSize += aRows.Length() - count;
1088 }
1089 else
1090 row->SetEmpty(true);
1091 } else if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty,
1092 nsGkAtoms::_true, eCaseMatters)) {
1093 row->SetEmpty(true);
1094 }
1095 }
1096 }
1097
1098 void
1099 nsTreeContentView::SerializeSeparator(nsIContent* aContent,
1100 int32_t aParentIndex, int32_t* aIndex,
1101 nsTArray<nsAutoPtr<Row> >& aRows)
1102 {
1103 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
1104 nsGkAtoms::_true, eCaseMatters))
1105 return;
1106
1107 Row* row = new Row(aContent, aParentIndex);
1108 row->SetSeparator(true);
1109 aRows.AppendElement(row);
1110 }
1111
1112 void
1113 nsTreeContentView::GetIndexInSubtree(nsIContent* aContainer,
1114 nsIContent* aContent, int32_t* aIndex)
1115 {
1116 uint32_t childCount = aContainer->GetChildCount();
1117
1118 if (!aContainer->IsXUL())
1119 return;
1120
1121 for (uint32_t i = 0; i < childCount; i++) {
1122 nsIContent *content = aContainer->GetChildAt(i);
1123
1124 if (content == aContent)
1125 break;
1126
1127 nsIAtom *tag = content->Tag();
1128
1129 if (content->IsXUL()) {
1130 if (tag == nsGkAtoms::treeitem) {
1131 if (! content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
1132 nsGkAtoms::_true, eCaseMatters)) {
1133 (*aIndex)++;
1134 if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
1135 nsGkAtoms::_true, eCaseMatters) &&
1136 content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
1137 nsGkAtoms::_true, eCaseMatters)) {
1138 nsIContent* child =
1139 nsTreeUtils::GetImmediateChild(content, nsGkAtoms::treechildren);
1140 if (child && child->IsXUL())
1141 GetIndexInSubtree(child, aContent, aIndex);
1142 }
1143 }
1144 }
1145 else if (tag == nsGkAtoms::treeseparator) {
1146 if (! content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
1147 nsGkAtoms::_true, eCaseMatters))
1148 (*aIndex)++;
1149 }
1150 }
1151 }
1152 }
1153
1154 int32_t
1155 nsTreeContentView::EnsureSubtree(int32_t aIndex)
1156 {
1157 Row* row = mRows[aIndex];
1158
1159 nsIContent* child;
1160 child = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treechildren);
1161 if (!child || !child->IsXUL()) {
1162 return 0;
1163 }
1164
1165 nsAutoTArray<nsAutoPtr<Row>, 8> rows;
1166 int32_t index = 0;
1167 Serialize(child, aIndex, &index, rows);
1168 // We can't use InsertElementsAt since the destination can't steal
1169 // ownership from its const source argument.
1170 for (nsTArray<Row>::index_type i = 0; i < rows.Length(); i++) {
1171 nsAutoPtr<Row>* newRow = mRows.InsertElementAt(aIndex + i + 1);
1172 *newRow = rows[i];
1173 }
1174 int32_t count = rows.Length();
1175
1176 row->mSubtreeSize += count;
1177 UpdateSubtreeSizes(row->mParentIndex, count);
1178
1179 // Update parent indexes, but skip newly added rows.
1180 // They already have correct values.
1181 UpdateParentIndexes(aIndex, count + 1, count);
1182
1183 return count;
1184 }
1185
1186 int32_t
1187 nsTreeContentView::RemoveSubtree(int32_t aIndex)
1188 {
1189 Row* row = mRows[aIndex];
1190 int32_t count = row->mSubtreeSize;
1191
1192 mRows.RemoveElementsAt(aIndex + 1, count);
1193
1194 row->mSubtreeSize -= count;
1195 UpdateSubtreeSizes(row->mParentIndex, -count);
1196
1197 UpdateParentIndexes(aIndex, 0, -count);
1198
1199 return count;
1200 }
1201
1202 void
1203 nsTreeContentView::InsertRowFor(nsIContent* aParent, nsIContent* aChild)
1204 {
1205 int32_t grandParentIndex = -1;
1206 bool insertRow = false;
1207
1208 nsCOMPtr<nsIContent> grandParent = aParent->GetParent();
1209 nsIAtom* grandParentTag = grandParent->Tag();
1210
1211 if (grandParent->IsXUL() && grandParentTag == nsGkAtoms::tree) {
1212 // Allow insertion to the outermost container.
1213 insertRow = true;
1214 }
1215 else {
1216 // Test insertion to an inner container.
1217
1218 // First try to find this parent in our array of rows, if we find one
1219 // we can be sure that all other parents are open too.
1220 grandParentIndex = FindContent(grandParent);
1221 if (grandParentIndex >= 0) {
1222 // Got it, now test if it is open.
1223 if (mRows[grandParentIndex]->IsOpen())
1224 insertRow = true;
1225 }
1226 }
1227
1228 if (insertRow) {
1229 int32_t index = 0;
1230 GetIndexInSubtree(aParent, aChild, &index);
1231
1232 int32_t count = InsertRow(grandParentIndex, index, aChild);
1233 if (mBoxObject)
1234 mBoxObject->RowCountChanged(grandParentIndex + index + 1, count);
1235 }
1236 }
1237
1238 int32_t
1239 nsTreeContentView::InsertRow(int32_t aParentIndex, int32_t aIndex, nsIContent* aContent)
1240 {
1241 nsAutoTArray<nsAutoPtr<Row>, 8> rows;
1242 nsIAtom *tag = aContent->Tag();
1243 if (aContent->IsXUL()) {
1244 if (tag == nsGkAtoms::treeitem)
1245 SerializeItem(aContent, aParentIndex, &aIndex, rows);
1246 else if (tag == nsGkAtoms::treeseparator)
1247 SerializeSeparator(aContent, aParentIndex, &aIndex, rows);
1248 }
1249
1250 // We can't use InsertElementsAt since the destination can't steal
1251 // ownership from its const source argument.
1252 for (nsTArray<Row>::index_type i = 0; i < rows.Length(); i++) {
1253 nsAutoPtr<Row>* newRow = mRows.InsertElementAt(aParentIndex + aIndex + i + 1);
1254 *newRow = rows[i];
1255 }
1256 int32_t count = rows.Length();
1257
1258 UpdateSubtreeSizes(aParentIndex, count);
1259
1260 // Update parent indexes, but skip added rows.
1261 // They already have correct values.
1262 UpdateParentIndexes(aParentIndex + aIndex, count + 1, count);
1263
1264 return count;
1265 }
1266
1267 int32_t
1268 nsTreeContentView::RemoveRow(int32_t aIndex)
1269 {
1270 Row* row = mRows[aIndex];
1271 int32_t count = row->mSubtreeSize + 1;
1272 int32_t parentIndex = row->mParentIndex;
1273
1274 mRows.RemoveElementsAt(aIndex, count);
1275
1276 UpdateSubtreeSizes(parentIndex, -count);
1277
1278 UpdateParentIndexes(aIndex, 0, -count);
1279
1280 return count;
1281 }
1282
1283 void
1284 nsTreeContentView::ClearRows()
1285 {
1286 mRows.Clear();
1287 mRoot = nullptr;
1288 mBody = nullptr;
1289 // Remove ourselves from mDocument's observers.
1290 if (mDocument) {
1291 mDocument->RemoveObserver(this);
1292 mDocument = nullptr;
1293 }
1294 }
1295
1296 void
1297 nsTreeContentView::OpenContainer(int32_t aIndex)
1298 {
1299 Row* row = mRows[aIndex];
1300 row->SetOpen(true);
1301
1302 int32_t count = EnsureSubtree(aIndex);
1303 if (mBoxObject) {
1304 mBoxObject->InvalidateRow(aIndex);
1305 mBoxObject->RowCountChanged(aIndex + 1, count);
1306 }
1307 }
1308
1309 void
1310 nsTreeContentView::CloseContainer(int32_t aIndex)
1311 {
1312 Row* row = mRows[aIndex];
1313 row->SetOpen(false);
1314
1315 int32_t count = RemoveSubtree(aIndex);
1316 if (mBoxObject) {
1317 mBoxObject->InvalidateRow(aIndex);
1318 mBoxObject->RowCountChanged(aIndex + 1, -count);
1319 }
1320 }
1321
1322 int32_t
1323 nsTreeContentView::FindContent(nsIContent* aContent)
1324 {
1325 for (uint32_t i = 0; i < mRows.Length(); i++) {
1326 if (mRows[i]->mContent == aContent) {
1327 return i;
1328 }
1329 }
1330
1331 return -1;
1332 }
1333
1334 void
1335 nsTreeContentView::UpdateSubtreeSizes(int32_t aParentIndex, int32_t count)
1336 {
1337 while (aParentIndex >= 0) {
1338 Row* row = mRows[aParentIndex];
1339 row->mSubtreeSize += count;
1340 aParentIndex = row->mParentIndex;
1341 }
1342 }
1343
1344 void
1345 nsTreeContentView::UpdateParentIndexes(int32_t aIndex, int32_t aSkip, int32_t aCount)
1346 {
1347 int32_t count = mRows.Length();
1348 for (int32_t i = aIndex + aSkip; i < count; i++) {
1349 Row* row = mRows[i];
1350 if (row->mParentIndex > aIndex) {
1351 row->mParentIndex += aCount;
1352 }
1353 }
1354 }
1355
1356 nsIContent*
1357 nsTreeContentView::GetCell(nsIContent* aContainer, nsITreeColumn* aCol)
1358 {
1359 nsCOMPtr<nsIAtom> colAtom;
1360 int32_t colIndex;
1361 aCol->GetAtom(getter_AddRefs(colAtom));
1362 aCol->GetIndex(&colIndex);
1363
1364 // Traverse through cells, try to find the cell by "ref" attribute or by cell
1365 // index in a row. "ref" attribute has higher priority.
1366 nsIContent* result = nullptr;
1367 int32_t j = 0;
1368 dom::FlattenedChildIterator iter(aContainer);
1369 for (nsIContent* cell = iter.GetNextChild(); cell; cell = iter.GetNextChild()) {
1370 if (cell->Tag() == nsGkAtoms::treecell) {
1371 if (colAtom && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref,
1372 colAtom, eCaseMatters)) {
1373 result = cell;
1374 break;
1375 }
1376 else if (j == colIndex) {
1377 result = cell;
1378 }
1379 j++;
1380 }
1381 }
1382
1383 return result;
1384 }

mercurial