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 "nsIDOMElement.h"
10 #include "nsIBoxObject.h"
11 #include "nsTreeColumns.h"
12 #include "nsTreeUtils.h"
13 #include "nsStyleContext.h"
14 #include "nsDOMClassInfoID.h"
15 #include "nsContentUtils.h"
16 #include "nsTreeBodyFrame.h"
17 #include "mozilla/dom/Element.h"
18 #include "mozilla/dom/TreeBoxObject.h"
19 #include "mozilla/dom/TreeColumnBinding.h"
20 #include "mozilla/dom/TreeColumnsBinding.h"
21 
22 using namespace mozilla;
23 
24 // Column class that caches all the info about our column.
nsTreeColumn(nsTreeColumns * aColumns,nsIContent * aContent)25 nsTreeColumn::nsTreeColumn(nsTreeColumns* aColumns, nsIContent* aContent)
26     : mContent(aContent), mColumns(aColumns), mPrevious(nullptr) {
27   NS_ASSERTION(aContent && aContent->NodeInfo()->Equals(nsGkAtoms::treecol,
28                                                         kNameSpaceID_XUL),
29                "nsTreeColumn's content must be a <xul:treecol>");
30 
31   Invalidate();
32 }
33 
~nsTreeColumn()34 nsTreeColumn::~nsTreeColumn() {
35   if (mNext) {
36     mNext->SetPrevious(nullptr);
37   }
38 }
39 
40 NS_IMPL_CYCLE_COLLECTION_CLASS(nsTreeColumn)
41 
42 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTreeColumn)
43   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
44   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
45   if (tmp->mNext) {
46     tmp->mNext->SetPrevious(nullptr);
47     NS_IMPL_CYCLE_COLLECTION_UNLINK(mNext)
48   }
49 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
50 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTreeColumn)
51   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
52   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNext)
53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
54 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsTreeColumn)
55 
56 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeColumn)
57 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeColumn)
58 
59 // QueryInterface implementation for nsTreeColumn
60 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumn)
61   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
62   NS_INTERFACE_MAP_ENTRY(nsITreeColumn)
63   NS_INTERFACE_MAP_ENTRY(nsISupports)
64   if (aIID.Equals(NS_GET_IID(nsTreeColumn))) {
65     AddRef();
66     *aInstancePtr = this;
67     return NS_OK;
68   } else
69 NS_INTERFACE_MAP_END
70 
GetFrame()71 nsIFrame* nsTreeColumn::GetFrame() {
72   NS_ENSURE_TRUE(mContent, nullptr);
73 
74   return mContent->GetPrimaryFrame();
75 }
76 
IsLastVisible(nsTreeBodyFrame * aBodyFrame)77 bool nsTreeColumn::IsLastVisible(nsTreeBodyFrame* aBodyFrame) {
78   NS_ASSERTION(GetFrame(), "should have checked for this already");
79 
80   // cyclers are fixed width, don't adjust them
81   if (IsCycler()) return false;
82 
83   // we're certainly not the last visible if we're not visible
84   if (GetFrame()->GetRect().width == 0) return false;
85 
86   // try to find a visible successor
87   for (nsTreeColumn* next = GetNext(); next; next = next->GetNext()) {
88     nsIFrame* frame = next->GetFrame();
89     if (frame && frame->GetRect().width > 0) return false;
90   }
91   return true;
92 }
93 
GetRect(nsTreeBodyFrame * aBodyFrame,nscoord aY,nscoord aHeight,nsRect * aResult)94 nsresult nsTreeColumn::GetRect(nsTreeBodyFrame* aBodyFrame, nscoord aY,
95                                nscoord aHeight, nsRect* aResult) {
96   nsIFrame* frame = GetFrame();
97   if (!frame) {
98     *aResult = nsRect();
99     return NS_ERROR_FAILURE;
100   }
101 
102   bool isRTL =
103       aBodyFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
104   *aResult = frame->GetRect();
105   aResult->y = aY;
106   aResult->height = aHeight;
107   if (isRTL)
108     aResult->x += aBodyFrame->mAdjustWidth;
109   else if (IsLastVisible(aBodyFrame))
110     aResult->width += aBodyFrame->mAdjustWidth;
111   return NS_OK;
112 }
113 
GetXInTwips(nsTreeBodyFrame * aBodyFrame,nscoord * aResult)114 nsresult nsTreeColumn::GetXInTwips(nsTreeBodyFrame* aBodyFrame,
115                                    nscoord* aResult) {
116   nsIFrame* frame = GetFrame();
117   if (!frame) {
118     *aResult = 0;
119     return NS_ERROR_FAILURE;
120   }
121   *aResult = frame->GetRect().x;
122   return NS_OK;
123 }
124 
GetWidthInTwips(nsTreeBodyFrame * aBodyFrame,nscoord * aResult)125 nsresult nsTreeColumn::GetWidthInTwips(nsTreeBodyFrame* aBodyFrame,
126                                        nscoord* aResult) {
127   nsIFrame* frame = GetFrame();
128   if (!frame) {
129     *aResult = 0;
130     return NS_ERROR_FAILURE;
131   }
132   *aResult = frame->GetRect().width;
133   if (IsLastVisible(aBodyFrame)) *aResult += aBodyFrame->mAdjustWidth;
134   return NS_OK;
135 }
136 
137 NS_IMETHODIMP
GetElement(nsIDOMElement ** aElement)138 nsTreeColumn::GetElement(nsIDOMElement** aElement) {
139   if (mContent) {
140     return CallQueryInterface(mContent, aElement);
141   }
142   *aElement = nullptr;
143   return NS_ERROR_FAILURE;
144 }
145 
146 NS_IMETHODIMP
GetColumns(nsITreeColumns ** aColumns)147 nsTreeColumn::GetColumns(nsITreeColumns** aColumns) {
148   NS_IF_ADDREF(*aColumns = mColumns);
149   return NS_OK;
150 }
151 
152 NS_IMETHODIMP
GetX(int32_t * aX)153 nsTreeColumn::GetX(int32_t* aX) {
154   nsIFrame* frame = GetFrame();
155   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
156 
157   *aX = nsPresContext::AppUnitsToIntCSSPixels(frame->GetRect().x);
158   return NS_OK;
159 }
160 
161 NS_IMETHODIMP
GetWidth(int32_t * aWidth)162 nsTreeColumn::GetWidth(int32_t* aWidth) {
163   nsIFrame* frame = GetFrame();
164   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
165 
166   *aWidth = nsPresContext::AppUnitsToIntCSSPixels(frame->GetRect().width);
167   return NS_OK;
168 }
169 
170 NS_IMETHODIMP
GetId(nsAString & aId)171 nsTreeColumn::GetId(nsAString& aId) {
172   aId = GetId();
173   return NS_OK;
174 }
175 
176 NS_IMETHODIMP
GetIdConst(const char16_t ** aIdConst)177 nsTreeColumn::GetIdConst(const char16_t** aIdConst) {
178   *aIdConst = mId.get();
179   return NS_OK;
180 }
181 
182 NS_IMETHODIMP
GetIndex(int32_t * aIndex)183 nsTreeColumn::GetIndex(int32_t* aIndex) {
184   *aIndex = GetIndex();
185   return NS_OK;
186 }
187 
188 NS_IMETHODIMP
GetPrimary(bool * aPrimary)189 nsTreeColumn::GetPrimary(bool* aPrimary) {
190   *aPrimary = IsPrimary();
191   return NS_OK;
192 }
193 
194 NS_IMETHODIMP
GetCycler(bool * aCycler)195 nsTreeColumn::GetCycler(bool* aCycler) {
196   *aCycler = IsCycler();
197   return NS_OK;
198 }
199 
200 NS_IMETHODIMP
GetEditable(bool * aEditable)201 nsTreeColumn::GetEditable(bool* aEditable) {
202   *aEditable = IsEditable();
203   return NS_OK;
204 }
205 
206 NS_IMETHODIMP
GetSelectable(bool * aSelectable)207 nsTreeColumn::GetSelectable(bool* aSelectable) {
208   *aSelectable = IsSelectable();
209   return NS_OK;
210 }
211 
212 NS_IMETHODIMP
GetType(int16_t * aType)213 nsTreeColumn::GetType(int16_t* aType) {
214   *aType = GetType();
215   return NS_OK;
216 }
217 
218 NS_IMETHODIMP
GetNext(nsITreeColumn ** _retval)219 nsTreeColumn::GetNext(nsITreeColumn** _retval) {
220   NS_IF_ADDREF(*_retval = GetNext());
221   return NS_OK;
222 }
223 
224 NS_IMETHODIMP
GetPrevious(nsITreeColumn ** _retval)225 nsTreeColumn::GetPrevious(nsITreeColumn** _retval) {
226   NS_IF_ADDREF(*_retval = GetPrevious());
227   return NS_OK;
228 }
229 
230 NS_IMETHODIMP
Invalidate()231 nsTreeColumn::Invalidate() {
232   nsIFrame* frame = GetFrame();
233   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
234 
235   // Fetch the Id.
236   mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mId);
237 
238   // If we have an Id, cache the Id as an atom.
239   if (!mId.IsEmpty()) {
240     mAtom = NS_Atomize(mId);
241   }
242 
243   // Cache our index.
244   nsTreeUtils::GetColumnIndex(mContent->AsElement(), &mIndex);
245 
246   const nsStyleVisibility* vis = frame->StyleVisibility();
247 
248   // Cache our text alignment policy.
249   const nsStyleText* textStyle = frame->StyleText();
250 
251   mTextAlignment = textStyle->mTextAlign;
252   // START or END alignment sometimes means RIGHT
253   if ((mTextAlignment == NS_STYLE_TEXT_ALIGN_START &&
254        vis->mDirection == NS_STYLE_DIRECTION_RTL) ||
255       (mTextAlignment == NS_STYLE_TEXT_ALIGN_END &&
256        vis->mDirection == NS_STYLE_DIRECTION_LTR)) {
257     mTextAlignment = NS_STYLE_TEXT_ALIGN_RIGHT;
258   } else if (mTextAlignment == NS_STYLE_TEXT_ALIGN_START ||
259              mTextAlignment == NS_STYLE_TEXT_ALIGN_END) {
260     mTextAlignment = NS_STYLE_TEXT_ALIGN_LEFT;
261   }
262 
263   // Figure out if we're the primary column (that has to have indentation
264   // and twisties drawn.
265   mIsPrimary = mContent->AsElement()->AttrValueIs(
266       kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eCaseMatters);
267 
268   // Figure out if we're a cycling column (one that doesn't cause a selection
269   // to happen).
270   mIsCycler = mContent->AsElement()->AttrValueIs(
271       kNameSpaceID_None, nsGkAtoms::cycler, nsGkAtoms::_true, eCaseMatters);
272 
273   mIsEditable = mContent->AsElement()->AttrValueIs(
274       kNameSpaceID_None, nsGkAtoms::editable, nsGkAtoms::_true, eCaseMatters);
275 
276   mIsSelectable = !mContent->AsElement()->AttrValueIs(
277       kNameSpaceID_None, nsGkAtoms::selectable, nsGkAtoms::_false,
278       eCaseMatters);
279 
280   mOverflow = mContent->AsElement()->AttrValueIs(
281       kNameSpaceID_None, nsGkAtoms::overflow, nsGkAtoms::_true, eCaseMatters);
282 
283   // Figure out our column type. Default type is text.
284   mType = nsITreeColumn::TYPE_TEXT;
285   static Element::AttrValuesArray typestrings[] = {
286       &nsGkAtoms::checkbox, &nsGkAtoms::password, nullptr};
287   switch (mContent->AsElement()->FindAttrValueIn(
288       kNameSpaceID_None, nsGkAtoms::type, typestrings, eCaseMatters)) {
289     case 0:
290       mType = nsITreeColumn::TYPE_CHECKBOX;
291       break;
292     case 1:
293       mType = nsITreeColumn::TYPE_PASSWORD;
294       break;
295   }
296 
297   // Fetch the crop style.
298   mCropStyle = 0;
299   static Element::AttrValuesArray cropstrings[] = {
300       &nsGkAtoms::center, &nsGkAtoms::left, &nsGkAtoms::start, nullptr};
301   switch (mContent->AsElement()->FindAttrValueIn(
302       kNameSpaceID_None, nsGkAtoms::crop, cropstrings, eCaseMatters)) {
303     case 0:
304       mCropStyle = 1;
305       break;
306     case 1:
307     case 2:
308       mCropStyle = 2;
309       break;
310   }
311 
312   return NS_OK;
313 }
314 
GetParentObject() const315 nsIContent* nsTreeColumn::GetParentObject() const { return mContent; }
316 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)317 /* virtual */ JSObject* nsTreeColumn::WrapObject(
318     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
319   return dom::TreeColumnBinding::Wrap(aCx, this, aGivenProto);
320 }
321 
GetElement(mozilla::ErrorResult & aRv)322 mozilla::dom::Element* nsTreeColumn::GetElement(mozilla::ErrorResult& aRv) {
323   nsCOMPtr<nsIDOMElement> element;
324   aRv = GetElement(getter_AddRefs(element));
325   if (aRv.Failed()) {
326     return nullptr;
327   }
328   nsCOMPtr<nsINode> node = do_QueryInterface(element);
329   return node->AsElement();
330 }
331 
GetX(mozilla::ErrorResult & aRv)332 int32_t nsTreeColumn::GetX(mozilla::ErrorResult& aRv) {
333   int32_t x;
334   aRv = GetX(&x);
335   return x;
336 }
337 
GetWidth(mozilla::ErrorResult & aRv)338 int32_t nsTreeColumn::GetWidth(mozilla::ErrorResult& aRv) {
339   int32_t width;
340   aRv = GetWidth(&width);
341   return width;
342 }
343 
Invalidate(mozilla::ErrorResult & aRv)344 void nsTreeColumn::Invalidate(mozilla::ErrorResult& aRv) { aRv = Invalidate(); }
345 
nsTreeColumns(nsTreeBodyFrame * aTree)346 nsTreeColumns::nsTreeColumns(nsTreeBodyFrame* aTree) : mTree(aTree) {}
347 
~nsTreeColumns()348 nsTreeColumns::~nsTreeColumns() { nsTreeColumns::InvalidateColumns(); }
349 
350 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsTreeColumns)
351 
352 // QueryInterface implementation for nsTreeColumns
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumns)353 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumns)
354   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
355   NS_INTERFACE_MAP_ENTRY(nsITreeColumns)
356   NS_INTERFACE_MAP_ENTRY(nsISupports)
357 NS_INTERFACE_MAP_END
358 
359 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeColumns)
360 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeColumns)
361 
362 nsIContent* nsTreeColumns::GetParentObject() const {
363   return mTree ? mTree->GetBaseElement() : nullptr;
364 }
365 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)366 /* virtual */ JSObject* nsTreeColumns::WrapObject(
367     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
368   return dom::TreeColumnsBinding::Wrap(aCx, this, aGivenProto);
369 }
370 
GetTree() const371 dom::TreeBoxObject* nsTreeColumns::GetTree() const {
372   return mTree ? static_cast<mozilla::dom::TreeBoxObject*>(
373                      mTree->GetTreeBoxObject())
374                : nullptr;
375 }
376 
377 NS_IMETHODIMP
GetTree(nsITreeBoxObject ** _retval)378 nsTreeColumns::GetTree(nsITreeBoxObject** _retval) {
379   NS_IF_ADDREF(*_retval = GetTree());
380   return NS_OK;
381 }
382 
Count()383 uint32_t nsTreeColumns::Count() {
384   EnsureColumns();
385   uint32_t count = 0;
386   for (nsTreeColumn* currCol = mFirstColumn; currCol;
387        currCol = currCol->GetNext()) {
388     ++count;
389   }
390   return count;
391 }
392 
393 NS_IMETHODIMP
GetCount(int32_t * _retval)394 nsTreeColumns::GetCount(int32_t* _retval) {
395   *_retval = Count();
396   return NS_OK;
397 }
398 
399 NS_IMETHODIMP
GetLength(int32_t * _retval)400 nsTreeColumns::GetLength(int32_t* _retval) {
401   *_retval = Length();
402   return NS_OK;
403 }
404 
405 NS_IMETHODIMP
GetFirstColumn(nsITreeColumn ** _retval)406 nsTreeColumns::GetFirstColumn(nsITreeColumn** _retval) {
407   NS_IF_ADDREF(*_retval = GetFirstColumn());
408   return NS_OK;
409 }
410 
GetLastColumn()411 nsTreeColumn* nsTreeColumns::GetLastColumn() {
412   EnsureColumns();
413   nsTreeColumn* currCol = mFirstColumn;
414   while (currCol) {
415     nsTreeColumn* next = currCol->GetNext();
416     if (!next) {
417       return currCol;
418     }
419     currCol = next;
420   }
421   return nullptr;
422 }
423 
424 NS_IMETHODIMP
GetLastColumn(nsITreeColumn ** _retval)425 nsTreeColumns::GetLastColumn(nsITreeColumn** _retval) {
426   NS_IF_ADDREF(*_retval = GetLastColumn());
427   return NS_OK;
428 }
429 
430 NS_IMETHODIMP
GetPrimaryColumn(nsITreeColumn ** _retval)431 nsTreeColumns::GetPrimaryColumn(nsITreeColumn** _retval) {
432   NS_IF_ADDREF(*_retval = GetPrimaryColumn());
433   return NS_OK;
434 }
435 
GetSortedColumn()436 nsTreeColumn* nsTreeColumns::GetSortedColumn() {
437   EnsureColumns();
438   for (nsTreeColumn* currCol = mFirstColumn; currCol;
439        currCol = currCol->GetNext()) {
440     if (currCol->mContent &&
441         nsContentUtils::HasNonEmptyAttr(currCol->mContent, kNameSpaceID_None,
442                                         nsGkAtoms::sortDirection)) {
443       return currCol;
444     }
445   }
446   return nullptr;
447 }
448 
449 NS_IMETHODIMP
GetSortedColumn(nsITreeColumn ** _retval)450 nsTreeColumns::GetSortedColumn(nsITreeColumn** _retval) {
451   NS_IF_ADDREF(*_retval = GetSortedColumn());
452   return NS_OK;
453 }
454 
GetKeyColumn()455 nsTreeColumn* nsTreeColumns::GetKeyColumn() {
456   EnsureColumns();
457 
458   nsTreeColumn* first = nullptr;
459   nsTreeColumn* primary = nullptr;
460   nsTreeColumn* sorted = nullptr;
461 
462   for (nsTreeColumn* currCol = mFirstColumn; currCol;
463        currCol = currCol->GetNext()) {
464     // Skip hidden columns.
465     if (!currCol->mContent || !currCol->mContent->IsElement() ||
466         currCol->mContent->AsElement()->AttrValueIs(
467             kNameSpaceID_None, nsGkAtoms::hidden, nsGkAtoms::_true,
468             eCaseMatters))
469       continue;
470 
471     // Skip non-text column
472     if (currCol->GetType() != nsITreeColumn::TYPE_TEXT) continue;
473 
474     if (!first) first = currCol;
475 
476     if (nsContentUtils::HasNonEmptyAttr(currCol->mContent, kNameSpaceID_None,
477                                         nsGkAtoms::sortDirection)) {
478       // Use sorted column as the key.
479       sorted = currCol;
480       break;
481     }
482 
483     if (currCol->IsPrimary())
484       if (!primary) primary = currCol;
485   }
486 
487   if (sorted) return sorted;
488   if (primary) return primary;
489   return first;
490 }
491 
492 NS_IMETHODIMP
GetKeyColumn(nsITreeColumn ** _retval)493 nsTreeColumns::GetKeyColumn(nsITreeColumn** _retval) {
494   NS_IF_ADDREF(*_retval = GetKeyColumn());
495   return NS_OK;
496 }
497 
GetColumnFor(dom::Element * aElement)498 nsTreeColumn* nsTreeColumns::GetColumnFor(dom::Element* aElement) {
499   EnsureColumns();
500   for (nsTreeColumn* currCol = mFirstColumn; currCol;
501        currCol = currCol->GetNext()) {
502     if (currCol->mContent == aElement) {
503       return currCol;
504     }
505   }
506   return nullptr;
507 }
508 
509 NS_IMETHODIMP
GetColumnFor(nsIDOMElement * aElement,nsITreeColumn ** _retval)510 nsTreeColumns::GetColumnFor(nsIDOMElement* aElement, nsITreeColumn** _retval) {
511   nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
512   NS_ADDREF(*_retval = GetColumnFor(element));
513   return NS_OK;
514 }
515 
NamedGetter(const nsAString & aId,bool & aFound)516 nsTreeColumn* nsTreeColumns::NamedGetter(const nsAString& aId, bool& aFound) {
517   EnsureColumns();
518   for (nsTreeColumn* currCol = mFirstColumn; currCol;
519        currCol = currCol->GetNext()) {
520     if (currCol->GetId().Equals(aId)) {
521       aFound = true;
522       return currCol;
523     }
524   }
525   aFound = false;
526   return nullptr;
527 }
528 
GetNamedColumn(const nsAString & aId)529 nsTreeColumn* nsTreeColumns::GetNamedColumn(const nsAString& aId) {
530   bool dummy;
531   return NamedGetter(aId, dummy);
532 }
533 
534 NS_IMETHODIMP
GetNamedColumn(const nsAString & aId,nsITreeColumn ** _retval)535 nsTreeColumns::GetNamedColumn(const nsAString& aId, nsITreeColumn** _retval) {
536   NS_IF_ADDREF(*_retval = GetNamedColumn(aId));
537   return NS_OK;
538 }
539 
GetSupportedNames(nsTArray<nsString> & aNames)540 void nsTreeColumns::GetSupportedNames(nsTArray<nsString>& aNames) {
541   for (nsTreeColumn* currCol = mFirstColumn; currCol;
542        currCol = currCol->GetNext()) {
543     aNames.AppendElement(currCol->GetId());
544   }
545 }
546 
IndexedGetter(uint32_t aIndex,bool & aFound)547 nsTreeColumn* nsTreeColumns::IndexedGetter(uint32_t aIndex, bool& aFound) {
548   EnsureColumns();
549   for (nsTreeColumn* currCol = mFirstColumn; currCol;
550        currCol = currCol->GetNext()) {
551     if (currCol->GetIndex() == static_cast<int32_t>(aIndex)) {
552       aFound = true;
553       return currCol;
554     }
555   }
556   aFound = false;
557   return nullptr;
558 }
559 
GetColumnAt(uint32_t aIndex)560 nsTreeColumn* nsTreeColumns::GetColumnAt(uint32_t aIndex) {
561   bool dummy;
562   return IndexedGetter(aIndex, dummy);
563 }
564 
565 NS_IMETHODIMP
GetColumnAt(int32_t aIndex,nsITreeColumn ** _retval)566 nsTreeColumns::GetColumnAt(int32_t aIndex, nsITreeColumn** _retval) {
567   NS_IF_ADDREF(*_retval = GetColumnAt(static_cast<uint32_t>(aIndex)));
568   return NS_OK;
569 }
570 
571 NS_IMETHODIMP
InvalidateColumns()572 nsTreeColumns::InvalidateColumns() {
573   for (nsTreeColumn* currCol = mFirstColumn; currCol;
574        currCol = currCol->GetNext()) {
575     currCol->SetColumns(nullptr);
576   }
577   mFirstColumn = nullptr;
578   return NS_OK;
579 }
580 
581 NS_IMETHODIMP
RestoreNaturalOrder()582 nsTreeColumns::RestoreNaturalOrder() {
583   if (!mTree) return NS_OK;
584 
585   nsIContent* content = mTree->GetBaseElement();
586 
587   // Strong ref, since we'll be setting attributes
588   nsCOMPtr<nsIContent> colsContent =
589       nsTreeUtils::GetImmediateChild(content, nsGkAtoms::treecols);
590   if (!colsContent) return NS_OK;
591 
592   int32_t i = 0;
593   for (nsINode* child = colsContent->GetFirstChild(); child;
594        child = child->GetNextSibling()) {
595     nsAutoString ordinal;
596     ordinal.AppendInt(i++);
597     if (child->IsElement()) {
598       child->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::ordinal,
599                                   ordinal, true);
600     }
601   }
602 
603   nsTreeColumns::InvalidateColumns();
604 
605   if (mTree) {
606     mTree->Invalidate();
607   }
608   return NS_OK;
609 }
610 
GetPrimaryColumn()611 nsTreeColumn* nsTreeColumns::GetPrimaryColumn() {
612   EnsureColumns();
613   for (nsTreeColumn* currCol = mFirstColumn; currCol;
614        currCol = currCol->GetNext()) {
615     if (currCol->IsPrimary()) {
616       return currCol;
617     }
618   }
619   return nullptr;
620 }
621 
EnsureColumns()622 void nsTreeColumns::EnsureColumns() {
623   if (mTree && !mFirstColumn) {
624     nsIContent* treeContent = mTree->GetBaseElement();
625     nsIContent* colsContent =
626         nsTreeUtils::GetDescendantChild(treeContent, nsGkAtoms::treecols);
627     if (!colsContent) return;
628 
629     nsIContent* colContent =
630         nsTreeUtils::GetDescendantChild(colsContent, nsGkAtoms::treecol);
631     if (!colContent) return;
632 
633     nsIFrame* colFrame = colContent->GetPrimaryFrame();
634     if (!colFrame) return;
635 
636     colFrame = colFrame->GetParent();
637     if (!colFrame) return;
638 
639     colFrame = colFrame->PrincipalChildList().FirstChild();
640     if (!colFrame) return;
641 
642     // Now that we have the first visible column,
643     // we can enumerate the columns in visible order
644     nsTreeColumn* currCol = nullptr;
645     while (colFrame) {
646       nsIContent* colContent = colFrame->GetContent();
647 
648       if (colContent->NodeInfo()->Equals(nsGkAtoms::treecol,
649                                          kNameSpaceID_XUL)) {
650         // Create a new column structure.
651         nsTreeColumn* col = new nsTreeColumn(this, colContent);
652         if (!col) return;
653 
654         if (currCol) {
655           currCol->SetNext(col);
656           col->SetPrevious(currCol);
657         } else {
658           mFirstColumn = col;
659         }
660         currCol = col;
661       }
662 
663       colFrame = colFrame->GetNextSibling();
664     }
665   }
666 }
667