Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/xul/XULFormControlAccessible.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "XULFormControlAccessible.h"
7
8
#include "Accessible-inl.h"
9
#include "HTMLFormControlAccessible.h"
10
#include "nsAccUtils.h"
11
#include "DocAccessible.h"
12
#include "nsIAccessibleRelation.h"
13
#include "Relation.h"
14
#include "Role.h"
15
#include "States.h"
16
#include "TreeWalker.h"
17
#include "XULMenuAccessible.h"
18
19
#include "nsIDOMXULButtonElement.h"
20
#include "nsIDOMXULMenuListElement.h"
21
#include "nsIDOMXULSelectCntrlItemEl.h"
22
#include "nsIEditor.h"
23
#include "nsIFrame.h"
24
#include "nsITextControlFrame.h"
25
#include "nsMenuPopupFrame.h"
26
#include "nsNameSpaceManager.h"
27
#include "mozilla/dom/Element.h"
28
29
using namespace mozilla::a11y;
30
31
////////////////////////////////////////////////////////////////////////////////
32
// XULButtonAccessible
33
////////////////////////////////////////////////////////////////////////////////
34
35
XULButtonAccessible::
36
  XULButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
37
  AccessibleWrap(aContent, aDoc)
38
0
{
39
0
  if (ContainsMenu()) {
40
0
    mGenericTypes |= eMenuButton;
41
0
  } else {
42
0
    mGenericTypes |= eButton;
43
0
  }
44
0
}
45
46
XULButtonAccessible::~XULButtonAccessible()
47
0
{
48
0
}
49
50
////////////////////////////////////////////////////////////////////////////////
51
// XULButtonAccessible: nsISupports
52
53
////////////////////////////////////////////////////////////////////////////////
54
// XULButtonAccessible: nsIAccessible
55
56
uint8_t
57
XULButtonAccessible::ActionCount() const
58
0
{
59
0
  return 1;
60
0
}
61
62
void
63
XULButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
64
0
{
65
0
  if (aIndex == eAction_Click)
66
0
    aName.AssignLiteral("press");
67
0
}
68
69
bool
70
XULButtonAccessible::DoAction(uint8_t aIndex) const
71
0
{
72
0
  if (aIndex != 0)
73
0
    return false;
74
0
75
0
  DoCommand();
76
0
  return true;
77
0
}
78
79
////////////////////////////////////////////////////////////////////////////////
80
// XULButtonAccessible: Accessible
81
82
role
83
XULButtonAccessible::NativeRole() const
84
0
{
85
0
  return roles::PUSHBUTTON;
86
0
}
87
88
uint64_t
89
XULButtonAccessible::NativeState() const
90
0
{
91
0
  // Possible states: focused, focusable, unavailable(disabled).
92
0
93
0
  // get focus and disable status from base class
94
0
  uint64_t state = Accessible::NativeState();
95
0
96
0
  // Buttons can be checked -- they simply appear pressed in rather than checked
97
0
  nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement(do_QueryInterface(mContent));
98
0
  if (xulButtonElement) {
99
0
    nsAutoString type;
100
0
    xulButtonElement->GetType(type);
101
0
    if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
102
0
      state |= states::CHECKABLE;
103
0
      bool checked = false;
104
0
      xulButtonElement->GetChecked(&checked);
105
0
      if (checked) {
106
0
        state |= states::PRESSED;
107
0
      }
108
0
    }
109
0
  }
110
0
111
0
  if (ContainsMenu())
112
0
    state |= states::HASPOPUP;
113
0
114
0
  if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_default))
115
0
    state |= states::DEFAULT;
116
0
117
0
  return state;
