Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/xul/XULTreeAccessible.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=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 "XULTreeAccessible.h"
8
9
#include "Accessible-inl.h"
10
#include "DocAccessible-inl.h"
11
#include "nsAccCache.h"
12
#include "nsAccUtils.h"
13
#include "nsCoreUtils.h"
14
#include "nsEventShell.h"
15
#include "DocAccessible.h"
16
#include "Relation.h"
17
#include "Role.h"
18
#include "States.h"
19
#include "XULTreeGridAccessible.h"
20
#include "nsQueryObject.h"
21
22
#include "nsComponentManagerUtils.h"
23
#include "nsIAccessibleRelation.h"
24
#include "nsIAutoCompleteInput.h"
25
#include "nsIAutoCompletePopup.h"
26
#include "nsIBoxObject.h"
27
#include "nsIDOMXULMenuListElement.h"
28
#include "nsIDOMXULMultSelectCntrlEl.h"
29
#include "nsITreeSelection.h"
30
#include "nsIMutableArray.h"
31
#include "nsTreeBodyFrame.h"
32
#include "nsTreeColumns.h"
33
#include "nsTreeUtils.h"
34
35
using namespace mozilla::a11y;
36
37
////////////////////////////////////////////////////////////////////////////////
38
// XULTreeAccessible
39
////////////////////////////////////////////////////////////////////////////////
40
41
XULTreeAccessible::
42
  XULTreeAccessible(nsIContent* aContent, DocAccessible* aDoc,
43
                    nsTreeBodyFrame* aTreeFrame) :
44
  AccessibleWrap(aContent, aDoc),
45
  mAccessibleCache(kDefaultTreeCacheLength)
46
0
{
47
0
  mType = eXULTreeType;
48
0
  mGenericTypes |= eSelect;
49
0
50
0
  nsCOMPtr<nsITreeView> view = aTreeFrame->GetExistingView();
51
0
  mTreeView = view;
52
0
53
0
  mTree = nsCoreUtils::GetTreeBoxObject(aContent);
54
0
  NS_ASSERTION(mTree, "Can't get mTree!\n");
55
0
56
0
  nsIContent* parentContent = mContent->GetParent();
57
0
  if (parentContent) {
58
0
    nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
59
0
      do_QueryInterface(parentContent);
60
0
    if (autoCompletePopupElm)
61
0
      mGenericTypes |= eAutoCompletePopup;
62
0
  }
63
0
}
64
65
XULTreeAccessible::~XULTreeAccessible()
66
0
{
67
0
}
68
69
////////////////////////////////////////////////////////////////////////////////
70
// XULTreeAccessible: nsISupports and cycle collection implementation
71
72
NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeAccessible, Accessible,
73
                                   mTree, mAccessibleCache)
74
75
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeAccessible)
76
0
NS_INTERFACE_MAP_END_INHERITING(Accessible)
77
78
NS_IMPL_ADDREF_INHERITED(XULTreeAccessible, Accessible)
79
NS_IMPL_RELEASE_INHERITED(XULTreeAccessible, Accessible)
80
81
////////////////////////////////////////////////////////////////////////////////
82
// XULTreeAccessible: Accessible implementation
83
84
uint64_t
85
XULTreeAccessible::NativeState() const
86
0
{
87
0
  // Get focus status from base class.
88
0
  uint64_t state = Accessible::NativeState();
89
0
90
0
  // readonly state
91
0
  state |= states::READONLY;
92
0
93
0
  // multiselectable state.
94
0
  if (!mTreeView)
95
0
    return state;
96
0
97
0
  nsCOMPtr<nsITreeSelection> selection;
98
0
  mTreeView->GetSelection(getter_AddRefs(selection));
99
0
  NS_ENSURE_TRUE(selection, state);
100
0
101
0
  bool isSingle = false;
102
0
  nsresult rv = selection->GetSingle(&isSingle);
103
0
  NS_ENSURE_SUCCESS(rv, state);
104
0
105
0
  if (!isSingle)
106
0
    state |= states::MULTISELECTABLE;
107
0
108
0
  return state;
109
0
}
110
111
void
112
XULTreeAccessible::Value(nsString& aValue) const
113
0
{
114
0
  aValue.Truncate();
115
0
  if (!mTreeView)
116
0
    return;
117
0
118
0
  // Return the value is the first selected child.
119
0
  nsCOMPtr<nsITreeSelection> selection;
120
0
  mTreeView->GetSelection(getter_AddRefs(selection));
121
0
  if (!selection)
122
0
    return;
123
0
124
0
  int32_t currentIndex;
125
0
  selection->GetCurrentIndex(&currentIndex);
126
0
  if (currentIndex >= 0) {
127
0
    RefPtr<nsTreeColumn> keyCol;
128
0
129
0
    RefPtr<nsTreeColumns> cols;
130
0
    mTree->GetColumns(getter_AddRefs(cols));
131
0
    if (cols)
132
0
      keyCol = cols->GetKeyColumn();
133
0
134
0
    mTreeView->GetCellText(currentIndex, keyCol, aValue);
135
0
  }
136
0
137
0
}
138
139
////////////////////////////////////////////////////////////////////////////////
140
// XULTreeAccessible: Accessible implementation
141
142
void
143
XULTreeAccessible::Shutdown()
144
0
{
145
0
  if (mDoc && !mDoc->IsDefunct()) {
146
0
    UnbindCacheEntriesFromDocument(mAccessibleCache);
147
0
  }
148
0
149
0
  mTree = nullptr;
150
0
  mTreeView = nullptr;
151
0
152
0
  AccessibleWrap::Shutdown();
153
0
}
154
155
role
156
XULTreeAccessible::NativeRole() const
157
0
{
158
0
  // No primary column means we're in a list. In fact, history and mail turn off
159
0
  // the primary flag when switching to a flat view.
160
0
161
0
  nsIContent* child = nsTreeUtils::GetDescendantChild(mContent, nsGkAtoms::treechildren);
162
0
  NS_ASSERTION(child, "tree without treechildren!");
163
0
  nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame());
