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