Coverage Report

Created: 2018-09-25 14:53

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