164
0
  NS_ASSERTION(treeFrame, "xul tree accessible for tree without a frame!");
165
0
  if (!treeFrame)
166
0
    return roles::LIST;
167
0
168
0
  RefPtr<nsTreeColumns> cols = treeFrame->Columns();
169
0
  nsTreeColumn* primaryCol = cols->GetPrimaryColumn();
170
0
171
0
  return primaryCol ? roles::OUTLINE : roles::LIST;
172
0
}
173
174
////////////////////////////////////////////////////////////////////////////////
175
// XULTreeAccessible: Accessible implementation (DON'T put methods here)
176
177
Accessible*
178
XULTreeAccessible::ChildAtPoint(int32_t aX, int32_t aY,
179
                                EWhichChildAtPoint aWhichChild)
180
0
{
181
0
  nsIFrame *frame = GetFrame();
182
0
  if (!frame)
183
0
    return nullptr;
184
0
185
0
  nsPresContext *presContext = frame->PresContext();
186
0
  nsIPresShell* presShell = presContext->PresShell();
187
0
188
0
  nsIFrame *rootFrame = presShell->GetRootFrame();
189
0
  NS_ENSURE_TRUE(rootFrame, nullptr);
190
0
191
0
  CSSIntRect rootRect = rootFrame->GetScreenRect();
192
0
193
0
  int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.X();
194
0
  int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.Y();
195
0
196
0
  int32_t row = -1;
197
0
  RefPtr<nsTreeColumn> column;
198
0
  nsAutoString childEltUnused;
199
0
  mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column),
200
0
                   childEltUnused);
201
0
202
0
  // If we failed to find tree cell for the given point then it might be
203
0
  // tree columns.
204
0
  if (row == -1 || !column)
205
0
    return AccessibleWrap::ChildAtPoint(aX, aY, aWhichChild);
206
0
207
0
  Accessible* child = GetTreeItemAccessible(row);
208
0
  if (aWhichChild == eDeepestChild && child) {
209
0
    // Look for accessible cell for the found item accessible.
210
0
    RefPtr<XULTreeItemAccessibleBase> treeitem = do_QueryObject(child);
211
0
212
0
    Accessible* cell = treeitem->GetCellAccessible(column);
213
0
    if (cell)
214
0
      child = cell;
215
0
  }
216
0
217
0
  return child;
218
0
}
219
220
////////////////////////////////////////////////////////////////////////////////
221
// XULTreeAccessible: SelectAccessible
222
223
Accessible*
224
XULTreeAccessible::CurrentItem() const
225
0
{
226
0
  if (!mTreeView)
227
0
    return nullptr;
228
0
229
0
  nsCOMPtr<nsITreeSelection> selection;
230
0
  mTreeView->GetSelection(getter_AddRefs(selection));
231
0
  if (selection) {
232
0
    int32_t currentIndex = -1;
233
0
    selection->GetCurrentIndex(&currentIndex);
234
0
    if (currentIndex >= 0)
235
0
      return GetTreeItemAccessible(currentIndex);
236
0
  }
237
0
238
0
  return nullptr;
239
0
}
240
241
void
242
XULTreeAccessible::SetCurrentItem(const Accessible* aItem)
243
0
{
244
0
  NS_ERROR("XULTreeAccessible::SetCurrentItem not implemented");
245
0
}
246
247
void
248
XULTreeAccessible::SelectedItems(nsTArray<Accessible*>* aItems)
249
0
{
250
0
  if (!mTreeView)
251
0
    return;
252
0
253
0
  nsCOMPtr<nsITreeSelection> selection;
254
0
  mTreeView->GetSelection(getter_AddRefs(selection));
255
0
  if (!selection)
256
0
    return;
257
0
258
0
  int32_t rangeCount = 0;
259
0
  selection->GetRangeCount(&rangeCount);
260
0
  for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) {
261
0
    int32_t firstIdx = 0, lastIdx = -1;
262
0
    selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx);
263
0
    for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) {
264
0
      Accessible* item = GetTreeItemAccessible(rowIdx);
265
0
      if (item)
266
0
        aItems->AppendElement(item);
267
0
    }
268
0
  }
