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