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/. */
6 #include "HTMLFormControlAccessible.h"
8 #include "CacheConstants.h"
9 #include "DocAccessible-inl.h"
10 #include "LocalAccessible-inl.h"
11 #include "nsAccUtils.h"
12 #include "nsEventShell.h"
13 #include "nsTextEquivUtils.h"
15 #include "mozilla/a11y/Role.h"
17 #include "TextLeafAccessible.h"
19 #include "nsContentList.h"
20 #include "mozilla/dom/HTMLInputElement.h"
21 #include "mozilla/dom/HTMLTextAreaElement.h"
22 #include "mozilla/dom/HTMLFormControlsCollection.h"
23 #include "nsIFormControl.h"
25 #include "mozilla/FloatingPoint.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/TextEditor.h"
29 using namespace mozilla
;
30 using namespace mozilla::dom
;
31 using namespace mozilla::a11y
;
33 ////////////////////////////////////////////////////////////////////////////////
35 ////////////////////////////////////////////////////////////////////////////////
37 role
HTMLFormAccessible::NativeRole() const {
39 const_cast<HTMLFormAccessible
*>(this)->Name(name
);
40 return name
.IsEmpty() ? roles::FORM
: roles::FORM_LANDMARK
;
43 void HTMLFormAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
46 const nsAttrValue
* aOldValue
,
48 HyperTextAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
49 aOldValue
, aOldState
);
50 if (aAttribute
== nsGkAtoms::autocomplete
) {
51 dom::HTMLFormElement
* formEl
= dom::HTMLFormElement::FromNode(mContent
);
53 HTMLFormControlsCollection
* controls
= formEl
->Elements();
54 uint32_t length
= controls
->Length();
55 for (uint32_t i
= 0; i
< length
; i
++) {
56 if (LocalAccessible
* acc
= mDoc
->GetAccessible(controls
->Item(i
))) {
57 if (acc
->IsTextField() && !acc
->IsPassword()) {
58 if (!acc
->Elm()->HasAttr(nsGkAtoms::list_
) &&
59 !acc
->Elm()->AttrValueIs(kNameSpaceID_None
,
60 nsGkAtoms::autocomplete
, nsGkAtoms::OFF
,
62 RefPtr
<AccEvent
> stateChangeEvent
=
63 new AccStateChangeEvent(acc
, states::SUPPORTS_AUTOCOMPLETION
);
64 mDoc
->FireDelayedEvent(stateChangeEvent
);
72 ////////////////////////////////////////////////////////////////////////////////
73 // HTMLRadioButtonAccessible
74 ////////////////////////////////////////////////////////////////////////////////
76 uint64_t HTMLRadioButtonAccessible::NativeState() const {
77 uint64_t state
= AccessibleWrap::NativeState();
79 state
|= states::CHECKABLE
;
81 HTMLInputElement
* input
= HTMLInputElement::FromNode(mContent
);
82 if (input
&& input
->Checked()) state
|= states::CHECKED
;
87 void HTMLRadioButtonAccessible::GetPositionAndSetSize(int32_t* aPosInSet
,
89 Unused
<< ComputeGroupAttributes(aPosInSet
, aSetSize
);
92 void HTMLRadioButtonAccessible::DOMAttributeChanged(
93 int32_t aNameSpaceID
, nsAtom
* aAttribute
, int32_t aModType
,
94 const nsAttrValue
* aOldValue
, uint64_t aOldState
) {
95 if (aAttribute
== nsGkAtoms::name
) {
96 // If our name changed, it's possible our MEMBER_OF relation
97 // also changed. Push a cache update for Relations.
98 mDoc
->QueueCacheUpdate(this, CacheDomain::Relations
);
100 // Otherwise, handle this attribute change the way our parent
101 // class wants us to handle it.
102 RadioButtonAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
,
103 aModType
, aOldValue
, aOldState
);
107 Relation
HTMLRadioButtonAccessible::ComputeGroupAttributes(
108 int32_t* aPosInSet
, int32_t* aSetSize
) const {
109 Relation rel
= Relation();
110 int32_t namespaceId
= mContent
->NodeInfo()->NamespaceID();
111 nsAutoString tagName
;
112 mContent
->NodeInfo()->GetName(tagName
);
115 mContent
->AsElement()->GetAttr(nsGkAtoms::type
, type
);
117 mContent
->AsElement()->GetAttr(nsGkAtoms::name
, name
);
119 RefPtr
<nsContentList
> inputElms
;
121 nsCOMPtr
<nsIFormControl
> formControlNode(do_QueryInterface(mContent
));
122 if (dom::Element
* formElm
= formControlNode
->GetForm()) {
123 inputElms
= NS_GetContentList(formElm
, namespaceId
, tagName
);
125 inputElms
= NS_GetContentList(mContent
->OwnerDoc(), namespaceId
, tagName
);
127 NS_ENSURE_TRUE(inputElms
, rel
);
129 uint32_t inputCount
= inputElms
->Length(false);
131 // Compute posinset and setsize.
135 for (uint32_t index
= 0; index
< inputCount
; index
++) {
136 nsIContent
* inputElm
= inputElms
->Item(index
, false);
137 if (inputElm
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
138 type
, eCaseMatters
) &&
139 inputElm
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
,
140 name
, eCaseMatters
) &&
141 mDoc
->HasAccessible(inputElm
)) {
143 rel
.AppendTarget(mDoc
->GetAccessible(inputElm
));
144 if (inputElm
== mContent
) indexOf
= count
;
148 *aPosInSet
= indexOf
;
153 Relation
HTMLRadioButtonAccessible::RelationByType(RelationType aType
) const {
154 if (aType
== RelationType::MEMBER_OF
) {
155 int32_t unusedPos
, unusedSetSize
;
156 return ComputeGroupAttributes(&unusedPos
, &unusedSetSize
);
159 return LocalAccessible::RelationByType(aType
);
162 ////////////////////////////////////////////////////////////////////////////////
163 // HTMLButtonAccessible
164 ////////////////////////////////////////////////////////////////////////////////
166 HTMLButtonAccessible::HTMLButtonAccessible(nsIContent
* aContent
,
168 : HyperTextAccessible(aContent
, aDoc
) {
169 mGenericTypes
|= eButton
;
172 bool HTMLButtonAccessible::HasPrimaryAction() const { return true; }
174 void HTMLButtonAccessible::ActionNameAt(uint8_t aIndex
, nsAString
& aName
) {
175 if (aIndex
== eAction_Click
) aName
.AssignLiteral("press");
178 uint64_t HTMLButtonAccessible::NativeState() const {
179 uint64_t state
= HyperTextAccessible::NativeState();
181 dom::Element
* elm
= Elm();
182 if (auto* popover
= elm
->GetEffectivePopoverTargetElement()) {
183 if (popover
->IsPopoverOpen()) {
184 state
|= states::EXPANDED
;
186 state
|= states::COLLAPSED
;
190 ElementState elmState
= mContent
->AsElement()->State();
191 if (elmState
.HasState(ElementState::DEFAULT
)) state
|= states::DEFAULT
;
196 role
HTMLButtonAccessible::NativeRole() const { return roles::PUSHBUTTON
; }
198 ENameValueFlag
HTMLButtonAccessible::NativeName(nsString
& aName
) const {
199 // No need to check @value attribute for buttons since this attribute results
200 // in native anonymous text node and the name is calculated from subtree.
201 // The same magic works for @alt and @value attributes in case of type="image"
202 // element that has no valid @src (note if input@type="image" has an image
203 // then neither @alt nor @value attributes are used to generate a visual label
204 // and thus we need to obtain the accessible name directly from attribute
205 // value). Also the same algorithm works in case of default labels for
206 // type="submit"/"reset"/"image" elements.
208 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
209 if (!aName
.IsEmpty() || !mContent
->IsHTMLElement(nsGkAtoms::input
) ||
210 !mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
211 nsGkAtoms::image
, eCaseMatters
)) {
215 if (!mContent
->AsElement()->GetAttr(nsGkAtoms::alt
, aName
)) {
216 mContent
->AsElement()->GetAttr(nsGkAtoms::value
, aName
);
219 aName
.CompressWhitespace();
223 void HTMLButtonAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
226 const nsAttrValue
* aOldValue
,
227 uint64_t aOldState
) {
228 HyperTextAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
229 aOldValue
, aOldState
);
231 if (aAttribute
== nsGkAtoms::value
) {
232 dom::Element
* elm
= Elm();
233 if (elm
->IsHTMLElement(nsGkAtoms::input
) ||
234 (elm
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
, nsGkAtoms::image
,
236 !elm
->HasAttr(nsGkAtoms::alt
))) {
237 if (!nsAccUtils::HasARIAAttr(elm
, nsGkAtoms::aria_labelledby
) &&
238 !nsAccUtils::HasARIAAttr(elm
, nsGkAtoms::aria_label
)) {
239 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE
, this);
245 ////////////////////////////////////////////////////////////////////////////////
246 // HTMLButtonAccessible: Widgets
248 bool HTMLButtonAccessible::IsWidget() const { return true; }
250 ////////////////////////////////////////////////////////////////////////////////
251 // HTMLTextFieldAccessible
252 ////////////////////////////////////////////////////////////////////////////////
254 HTMLTextFieldAccessible::HTMLTextFieldAccessible(nsIContent
* aContent
,
256 : HyperTextAccessible(aContent
, aDoc
) {
257 mType
= mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
258 nsGkAtoms::password
, eIgnoreCase
)
259 ? eHTMLTextPasswordFieldType
260 : eHTMLTextFieldType
;
263 role
HTMLTextFieldAccessible::NativeRole() const {
264 if (mType
== eHTMLTextPasswordFieldType
) {
265 return roles::PASSWORD_TEXT
;
267 if (mContent
->AsElement()->HasAttr(nsGkAtoms::list_
)) {
268 return roles::EDITCOMBOBOX
;
273 already_AddRefed
<AccAttributes
> HTMLTextFieldAccessible::NativeAttributes() {
274 RefPtr
<AccAttributes
> attributes
= HyperTextAccessible::NativeAttributes();
276 // Expose type for text input elements as it gives some useful context,
277 // especially for mobile.
278 if (const nsAttrValue
* attr
=
279 mContent
->AsElement()->GetParsedAttr(nsGkAtoms::type
)) {
280 RefPtr
<nsAtom
> inputType
= attr
->GetAsAtom();
282 if (!ARIARoleMap() && inputType
== nsGkAtoms::search
) {
283 attributes
->SetAttribute(nsGkAtoms::xmlroles
, nsGkAtoms::searchbox
);
285 attributes
->SetAttribute(nsGkAtoms::textInputType
, inputType
);
288 // If this element has the placeholder attribute set,
289 // and if that is not identical to the name, expose it as an object attribute.
290 nsString placeholderText
;
291 if (mContent
->AsElement()->GetAttr(nsGkAtoms::placeholder
, placeholderText
)) {
293 const_cast<HTMLTextFieldAccessible
*>(this)->Name(name
);
294 if (!name
.Equals(placeholderText
)) {
295 attributes
->SetAttribute(nsGkAtoms::placeholder
,
296 std::move(placeholderText
));
300 return attributes
.forget();
303 ENameValueFlag
HTMLTextFieldAccessible::NativeName(nsString
& aName
) const {
304 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
305 if (!aName
.IsEmpty()) return nameFlag
;
307 if (!aName
.IsEmpty()) return eNameOK
;
309 // text inputs and textareas might have useful placeholder text
310 mContent
->AsElement()->GetAttr(nsGkAtoms::placeholder
, aName
);
314 void HTMLTextFieldAccessible::Value(nsString
& aValue
) const {
316 if (NativeState() & states::PROTECTED
) { // Don't return password text!
320 HTMLTextAreaElement
* textArea
= HTMLTextAreaElement::FromNode(mContent
);
322 textArea
->GetValue(aValue
);
326 HTMLInputElement
* input
= HTMLInputElement::FromNode(mContent
);
328 // Pass NonSystem as the caller type, to be safe. We don't expect to have a
330 input
->GetValue(aValue
, CallerType::NonSystem
);
334 bool HTMLTextFieldAccessible::AttributeChangesState(nsAtom
* aAttribute
) {
335 if (aAttribute
== nsGkAtoms::readonly
|| aAttribute
== nsGkAtoms::list_
||
336 aAttribute
== nsGkAtoms::autocomplete
) {
340 return LocalAccessible::AttributeChangesState(aAttribute
);
343 void HTMLTextFieldAccessible::ApplyARIAState(uint64_t* aState
) const {
344 HyperTextAccessible::ApplyARIAState(aState
);
345 aria::MapToState(aria::eARIAAutoComplete
, mContent
->AsElement(), aState
);
348 uint64_t HTMLTextFieldAccessible::NativeState() const {
349 uint64_t state
= HyperTextAccessible::NativeState();
351 // Text fields are always editable, even if they are also read only or
353 state
|= states::EDITABLE
;
355 // can be focusable, focused, protected. readonly, unavailable, selected
356 if (mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
357 nsGkAtoms::password
, eIgnoreCase
)) {
358 state
|= states::PROTECTED
;
361 if (mContent
->AsElement()->HasAttr(nsGkAtoms::readonly
)) {
362 state
|= states::READONLY
;
365 // Is it an <input> or a <textarea> ?
366 HTMLInputElement
* input
= HTMLInputElement::FromNode(mContent
);
367 state
|= input
&& input
->IsSingleLineTextControl() ? states::SINGLE_LINE
368 : states::MULTI_LINE
;
370 if (state
& (states::PROTECTED
| states::MULTI_LINE
| states::READONLY
|
371 states::UNAVAILABLE
)) {
375 // Expose autocomplete state if it has associated autocomplete list.
376 if (mContent
->AsElement()->HasAttr(nsGkAtoms::list_
)) {
377 return state
| states::SUPPORTS_AUTOCOMPLETION
| states::HASPOPUP
;
380 if (Preferences::GetBool("browser.formfill.enable")) {
381 // Check to see if autocompletion is allowed on this input. We don't expose
382 // it for password fields even though the entire password can be remembered
383 // for a page if the user asks it to be. However, the kind of autocomplete
384 // we're talking here is based on what the user types, where a popup of
385 // possible choices comes up.
386 nsAutoString autocomplete
;
387 mContent
->AsElement()->GetAttr(nsGkAtoms::autocomplete
, autocomplete
);
389 if (!autocomplete
.LowerCaseEqualsLiteral("off")) {
390 Element
* formElement
= input
->GetForm();
392 formElement
->GetAttr(nsGkAtoms::autocomplete
, autocomplete
);
395 if (!formElement
|| !autocomplete
.LowerCaseEqualsLiteral("off")) {
396 state
|= states::SUPPORTS_AUTOCOMPLETION
;
404 bool HTMLTextFieldAccessible::HasPrimaryAction() const { return true; }
406 void HTMLTextFieldAccessible::ActionNameAt(uint8_t aIndex
, nsAString
& aName
) {
407 if (aIndex
== eAction_Click
) aName
.AssignLiteral("activate");
410 bool HTMLTextFieldAccessible::DoAction(uint8_t aIndex
) const {
411 if (aIndex
!= 0) return false;
413 if (FocusMgr()->IsFocused(this)) {
414 // This already has focus, so TakeFocus()will do nothing. However, the user
415 // might be activating this element because they dismissed a touch keyboard
416 // and want to bring it back.
424 already_AddRefed
<EditorBase
> HTMLTextFieldAccessible::GetEditor() const {
425 RefPtr
<TextControlElement
> textControlElement
=
426 TextControlElement::FromNodeOrNull(mContent
);
427 if (!textControlElement
) {
430 RefPtr
<TextEditor
> textEditor
= textControlElement
->GetTextEditor();
431 return textEditor
.forget();
434 void HTMLTextFieldAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
437 const nsAttrValue
* aOldValue
,
438 uint64_t aOldState
) {
439 if (aAttribute
== nsGkAtoms::placeholder
) {
440 mDoc
->QueueCacheUpdate(this, CacheDomain::NameAndDescription
);
443 HyperTextAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
444 aOldValue
, aOldState
);
447 ////////////////////////////////////////////////////////////////////////////////
448 // HTMLTextFieldAccessible: Widgets
450 bool HTMLTextFieldAccessible::IsWidget() const { return true; }
452 LocalAccessible
* HTMLTextFieldAccessible::ContainerWidget() const {
456 ////////////////////////////////////////////////////////////////////////////////
457 // HTMLFileInputAccessible
458 ////////////////////////////////////////////////////////////////////////////////
460 HTMLFileInputAccessible::HTMLFileInputAccessible(nsIContent
* aContent
,
462 : HyperTextAccessible(aContent
, aDoc
) {
463 mType
= eHTMLFileInputType
;
464 mGenericTypes
|= eButton
;
467 role
HTMLFileInputAccessible::NativeRole() const { return roles::PUSHBUTTON
; }
469 bool HTMLFileInputAccessible::IsAcceptableChild(nsIContent
* aEl
) const {
470 // File inputs are rendered using native anonymous children. However, we
471 // want to expose this as a button Accessible so that clients can pick up the
472 // name and description from the button they activate, rather than a
473 // container. We still expose the text leaf descendants so we can get the
474 // name of the Browse button and the file name.
475 return aEl
->IsText();
478 ENameValueFlag
HTMLFileInputAccessible::Name(nsString
& aName
) const {
479 ENameValueFlag flag
= HyperTextAccessible::Name(aName
);
480 if (flag
== eNameFromSubtree
) {
481 // The author didn't provide a name. We'll compute the name from our subtree
485 // The author provided a name. We do use that, but we also append our
486 // subtree text so the user knows this is a file chooser button and what
487 // file has been chosen.
488 if (aName
.IsEmpty()) {
489 // Name computation is recursing, perhaps due to a wrapping <label>. Don't
490 // append the subtree text. Return " " to prevent
491 // nsTextEquivUtils::AppendFromAccessible walking the subtree itself.
496 // Unfortunately, GetNameFromSubtree doesn't separate the button text from the
497 // file name text. Compute the text ourselves.
498 uint32_t count
= ChildCount();
499 for (uint32_t c
= 0; c
< count
; ++c
) {
500 TextLeafAccessible
* leaf
= LocalChildAt(c
)->AsTextLeaf();
502 if (!aName
.IsEmpty()) {
505 aName
+= leaf
->Text();
510 bool HTMLFileInputAccessible::HasPrimaryAction() const { return true; }
512 void HTMLFileInputAccessible::ActionNameAt(uint8_t aIndex
, nsAString
& aName
) {
514 aName
.AssignLiteral("press");
518 bool HTMLFileInputAccessible::IsWidget() const { return true; }
520 ////////////////////////////////////////////////////////////////////////////////
521 // HTMLSpinnerAccessible
522 ////////////////////////////////////////////////////////////////////////////////
524 role
HTMLSpinnerAccessible::NativeRole() const { return roles::SPINBUTTON
; }
526 void HTMLSpinnerAccessible::Value(nsString
& aValue
) const {
527 HTMLTextFieldAccessible::Value(aValue
);
528 if (!aValue
.IsEmpty()) return;
530 // Pass NonSystem as the caller type, to be safe. We don't expect to have a
532 HTMLInputElement::FromNode(mContent
)->GetValue(aValue
, CallerType::NonSystem
);
535 double HTMLSpinnerAccessible::MaxValue() const {
536 double value
= HTMLTextFieldAccessible::MaxValue();
537 if (!std::isnan(value
)) return value
;
539 return HTMLInputElement::FromNode(mContent
)->GetMaximum().toDouble();
542 double HTMLSpinnerAccessible::MinValue() const {
543 double value
= HTMLTextFieldAccessible::MinValue();
544 if (!std::isnan(value
)) return value
;
546 return HTMLInputElement::FromNode(mContent
)->GetMinimum().toDouble();
549 double HTMLSpinnerAccessible::Step() const {
550 double value
= HTMLTextFieldAccessible::Step();
551 if (!std::isnan(value
)) return value
;
553 return HTMLInputElement::FromNode(mContent
)->GetStep().toDouble();
556 double HTMLSpinnerAccessible::CurValue() const {
557 double value
= HTMLTextFieldAccessible::CurValue();
558 if (!std::isnan(value
)) return value
;
560 return HTMLInputElement::FromNode(mContent
)->GetValueAsDecimal().toDouble();
563 bool HTMLSpinnerAccessible::SetCurValue(double aValue
) {
565 HTMLInputElement::FromNode(mContent
)->SetValueAsNumber(aValue
, er
);
569 ////////////////////////////////////////////////////////////////////////////////
570 // HTMLRangeAccessible
571 ////////////////////////////////////////////////////////////////////////////////
573 role
HTMLRangeAccessible::NativeRole() const { return roles::SLIDER
; }
575 bool HTMLRangeAccessible::IsWidget() const { return true; }
577 void HTMLRangeAccessible::Value(nsString
& aValue
) const {
578 LeafAccessible::Value(aValue
);
579 if (!aValue
.IsEmpty()) return;
581 // Pass NonSystem as the caller type, to be safe. We don't expect to have a
583 HTMLInputElement::FromNode(mContent
)->GetValue(aValue
, CallerType::NonSystem
);
586 double HTMLRangeAccessible::MaxValue() const {
587 double value
= LeafAccessible::MaxValue();
588 if (!std::isnan(value
)) return value
;
590 return HTMLInputElement::FromNode(mContent
)->GetMaximum().toDouble();
593 double HTMLRangeAccessible::MinValue() const {
594 double value
= LeafAccessible::MinValue();
595 if (!std::isnan(value
)) return value
;
597 return HTMLInputElement::FromNode(mContent
)->GetMinimum().toDouble();
600 double HTMLRangeAccessible::Step() const {
601 double value
= LeafAccessible::Step();
602 if (!std::isnan(value
)) return value
;
604 return HTMLInputElement::FromNode(mContent
)->GetStep().toDouble();
607 double HTMLRangeAccessible::CurValue() const {
608 double value
= LeafAccessible::CurValue();
609 if (!std::isnan(value
)) return value
;
611 return HTMLInputElement::FromNode(mContent
)->GetValueAsDecimal().toDouble();
614 bool HTMLRangeAccessible::SetCurValue(double aValue
) {
615 nsAutoString strValue
;
616 strValue
.AppendFloat(aValue
);
617 HTMLInputElement::FromNode(mContent
)->SetUserInput(
618 strValue
, *nsContentUtils::GetSystemPrincipal());
622 ////////////////////////////////////////////////////////////////////////////////
623 // HTMLGroupboxAccessible
624 ////////////////////////////////////////////////////////////////////////////////
626 HTMLGroupboxAccessible::HTMLGroupboxAccessible(nsIContent
* aContent
,
628 : HyperTextAccessible(aContent
, aDoc
) {}
630 role
HTMLGroupboxAccessible::NativeRole() const { return roles::GROUPING
; }
632 nsIContent
* HTMLGroupboxAccessible::GetLegend() const {
633 for (nsIContent
* legendContent
= mContent
->GetFirstChild(); legendContent
;
634 legendContent
= legendContent
->GetNextSibling()) {
635 if (legendContent
->NodeInfo()->Equals(nsGkAtoms::legend
,
636 mContent
->GetNameSpaceID())) {
637 // Either XHTML namespace or no namespace
638 return legendContent
;
645 ENameValueFlag
HTMLGroupboxAccessible::NativeName(nsString
& aName
) const {
646 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
647 if (!aName
.IsEmpty()) return nameFlag
;
649 nsIContent
* legendContent
= GetLegend();
651 nsTextEquivUtils::AppendTextEquivFromContent(this, legendContent
, &aName
);
654 aName
.CompressWhitespace();
658 Relation
HTMLGroupboxAccessible::RelationByType(RelationType aType
) const {
659 Relation rel
= HyperTextAccessible::RelationByType(aType
);
660 // No override for label, so use <legend> for this <fieldset>
661 if (aType
== RelationType::LABELLED_BY
) rel
.AppendTarget(mDoc
, GetLegend());
666 ////////////////////////////////////////////////////////////////////////////////
667 // HTMLLegendAccessible
668 ////////////////////////////////////////////////////////////////////////////////
670 HTMLLegendAccessible::HTMLLegendAccessible(nsIContent
* aContent
,
672 : HyperTextAccessible(aContent
, aDoc
) {}
674 Relation
HTMLLegendAccessible::RelationByType(RelationType aType
) const {
675 Relation rel
= HyperTextAccessible::RelationByType(aType
);
676 if (aType
!= RelationType::LABEL_FOR
) return rel
;
678 LocalAccessible
* groupbox
= LocalParent();
679 if (groupbox
&& groupbox
->Role() == roles::GROUPING
) {
680 rel
.AppendTarget(groupbox
);
686 ////////////////////////////////////////////////////////////////////////////////
687 // HTMLFigureAccessible
688 ////////////////////////////////////////////////////////////////////////////////
690 HTMLFigureAccessible::HTMLFigureAccessible(nsIContent
* aContent
,
692 : HyperTextAccessible(aContent
, aDoc
) {}
694 ENameValueFlag
HTMLFigureAccessible::NativeName(nsString
& aName
) const {
695 ENameValueFlag nameFlag
= HyperTextAccessible::NativeName(aName
);
696 if (!aName
.IsEmpty()) return nameFlag
;
698 nsIContent
* captionContent
= Caption();
699 if (captionContent
) {
700 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent
, &aName
);
703 aName
.CompressWhitespace();
707 Relation
HTMLFigureAccessible::RelationByType(RelationType aType
) const {
708 Relation rel
= HyperTextAccessible::RelationByType(aType
);
709 if (aType
== RelationType::LABELLED_BY
) rel
.AppendTarget(mDoc
, Caption());
714 nsIContent
* HTMLFigureAccessible::Caption() const {
715 for (nsIContent
* childContent
= mContent
->GetFirstChild(); childContent
;
716 childContent
= childContent
->GetNextSibling()) {
717 if (childContent
->NodeInfo()->Equals(nsGkAtoms::figcaption
,
718 mContent
->GetNameSpaceID())) {
726 ////////////////////////////////////////////////////////////////////////////////
727 // HTMLFigcaptionAccessible
728 ////////////////////////////////////////////////////////////////////////////////
730 HTMLFigcaptionAccessible::HTMLFigcaptionAccessible(nsIContent
* aContent
,
732 : HyperTextAccessible(aContent
, aDoc
) {}
734 Relation
HTMLFigcaptionAccessible::RelationByType(RelationType aType
) const {
735 Relation rel
= HyperTextAccessible::RelationByType(aType
);
736 if (aType
!= RelationType::LABEL_FOR
) return rel
;
738 LocalAccessible
* figure
= LocalParent();
739 if (figure
&& figure
->GetContent()->NodeInfo()->Equals(
740 nsGkAtoms::figure
, mContent
->GetNameSpaceID())) {
741 rel
.AppendTarget(figure
);
747 ////////////////////////////////////////////////////////////////////////////////
748 // HTMLProgressAccessible
749 ////////////////////////////////////////////////////////////////////////////////
751 role
HTMLProgressAccessible::NativeRole() const { return roles::PROGRESSBAR
; }
753 uint64_t HTMLProgressAccessible::NativeState() const {
754 uint64_t state
= LeafAccessible::NativeState();
756 // An undetermined progressbar (i.e. without a value) has a mixed state.
757 nsAutoString attrValue
;
758 mContent
->AsElement()->GetAttr(nsGkAtoms::value
, attrValue
);
759 if (attrValue
.IsEmpty()) {
760 state
|= states::MIXED
;
766 bool HTMLProgressAccessible::IsWidget() const { return true; }
768 void HTMLProgressAccessible::Value(nsString
& aValue
) const {
769 LeafAccessible::Value(aValue
);
770 if (!aValue
.IsEmpty()) {
774 double maxValue
= MaxValue();
775 if (std::isnan(maxValue
) || maxValue
== 0) {
779 double curValue
= CurValue();
780 if (std::isnan(curValue
)) {
784 // Treat the current value bigger than maximum as 100%.
785 double percentValue
=
786 (curValue
< maxValue
) ? (curValue
/ maxValue
) * 100 : 100;
788 aValue
.AppendFloat(percentValue
);
792 double HTMLProgressAccessible::MaxValue() const {
793 double value
= LeafAccessible::MaxValue();
794 if (!std::isnan(value
)) {
798 nsAutoString strValue
;
799 if (mContent
->AsElement()->GetAttr(nsGkAtoms::max
, strValue
)) {
800 nsresult result
= NS_OK
;
801 value
= strValue
.ToDouble(&result
);
802 if (NS_SUCCEEDED(result
)) {
810 double HTMLProgressAccessible::MinValue() const {
811 double value
= LeafAccessible::MinValue();
812 return std::isnan(value
) ? 0 : value
;
815 double HTMLProgressAccessible::Step() const {
816 double value
= LeafAccessible::Step();
817 return std::isnan(value
) ? 0 : value
;
820 double HTMLProgressAccessible::CurValue() const {
821 double value
= LeafAccessible::CurValue();
822 if (!std::isnan(value
)) {
826 nsAutoString attrValue
;
827 if (!mContent
->AsElement()->GetAttr(nsGkAtoms::value
, attrValue
)) {
828 return UnspecifiedNaN
<double>();
831 nsresult error
= NS_OK
;
832 value
= attrValue
.ToDouble(&error
);
833 return NS_FAILED(error
) ? UnspecifiedNaN
<double>() : value
;
836 bool HTMLProgressAccessible::SetCurValue(double aValue
) {
837 return false; // progress meters are readonly.
840 void HTMLProgressAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
843 const nsAttrValue
* aOldValue
,
844 uint64_t aOldState
) {
845 LeafAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
846 aOldValue
, aOldState
);
848 if (aAttribute
== nsGkAtoms::value
) {
849 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE
, this);
851 uint64_t currState
= NativeState();
852 if ((aOldState
^ currState
) & states::MIXED
) {
853 RefPtr
<AccEvent
> stateChangeEvent
= new AccStateChangeEvent(
854 this, states::MIXED
, (currState
& states::MIXED
));
855 mDoc
->FireDelayedEvent(stateChangeEvent
);
860 ////////////////////////////////////////////////////////////////////////////////
861 // HTMLMeterAccessible
862 ////////////////////////////////////////////////////////////////////////////////
864 role
HTMLMeterAccessible::NativeRole() const { return roles::METER
; }
866 bool HTMLMeterAccessible::IsWidget() const { return true; }
868 void HTMLMeterAccessible::Value(nsString
& aValue
) const {
869 LeafAccessible::Value(aValue
);
870 if (!aValue
.IsEmpty()) {
874 // If we did not get a value from the above LeafAccessible call,
875 // we should check to see if the meter has inner text.
876 // If it does, we'll use that as our value.
877 nsTextEquivUtils::AppendFromDOMChildren(mContent
, &aValue
);
878 aValue
.CompressWhitespace();
879 if (!aValue
.IsEmpty()) {
883 // If no inner text is found, use curValue
884 double curValue
= CurValue();
885 if (std::isnan(curValue
)) {
889 aValue
.AppendFloat(curValue
);
892 double HTMLMeterAccessible::MaxValue() const {
893 double max
= LeafAccessible::MaxValue();
894 double min
= MinValue();
896 if (!std::isnan(max
)) {
897 return max
> min
? max
: min
;
900 // If we didn't find a max value, check for the max attribute
901 nsAutoString strValue
;
902 if (mContent
->AsElement()->GetAttr(nsGkAtoms::max
, strValue
)) {
903 nsresult result
= NS_OK
;
904 max
= strValue
.ToDouble(&result
);
905 if (NS_SUCCEEDED(result
)) {
906 return max
> min
? max
: min
;
910 return 1 > min
? 1 : min
;
913 double HTMLMeterAccessible::MinValue() const {
914 double min
= LeafAccessible::MinValue();
915 if (!std::isnan(min
)) {
919 nsAutoString strValue
;
920 if (mContent
->AsElement()->GetAttr(nsGkAtoms::min
, strValue
)) {
921 nsresult result
= NS_OK
;
922 min
= strValue
.ToDouble(&result
);
923 if (NS_SUCCEEDED(result
)) {
931 double HTMLMeterAccessible::CurValue() const {
932 double value
= LeafAccessible::CurValue();
933 double minValue
= MinValue();
935 if (std::isnan(value
)) {
936 /* If we didn't find a value from the LeafAccessible call above, check
937 * for a value attribute */
938 nsAutoString attrValue
;
939 if (!mContent
->AsElement()->GetAttr(nsGkAtoms::value
, attrValue
)) {
943 // If we find a value attribute, attempt to convert it to a double
944 nsresult error
= NS_OK
;
945 value
= attrValue
.ToDouble(&error
);
946 if (NS_FAILED(error
)) {
951 /* If we end up with a defined value, verify it falls between
952 * our established min/max. Otherwise, snap it to the nearest boundary. */
953 double maxValue
= MaxValue();
954 if (value
> maxValue
) {
956 } else if (value
< minValue
) {
963 bool HTMLMeterAccessible::SetCurValue(double aValue
) {
964 return false; // meters are readonly.
967 void HTMLMeterAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
970 const nsAttrValue
* aOldValue
,
971 uint64_t aOldState
) {
972 LeafAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
973 aOldValue
, aOldState
);
975 if (aAttribute
== nsGkAtoms::value
) {
976 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE
, this);