269
0
}
270
271
uint32_t
272
XULTreeAccessible::SelectedItemCount()
273
0
{
274
0
  if (!mTreeView)
275
0
    return 0;
276
0
277
0
  nsCOMPtr<nsITreeSelection> selection;
278
0
  mTreeView->GetSelection(getter_AddRefs(selection));
279
0
  if (selection) {
280
0
    int32_t count = 0;
281
0
    selection->GetCount(&count);
282
0
    return count;
283
0
  }
284
0
285
0
  return 0;
286
0
}
287
288
bool
289
XULTreeAccessible::AddItemToSelection(uint32_t aIndex)
290
0
{
291
0
  if (!mTreeView)
292
0
    return false;
293
0
294
0
  nsCOMPtr<nsITreeSelection> selection;
295
0
  mTreeView->GetSelection(getter_AddRefs(selection));
296
0
  if (selection) {
297
0
    bool isSelected = false;
298
0
    selection->IsSelected(aIndex, &isSelected);
299
0
    if (!isSelected)
300
0
      selection->ToggleSelect(aIndex);
301
0
302
0
    return true;
303
0
  }
304
0
  return false;
305
0
}
306
307
bool
308
XULTreeAccessible::RemoveItemFromSelection(uint32_t aIndex)
309
0
{
310
0
  if (!mTreeView)
311
0
    return false;
312
0
313
0
  nsCOMPtr<nsITreeSelection> selection;
314
0
  mTreeView->GetSelection(getter_AddRefs(selection));
315
0
  if (selection) {
316
0
    bool isSelected = false;
317
0
    selection->IsSelected(aIndex, &isSelected);
318
0
    if (isSelected)
319
0
      selection->ToggleSelect(aIndex);
320
0
321
0
    return true;
322
0
  }
323
0
  return false;
324
0
}
325
326
bool
327
XULTreeAccessible::IsItemSelected(uint32_t aIndex)
328
0
{
329
0
  if (!mTreeView)
330
0
    return false;
331
0
332
0
  nsCOMPtr<nsITreeSelection> selection;
333
0
  mTreeView->GetSelection(getter_AddRefs(selection));
334
0
  if (selection) {
335
0
    bool isSelected = false;
336
0
    selection->IsSelected(aIndex, &isSelected);
337
0
    return isSelected;
338
0
  }
339
0
  return false;
340
0
}
341
342
bool
343
XULTreeAccessible::UnselectAll()
344
0
{
345
0
  if (!mTreeView)
346
0
    return false;
347
0
348
0
  nsCOMPtr<nsITreeSelection> selection;
349
0
  mTreeView->GetSelection(getter_AddRefs(selection));
350
0
  if (!selection)
351
0
    return false;
352
0
353
0
  selection->ClearSelection();
354
0
  return true;
355
0
}
356
357
Accessible*
358
XULTreeAccessible::GetSelectedItem(uint32_t aIndex)
359
0
{
360
0
  if (!mTreeView)
361
0
    return nullptr;
362
0
363
0
  nsCOMPtr<nsITreeSelection> selection;
364
0
  mTreeView->GetSelection(getter_AddRefs(selection));
365
0
  if (!selection)
366
0
    return nullptr;
367
0
368
0
  uint32_t selCount = 0;
369
0
  int32_t rangeCount = 0;
370
0
  selection->GetRangeCount(&rangeCount);
371
0
  for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) {
372
0
    int32_t firstIdx = 0, lastIdx = -1;
373
0
    selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx);
374
0
    for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) {
375
0
      if (selCount == aIndex)
376
0
        return GetTreeItemAccessible(rowIdx);
377
0
378
0
      selCount++;
379
0
    }
380
0
  }
381
0
382
0
  return nullptr;