118
0
}
119
120
////////////////////////////////////////////////////////////////////////////////
121
// XULButtonAccessible: Widgets
122
123
bool
124
XULButtonAccessible::IsWidget() const
125
0
{
126
0
  return true;
127
0
}
128
129
bool
130
XULButtonAccessible::IsActiveWidget() const
131
0
{
132
0
  return FocusMgr()->HasDOMFocus(mContent);
133
0
}
134
135
bool
136
XULButtonAccessible::AreItemsOperable() const
137
0
{
138
0
  if (IsMenuButton()) {
139
0
    Accessible* menuPopup = mChildren.SafeElementAt(0, nullptr);
140
0
    if (menuPopup) {
141
0
      nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
142
0
      return menuPopupFrame->IsOpen();
143
0
    }
144
0
  }
145
0
  return false; // no items
146
0
}
147
148
Accessible*
149
XULButtonAccessible::ContainerWidget() const
150
0
{
151
0
  if (IsMenuButton() && mParent && mParent->IsAutoComplete())
152
0
    return mParent;
153
0
  return nullptr;
154
0
}
155
156
bool
157
XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const
158
0
{
159
0
  // In general XUL button has not accessible children. Nevertheless menu
160
0
  // buttons can have popup accessibles (@type="menu" or columnpicker).
161
0
  return aEl->IsXULElement(nsGkAtoms::menupopup) ||
162
0
         aEl->IsXULElement(nsGkAtoms::popup);
163
0
}
164
165
////////////////////////////////////////////////////////////////////////////////
166
// XULButtonAccessible protected
167
168
bool
169
XULButtonAccessible::ContainsMenu() const
170
0
{
171
0
  return mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
172
0
                                            nsGkAtoms::menu, eCaseMatters);
173
0
}
174
175
////////////////////////////////////////////////////////////////////////////////
176
// XULDropmarkerAccessible
177
////////////////////////////////////////////////////////////////////////////////
178
179
XULDropmarkerAccessible::
180
  XULDropmarkerAccessible(nsIContent* aContent, DocAccessible* aDoc) :
181
  LeafAccessible(aContent, aDoc)
182
0
{
183
0
}
184
185
uint8_t
186
XULDropmarkerAccessible::ActionCount() const
187
0
{
188
0
  return 1;
189
0
}
190
191
bool
192
XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const
193
0
{
194
0
  bool isOpen = false;
195
0
196
0
  nsIContent* parent = mContent->GetFlattenedTreeParent();
197
0
198
0
  while (parent) {
199
0
    nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
200
0
      do_QueryInterface(parent);
201
0
    if (parentButtonElement) {
202
0
      parentButtonElement->GetOpen(&isOpen);
203
0
      if (aToggleOpen)
204
0
        parentButtonElement->SetOpen(!isOpen);
205
0
      return isOpen;
206
0
    }
207
0
208
0
    nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
209
0
      do_QueryInterface(parent);
210
0
    if (parentMenuListElement) {
211
0
      parentMenuListElement->GetOpen(&isOpen);
212
0
      if (aToggleOpen)
213
0
        parentMenuListElement->SetOpen(!isOpen);
214
0
      return isOpen;
215
0
    }
216
0
    parent = parent->GetFlattenedTreeParent();
217
0
  }
218
0
219
0
  return isOpen;
220
0
}
221
222
void
223
XULDropmarkerAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
224
0
{
225
0
  aName.Truncate();
226
0
  if (aIndex == eAction_Click) {
227
0
    if (DropmarkerOpen(false))
228
0
      aName.AssignLiteral("close");
229
0
    else
230
0
      aName.AssignLiteral("open");
231
0
  }
232
0
}
233
234
bool
235
XULDropmarkerAccessible::DoAction(uint8_t index) const
236
0
{
237
0
  if (index == eAction_Click) {
238
0
    DropmarkerOpen(true); // Reverse the open attribute
239
0
    return true;
240
0
  }
241
0
  return false;
242
0
}
243
244
role
245
XULDropmarkerAccessible::NativeRole() const
246
0
{
247
0
  return roles::PUSHBUTTON;
248
0
}
249
250
uint64_t
251
XULDropmarkerAccessible::NativeState() const
252
0
{
253
0
  return DropmarkerOpen(false) ? states::PRESSED : 0;
254
0
}
255
256
257
////////////////////////////////////////////////////////////////////////////////
258
// XULGroupboxAccessible
259
////////////////////////////////////////////////////////////////////////////////
260
261
XULGroupboxAccessible::
262
  XULGroupboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
263
  AccessibleWrap(aContent, aDoc)
