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 "DocAccessible-inl.h"
9 #include "LocalAccessible-inl.h"
10 #include "nsAccUtils.h"
11 #include "nsEventShell.h"
12 #include "nsTextEquivUtils.h"
17 #include "nsContentList.h"
18 #include "mozilla/dom/HTMLInputElement.h"
19 #include "mozilla/dom/HTMLTextAreaElement.h"
20 #include "nsIFormControl.h"
21 #include "nsITextControlFrame.h"
22 #include "nsNameSpaceManager.h"
23 #include "mozilla/dom/ScriptSettings.h"
25 #include "mozilla/EditorBase.h"
26 #include "mozilla/EventStates.h"
27 #include "mozilla/FloatingPoint.h"
28 #include "mozilla/Preferences.h"
29 #include "mozilla/TextEditor.h"
31 using namespace mozilla
;
32 using namespace mozilla::dom
;
33 using namespace mozilla::a11y
;
35 ////////////////////////////////////////////////////////////////////////////////
37 ////////////////////////////////////////////////////////////////////////////////
39 role
HTMLFormAccessible::NativeRole() const {
41 const_cast<HTMLFormAccessible
*>(this)->Name(name
);
42 return name
.IsEmpty() ? roles::FORM
: roles::FORM_LANDMARK
;
45 nsAtom
* HTMLFormAccessible::LandmarkRole() const {
46 if (!HasOwnContent()) {
50 // Only return xml-roles "form" if the form has an accessible name.
52 const_cast<HTMLFormAccessible
*>(this)->Name(name
);
53 return name
.IsEmpty() ? HyperTextAccessibleWrap::LandmarkRole()
57 ////////////////////////////////////////////////////////////////////////////////
58 // HTMLRadioButtonAccessible
59 ////////////////////////////////////////////////////////////////////////////////
61 uint64_t HTMLRadioButtonAccessible::NativeState() const {
62 uint64_t state
= AccessibleWrap::NativeState();
64 state
|= states::CHECKABLE
;
66 HTMLInputElement
* input
= HTMLInputElement::FromNode(mContent
);
67 if (input
&& input
->Checked()) state
|= states::CHECKED
;
72 void HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet
,
74 Unused
<< ComputeGroupAttributes(aPosInSet
, aSetSize
);
77 Relation
HTMLRadioButtonAccessible::ComputeGroupAttributes(
78 int32_t* aPosInSet
, int32_t* aSetSize
) const {
79 Relation rel
= Relation();
80 int32_t namespaceId
= mContent
->NodeInfo()->NamespaceID();
82 mContent
->NodeInfo()->GetName(tagName
);
85 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::type
, type
);
87 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, name
);
89 RefPtr
<nsContentList
> inputElms
;
91 nsCOMPtr
<nsIFormControl
> formControlNode(do_QueryInterface(mContent
));
92 if (dom::Element
* formElm
= formControlNode
->GetForm()) {
93 inputElms
= NS_GetContentList(formElm
, namespaceId
, tagName
);
95 inputElms
= NS_GetContentList(mContent
->OwnerDoc(), namespaceId
, tagName
);
97 NS_ENSURE_TRUE(inputElms
, rel
);
99 uint32_t inputCount
= inputElms
->Length(false);
101 // Compute posinset and setsize.
105 for (uint32_t index
= 0; index
< inputCount
; index
++) {
106 nsIContent
* inputElm
= inputElms
->Item(index
, false);
107 if (inputElm
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
108 type
, eCaseMatters
) &&
109 inputElm
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
,
110 name
, eCaseMatters
) &&
111 mDoc
->HasAccessible(inputElm
)) {
113 rel
.AppendTarget(mDoc
->GetAccessible(inputElm
));
114 if (inputElm
== mContent
) indexOf
= count
;
118 *aPosInSet
= indexOf
;
123 Relation
HTMLRadioButtonAccessible::RelationByType(RelationType aType
) const {
124 if (aType
== RelationType::MEMBER_OF
) {
125 int32_t unusedPos
, unusedSetSize
;
126 return ComputeGroupAttributes(&unusedPos
, &unusedSetSize
);
129 return LocalAccessible::RelationByType(aType
);
132 ////////////////////////////////////////////////////////////////////////////////
133 // HTMLButtonAccessible
134 ////////////////////////////////////////////////////////////////////////////////
136 HTMLButtonAccessible::HTMLButtonAccessible(nsIContent
* aContent
,
138 : HyperTextAccessibleWrap(aContent
, aDoc
) {
139 mGenericTypes
|= eButton
;
142 uint8_t HTMLButtonAccessible::ActionCount() const { return 1; }
144 void HTMLButtonAccessible::ActionNameAt(uint8_t aIndex
, nsAString
& aName
) {
145 if (aIndex
== eAction_Click
) aName
.AssignLiteral("press");
148 bool HTMLButtonAccessible::DoAction(uint8_t aIndex
) const {
149 if (aIndex
!= eAction_Click
) return false;
155 uint64_t HTMLButtonAccessible::State() {
156 uint64_t state
= HyperTextAccessibleWrap::State();
157 if (state
== states::DEFUNCT
) return state
;
159 // Inherit states from input@type="file" suitable for the button. Note,
160 // no special processing for unavailable state since inheritance is supplied
162 if (mParent
&& mParent
->IsHTMLFileInput()) {
163 uint64_t parentState
= mParent
->State();
164 state
|= parentState
& (states::BUSY
| states::REQUIRED
| states::HASPOPUP
|
171 uint64_t HTMLButtonAccessible::NativeState() const {
172 uint64_t state
= HyperTextAccessibleWrap::NativeState();
174 EventStates elmState
= mContent
->AsElement()->State();
175 if (elmState
.HasState(NS_EVENT_STATE_DEFAULT
)) state
|= states::DEFAULT
;
180 role
HTMLButtonAccessible::NativeRole() const { return roles::PUSHBUTTON
; }
182 ENameValueFlag
HTMLButtonAccessible::NativeName(nsString
& aName
) const {
183 // No need to check @value attribute for buttons since this attribute results
184 // in native anonymous text node and the name is calculated from subtree.
185 // The same magic works for @alt and @value attributes in case of type="image"
186 // element that has no valid @src (note if input@type="image" has an image
187 // then neither @alt nor @value attributes are used to generate a visual label
188 // and thus we need to obtain the accessible name directly from attribute
189 // value). Also the same algorithm works in case of default labels for
190 // type="submit"/"reset"/"image" elements.
192 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
193 if (!aName
.IsEmpty() || !mContent
->IsHTMLElement(nsGkAtoms::input
) ||
194 !mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
195 nsGkAtoms::image
, eCaseMatters
)) {
199 if (!mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::alt
,
201 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::value
, aName
);
204 aName
.CompressWhitespace();
208 void HTMLButtonAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
211 const nsAttrValue
* aOldValue
,
212 uint64_t aOldState
) {
213 HyperTextAccessibleWrap::DOMAttributeChanged(aNameSpaceID
, aAttribute
,
214 aModType
, aOldValue
, aOldState
);
216 if (aAttribute
== nsGkAtoms::value
) {
217 dom::Element
* elm
= Elm();
218 if (elm
->IsHTMLElement(nsGkAtoms::input
) ||
219 (elm
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
, nsGkAtoms::image
,
221 !elm
->HasAttr(kNameSpaceID_None
, nsGkAtoms::alt
))) {
222 if (!elm
->HasAttr(kNameSpaceID_None
, nsGkAtoms::aria_labelledby
) &&
223 !elm
->HasAttr(kNameSpaceID_None
, nsGkAtoms::aria_label
)) {
224 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE
, this);
230 ////////////////////////////////////////////////////////////////////////////////
231 // HTMLButtonAccessible: Widgets
233 bool HTMLButtonAccessible::IsWidget() const { return true; }
235 ////////////////////////////////////////////////////////////////////////////////
236 // HTMLTextFieldAccessible
237 ////////////////////////////////////////////////////////////////////////////////
239 HTMLTextFieldAccessible::HTMLTextFieldAccessible(nsIContent
* aContent
,
241 : HyperTextAccessibleWrap(aContent
, aDoc
) {
242 mType
= mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
243 nsGkAtoms::password
, eIgnoreCase
)
244 ? eHTMLTextPasswordFieldType
245 : eHTMLTextFieldType
;
248 role
HTMLTextFieldAccessible::NativeRole() const {
249 if (mType
== eHTMLTextPasswordFieldType
) {
250 return roles::PASSWORD_TEXT
;
252 if (mContent
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::list_
)) {
253 return roles::EDITCOMBOBOX
;
258 already_AddRefed
<AccAttributes
> HTMLTextFieldAccessible::NativeAttributes() {
259 RefPtr
<AccAttributes
> attributes
=
260 HyperTextAccessibleWrap::NativeAttributes();
262 // Expose type for text input elements as it gives some useful context,
263 // especially for mobile.
265 // In the case of this element being part of a binding, the binding's
266 // parent's type should have precedence. For example an input[type=number]
267 // has an embedded anonymous input[type=text] (along with spinner buttons).
268 // In that case, we would want to take the input type from the parent
269 // and not the anonymous content.
270 nsIContent
* widgetElm
= BindingOrWidgetParent();
271 if ((widgetElm
&& widgetElm
->AsElement()->GetAttr(kNameSpaceID_None
,
272 nsGkAtoms::type
, type
)) ||
273 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::type
,
275 attributes
->SetAttribute(nsGkAtoms::textInputType
, type
);
276 if (!ARIARoleMap() && type
.EqualsLiteral("search")) {
277 attributes
->SetAttribute(nsGkAtoms::xmlroles
, u
"searchbox"_ns
);
281 // If this element has the placeholder attribute set,
282 // and if that is not identical to the name, expose it as an object attribute.
283 nsAutoString placeholderText
;
284 if (mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::placeholder
,
287 const_cast<HTMLTextFieldAccessible
*>(this)->Name(name
);
288 if (!name
.Equals(placeholderText
)) {
289 attributes
->SetAttribute(nsGkAtoms::placeholder
, placeholderText
);
293 return attributes
.forget();
296 ENameValueFlag
HTMLTextFieldAccessible::NativeName(nsString
& aName
) const {
297 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
298 if (!aName
.IsEmpty()) return nameFlag
;
300 // If part of compound of XUL widget then grab a name from XUL widget element.
301 nsIContent
* widgetElm
= BindingOrWidgetParent();
302 if (widgetElm
) XULElmName(mDoc
, widgetElm
, aName
);
304 if (!aName
.IsEmpty()) return eNameOK
;
306 // text inputs and textareas might have useful placeholder text
307 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::placeholder
,
312 void HTMLTextFieldAccessible::Value(nsString
& aValue
) const {
314 if (NativeState() & states::PROTECTED
) { // Don't return password text!
318 HTMLTextAreaElement
* textArea
= HTMLTextAreaElement::FromNode(mContent
);
320 textArea
->GetValue(aValue
);
324 HTMLInputElement
* input
= HTMLInputElement::FromNode(mContent
);
326 // Pass NonSystem as the caller type, to be safe. We don't expect to have a
328 input
->GetValue(aValue
, CallerType::NonSystem
);
332 void HTMLTextFieldAccessible::ApplyARIAState(uint64_t* aState
) const {
333 HyperTextAccessibleWrap::ApplyARIAState(aState
);
334 aria::MapToState(aria::eARIAAutoComplete
, mContent
->AsElement(), aState
);
336 // If part of compound of XUL widget then pick up ARIA stuff from XUL widget
338 nsIContent
* widgetElm
= BindingOrWidgetParent();
340 aria::MapToState(aria::eARIAAutoComplete
, widgetElm
->AsElement(), aState
);
344 uint64_t HTMLTextFieldAccessible::NativeState() const {
345 uint64_t state
= HyperTextAccessibleWrap::NativeState();
347 // Text fields are always editable, even if they are also read only or
349 state
|= states::EDITABLE
;
351 // can be focusable, focused, protected. readonly, unavailable, selected
352 if (mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
353 nsGkAtoms::password
, eIgnoreCase
)) {
354 state
|= states::PROTECTED
;
357 if (mContent
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::readonly
)) {
358 state
|= states::READONLY
;
361 // Is it an <input> or a <textarea> ?
362 HTMLInputElement
* input
= HTMLInputElement::FromNode(mContent
);
363 state
|= input
&& input
->IsSingleLineTextControl() ? states::SINGLE_LINE
364 : states::MULTI_LINE
;
366 if (state
& (states::PROTECTED
| states::MULTI_LINE
| states::READONLY
|
367 states::UNAVAILABLE
)) {
371 // Expose autocomplete states if this input is part of autocomplete widget.
372 LocalAccessible
* widget
= ContainerWidget();
373 if (widget
&& widget
- IsAutoComplete()) {
374 state
|= states::HASPOPUP
| states::SUPPORTS_AUTOCOMPLETION
;
378 // Expose autocomplete state if it has associated autocomplete list.
379 if (mContent
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::list_
)) {
380 return state
| states::SUPPORTS_AUTOCOMPLETION
| states::HASPOPUP
;
383 // Ordinal XUL textboxes don't support autocomplete.
384 if (!BindingOrWidgetParent() &&
385 Preferences::GetBool("browser.formfill.enable")) {
386 // Check to see if autocompletion is allowed on this input. We don't expose
387 // it for password fields even though the entire password can be remembered
388 // for a page if the user asks it to be. However, the kind of autocomplete
389 // we're talking here is based on what the user types, where a popup of
390 // possible choices comes up.
391 nsAutoString autocomplete
;
392 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::autocomplete
,
395 if (!autocomplete
.LowerCaseEqualsLiteral("off")) {
396 Element
* formElement
= input
->GetForm();
398 formElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::autocomplete
,
402 if (!formElement
|| !autocomplete
.LowerCaseEqualsLiteral("off")) {
403 state
|= states::SUPPORTS_AUTOCOMPLETION
;
411 uint8_t HTMLTextFieldAccessible::ActionCount() const { return 1; }
413 void HTMLTextFieldAccessible::ActionNameAt(uint8_t aIndex
, nsAString
& aName
) {
414 if (aIndex
== eAction_Click
) aName
.AssignLiteral("activate");
417 bool HTMLTextFieldAccessible::DoAction(uint8_t aIndex
) const {
418 if (aIndex
!= 0) return false;
420 if (FocusMgr()->IsFocused(this)) {
421 // This already has focus, so TakeFocus()will do nothing. However, the user
422 // might be activating this element because they dismissed a touch keyboard
423 // and want to bring it back.
431 already_AddRefed
<EditorBase
> HTMLTextFieldAccessible::GetEditor() const {
432 RefPtr
<TextControlElement
> textControlElement
=
433 TextControlElement::FromNodeOrNull(mContent
);
434 if (!textControlElement
) {
437 RefPtr
<TextEditor
> textEditor
= textControlElement
->GetTextEditor();
438 return textEditor
.forget();
441 ////////////////////////////////////////////////////////////////////////////////
442 // HTMLTextFieldAccessible: Widgets
444 bool HTMLTextFieldAccessible::IsWidget() const { return true; }
446 LocalAccessible
* HTMLTextFieldAccessible::ContainerWidget() const {
447 if (!mParent
|| mParent
->Role() != roles::AUTOCOMPLETE
) {
453 ////////////////////////////////////////////////////////////////////////////////
454 // HTMLFileInputAccessible
455 ////////////////////////////////////////////////////////////////////////////////
457 HTMLFileInputAccessible::HTMLFileInputAccessible(nsIContent
* aContent
,
459 : HyperTextAccessibleWrap(aContent
, aDoc
) {
460 mType
= eHTMLFileInputType
;
463 role
HTMLFileInputAccessible::NativeRole() const {
464 // No specific role in AT APIs. We use GROUPING so that the label will be
465 // reported by screen readers when focus enters this control .
466 return roles::GROUPING
;
469 nsresult
HTMLFileInputAccessible::HandleAccEvent(AccEvent
* aEvent
) {
470 nsresult rv
= HyperTextAccessibleWrap::HandleAccEvent(aEvent
);
471 NS_ENSURE_SUCCESS(rv
, rv
);
473 // Redirect state change events for inherited states to child controls. Note,
474 // unavailable state is not redirected. That's a standard for unavailable
476 AccStateChangeEvent
* event
= downcast_accEvent(aEvent
);
477 if (event
&& (event
->GetState() == states::BUSY
||
478 event
->GetState() == states::REQUIRED
||
479 event
->GetState() == states::HASPOPUP
||
480 event
->GetState() == states::INVALID
)) {
481 LocalAccessible
* button
= LocalChildAt(0);
482 if (button
&& button
->Role() == roles::PUSHBUTTON
) {
483 RefPtr
<AccStateChangeEvent
> childEvent
= new AccStateChangeEvent(
484 button
, event
->GetState(), event
->IsStateEnabled(),
485 event
->FromUserInput());
486 nsEventShell::FireEvent(childEvent
);
493 LocalAccessible
* HTMLFileInputAccessible::CurrentItem() const {
494 // Allow aria-activedescendant to override.
495 if (LocalAccessible
* item
= HyperTextAccessibleWrap::CurrentItem()) {
499 // The HTML file input itself gets DOM focus, not the button inside it.
500 // For a11y, we want the button to get focus.
501 LocalAccessible
* button
= LocalFirstChild();
503 MOZ_ASSERT_UNREACHABLE("File input doesn't contain a button");
506 MOZ_ASSERT(button
->IsButton());
510 ////////////////////////////////////////////////////////////////////////////////
511 // HTMLSpinnerAccessible
512 ////////////////////////////////////////////////////////////////////////////////
514 role
HTMLSpinnerAccessible::NativeRole() const { return roles::SPINBUTTON
; }
516 void HTMLSpinnerAccessible::Value(nsString
& aValue
) const {
517 HTMLTextFieldAccessible::Value(aValue
);
518 if (!aValue
.IsEmpty()) return;
520 // Pass NonSystem as the caller type, to be safe. We don't expect to have a
522 HTMLInputElement::FromNode(mContent
)->GetValue(aValue
, CallerType::NonSystem
);
525 double HTMLSpinnerAccessible::MaxValue() const {
526 double value
= HTMLTextFieldAccessible::MaxValue();
527 if (!IsNaN(value
)) return value
;
529 return HTMLInputElement::FromNode(mContent
)->GetMaximum().toDouble();
532 double HTMLSpinnerAccessible::MinValue() const {
533 double value
= HTMLTextFieldAccessible::MinValue();
534 if (!IsNaN(value
)) return value
;
536 return HTMLInputElement::FromNode(mContent
)->GetMinimum().toDouble();
539 double HTMLSpinnerAccessible::Step() const {
540 double value
= HTMLTextFieldAccessible::Step();
541 if (!IsNaN(value
)) return value
;
543 return HTMLInputElement::FromNode(mContent
)->GetStep().toDouble();
546 double HTMLSpinnerAccessible::CurValue() const {
547 double value
= HTMLTextFieldAccessible::CurValue();
548 if (!IsNaN(value
)) return value
;
550 return HTMLInputElement::FromNode(mContent
)->GetValueAsDecimal().toDouble();
553 bool HTMLSpinnerAccessible::SetCurValue(double aValue
) {
555 HTMLInputElement::FromNode(mContent
)->SetValueAsNumber(aValue
, er
);
559 ////////////////////////////////////////////////////////////////////////////////
560 // HTMLRangeAccessible
561 ////////////////////////////////////////////////////////////////////////////////
563 role
HTMLRangeAccessible::NativeRole() const { return roles::SLIDER
; }
565 bool HTMLRangeAccessible::IsWidget() const { return true; }
567 void HTMLRangeAccessible::Value(nsString
& aValue
) const {
568 LeafAccessible::Value(aValue
);
569 if (!aValue
.IsEmpty()) return;
571 // Pass NonSystem as the caller type, to be safe. We don't expect to have a
573 HTMLInputElement::FromNode(mContent
)->GetValue(aValue
, CallerType::NonSystem
);
576 double HTMLRangeAccessible::MaxValue() const {
577 double value
= LeafAccessible::MaxValue();
578 if (!IsNaN(value
)) return value
;
580 return HTMLInputElement::FromNode(mContent
)->GetMaximum().toDouble();
583 double HTMLRangeAccessible::MinValue() const {
584 double value
= LeafAccessible::MinValue();
585 if (!IsNaN(value
)) return value
;
587 return HTMLInputElement::FromNode(mContent
)->GetMinimum().toDouble();
590 double HTMLRangeAccessible::Step() const {
591 double value
= LeafAccessible::Step();
592 if (!IsNaN(value
)) return value
;
594 return HTMLInputElement::FromNode(mContent
)->GetStep().toDouble();
597 double HTMLRangeAccessible::CurValue() const {
598 double value
= LeafAccessible::CurValue();
599 if (!IsNaN(value
)) return value
;
601 return HTMLInputElement::FromNode(mContent
)->GetValueAsDecimal().toDouble();
604 bool HTMLRangeAccessible::SetCurValue(double aValue
) {
606 HTMLInputElement::FromNode(mContent
)->SetValueAsNumber(aValue
, er
);
610 ////////////////////////////////////////////////////////////////////////////////
611 // HTMLGroupboxAccessible
612 ////////////////////////////////////////////////////////////////////////////////
614 HTMLGroupboxAccessible::HTMLGroupboxAccessible(nsIContent
* aContent
,
616 : HyperTextAccessibleWrap(aContent
, aDoc
) {}
618 role
HTMLGroupboxAccessible::NativeRole() const { return roles::GROUPING
; }
620 nsIContent
* HTMLGroupboxAccessible::GetLegend() const {
621 for (nsIContent
* legendContent
= mContent
->GetFirstChild(); legendContent
;
622 legendContent
= legendContent
->GetNextSibling()) {
623 if (legendContent
->NodeInfo()->Equals(nsGkAtoms::legend
,
624 mContent
->GetNameSpaceID())) {
625 // Either XHTML namespace or no namespace
626 return legendContent
;
633 ENameValueFlag
HTMLGroupboxAccessible::NativeName(nsString
& aName
) const {
634 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
635 if (!aName
.IsEmpty()) return nameFlag
;
637 nsIContent
* legendContent
= GetLegend();
639 nsTextEquivUtils::AppendTextEquivFromContent(this, legendContent
, &aName
);
642 aName
.CompressWhitespace();
646 Relation
HTMLGroupboxAccessible::RelationByType(RelationType aType
) const {
647 Relation rel
= HyperTextAccessibleWrap::RelationByType(aType
);
648 // No override for label, so use <legend> for this <fieldset>
649 if (aType
== RelationType::LABELLED_BY
) rel
.AppendTarget(mDoc
, GetLegend());
654 ////////////////////////////////////////////////////////////////////////////////
655 // HTMLLegendAccessible
656 ////////////////////////////////////////////////////////////////////////////////
658 HTMLLegendAccessible::HTMLLegendAccessible(nsIContent
* aContent
,
660 : HyperTextAccessibleWrap(aContent
, aDoc
) {}
662 Relation
HTMLLegendAccessible::RelationByType(RelationType aType
) const {
663 Relation rel
= HyperTextAccessibleWrap::RelationByType(aType
);
664 if (aType
!= RelationType::LABEL_FOR
) return rel
;
666 LocalAccessible
* groupbox
= LocalParent();
667 if (groupbox
&& groupbox
->Role() == roles::GROUPING
) {
668 rel
.AppendTarget(groupbox
);
674 ////////////////////////////////////////////////////////////////////////////////
675 // HTMLFigureAccessible
676 ////////////////////////////////////////////////////////////////////////////////
678 HTMLFigureAccessible::HTMLFigureAccessible(nsIContent
* aContent
,
680 : HyperTextAccessibleWrap(aContent
, aDoc
) {}
682 ENameValueFlag
HTMLFigureAccessible::NativeName(nsString
& aName
) const {
683 ENameValueFlag nameFlag
= HyperTextAccessibleWrap::NativeName(aName
);
684 if (!aName
.IsEmpty()) return nameFlag
;
686 nsIContent
* captionContent
= Caption();
687 if (captionContent
) {
688 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent
, &aName
);
691 aName
.CompressWhitespace();
695 Relation
HTMLFigureAccessible::RelationByType(RelationType aType
) const {
696 Relation rel
= HyperTextAccessibleWrap::RelationByType(aType
);
697 if (aType
== RelationType::LABELLED_BY
) rel
.AppendTarget(mDoc
, Caption());
702 nsIContent
* HTMLFigureAccessible::Caption() const {
703 for (nsIContent
* childContent
= mContent
->GetFirstChild(); childContent
;
704 childContent
= childContent
->GetNextSibling()) {
705 if (childContent
->NodeInfo()->Equals(nsGkAtoms::figcaption
,
706 mContent
->GetNameSpaceID())) {
714 ////////////////////////////////////////////////////////////////////////////////
715 // HTMLFigcaptionAccessible
716 ////////////////////////////////////////////////////////////////////////////////
718 HTMLFigcaptionAccessible::HTMLFigcaptionAccessible(nsIContent
* aContent
,
720 : HyperTextAccessibleWrap(aContent
, aDoc
) {}
722 Relation
HTMLFigcaptionAccessible::RelationByType(RelationType aType
) const {
723 Relation rel
= HyperTextAccessibleWrap::RelationByType(aType
);
724 if (aType
!= RelationType::LABEL_FOR
) return rel
;
726 LocalAccessible
* figure
= LocalParent();
727 if (figure
&& figure
->GetContent()->NodeInfo()->Equals(
728 nsGkAtoms::figure
, mContent
->GetNameSpaceID())) {
729 rel
.AppendTarget(figure
);
735 ////////////////////////////////////////////////////////////////////////////////
736 // HTMLProgressAccessible
737 ////////////////////////////////////////////////////////////////////////////////
739 role
HTMLProgressAccessible::NativeRole() const { return roles::PROGRESSBAR
; }
741 uint64_t HTMLProgressAccessible::NativeState() const {
742 uint64_t state
= LeafAccessible::NativeState();
744 // An undetermined progressbar (i.e. without a value) has a mixed state.
745 nsAutoString attrValue
;
746 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::value
,
748 if (attrValue
.IsEmpty()) {
749 state
|= states::MIXED
;
755 bool HTMLProgressAccessible::IsWidget() const { return true; }
757 void HTMLProgressAccessible::Value(nsString
& aValue
) const {
758 LeafAccessible::Value(aValue
);
759 if (!aValue
.IsEmpty()) {
763 double maxValue
= MaxValue();
764 if (IsNaN(maxValue
) || maxValue
== 0) {
768 double curValue
= CurValue();
769 if (IsNaN(curValue
)) {
773 // Treat the current value bigger than maximum as 100%.
774 double percentValue
=
775 (curValue
< maxValue
) ? (curValue
/ maxValue
) * 100 : 100;
777 aValue
.AppendFloat(percentValue
);
781 double HTMLProgressAccessible::MaxValue() const {
782 double value
= LeafAccessible::MaxValue();
787 nsAutoString strValue
;
788 if (mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::max
,
790 nsresult result
= NS_OK
;
791 value
= strValue
.ToDouble(&result
);
792 if (NS_SUCCEEDED(result
)) {
800 double HTMLProgressAccessible::MinValue() const {
801 double value
= LeafAccessible::MinValue();
802 return IsNaN(value
) ? 0 : value
;
805 double HTMLProgressAccessible::Step() const {
806 double value
= LeafAccessible::Step();
807 return IsNaN(value
) ? 0 : value
;
810 double HTMLProgressAccessible::CurValue() const {
811 double value
= LeafAccessible::CurValue();
816 nsAutoString attrValue
;
817 if (!mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::value
,
819 return UnspecifiedNaN
<double>();
822 nsresult error
= NS_OK
;
823 value
= attrValue
.ToDouble(&error
);
824 return NS_FAILED(error
) ? UnspecifiedNaN
<double>() : value
;
827 bool HTMLProgressAccessible::SetCurValue(double aValue
) {
828 return false; // progress meters are readonly.
831 void HTMLProgressAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
834 const nsAttrValue
* aOldValue
,
835 uint64_t aOldState
) {
836 LeafAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
837 aOldValue
, aOldState
);
839 if (aAttribute
== nsGkAtoms::value
) {
840 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE
, this);
842 uint64_t currState
= NativeState();
843 if ((aOldState
^ currState
) & states::MIXED
) {
844 RefPtr
<AccEvent
> stateChangeEvent
= new AccStateChangeEvent(
845 this, states::MIXED
, (currState
& states::MIXED
));
846 mDoc
->FireDelayedEvent(stateChangeEvent
);
851 ////////////////////////////////////////////////////////////////////////////////
852 // HTMLMeterAccessible
853 ////////////////////////////////////////////////////////////////////////////////
855 role
HTMLMeterAccessible::NativeRole() const { return roles::METER
; }
857 bool HTMLMeterAccessible::IsWidget() const { return true; }
859 void HTMLMeterAccessible::Value(nsString
& aValue
) const {
860 LeafAccessible::Value(aValue
);
861 if (!aValue
.IsEmpty()) {
865 // If we did not get a value from the above LeafAccessible call,
866 // we should check to see if the meter has inner text.
867 // If it does, we'll use that as our value.
868 nsTextEquivUtils::AppendFromDOMChildren(mContent
, &aValue
);
869 aValue
.CompressWhitespace();
870 if (!aValue
.IsEmpty()) {
874 // If no inner text is found, use curValue
875 double curValue
= CurValue();
876 if (IsNaN(curValue
)) {
880 aValue
.AppendFloat(curValue
);
883 double HTMLMeterAccessible::MaxValue() const {
884 double max
= LeafAccessible::MaxValue();
885 double min
= MinValue();
888 return max
> min
? max
: min
;
891 // If we didn't find a max value, check for the max attribute
892 nsAutoString strValue
;
893 if (mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::max
,
895 nsresult result
= NS_OK
;
896 max
= strValue
.ToDouble(&result
);
897 if (NS_SUCCEEDED(result
)) {
898 return max
> min
? max
: min
;
902 return 1 > min
? 1 : min
;
905 double HTMLMeterAccessible::MinValue() const {
906 double min
= LeafAccessible::MinValue();
911 nsAutoString strValue
;
912 if (mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::min
,
914 nsresult result
= NS_OK
;
915 min
= strValue
.ToDouble(&result
);
916 if (NS_SUCCEEDED(result
)) {
924 double HTMLMeterAccessible::CurValue() const {
925 double value
= LeafAccessible::CurValue();
926 double minValue
= MinValue();
929 /* If we didn't find a value from the LeafAccessible call above, check
930 * for a value attribute */
931 nsAutoString attrValue
;
932 if (!mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::value
,
937 // If we find a value attribute, attempt to convert it to a double
938 nsresult error
= NS_OK
;
939 value
= attrValue
.ToDouble(&error
);
940 if (NS_FAILED(error
)) {
945 /* If we end up with a defined value, verify it falls between
946 * our established min/max. Otherwise, snap it to the nearest boundary. */
947 double maxValue
= MaxValue();
948 if (value
> maxValue
) {
950 } else if (value
< minValue
) {
957 bool HTMLMeterAccessible::SetCurValue(double aValue
) {
958 return false; // meters are readonly.
961 void HTMLMeterAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
964 const nsAttrValue
* aOldValue
,
965 uint64_t aOldState
) {
966 LeafAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
967 aOldValue
, aOldState
);
969 if (aAttribute
== nsGkAtoms::value
) {
970 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE
, this);