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