383
0
}
384
385
bool
386
XULTreeAccessible::SelectAll()
387
0
{
388
0
  // see if we are multiple select if so set ourselves as such
389
0
  if (!mTreeView)
390
0
    return false;
391
0
392
0
  nsCOMPtr<nsITreeSelection> selection;
393
0
  mTreeView->GetSelection(getter_AddRefs(selection));
394
0
  if (selection) {
395
0
    bool single = false;
396
0
    selection->GetSingle(&single);
397
0
    if (!single) {
398
0
      selection->SelectAll();
399
0
      return true;
400
0
    }
401
0
  }
402
0
403
0
  return false;
404
0
}
405
406
////////////////////////////////////////////////////////////////////////////////
407
// XULTreeAccessible: Accessible implementation
408
409
Accessible*
410
XULTreeAccessible::GetChildAt(uint32_t aIndex) const
411
0
{
412
0
  uint32_t childCount = Accessible::ChildCount();
413
0
  if (aIndex < childCount)
414
0
    return Accessible::GetChildAt(aIndex);
415
0
416
0
  return GetTreeItemAccessible(aIndex - childCount);
417
0
}
418
419
uint32_t
420
XULTreeAccessible::ChildCount() const
421
0
{
422
0
  // Tree's children count is row count + treecols count.
423
0
  uint32_t childCount = Accessible::ChildCount();
424
0
  if (!mTreeView)
425
0
    return childCount;
426
0
427
0
  int32_t rowCount = 0;
428
0
  mTreeView->GetRowCount(&rowCount);
429
0
  childCount += rowCount;
430
0
431
0
  return childCount;
432
0
}
433
434
Relation
435
XULTreeAccessible::RelationByType(RelationType aType) const
436
0
{
437
0
  if (aType == RelationType::NODE_PARENT_OF) {
438
0
    if (mTreeView)
439
0
      return Relation(new XULTreeItemIterator(this, mTreeView, -1));
440
0
441
0
    return Relation();
442
0
  }
443
0
444
0
  return Accessible::RelationByType(aType);
445
0
}
446
447
////////////////////////////////////////////////////////////////////////////////
448
// XULTreeAccessible: Widgets
449
450
bool
451
XULTreeAccessible::IsWidget() const
452
0
{
453
0
  return true;
454
0
}
455
456
bool
457
XULTreeAccessible::IsActiveWidget() const
458
0
{
459
0
  if (IsAutoCompletePopup()) {
460
0
    nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
461
0
      do_QueryInterface(mContent->GetParent());
462
0
463
0
    if (autoCompletePopupElm) {
464
0
      bool isOpen = false;
465
0
      autoCompletePopupElm->GetPopupOpen(&isOpen);
466
0
      return isOpen;
467
0
    }
468
0
  }
469
0
  return FocusMgr()->HasDOMFocus(mContent);
470
0
}
471
472
bool
473
XULTreeAccessible::AreItemsOperable() const
474
0
{
475
0
  if (IsAutoCompletePopup()) {
476
0
    nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
477
0
      do_QueryInterface(mContent->GetParent());
478
0
479
0
    if (autoCompletePopupElm) {
480
0
      bool isOpen = false;
481
0
      autoCompletePopupElm->GetPopupOpen(&isOpen);
482
0
      return isOpen;
483
0
    }
484
0
  }
485
0
  return true;
486
0
}
487
488
Accessible*
489
XULTreeAccessible::ContainerWidget() const
490
0
{
491
0
  if (IsAutoCompletePopup()) {
492
0
    // This works for XUL autocompletes. It doesn't work for HTML forms
493
0
    // autocomplete because of potential crossprocess calls (when autocomplete
494
0
    // lives in content process while popup lives in chrome process). If that's
495
0
    // a problem then rethink Widgets interface.
496
0
    nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
497
0
      do_QueryInterface(mContent->GetParent());
498
0
    if (menuListElm) {
499
0
      RefPtr<mozilla::dom::Element> inputElm;
500
0
      menuListElm->GetInputField(getter_AddRefs(inputElm));
501
0
      if (inputElm) {
502
0
        Accessible* input = mDoc->GetAccessible(inputElm);
503
0
        return input ? input->ContainerWidget() : nullptr;
504
0
      }
505
0
    }
506
0
  }
507
0
  return nullptr;
508
0
}
509
510
////////////////////////////////////////////////////////////////////////////////
511
// XULTreeAccessible: public implementation
512
513
Accessible*
514
XULTreeAccessible::GetTreeItemAccessible(int32_t aRow) const
515
0
{
516
0
  if (aRow < 0 || IsDefunct() || !mTreeView)
517
0
    return nullptr;
518
0
519
0
  int32_t rowCount = 0;
520
0
  nsresult rv = mTreeView->GetRowCount(&rowCount);
521
0
  if (NS_FAILED(rv) || aRow >= rowCount)
522
0
    return nullptr;
523
0
524
0
  void *key = reinterpret_cast<void*>(intptr_t(aRow));
525
0
  Accessible* cachedTreeItem = mAccessibleCache.GetWeak(key);
526
0
  if (cachedTreeItem)
527
0
    return cachedTreeItem;
528
0
529
0
  RefPtr<Accessible> treeItem = CreateTreeItemAccessible(aRow);
530
0
  if (treeItem) {
531
0
    mAccessibleCache.Put(key, treeItem);
532
0
    Document()->BindToDocument(treeItem, nullptr);
533
0
    return treeItem;
534
0
  }
535
0
536
0
  return nullptr;
537
0
}
538
539
void
540
XULTreeAccessible::InvalidateCache(int32_t aRow, int32_t aCount)
541
0
{
542
0
  if (IsDefunct())
543
0
    return;
544
0
545
0
  if (!mTreeView) {
546
0
    UnbindCacheEntriesFromDocument(mAccessibleCache);
547
0
    return;
548
0
  }
549
0
550
0
  // Do not invalidate the cache if rows have been inserted.
551
0
  if (aCount > 0)
552
0
    return;
553
0
554
0
  DocAccessible* document = Document();
555
0
556
0
  // Fire destroy event for removed tree items and delete them from caches.
557
0
  for (int32_t rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) {
558
0
559
0
    void* key = reinterpret_cast<void*>(intptr_t(rowIdx));
560
0
    Accessible* treeItem = mAccessibleCache.GetWeak(key);
561
0
562
0
    if (treeItem) {
563
0
      RefPtr<AccEvent> event =
564
0
        new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem);
565
0
      nsEventShell::FireEvent(event);
566
0
567
0
      // Unbind from document, shutdown and remove from tree cache.
568
0
      document->UnbindFromDocument(treeItem);
569
0
      mAccessibleCache.Remove(key);
570
0
    }
571
0
  }
572
0
573
0
  // We dealt with removed tree items already however we may keep tree items
574
0
  // having row indexes greater than row count. We should remove these dead tree
575
0
  // items silently from caches.
576
0
  int32_t newRowCount = 0;
577
0
  nsresult rv = mTreeView->GetRowCount(&newRowCount);
578
0
  if (NS_FAILED(rv))
579
0
    return;
580
0
581
0
  int32_t oldRowCount = newRowCount - aCount;
582
0
583
0
  for (int32_t rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) {
584
0
585
0
    void *key = reinterpret_cast<void*>(intptr_t(rowIdx));
586
0
    Accessible* treeItem = mAccessibleCache.GetWeak(key);
587
0
588
0
    if (treeItem) {
589
0
      // Unbind from document, shutdown and remove from tree cache.
590
0
      document->UnbindFromDocument(treeItem);
591
0
      mAccessibleCache.Remove(key);
592
0
    }
593
0
  }
594
0
}
595
596
void
597
XULTreeAccessible::TreeViewInvalidated(int32_t aStartRow, int32_t aEndRow,
598
                                       int32_t aStartCol, int32_t aEndCol)
