|
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 "HTMLFormControlAccessible.h" |
|
7 |
|
8 #include "Accessible-inl.h" |
|
9 #include "nsAccUtils.h" |
|
10 #include "nsEventShell.h" |
|
11 #include "nsTextEquivUtils.h" |
|
12 #include "Relation.h" |
|
13 #include "Role.h" |
|
14 #include "States.h" |
|
15 |
|
16 #include "nsContentList.h" |
|
17 #include "mozilla/dom/HTMLInputElement.h" |
|
18 #include "nsIAccessibleRelation.h" |
|
19 #include "nsIDOMNSEditableElement.h" |
|
20 #include "nsIDOMHTMLTextAreaElement.h" |
|
21 #include "nsIEditor.h" |
|
22 #include "nsIFormControl.h" |
|
23 #include "nsIPersistentProperties2.h" |
|
24 #include "nsISelectionController.h" |
|
25 #include "nsIServiceManager.h" |
|
26 #include "nsITextControlFrame.h" |
|
27 #include "nsNameSpaceManager.h" |
|
28 #include "mozilla/dom/ScriptSettings.h" |
|
29 |
|
30 #include "mozilla/EventStates.h" |
|
31 #include "mozilla/FloatingPoint.h" |
|
32 #include "mozilla/Preferences.h" |
|
33 |
|
34 using namespace mozilla; |
|
35 using namespace mozilla::dom; |
|
36 using namespace mozilla::a11y; |
|
37 |
|
38 //////////////////////////////////////////////////////////////////////////////// |
|
39 // HTMLCheckboxAccessible |
|
40 //////////////////////////////////////////////////////////////////////////////// |
|
41 |
|
42 role |
|
43 HTMLCheckboxAccessible::NativeRole() |
|
44 { |
|
45 return roles::CHECKBUTTON; |
|
46 } |
|
47 |
|
48 uint8_t |
|
49 HTMLCheckboxAccessible::ActionCount() |
|
50 { |
|
51 return 1; |
|
52 } |
|
53 |
|
54 NS_IMETHODIMP |
|
55 HTMLCheckboxAccessible::GetActionName(uint8_t aIndex, nsAString& aName) |
|
56 { |
|
57 if (aIndex == eAction_Click) { // 0 is the magic value for default action |
|
58 // cycle, check or uncheck |
|
59 uint64_t state = NativeState(); |
|
60 |
|
61 if (state & states::CHECKED) |
|
62 aName.AssignLiteral("uncheck"); |
|
63 else if (state & states::MIXED) |
|
64 aName.AssignLiteral("cycle"); |
|
65 else |
|
66 aName.AssignLiteral("check"); |
|
67 |
|
68 return NS_OK; |
|
69 } |
|
70 return NS_ERROR_INVALID_ARG; |
|
71 } |
|
72 |
|
73 NS_IMETHODIMP |
|
74 HTMLCheckboxAccessible::DoAction(uint8_t aIndex) |
|
75 { |
|
76 if (aIndex != 0) |
|
77 return NS_ERROR_INVALID_ARG; |
|
78 |
|
79 DoCommand(); |
|
80 return NS_OK; |
|
81 } |
|
82 |
|
83 uint64_t |
|
84 HTMLCheckboxAccessible::NativeState() |
|
85 { |
|
86 uint64_t state = LeafAccessible::NativeState(); |
|
87 |
|
88 state |= states::CHECKABLE; |
|
89 HTMLInputElement* input = HTMLInputElement::FromContent(mContent); |
|
90 if (!input) |
|
91 return state; |
|
92 |
|
93 if (input->Indeterminate()) |
|
94 return state | states::MIXED; |
|
95 |
|
96 if (input->Checked()) |
|
97 return state | states::CHECKED; |
|
98 |
|
99 return state; |
|
100 } |
|
101 |
|
102 //////////////////////////////////////////////////////////////////////////////// |
|
103 // HTMLCheckboxAccessible: Widgets |
|
104 |
|
105 bool |
|
106 HTMLCheckboxAccessible::IsWidget() const |
|
107 { |
|
108 return true; |
|
109 } |
|
110 |
|
111 |
|
112 //////////////////////////////////////////////////////////////////////////////// |
|
113 // HTMLRadioButtonAccessible |
|
114 //////////////////////////////////////////////////////////////////////////////// |
|
115 |
|
116 uint64_t |
|
117 HTMLRadioButtonAccessible::NativeState() |
|
118 { |
|
119 uint64_t state = AccessibleWrap::NativeState(); |
|
120 |
|
121 state |= states::CHECKABLE; |
|
122 |
|
123 HTMLInputElement* input = HTMLInputElement::FromContent(mContent); |
|
124 if (input && input->Checked()) |
|
125 state |= states::CHECKED; |
|
126 |
|
127 return state; |
|
128 } |
|
129 |
|
130 void |
|
131 HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet, |
|
132 int32_t* aSetSize) |
|
133 { |
|
134 int32_t namespaceId = mContent->NodeInfo()->NamespaceID(); |
|
135 nsAutoString tagName; |
|
136 mContent->NodeInfo()->GetName(tagName); |
|
137 |
|
138 nsAutoString type; |
|
139 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type); |
|
140 nsAutoString name; |
|
141 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name); |
|
142 |
|
143 nsRefPtr<nsContentList> inputElms; |
|
144 |
|
145 nsCOMPtr<nsIFormControl> formControlNode(do_QueryInterface(mContent)); |
|
146 dom::Element* formElm = formControlNode->GetFormElement(); |
|
147 if (formElm) |
|
148 inputElms = NS_GetContentList(formElm, namespaceId, tagName); |
|
149 else |
|
150 inputElms = NS_GetContentList(mContent->OwnerDoc(), namespaceId, tagName); |
|
151 NS_ENSURE_TRUE_VOID(inputElms); |
|
152 |
|
153 uint32_t inputCount = inputElms->Length(false); |
|
154 |
|
155 // Compute posinset and setsize. |
|
156 int32_t indexOf = 0; |
|
157 int32_t count = 0; |
|
158 |
|
159 for (uint32_t index = 0; index < inputCount; index++) { |
|
160 nsIContent* inputElm = inputElms->Item(index, false); |
|
161 if (inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, |
|
162 type, eCaseMatters) && |
|
163 inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, |
|
164 name, eCaseMatters) && mDoc->HasAccessible(inputElm)) { |
|
165 count++; |
|
166 if (inputElm == mContent) |
|
167 indexOf = count; |
|
168 } |
|
169 } |
|
170 |
|
171 *aPosInSet = indexOf; |
|
172 *aSetSize = count; |
|
173 } |
|
174 |
|
175 //////////////////////////////////////////////////////////////////////////////// |
|
176 // HTMLButtonAccessible |
|
177 //////////////////////////////////////////////////////////////////////////////// |
|
178 |
|
179 HTMLButtonAccessible:: |
|
180 HTMLButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
|
181 HyperTextAccessibleWrap(aContent, aDoc) |
|
182 { |
|
183 mGenericTypes |= eButton; |
|
184 } |
|
185 |
|
186 uint8_t |
|
187 HTMLButtonAccessible::ActionCount() |
|
188 { |
|
189 return 1; |
|
190 } |
|
191 |
|
192 NS_IMETHODIMP |
|
193 HTMLButtonAccessible::GetActionName(uint8_t aIndex, nsAString& aName) |
|
194 { |
|
195 if (aIndex == eAction_Click) { |
|
196 aName.AssignLiteral("press"); |
|
197 return NS_OK; |
|
198 } |
|
199 return NS_ERROR_INVALID_ARG; |
|
200 } |
|
201 |
|
202 NS_IMETHODIMP |
|
203 HTMLButtonAccessible::DoAction(uint8_t aIndex) |
|
204 { |
|
205 if (aIndex != eAction_Click) |
|
206 return NS_ERROR_INVALID_ARG; |
|
207 |
|
208 DoCommand(); |
|
209 return NS_OK; |
|
210 } |
|
211 |
|
212 uint64_t |
|
213 HTMLButtonAccessible::State() |
|
214 { |
|
215 uint64_t state = HyperTextAccessibleWrap::State(); |
|
216 if (state == states::DEFUNCT) |
|
217 return state; |
|
218 |
|
219 // Inherit states from input@type="file" suitable for the button. Note, |
|
220 // no special processing for unavailable state since inheritance is supplied |
|
221 // other code paths. |
|
222 if (mParent && mParent->IsHTMLFileInput()) { |
|
223 uint64_t parentState = mParent->State(); |
|
224 state |= parentState & (states::BUSY | states::REQUIRED | |
|
225 states::HASPOPUP | states::INVALID); |
|
226 } |
|
227 |
|
228 return state; |
|
229 } |
|
230 |
|
231 uint64_t |
|
232 HTMLButtonAccessible::NativeState() |
|
233 { |
|
234 uint64_t state = HyperTextAccessibleWrap::NativeState(); |
|
235 |
|
236 EventStates elmState = mContent->AsElement()->State(); |
|
237 if (elmState.HasState(NS_EVENT_STATE_DEFAULT)) |
|
238 state |= states::DEFAULT; |
|
239 |
|
240 return state; |
|
241 } |
|
242 |
|
243 role |
|
244 HTMLButtonAccessible::NativeRole() |
|
245 { |
|
246 return roles::PUSHBUTTON; |
|
247 } |
|
248 |
|
249 ENameValueFlag |
|
250 HTMLButtonAccessible::NativeName(nsString& aName) |
|
251 { |
|
252 // No need to check @value attribute for buttons since this attribute results |
|
253 // in native anonymous text node and the name is calculated from subtree. |
|
254 // The same magic works for @alt and @value attributes in case of type="image" |
|
255 // element that has no valid @src (note if input@type="image" has an image |
|
256 // then neither @alt nor @value attributes are used to generate a visual label |
|
257 // and thus we need to obtain the accessible name directly from attribute |
|
258 // value). Also the same algorithm works in case of default labels for |
|
259 // type="submit"/"reset"/"image" elements. |
|
260 |
|
261 ENameValueFlag nameFlag = Accessible::NativeName(aName); |
|
262 if (!aName.IsEmpty() || mContent->Tag() != nsGkAtoms::input || |
|
263 !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, |
|
264 nsGkAtoms::image, eCaseMatters)) |
|
265 return nameFlag; |
|
266 |
|
267 if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName)) |
|
268 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName); |
|
269 |
|
270 aName.CompressWhitespace(); |
|
271 return eNameOK; |
|
272 } |
|
273 |
|
274 //////////////////////////////////////////////////////////////////////////////// |
|
275 // HTMLButtonAccessible: Widgets |
|
276 |
|
277 bool |
|
278 HTMLButtonAccessible::IsWidget() const |
|
279 { |
|
280 return true; |
|
281 } |
|
282 |
|
283 |
|
284 //////////////////////////////////////////////////////////////////////////////// |
|
285 // HTMLTextFieldAccessible |
|
286 //////////////////////////////////////////////////////////////////////////////// |
|
287 |
|
288 HTMLTextFieldAccessible:: |
|
289 HTMLTextFieldAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
|
290 HyperTextAccessibleWrap(aContent, aDoc) |
|
291 { |
|
292 mType = eHTMLTextFieldType; |
|
293 } |
|
294 |
|
295 NS_IMPL_ISUPPORTS_INHERITED(HTMLTextFieldAccessible, |
|
296 Accessible, |
|
297 nsIAccessibleText, |
|
298 nsIAccessibleEditableText) |
|
299 |
|
300 role |
|
301 HTMLTextFieldAccessible::NativeRole() |
|
302 { |
|
303 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, |
|
304 nsGkAtoms::password, eIgnoreCase)) { |
|
305 return roles::PASSWORD_TEXT; |
|
306 } |
|
307 |
|
308 return roles::ENTRY; |
|
309 } |
|
310 |
|
311 already_AddRefed<nsIPersistentProperties> |
|
312 HTMLTextFieldAccessible::NativeAttributes() |
|
313 { |
|
314 nsCOMPtr<nsIPersistentProperties> attributes = |
|
315 HyperTextAccessibleWrap::NativeAttributes(); |
|
316 |
|
317 // Expose type for text input elements as it gives some useful context, |
|
318 // especially for mobile. |
|
319 nsAutoString type; |
|
320 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) |
|
321 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType, type); |
|
322 |
|
323 return attributes.forget(); |
|
324 } |
|
325 |
|
326 ENameValueFlag |
|
327 HTMLTextFieldAccessible::NativeName(nsString& aName) |
|
328 { |
|
329 ENameValueFlag nameFlag = Accessible::NativeName(aName); |
|
330 if (!aName.IsEmpty()) |
|
331 return nameFlag; |
|
332 |
|
333 // If part of compound of XUL widget then grab a name from XUL widget element. |
|
334 nsIContent* widgetElm = XULWidgetElm(); |
|
335 if (widgetElm) |
|
336 XULElmName(mDoc, widgetElm, aName); |
|
337 |
|
338 if (!aName.IsEmpty()) |
|
339 return eNameOK; |
|
340 |
|
341 // text inputs and textareas might have useful placeholder text |
|
342 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, aName); |
|
343 return eNameOK; |
|
344 } |
|
345 |
|
346 void |
|
347 HTMLTextFieldAccessible::Value(nsString& aValue) |
|
348 { |
|
349 aValue.Truncate(); |
|
350 if (NativeState() & states::PROTECTED) // Don't return password text! |
|
351 return; |
|
352 |
|
353 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea(do_QueryInterface(mContent)); |
|
354 if (textArea) { |
|
355 textArea->GetValue(aValue); |
|
356 return; |
|
357 } |
|
358 |
|
359 HTMLInputElement* input = HTMLInputElement::FromContent(mContent); |
|
360 if (input) |
|
361 input->GetValue(aValue); |
|
362 } |
|
363 |
|
364 void |
|
365 HTMLTextFieldAccessible::ApplyARIAState(uint64_t* aState) const |
|
366 { |
|
367 HyperTextAccessibleWrap::ApplyARIAState(aState); |
|
368 aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState); |
|
369 |
|
370 // If part of compound of XUL widget then pick up ARIA stuff from XUL widget |
|
371 // element. |
|
372 nsIContent* widgetElm = XULWidgetElm(); |
|
373 if (widgetElm) |
|
374 aria::MapToState(aria::eARIAAutoComplete, widgetElm->AsElement(), aState); |
|
375 } |
|
376 |
|
377 uint64_t |
|
378 HTMLTextFieldAccessible::NativeState() |
|
379 { |
|
380 uint64_t state = HyperTextAccessibleWrap::NativeState(); |
|
381 |
|
382 // Text fields are always editable, even if they are also read only or |
|
383 // disabled. |
|
384 state |= states::EDITABLE; |
|
385 |
|
386 // can be focusable, focused, protected. readonly, unavailable, selected |
|
387 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, |
|
388 nsGkAtoms::password, eIgnoreCase)) { |
|
389 state |= states::PROTECTED; |
|
390 } |
|
391 |
|
392 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) { |
|
393 state |= states::READONLY; |
|
394 } |
|
395 |
|
396 // Is it an <input> or a <textarea> ? |
|
397 HTMLInputElement* input = HTMLInputElement::FromContent(mContent); |
|
398 state |= input && input->IsSingleLineTextControl() ? |
|
399 states::SINGLE_LINE : states::MULTI_LINE; |
|
400 |
|
401 if (state & (states::PROTECTED | states::MULTI_LINE | states::READONLY | |
|
402 states::UNAVAILABLE)) |
|
403 return state; |
|
404 |
|
405 // Expose autocomplete states if this input is part of autocomplete widget. |
|
406 Accessible* widget = ContainerWidget(); |
|
407 if (widget && widget-IsAutoComplete()) { |
|
408 state |= states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION; |
|
409 return state; |
|
410 } |
|
411 |
|
412 // Expose autocomplete state if it has associated autocomplete list. |
|
413 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::list)) |
|
414 return state | states::SUPPORTS_AUTOCOMPLETION | states::HASPOPUP; |
|
415 |
|
416 // Ordinal XUL textboxes don't support autocomplete. |
|
417 if (!XULWidgetElm() && Preferences::GetBool("browser.formfill.enable")) { |
|
418 // Check to see if autocompletion is allowed on this input. We don't expose |
|
419 // it for password fields even though the entire password can be remembered |
|
420 // for a page if the user asks it to be. However, the kind of autocomplete |
|
421 // we're talking here is based on what the user types, where a popup of |
|
422 // possible choices comes up. |
|
423 nsAutoString autocomplete; |
|
424 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, |
|
425 autocomplete); |
|
426 |
|
427 if (!autocomplete.LowerCaseEqualsLiteral("off")) { |
|
428 nsIContent* formContent = input->GetFormElement(); |
|
429 if (formContent) { |
|
430 formContent->GetAttr(kNameSpaceID_None, |
|
431 nsGkAtoms::autocomplete, autocomplete); |
|
432 } |
|
433 |
|
434 if (!formContent || !autocomplete.LowerCaseEqualsLiteral("off")) |
|
435 state |= states::SUPPORTS_AUTOCOMPLETION; |
|
436 } |
|
437 } |
|
438 |
|
439 return state; |
|
440 } |
|
441 |
|
442 uint8_t |
|
443 HTMLTextFieldAccessible::ActionCount() |
|
444 { |
|
445 return 1; |
|
446 } |
|
447 |
|
448 NS_IMETHODIMP |
|
449 HTMLTextFieldAccessible::GetActionName(uint8_t aIndex, nsAString& aName) |
|
450 { |
|
451 if (aIndex == eAction_Click) { |
|
452 aName.AssignLiteral("activate"); |
|
453 return NS_OK; |
|
454 } |
|
455 return NS_ERROR_INVALID_ARG; |
|
456 } |
|
457 |
|
458 NS_IMETHODIMP |
|
459 HTMLTextFieldAccessible::DoAction(uint8_t aIndex) |
|
460 { |
|
461 if (aIndex == 0) |
|
462 return TakeFocus(); |
|
463 |
|
464 return NS_ERROR_INVALID_ARG; |
|
465 } |
|
466 |
|
467 already_AddRefed<nsIEditor> |
|
468 HTMLTextFieldAccessible::GetEditor() const |
|
469 { |
|
470 nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(mContent)); |
|
471 if (!editableElt) |
|
472 return nullptr; |
|
473 |
|
474 // nsGenericHTMLElement::GetEditor has a security check. |
|
475 // Make sure we're not restricted by the permissions of |
|
476 // whatever script is currently running. |
|
477 mozilla::dom::AutoNoJSAPI nojsapi; |
|
478 |
|
479 nsCOMPtr<nsIEditor> editor; |
|
480 editableElt->GetEditor(getter_AddRefs(editor)); |
|
481 |
|
482 return editor.forget(); |
|
483 } |
|
484 |
|
485 //////////////////////////////////////////////////////////////////////////////// |
|
486 // HTMLTextFieldAccessible: Widgets |
|
487 |
|
488 bool |
|
489 HTMLTextFieldAccessible::IsWidget() const |
|
490 { |
|
491 return true; |
|
492 } |
|
493 |
|
494 Accessible* |
|
495 HTMLTextFieldAccessible::ContainerWidget() const |
|
496 { |
|
497 return mParent && mParent->Role() == roles::AUTOCOMPLETE ? mParent : nullptr; |
|
498 } |
|
499 |
|
500 |
|
501 //////////////////////////////////////////////////////////////////////////////// |
|
502 // HTMLFileInputAccessible |
|
503 //////////////////////////////////////////////////////////////////////////////// |
|
504 |
|
505 HTMLFileInputAccessible:: |
|
506 HTMLFileInputAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
|
507 HyperTextAccessibleWrap(aContent, aDoc) |
|
508 { |
|
509 mType = eHTMLFileInputType; |
|
510 } |
|
511 |
|
512 role |
|
513 HTMLFileInputAccessible::NativeRole() |
|
514 { |
|
515 // JAWS wants a text container, others don't mind. No specific role in |
|
516 // AT APIs. |
|
517 return roles::TEXT_CONTAINER; |
|
518 } |
|
519 |
|
520 nsresult |
|
521 HTMLFileInputAccessible::HandleAccEvent(AccEvent* aEvent) |
|
522 { |
|
523 nsresult rv = HyperTextAccessibleWrap::HandleAccEvent(aEvent); |
|
524 NS_ENSURE_SUCCESS(rv, rv); |
|
525 |
|
526 // Redirect state change events for inherited states to child controls. Note, |
|
527 // unavailable state is not redirected. That's a standard for unavailable |
|
528 // state handling. |
|
529 AccStateChangeEvent* event = downcast_accEvent(aEvent); |
|
530 if (event && |
|
531 (event->GetState() == states::BUSY || |
|
532 event->GetState() == states::REQUIRED || |
|
533 event->GetState() == states::HASPOPUP || |
|
534 event->GetState() == states::INVALID)) { |
|
535 Accessible* button = GetChildAt(0); |
|
536 if (button && button->Role() == roles::PUSHBUTTON) { |
|
537 nsRefPtr<AccStateChangeEvent> childEvent = |
|
538 new AccStateChangeEvent(button, event->GetState(), |
|
539 event->IsStateEnabled(), event->FromUserInput()); |
|
540 nsEventShell::FireEvent(childEvent); |
|
541 } |
|
542 } |
|
543 |
|
544 return NS_OK; |
|
545 } |
|
546 |
|
547 |
|
548 //////////////////////////////////////////////////////////////////////////////// |
|
549 // HTMLSpinnerAccessible |
|
550 //////////////////////////////////////////////////////////////////////////////// |
|
551 |
|
552 role |
|
553 HTMLSpinnerAccessible::NativeRole() |
|
554 { |
|
555 return roles::SPINBUTTON; |
|
556 } |
|
557 |
|
558 void |
|
559 HTMLSpinnerAccessible::Value(nsString& aValue) |
|
560 { |
|
561 AccessibleWrap::Value(aValue); |
|
562 if (!aValue.IsEmpty()) |
|
563 return; |
|
564 |
|
565 HTMLInputElement::FromContent(mContent)->GetValue(aValue); |
|
566 } |
|
567 |
|
568 double |
|
569 HTMLSpinnerAccessible::MaxValue() const |
|
570 { |
|
571 double value = AccessibleWrap::MaxValue(); |
|
572 if (!IsNaN(value)) |
|
573 return value; |
|
574 |
|
575 return HTMLInputElement::FromContent(mContent)->GetMaximum().toDouble(); |
|
576 } |
|
577 |
|
578 |
|
579 double |
|
580 HTMLSpinnerAccessible::MinValue() const |
|
581 { |
|
582 double value = AccessibleWrap::MinValue(); |
|
583 if (!IsNaN(value)) |
|
584 return value; |
|
585 |
|
586 return HTMLInputElement::FromContent(mContent)->GetMinimum().toDouble(); |
|
587 } |
|
588 |
|
589 double |
|
590 HTMLSpinnerAccessible::Step() const |
|
591 { |
|
592 double value = AccessibleWrap::Step(); |
|
593 if (!IsNaN(value)) |
|
594 return value; |
|
595 |
|
596 return HTMLInputElement::FromContent(mContent)->GetStep().toDouble(); |
|
597 } |
|
598 |
|
599 double |
|
600 HTMLSpinnerAccessible::CurValue() const |
|
601 { |
|
602 double value = AccessibleWrap::CurValue(); |
|
603 if (!IsNaN(value)) |
|
604 return value; |
|
605 |
|
606 return HTMLInputElement::FromContent(mContent)->GetValueAsDecimal().toDouble(); |
|
607 } |
|
608 |
|
609 bool |
|
610 HTMLSpinnerAccessible::SetCurValue(double aValue) |
|
611 { |
|
612 ErrorResult er; |
|
613 HTMLInputElement::FromContent(mContent)->SetValueAsNumber(aValue, er); |
|
614 return !er.Failed(); |
|
615 } |
|
616 |
|
617 |
|
618 //////////////////////////////////////////////////////////////////////////////// |
|
619 // HTMLRangeAccessible |
|
620 //////////////////////////////////////////////////////////////////////////////// |
|
621 |
|
622 role |
|
623 HTMLRangeAccessible::NativeRole() |
|
624 { |
|
625 return roles::SLIDER; |
|
626 } |
|
627 |
|
628 bool |
|
629 HTMLRangeAccessible::IsWidget() const |
|
630 { |
|
631 return true; |
|
632 } |
|
633 |
|
634 void |
|
635 HTMLRangeAccessible::Value(nsString& aValue) |
|
636 { |
|
637 LeafAccessible::Value(aValue); |
|
638 if (!aValue.IsEmpty()) |
|
639 return; |
|
640 |
|
641 HTMLInputElement::FromContent(mContent)->GetValue(aValue); |
|
642 } |
|
643 |
|
644 double |
|
645 HTMLRangeAccessible::MaxValue() const |
|
646 { |
|
647 double value = LeafAccessible::MaxValue(); |
|
648 if (!IsNaN(value)) |
|
649 return value; |
|
650 |
|
651 return HTMLInputElement::FromContent(mContent)->GetMaximum().toDouble(); |
|
652 } |
|
653 |
|
654 double |
|
655 HTMLRangeAccessible::MinValue() const |
|
656 { |
|
657 double value = LeafAccessible::MinValue(); |
|
658 if (!IsNaN(value)) |
|
659 return value; |
|
660 |
|
661 return HTMLInputElement::FromContent(mContent)->GetMinimum().toDouble(); |
|
662 } |
|
663 |
|
664 double |
|
665 HTMLRangeAccessible::Step() const |
|
666 { |
|
667 double value = LeafAccessible::Step(); |
|
668 if (!IsNaN(value)) |
|
669 return value; |
|
670 |
|
671 return HTMLInputElement::FromContent(mContent)->GetStep().toDouble(); |
|
672 } |
|
673 |
|
674 double |
|
675 HTMLRangeAccessible::CurValue() const |
|
676 { |
|
677 double value = LeafAccessible::CurValue(); |
|
678 if (!IsNaN(value)) |
|
679 return value; |
|
680 |
|
681 return HTMLInputElement::FromContent(mContent)->GetValueAsDecimal().toDouble(); |
|
682 } |
|
683 |
|
684 bool |
|
685 HTMLRangeAccessible::SetCurValue(double aValue) |
|
686 { |
|
687 ErrorResult er; |
|
688 HTMLInputElement::FromContent(mContent)->SetValueAsNumber(aValue, er); |
|
689 return !er.Failed(); |
|
690 } |
|
691 |
|
692 |
|
693 //////////////////////////////////////////////////////////////////////////////// |
|
694 // HTMLGroupboxAccessible |
|
695 //////////////////////////////////////////////////////////////////////////////// |
|
696 |
|
697 HTMLGroupboxAccessible:: |
|
698 HTMLGroupboxAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
|
699 HyperTextAccessibleWrap(aContent, aDoc) |
|
700 { |
|
701 } |
|
702 |
|
703 role |
|
704 HTMLGroupboxAccessible::NativeRole() |
|
705 { |
|
706 return roles::GROUPING; |
|
707 } |
|
708 |
|
709 nsIContent* |
|
710 HTMLGroupboxAccessible::GetLegend() |
|
711 { |
|
712 for (nsIContent* legendContent = mContent->GetFirstChild(); legendContent; |
|
713 legendContent = legendContent->GetNextSibling()) { |
|
714 if (legendContent->NodeInfo()->Equals(nsGkAtoms::legend, |
|
715 mContent->GetNameSpaceID())) { |
|
716 // Either XHTML namespace or no namespace |
|
717 return legendContent; |
|
718 } |
|
719 } |
|
720 |
|
721 return nullptr; |
|
722 } |
|
723 |
|
724 ENameValueFlag |
|
725 HTMLGroupboxAccessible::NativeName(nsString& aName) |
|
726 { |
|
727 ENameValueFlag nameFlag = Accessible::NativeName(aName); |
|
728 if (!aName.IsEmpty()) |
|
729 return nameFlag; |
|
730 |
|
731 nsIContent* legendContent = GetLegend(); |
|
732 if (legendContent) |
|
733 nsTextEquivUtils::AppendTextEquivFromContent(this, legendContent, &aName); |
|
734 |
|
735 return eNameOK; |
|
736 } |
|
737 |
|
738 Relation |
|
739 HTMLGroupboxAccessible::RelationByType(RelationType aType) |
|
740 { |
|
741 Relation rel = HyperTextAccessibleWrap::RelationByType(aType); |
|
742 // No override for label, so use <legend> for this <fieldset> |
|
743 if (aType == RelationType::LABELLED_BY) |
|
744 rel.AppendTarget(mDoc, GetLegend()); |
|
745 |
|
746 return rel; |
|
747 } |
|
748 |
|
749 //////////////////////////////////////////////////////////////////////////////// |
|
750 // HTMLLegendAccessible |
|
751 //////////////////////////////////////////////////////////////////////////////// |
|
752 |
|
753 HTMLLegendAccessible:: |
|
754 HTMLLegendAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
|
755 HyperTextAccessibleWrap(aContent, aDoc) |
|
756 { |
|
757 } |
|
758 |
|
759 Relation |
|
760 HTMLLegendAccessible::RelationByType(RelationType aType) |
|
761 { |
|
762 Relation rel = HyperTextAccessibleWrap::RelationByType(aType); |
|
763 if (aType != RelationType::LABEL_FOR) |
|
764 return rel; |
|
765 |
|
766 Accessible* groupbox = Parent(); |
|
767 if (groupbox && groupbox->Role() == roles::GROUPING) |
|
768 rel.AppendTarget(groupbox); |
|
769 |
|
770 return rel; |
|
771 } |
|
772 |
|
773 role |
|
774 HTMLLegendAccessible::NativeRole() |
|
775 { |
|
776 return roles::LABEL; |
|
777 } |
|
778 |
|
779 //////////////////////////////////////////////////////////////////////////////// |
|
780 // HTMLFigureAccessible |
|
781 //////////////////////////////////////////////////////////////////////////////// |
|
782 |
|
783 HTMLFigureAccessible:: |
|
784 HTMLFigureAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
|
785 HyperTextAccessibleWrap(aContent, aDoc) |
|
786 { |
|
787 } |
|
788 |
|
789 already_AddRefed<nsIPersistentProperties> |
|
790 HTMLFigureAccessible::NativeAttributes() |
|
791 { |
|
792 nsCOMPtr<nsIPersistentProperties> attributes = |
|
793 HyperTextAccessibleWrap::NativeAttributes(); |
|
794 |
|
795 // Expose figure xml-role. |
|
796 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles, |
|
797 NS_LITERAL_STRING("figure")); |
|
798 return attributes.forget(); |
|
799 } |
|
800 |
|
801 role |
|
802 HTMLFigureAccessible::NativeRole() |
|
803 { |
|
804 return roles::FIGURE; |
|
805 } |
|
806 |
|
807 ENameValueFlag |
|
808 HTMLFigureAccessible::NativeName(nsString& aName) |
|
809 { |
|
810 ENameValueFlag nameFlag = HyperTextAccessibleWrap::NativeName(aName); |
|
811 if (!aName.IsEmpty()) |
|
812 return nameFlag; |
|
813 |
|
814 nsIContent* captionContent = Caption(); |
|
815 if (captionContent) |
|
816 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName); |
|
817 |
|
818 return eNameOK; |
|
819 } |
|
820 |
|
821 Relation |
|
822 HTMLFigureAccessible::RelationByType(RelationType aType) |
|
823 { |
|
824 Relation rel = HyperTextAccessibleWrap::RelationByType(aType); |
|
825 if (aType == RelationType::LABELLED_BY) |
|
826 rel.AppendTarget(mDoc, Caption()); |
|
827 |
|
828 return rel; |
|
829 } |
|
830 |
|
831 nsIContent* |
|
832 HTMLFigureAccessible::Caption() const |
|
833 { |
|
834 for (nsIContent* childContent = mContent->GetFirstChild(); childContent; |
|
835 childContent = childContent->GetNextSibling()) { |
|
836 if (childContent->NodeInfo()->Equals(nsGkAtoms::figcaption, |
|
837 mContent->GetNameSpaceID())) { |
|
838 return childContent; |
|
839 } |
|
840 } |
|
841 |
|
842 return nullptr; |
|
843 } |
|
844 |
|
845 //////////////////////////////////////////////////////////////////////////////// |
|
846 // HTMLFigcaptionAccessible |
|
847 //////////////////////////////////////////////////////////////////////////////// |
|
848 |
|
849 HTMLFigcaptionAccessible:: |
|
850 HTMLFigcaptionAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
|
851 HyperTextAccessibleWrap(aContent, aDoc) |
|
852 { |
|
853 } |
|
854 |
|
855 role |
|
856 HTMLFigcaptionAccessible::NativeRole() |
|
857 { |
|
858 return roles::CAPTION; |
|
859 } |
|
860 |
|
861 Relation |
|
862 HTMLFigcaptionAccessible::RelationByType(RelationType aType) |
|
863 { |
|
864 Relation rel = HyperTextAccessibleWrap::RelationByType(aType); |
|
865 if (aType != RelationType::LABEL_FOR) |
|
866 return rel; |
|
867 |
|
868 Accessible* figure = Parent(); |
|
869 if (figure && |
|
870 figure->GetContent()->NodeInfo()->Equals(nsGkAtoms::figure, |
|
871 mContent->GetNameSpaceID())) { |
|
872 rel.AppendTarget(figure); |
|
873 } |
|
874 |
|
875 return rel; |
|
876 } |