264
0
{
265
0
}
266
267
role
268
XULGroupboxAccessible::NativeRole() const
269
0
{
270
0
  return roles::GROUPING;
271
0
}
272
273
ENameValueFlag
274
XULGroupboxAccessible::NativeName(nsString& aName) const
275
0
{
276
0
  // XXX: we use the first related accessible only.
277
0
  Accessible* label =
278
0
    RelationByType(RelationType::LABELLED_BY).Next();
279
0
  if (label)
280
0
    return label->Name(aName);
281
0
282
0
  return eNameOK;
283
0
}
284
285
Relation
286
XULGroupboxAccessible::RelationByType(RelationType aType) const
287
0
{
288
0
  Relation rel = AccessibleWrap::RelationByType(aType);
289
0
  if (aType != RelationType::LABELLED_BY)
290
0
    return rel;
291
0
292
0
  // The label for xul:groupbox is generated from xul:label that is
293
0
  // inside the anonymous content of the xul:caption.
294
0
  // The xul:label has an accessible object but the xul:caption does not
295
0
  uint32_t childCount = ChildCount();
296
0
  for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
297
0
    Accessible* childAcc = GetChildAt(childIdx);
298
0
    if (childAcc->Role() == roles::LABEL) {
299
0
      // Ensure that it's our label
300
0
      Relation reverseRel = childAcc->RelationByType(RelationType::LABEL_FOR);
301
0
      Accessible* testGroupbox = nullptr;
302
0
      while ((testGroupbox = reverseRel.Next()))
303
0
        if (testGroupbox == this) {
304
0
          // The <label> points back to this groupbox
305
0
          rel.AppendTarget(childAcc);
306
0
        }
307
0
    }
308
0
  }
309
0
310
0
  return rel;
311
0
}
312
313
////////////////////////////////////////////////////////////////////////////////
314
// XULRadioButtonAccessible
315
////////////////////////////////////////////////////////////////////////////////
316
317
XULRadioButtonAccessible::
318
  XULRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
319
  RadioButtonAccessible(aContent, aDoc)
320
0
{
321
0
}
322
323
uint64_t
324
XULRadioButtonAccessible::NativeState() const
325
0
{
326
0
  uint64_t state = LeafAccessible::NativeState();
327
0
  state |= states::CHECKABLE;
328
0
329
0
  nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
330
0
    do_QueryInterface(mContent);
331
0
  if (radioButton) {
332
0
    bool selected = false;   // Radio buttons can be selected
333
0
    radioButton->GetSelected(&selected);
334
0
    if (selected) {
335
0
      state |= states::CHECKED;
336
0
    }
337
0
  }
338
0
339
0
  return state;
340
0
}
341
342
uint64_t
343
XULRadioButtonAccessible::NativeInteractiveState() const
344
0
{
345
0
  return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
346
0
}
347
348
////////////////////////////////////////////////////////////////////////////////
349
// XULRadioButtonAccessible: Widgets
350
351
Accessible*
352
XULRadioButtonAccessible::ContainerWidget() const
353
0
{
354
0
  return mParent;
355
0
}
356
357
358
////////////////////////////////////////////////////////////////////////////////
359
// XULRadioGroupAccessible
360
////////////////////////////////////////////////////////////////////////////////
361
362
/**
363
  * XUL Radio Group
364
  *   The Radio Group proxies for the Radio Buttons themselves. The Group gets
365
  *   focus whereas the Buttons do not. So we only have an accessible object for
366
  *   this for the purpose of getting the proper RadioButton. Need this here to
367
  *   avoid circular reference problems when navigating the accessible tree and
368
  *   for getting to the radiobuttons.
369
  */
370
371
XULRadioGroupAccessible::
372
  XULRadioGroupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
373
  XULSelectControlAccessible(aContent, aDoc)