599
0
{
600
0
  if (IsDefunct())
601
0
    return;
602
0
603
0
  if (!mTreeView) {
604
0
    UnbindCacheEntriesFromDocument(mAccessibleCache);
605
0
    return;
606
0
  }
607
0
608
0
  int32_t endRow = aEndRow;
609
0
610
0
  nsresult rv;
611
0
  if (endRow == -1) {
612
0
    int32_t rowCount = 0;
613
0
    rv = mTreeView->GetRowCount(&rowCount);
614
0
    if (NS_FAILED(rv))
615
0
      return;
616
0
617
0
    endRow = rowCount - 1;
618
0
  }
619
0
620
0
  RefPtr<nsTreeColumns> treeColumns;
621
0
  mTree->GetColumns(getter_AddRefs(treeColumns));
622
0
  if (!treeColumns)
623
0
    return;
624
0
625
0
  int32_t endCol = aEndCol;
626
0
627
0
  if (endCol == -1) {
628
0
    // We need to make sure to cast to int32_t before we do the subtraction, in
629
0
    // case the column count is 0.
630
0
    endCol = static_cast<int32_t>(treeColumns->Count()) - 1;
631
0
  }
632
0
633
0
  for (int32_t rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) {
634
0
635
0
    void *key = reinterpret_cast<void*>(intptr_t(rowIdx));
636
0
    Accessible* accessible = mAccessibleCache.GetWeak(key);
637
0
638
0
    if (accessible) {
639
0
      RefPtr<XULTreeItemAccessibleBase> treeitemAcc = do_QueryObject(accessible);
640
0
      NS_ASSERTION(treeitemAcc, "Wrong accessible at the given key!");
641
0
642
0
      treeitemAcc->RowInvalidated(aStartCol, endCol);
643
0
    }
644
0
  }
645
0
}
646
647
void
648
XULTreeAccessible::TreeViewChanged(nsITreeView* aView)
649
0
{
650
0
  if (IsDefunct())
651
0
    return;
652
0
653
0
  // Fire reorder event on tree accessible on accessible tree (do not fire
654
0
  // show/hide events on tree items because it can be expensive to fire them for
655
0
  // each tree item.
656
0
  RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
657
0
  Document()->FireDelayedEvent(reorderEvent);
658
0
659
0
  // Clear cache.
660
0
  UnbindCacheEntriesFromDocument(mAccessibleCache);
661
0
662
0
  mTreeView = aView;
663
0
  Accessible* item = CurrentItem();
664
0
  if (item) {
665
0
    FocusMgr()->ActiveItemChanged(item, true);
666
0
  }
667
0
}
668
669
////////////////////////////////////////////////////////////////////////////////
670
// XULTreeAccessible: protected implementation
671
672
already_AddRefed<Accessible>
673
XULTreeAccessible::CreateTreeItemAccessible(int32_t aRow) const
674
0
{
675
0
  RefPtr<Accessible> accessible =
676
0
    new XULTreeItemAccessible(mContent, mDoc, const_cast<XULTreeAccessible*>(this),
677
0
                              mTree, mTreeView, aRow);
678
0
679
0
  return accessible.forget();
680
0
}
681
682
////////////////////////////////////////////////////////////////////////////////
683
// XULTreeItemAccessibleBase
684
////////////////////////////////////////////////////////////////////////////////
685
686
XULTreeItemAccessibleBase::
687
  XULTreeItemAccessibleBase(nsIContent* aContent, DocAccessible* aDoc,
688
                            Accessible* aParent, nsITreeBoxObject* aTree,
689
                            nsITreeView* aTreeView, int32_t aRow) :
690
  AccessibleWrap(aContent, aDoc),
691
  mTree(aTree), mTreeView(aTreeView), mRow(aRow)
692
0
{
693
0
  mParent = aParent;
694
0
  mStateFlags |= eSharedNode;
695
0
}
696
697
XULTreeItemAccessibleBase::~XULTreeItemAccessibleBase()
698
0
{
699
0
}
700
701
////////////////////////////////////////////////////////////////////////////////
702
// XULTreeItemAccessibleBase: nsISupports implementation
703
704
NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase, Accessible,
705
                                   mTree)
706
707
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase,
708
                                             Accessible,
709
                                             XULTreeItemAccessibleBase)
710
711
////////////////////////////////////////////////////////////////////////////////
712
// XULTreeItemAccessibleBase: Accessible
713
714
Accessible*
715
XULTreeItemAccessibleBase::FocusedChild()
716
0
{
717
0
  return FocusMgr()->FocusedAccessible() == this ? this : nullptr;
718
0
}
719
720
nsIntRect
721
XULTreeItemAccessibleBase::BoundsInCSSPixels() const
722
0
{
723
0
  // Get x coordinate and width from treechildren element, get y coordinate and
724
0
  // height from tree cell.
725
0
726
0
  nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree);
727
0
  if (!boxObj) {
728
0
    return nsIntRect();
729
0
  }
730
0
731
0
  RefPtr<nsTreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree);
732
0
733
0
  int32_t x = 0, y = 0, width = 0, height = 0;
734
0
  nsresult rv = mTree->GetCoordsForCellItem(mRow, column, EmptyString(),
735
0
                                            &x, &y, &width, &height);
736
0
  if (NS_FAILED(rv)) {
737
0
    return nsIntRect();
738
0
  }
739
0
740
0
  boxObj->GetWidth(&width);
741
0
742
0
  int32_t tcX = 0, tcY = 0;
743
0
  boxObj->GetScreenX(&tcX);
744
0
  boxObj->GetScreenY(&tcY);
745
0
746
0
  x = tcX;
747
0
  y += tcY;
748
0
749
0
  return nsIntRect(x, y, width, height);
