Coverage Report

Created: 2018-09-25 14:53

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