374
0
{
375
0
}
376
377
role
378
XULRadioGroupAccessible::NativeRole() const
379
0
{
380
0
  return roles::RADIO_GROUP;
381
0
}
382
383
uint64_t
384
XULRadioGroupAccessible::NativeInteractiveState() const
385
0
{
386
0
  // The radio group is not focusable. Sometimes the focus controller will
387
0
  // report that it is focused. That means that the actual selected radio button
388
0
  // should be considered focused.
389
0
  return NativelyUnavailable() ? states::UNAVAILABLE : 0;
390
0
}
391
392
////////////////////////////////////////////////////////////////////////////////
393
// XULRadioGroupAccessible: Widgets
394
395
bool
396
XULRadioGroupAccessible::IsWidget() const
397
0
{
398
0
  return true;
399
0
}
400
401
bool
402
XULRadioGroupAccessible::IsActiveWidget() const
403
0
{
404
0
  return FocusMgr()->HasDOMFocus(mContent);
405
0
}
406
407
bool
408
XULRadioGroupAccessible::AreItemsOperable() const
409
0
{
410
0
  return true;
411
0
}
412
413
414
////////////////////////////////////////////////////////////////////////////////
415
// XULStatusBarAccessible
416
////////////////////////////////////////////////////////////////////////////////
417
418
XULStatusBarAccessible::
419
  XULStatusBarAccessible(nsIContent* aContent, DocAccessible* aDoc) :
420
  AccessibleWrap(aContent, aDoc)
421
0
{
422
0
}
423
424
role
425
XULStatusBarAccessible::NativeRole() const
426
0
{
427
0
  return roles::STATUSBAR;
428
0
}
429
430
431
////////////////////////////////////////////////////////////////////////////////
432
// XULToolbarButtonAccessible
433
////////////////////////////////////////////////////////////////////////////////
434
435
XULToolbarButtonAccessible::
436
  XULToolbarButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
437
  XULButtonAccessible(aContent, aDoc)
438
0
{
439
0
}
440
441
void
442
XULToolbarButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
443
                                                       int32_t* aSetSize)
444
0
{
445
0
  int32_t setSize = 0;
446
0
  int32_t posInSet = 0;
447
0
448
0
  Accessible* parent = Parent();
449
0
  if (!parent)
450
0
    return;
451
0
452
0
  uint32_t childCount = parent->ChildCount();
453
0
  for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
454
0
    Accessible* child = parent->GetChildAt(childIdx);
455
0
    if (IsSeparator(child)) { // end of a group of buttons
456
0
      if (posInSet)
457
0
        break; // we've found our group, so we're done
458
0
459
0
      setSize = 0; // not our group, so start a new group
460
0
461
0
    } else {
462
0
      setSize++; // another button in the group
463
0
464
0
      if (child == this)
465
0
        posInSet = setSize; // we've found our button
466
0
    }
467
0
  }
468
0
469
0
  *aPosInSet = posInSet;
470
0
  *aSetSize = setSize;
471
0
}
472
473
bool
474
XULToolbarButtonAccessible::IsSeparator(Accessible* aAccessible)
475
0
{
476
0
  nsIContent* content = aAccessible->GetContent();
477
0
  return content && content->IsAnyOfXULElements(nsGkAtoms::toolbarseparator,
478
0
                                                nsGkAtoms::toolbarspacer,
479
0
                                                nsGkAtoms::toolbarspring);
480
0
}
481
482
483
////////////////////////////////////////////////////////////////////////////////
484
// XULToolbarAccessible
485
////////////////////////////////////////////////////////////////////////////////
486
487
XULToolbarAccessible::
488
  XULToolbarAccessible(nsIContent* aContent, DocAccessible* aDoc) :
489
  AccessibleWrap(aContent, aDoc)
490
0
{
491
0
}
492
493
role
494
XULToolbarAccessible::NativeRole() const
495
0
{
496
0
  return roles::TOOLBAR;
497
0
}
498
499
ENameValueFlag
500
XULToolbarAccessible::NativeName(nsString& aName) const
501
0
{
502
0
  if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::toolbarname, aName))
503
0
    aName.CompressWhitespace();
504
0
505
0
  return eNameOK;
506
0
}
507
508
509
////////////////////////////////////////////////////////////////////////////////
510
// XULToolbarAccessible
511
////////////////////////////////////////////////////////////////////////////////
512
513
XULToolbarSeparatorAccessible::
514
  XULToolbarSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc) :
515
  LeafAccessible(aContent, aDoc)
516
0
{
517
0
}
518
519
role
520
XULToolbarSeparatorAccessible::NativeRole() const
521
0
{
522
0
  return roles::SEPARATOR;
523
0
}
524
525
uint64_t
526
XULToolbarSeparatorAccessible::NativeState() const
527
0
{
528
0
  return 0;
529
0
}