750
0
}
751
752
nsRect
753
XULTreeItemAccessibleBase::BoundsInAppUnits() const
754
0
{
755
0
  nsIntRect bounds = BoundsInCSSPixels();
756
0
  nsPresContext* presContext = mDoc->PresContext();
757
0
  return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()),
758
0
                presContext->CSSPixelsToAppUnits(bounds.Y()),
759
0
                presContext->CSSPixelsToAppUnits(bounds.Width()),
760
0
                presContext->CSSPixelsToAppUnits(bounds.Height()));
761
0
}
762
763
void
764
XULTreeItemAccessibleBase::SetSelected(bool aSelect)
765
0
{
766
0
  nsCOMPtr<nsITreeSelection> selection;
767
0
  mTreeView->GetSelection(getter_AddRefs(selection));
768
0
  if (selection) {
769
0
    bool isSelected = false;
770
0
    selection->IsSelected(mRow, &isSelected);
771
0
    if (isSelected != aSelect)
772
0
      selection->ToggleSelect(mRow);
773
0
  }
774
0
}
775
776
void
777
XULTreeItemAccessibleBase::TakeFocus() const
778
0
{
779
0
  nsCOMPtr<nsITreeSelection> selection;
780
0
  mTreeView->GetSelection(getter_AddRefs(selection));
781
0
  if (selection)
782
0
    selection->SetCurrentIndex(mRow);
783
0
784
0
  // focus event will be fired here
785
0
  Accessible::TakeFocus();
786
0
}
787
788
Relation
789
XULTreeItemAccessibleBase::RelationByType(RelationType aType) const
790
0
{
791
0
792
0
  switch (aType) {
793
0
    case RelationType::NODE_CHILD_OF: {
794
0
      int32_t parentIndex = -1;
795
0
      if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex)))
796
0
        return Relation();
797
0
798
0
      if (parentIndex == -1)
799
0
        return Relation(mParent);
800
0
801
0
      XULTreeAccessible* treeAcc = mParent->AsXULTree();
802
0
      return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
803
0
    }
804
0
805
0
    case RelationType::NODE_PARENT_OF: {
806
0
      bool isTrue = false;
807
0
      if (NS_FAILED(mTreeView->IsContainerEmpty(mRow, &isTrue)) || isTrue)
808
0
        return Relation();
809
0
810
0
      if (NS_FAILED(mTreeView->IsContainerOpen(mRow, &isTrue)) || !isTrue)
811
0
        return Relation();
812
0
813
0
      XULTreeAccessible* tree = mParent->AsXULTree();
814
0
      return Relation(new XULTreeItemIterator(tree, mTreeView, mRow));
815
0
    }
816
0
817
0
    default:
818
0
      return Relation();
819
0
  }
820
0
}
821
822
uint8_t
823
XULTreeItemAccessibleBase::ActionCount() const
824
0
{
825
0
  // "activate" action is available for all treeitems, "expand/collapse" action
826
0
  // is avaible for treeitem which is container.
827
0
  return IsExpandable() ? 2 : 1;
828
0
}
829
830
void
831
XULTreeItemAccessibleBase::ActionNameAt(uint8_t aIndex, nsAString& aName)
832
0
{
833
0
  if (aIndex == eAction_Click) {
834
0
    aName.AssignLiteral("activate");
835
0
    return;
836
0
  }
837
0
838
0
  if (aIndex == eAction_Expand && IsExpandable()) {
839
0
    bool isContainerOpen = false;
840
0
    mTreeView->IsContainerOpen(mRow, &isContainerOpen);
841
0
    if (isContainerOpen)
842
0
      aName.AssignLiteral("collapse");
843
0
    else
844
0
      aName.AssignLiteral("expand");
845
0
  }
846
0
}
847
848
bool
849
XULTreeItemAccessibleBase::DoAction(uint8_t aIndex) const
850
0
{
851
0
  if (aIndex != eAction_Click &&
852
0
      (aIndex != eAction_Expand || !IsExpandable()))
853
0
    return false;
854
0
855
0
  DoCommand(nullptr, aIndex);
856
0
  return true;
857
0
}
858
859
////////////////////////////////////////////////////////////////////////////////
860
// XULTreeItemAccessibleBase: Accessible implementation
861
862
void
863
XULTreeItemAccessibleBase::Shutdown()
864
0
{
865
0
  mTree = nullptr;
866
0
  mTreeView = nullptr;
867
0
  mRow = -1;
868
0
  mParent = nullptr; // null-out to prevent base class's shutdown ops
869
0
870
0
  AccessibleWrap::Shutdown();
871
0
}
872
873
GroupPos
874
XULTreeItemAccessibleBase::GroupPosition()
875
0
{
876
0
  GroupPos groupPos;
877
0
878
0
  int32_t level;
879
0
  nsresult rv = mTreeView->GetLevel(mRow, &level);
880
0
  NS_ENSURE_SUCCESS(rv, groupPos);
881
0
882
0
  int32_t topCount = 1;
883
0
  for (int32_t index = mRow - 1; index >= 0; index--) {
884
0
    int32_t lvl = -1;
885
0
    if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) {
886
0
      if (lvl < level)
887
0
        break;
888
0
889
0
      if (lvl == level)
890
0
        topCount++;
891
0
    }
892
0
  }
893
0
894
0
  int32_t rowCount = 0;
895
0
  rv = mTreeView->GetRowCount(&rowCount);
896
0
  NS_ENSURE_SUCCESS(rv, groupPos);
897
0
898
0
  int32_t bottomCount = 0;
899
0
  for (int32_t index = mRow + 1; index < rowCount; index++) {
900
0
    int32_t lvl = -1;
901
0
    if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) {
902
0
      if (lvl < level)
903
0
        break;
904
0
905
0
      if (lvl == level)
906
0
        bottomCount++;
907
0
    }
908
0
  }
909
0
910
0
  groupPos.level = level + 1;
911
0
  groupPos.setSize = topCount + bottomCount;
912
0
  groupPos.posInSet = topCount;
913
0
914
0
  return groupPos;
915
0
}
916
917
uint64_t
918
XULTreeItemAccessibleBase::NativeState() const
919
0
{
920
0
921
0
  // focusable and selectable states
922
0
  uint64_t state = NativeInteractiveState();
923
0
924
0
  // expanded/collapsed state
925
0
  if (IsExpandable()) {
926
0
    bool isContainerOpen;
927
0
    mTreeView->IsContainerOpen(mRow, &isContainerOpen);
928
0
    state |= isContainerOpen ? states::EXPANDED : states::COLLAPSED;
929
0
  }
930
0
931
0
  // selected state
932
0
  nsCOMPtr<nsITreeSelection> selection;
933
0
  mTreeView->GetSelection(getter_AddRefs(selection));
934
0
  if (selection) {
935
0
    bool isSelected;
936
0
    selection->IsSelected(mRow, &isSelected);
937
0
    if (isSelected)
938
0
      state |= states::SELECTED;
939
0
  }
940
0
941
0
  // focused state
942
0
  if (FocusMgr()->IsFocused(this))
943
0
    state |= states::FOCUSED;
944
0
945
0
  // invisible state
946
0
  int32_t firstVisibleRow, lastVisibleRow;
947
0
  mTree->GetFirstVisibleRow(&firstVisibleRow);
948
0
  mTree->GetLastVisibleRow(&lastVisibleRow);
949
0
  if (mRow < firstVisibleRow || mRow > lastVisibleRow)
950
0
    state |= states::INVISIBLE;
951
0
952
0
  return state;
953
0
}
954
955
uint64_t
956
XULTreeItemAccessibleBase::NativeInteractiveState() const
957
0
{
958
0
  return states::FOCUSABLE | states::SELECTABLE;
959
0
}
960
961
int32_t
962
XULTreeItemAccessibleBase::IndexInParent() const
963
0
{
964
0
  return mParent ? mParent->ContentChildCount() + mRow : -1;
965
0
}
966
967
////////////////////////////////////////////////////////////////////////////////
968
// XULTreeItemAccessibleBase: Widgets
969
970
Accessible*
971
XULTreeItemAccessibleBase::ContainerWidget() const
972
0
{
973
0
  return mParent;
974
0
}
975
976
////////////////////////////////////////////////////////////////////////////////
977
// XULTreeItemAccessibleBase: Accessible protected methods
978
979
void
980
XULTreeItemAccessibleBase::DispatchClickEvent(nsIContent* aContent,
981
                                              uint32_t aActionIndex) const
982
0
{
983
0
  if (IsDefunct())
984
0
    return;
985
0
986
0
  RefPtr<nsTreeColumns> columns;
987
0
  mTree->GetColumns(getter_AddRefs(columns));
988
0
  if (!columns)
989
0
    return;
990
0
991
0
  // Get column and pseudo element.
992
0
  RefPtr<nsTreeColumn> column;
993
0
  nsAutoString pseudoElm;
994
0
995
0
  if (aActionIndex == eAction_Click) {
996
0
    // Key column is visible and clickable.
997
0
    column = columns->GetKeyColumn();
998
0
  } else {
999
0
    // Primary column contains a twisty we should click on.
1000
0
    column = columns->GetPrimaryColumn();
1001
0
    pseudoElm = NS_LITERAL_STRING("twisty");
1002
0
  }
1003
0
1004
0
  if (column)
1005
0
    nsCoreUtils::DispatchClickEvent(mTree, mRow, column, pseudoElm);
1006
0
}
1007
1008
Accessible*
1009
XULTreeItemAccessibleBase::GetSiblingAtOffset(int32_t aOffset,
1010
                                              nsresult* aError) const
1011
0
{
1012
0
  if (aError)
1013
0
    *aError = NS_OK; // fail peacefully
1014
0
1015
0
  return mParent->GetChildAt(IndexInParent() + aOffset);
1016
0
}
1017
1018
////////////////////////////////////////////////////////////////////////////////
1019
// XULTreeItemAccessibleBase: protected implementation
1020
1021
bool
1022
XULTreeItemAccessibleBase::IsExpandable() const
1023
0
{
1024
0
1025
0
  bool isContainer = false;
1026
0
  mTreeView->IsContainer(mRow, &isContainer);
1027
0
  if (isContainer) {
1028
0
    bool isEmpty = false;
1029
0
    mTreeView->IsContainerEmpty(mRow, &isEmpty);
1030
0
    if (!isEmpty) {
1031
0
      RefPtr<nsTreeColumns> columns;
1032
0
      mTree->GetColumns(getter_AddRefs(columns));
1033
0
      if (columns) {
1034
0
        nsTreeColumn* primaryColumn = columns->GetPrimaryColumn();
1035
0
        if (primaryColumn &&
1036
0
            !nsCoreUtils::IsColumnHidden(primaryColumn))
1037
0
          return true;
1038
0
      }
1039
0
    }
1040
0
  }
1041
0
1042
0
  return false;
1043
0
}
1044
1045
void
1046
XULTreeItemAccessibleBase::GetCellName(nsTreeColumn* aColumn, nsAString& aName) const
1047
0
{
1048
0
1049
0
  mTreeView->GetCellText(mRow, aColumn, aName);
1050
0
1051
0
  // If there is still no name try the cell value:
1052
0
  // This is for graphical cells. We need tree/table view implementors to
1053
0
  // implement FooView::GetCellValue to return a meaningful string for cases
1054
0
  // where there is something shown in the cell (non-text) such as a star icon;
1055
0
  // in which case GetCellValue for that cell would return "starred" or
1056
0
  // "flagged" for example.
1057
0
  if (aName.IsEmpty())
1058
0
    mTreeView->GetCellValue(mRow, aColumn, aName);
1059
0
}
1060
1061
1062
////////////////////////////////////////////////////////////////////////////////
1063
// XULTreeItemAccessible
1064
////////////////////////////////////////////////////////////////////////////////
1065
1066
XULTreeItemAccessible::
1067
  XULTreeItemAccessible(nsIContent* aContent, DocAccessible* aDoc,
1068
                        Accessible* aParent, nsITreeBoxObject* aTree,
1069
                        nsITreeView* aTreeView, int32_t aRow) :
1070
  XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView, aRow)
1071
0
{
1072
0
  mStateFlags |= eNoKidsFromDOM;
1073
0
  mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree);
1074
0
  GetCellName(mColumn, mCachedName);
1075
0
}
1076
1077
XULTreeItemAccessible::~XULTreeItemAccessible()
1078
0
{
1079
0
}
1080
1081
////////////////////////////////////////////////////////////////////////////////
1082
// XULTreeItemAccessible: nsISupports implementation
1083
1084
NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible,
1085
                                   XULTreeItemAccessibleBase,
1086
                                   mColumn)
1087
1088
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeItemAccessible)
1089
0
NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase)
1090
NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
1091
NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
1092
1093
////////////////////////////////////////////////////////////////////////////////
1094
// XULTreeItemAccessible: nsIAccessible implementation
1095
1096
ENameValueFlag
1097
XULTreeItemAccessible::Name(nsString& aName) const
1098
0
{
1099
0
  aName.Truncate();
1100
0
1101
0
  GetCellName(mColumn, aName);
1102
0
  return eNameOK;
1103
0
}
1104
1105
////////////////////////////////////////////////////////////////////////////////
1106
// XULTreeItemAccessible: Accessible implementation
1107
1108
void
1109
XULTreeItemAccessible::Shutdown()
1110
0
{
1111
0
  mColumn = nullptr;
1112
0
  XULTreeItemAccessibleBase::Shutdown();
1113
0
}
1114
1115
role
1116
XULTreeItemAccessible::NativeRole() const
1117
0
{
1118
0
  RefPtr<nsTreeColumns> columns;
1119
0
  mTree->GetColumns(getter_AddRefs(columns));
1120
0
  if (!columns) {
1121
0
    NS_ERROR("No tree columns object in the tree!");
1122
0
    return roles::NOTHING;
1123
0
  }
1124
0
1125
0
  nsTreeColumn* primaryColumn = columns->GetPrimaryColumn();
1126
0
1127
0
  return primaryColumn ? roles::OUTLINEITEM : roles::LISTITEM;
1128
0
}
1129
1130
////////////////////////////////////////////////////////////////////////////////
1131
// XULTreeItemAccessible: XULTreeItemAccessibleBase implementation
1132
1133
void
1134
XULTreeItemAccessible::RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx)
1135
0
{
1136
0
  nsAutoString name;
1137
0
  Name(name);
1138
0
1139
0
  if (name != mCachedName) {
1140
0
    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
1141
0
    mCachedName = name;
1142
0
  }
1143
0
}
1144
1145
1146
////////////////////////////////////////////////////////////////////////////////
1147
//  XULTreeColumAccessible
1148
////////////////////////////////////////////////////////////////////////////////
1149
1150
XULTreeColumAccessible::
1151
  XULTreeColumAccessible(nsIContent* aContent, DocAccessible* aDoc) :
1152
  XULColumAccessible(aContent, aDoc)
1153
0
{
1154
0
}
1155
1156
Accessible*
1157
XULTreeColumAccessible::GetSiblingAtOffset(int32_t aOffset,
1158
                                           nsresult* aError) const
1159
0
{
1160
0
  if (aOffset < 0)
1161
0
    return XULColumAccessible::GetSiblingAtOffset(aOffset, aError);
1162
0
1163
0
  if (aError)
1164
0
    *aError =  NS_OK; // fail peacefully
1165
0
1166
0
  nsCOMPtr<nsITreeBoxObject> tree = nsCoreUtils::GetTreeBoxObject(mContent);
1167
0
  if (tree) {
1168
0
    nsCOMPtr<nsITreeView> treeView;
1169
0
    tree->GetView(getter_AddRefs(treeView));
1170
0
    if (treeView) {
1171
0
      int32_t rowCount = 0;
1172
0
      treeView->GetRowCount(&rowCount);
1173
0
      if (rowCount > 0 && aOffset <= rowCount) {
1174
0
        XULTreeAccessible* treeAcc = Parent()->AsXULTree();
1175
0
1176
0
        if (treeAcc)
1177
0
          return treeAcc->GetTreeItemAccessible(aOffset - 1);
1178
0
      }
1179
0
    }
1180
0
  }
1181
0
1182
0
  return nullptr;
1183
0
}