Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / accessible / src / html / nsHTMLFormControlAccessible.cpp
blob56356972d6ab95d50e7fb3b765376c7b3936106f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Author: Eric Vaughan (evaughan@netscape.com)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 // NOTE: alphabetically ordered
40 #include "nsAccessibleTreeWalker.h"
41 #include "nsAccessibilityAtoms.h"
42 #include "nsHTMLFormControlAccessible.h"
43 #include "nsIDOMDocument.h"
44 #include "nsIDOMHTMLInputElement.h"
45 #include "nsIDOMNSHTMLElement.h"
46 #include "nsIDOMNSEditableElement.h"
47 #include "nsIDOMNSHTMLButtonElement.h"
48 #include "nsIDOMHTMLFormElement.h"
49 #include "nsIDOMHTMLLegendElement.h"
50 #include "nsIDOMHTMLTextAreaElement.h"
51 #include "nsIEditor.h"
52 #include "nsIFrame.h"
53 #include "nsINameSpaceManager.h"
54 #include "nsISelectionController.h"
55 #include "jsapi.h"
56 #include "nsIJSContextStack.h"
57 #include "nsIServiceManager.h"
58 #include "nsITextControlFrame.h"
60 // --- checkbox -----
62 nsHTMLCheckboxAccessible::nsHTMLCheckboxAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
63 nsFormControlAccessible(aNode, aShell)
67 NS_IMETHODIMP nsHTMLCheckboxAccessible::GetRole(PRUint32 *_retval)
69 *_retval = nsIAccessibleRole::ROLE_CHECKBUTTON;
70 return NS_OK;
73 NS_IMETHODIMP nsHTMLCheckboxAccessible::GetNumActions(PRUint8 *_retval)
75 *_retval = 1;
76 return NS_OK;
79 NS_IMETHODIMP nsHTMLCheckboxAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
81 if (aIndex == eAction_Click) { // 0 is the magic value for default action
82 // check or uncheck
83 PRUint32 state;
84 nsresult rv = GetStateInternal(&state, nsnull);
85 NS_ENSURE_SUCCESS(rv, rv);
87 if (state & nsIAccessibleStates::STATE_CHECKED)
88 aName.AssignLiteral("uncheck");
89 else
90 aName.AssignLiteral("check");
92 return NS_OK;
94 return NS_ERROR_INVALID_ARG;
97 NS_IMETHODIMP nsHTMLCheckboxAccessible::DoAction(PRUint8 index)
99 if (index == 0) { // 0 is the magic value for default action
100 return DoCommand();
102 return NS_ERROR_INVALID_ARG;
105 nsresult
106 nsHTMLCheckboxAccessible::GetStateInternal(PRUint32 *aState,
107 PRUint32 *aExtraState)
109 nsresult rv = nsFormControlAccessible::GetStateInternal(aState, aExtraState);
110 NS_ENSURE_A11Y_SUCCESS(rv, rv);
112 *aState |= nsIAccessibleStates::STATE_CHECKABLE;
114 PRBool checked = PR_FALSE; // Radio buttons and check boxes can be checked
116 nsCOMPtr<nsIDOMHTMLInputElement> htmlCheckboxElement(do_QueryInterface(mDOMNode));
117 if (htmlCheckboxElement)
118 htmlCheckboxElement->GetChecked(&checked);
120 if (checked)
121 *aState |= nsIAccessibleStates::STATE_CHECKED;
123 return NS_OK;
126 //------ Radio button -------
128 nsHTMLRadioButtonAccessible::nsHTMLRadioButtonAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
129 nsRadioButtonAccessible(aNode, aShell)
133 nsresult
134 nsHTMLRadioButtonAccessible::GetStateInternal(PRUint32 *aState,
135 PRUint32 *aExtraState)
137 nsresult rv = nsAccessibleWrap::GetStateInternal(aState, aExtraState);
138 NS_ENSURE_A11Y_SUCCESS(rv, rv);
140 *aState |= nsIAccessibleStates::STATE_CHECKABLE;
142 PRBool checked = PR_FALSE; // Radio buttons and check boxes can be checked
144 nsCOMPtr<nsIDOMHTMLInputElement> htmlRadioElement(do_QueryInterface(mDOMNode));
145 if (htmlRadioElement)
146 htmlRadioElement->GetChecked(&checked);
148 if (checked)
149 *aState |= nsIAccessibleStates::STATE_CHECKED;
151 return NS_OK;
154 nsresult
155 nsHTMLRadioButtonAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
157 NS_ENSURE_ARG_POINTER(aAttributes);
158 NS_ENSURE_TRUE(mDOMNode, NS_ERROR_FAILURE);
160 nsresult rv = nsRadioButtonAccessible::GetAttributesInternal(aAttributes);
161 NS_ENSURE_SUCCESS(rv, rv);
163 nsAutoString nsURI;
164 mDOMNode->GetNamespaceURI(nsURI);
165 nsAutoString tagName;
166 mDOMNode->GetLocalName(tagName);
168 nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
169 NS_ENSURE_STATE(content);
171 nsAutoString type;
172 content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::type, type);
173 nsAutoString name;
174 content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::name, name);
176 nsCOMPtr<nsIDOMNodeList> inputs;
178 nsCOMPtr<nsIDOMHTMLInputElement> radio(do_QueryInterface(mDOMNode));
179 nsCOMPtr<nsIDOMHTMLFormElement> form;
180 radio->GetForm(getter_AddRefs(form));
181 if (form) {
182 form->GetElementsByTagNameNS(nsURI, tagName, getter_AddRefs(inputs));
183 } else {
184 nsCOMPtr<nsIDOMDocument> document;
185 mDOMNode->GetOwnerDocument(getter_AddRefs(document));
186 if (document)
187 document->GetElementsByTagNameNS(nsURI, tagName, getter_AddRefs(inputs));
190 NS_ENSURE_TRUE(inputs, NS_OK);
192 PRUint32 inputsCount = 0;
193 inputs->GetLength(&inputsCount);
195 // Get posinset and setsize.
196 PRInt32 indexOf = 0;
197 PRInt32 count = 0;
199 for (PRUint32 index = 0; index < inputsCount; index++) {
200 nsCOMPtr<nsIDOMNode> itemNode;
201 inputs->Item(index, getter_AddRefs(itemNode));
203 nsCOMPtr<nsIContent> item(do_QueryInterface(itemNode));
204 if (item &&
205 item->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type,
206 type, eCaseMatters) &&
207 item->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::name,
208 name, eCaseMatters)) {
210 count++;
212 if (itemNode == mDOMNode)
213 indexOf = count;
217 nsAccUtils::SetAccGroupAttrs(aAttributes, 0, indexOf, count);
219 return NS_OK;
222 // ----- Button -----
224 nsHTMLButtonAccessible::nsHTMLButtonAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
225 nsHyperTextAccessibleWrap(aNode, aShell)
229 NS_IMETHODIMP nsHTMLButtonAccessible::GetNumActions(PRUint8 *_retval)
231 *_retval = 1;
232 return NS_OK;
235 NS_IMETHODIMP nsHTMLButtonAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
237 if (aIndex == eAction_Click) {
238 aName.AssignLiteral("press");
239 return NS_OK;
241 return NS_ERROR_INVALID_ARG;
244 NS_IMETHODIMP nsHTMLButtonAccessible::DoAction(PRUint8 index)
246 if (index == eAction_Click) {
247 return DoCommand();
249 return NS_ERROR_INVALID_ARG;
252 nsresult
253 nsHTMLButtonAccessible::GetStateInternal(PRUint32 *aState,
254 PRUint32 *aExtraState)
256 nsresult rv = nsHyperTextAccessibleWrap::GetStateInternal(aState,
257 aExtraState);
258 NS_ENSURE_A11Y_SUCCESS(rv, rv);
260 nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mDOMNode));
261 NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
263 nsAutoString buttonType;
264 element->GetAttribute(NS_LITERAL_STRING("type"), buttonType);
265 if (buttonType.LowerCaseEqualsLiteral("submit"))
266 *aState |= nsIAccessibleStates::STATE_DEFAULT;
268 return NS_OK;
271 NS_IMETHODIMP nsHTMLButtonAccessible::GetRole(PRUint32 *_retval)
273 *_retval = nsIAccessibleRole::ROLE_PUSHBUTTON;
274 return NS_OK;
277 nsresult
278 nsHTMLButtonAccessible::GetNameInternal(nsAString& aName)
280 nsresult rv = nsAccessible::GetNameInternal(aName);
281 if (!aName.IsEmpty())
282 return NS_OK;
284 nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
286 // No name from HTML or ARIA
287 nsAutoString name;
288 if (!content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::value,
289 name) &&
290 !content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::alt,
291 name)) {
292 // Use the button's (default) label if nothing else works
293 nsIFrame* frame = GetFrame();
294 if (frame) {
295 nsIFormControlFrame* fcFrame = nsnull;
296 CallQueryInterface(frame, &fcFrame);
297 if (fcFrame)
298 fcFrame->GetFormProperty(nsAccessibilityAtoms::defaultLabel, name);
302 if (name.IsEmpty() &&
303 !content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::src,
304 name)) {
305 content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::data, name);
308 name.CompressWhitespace();
309 aName = name;
311 return NS_OK;
315 // ----- HTML 4 Button: can contain arbitrary HTML content -----
317 nsHTML4ButtonAccessible::nsHTML4ButtonAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
318 nsHyperTextAccessibleWrap(aNode, aShell)
322 NS_IMETHODIMP nsHTML4ButtonAccessible::GetNumActions(PRUint8 *_retval)
324 *_retval = 1;
325 return NS_OK;;
328 NS_IMETHODIMP nsHTML4ButtonAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
330 if (aIndex == eAction_Click) {
331 aName.AssignLiteral("press");
332 return NS_OK;
334 return NS_ERROR_INVALID_ARG;
337 NS_IMETHODIMP nsHTML4ButtonAccessible::DoAction(PRUint8 index)
339 if (index == 0) {
340 return DoCommand();
342 return NS_ERROR_INVALID_ARG;
345 NS_IMETHODIMP nsHTML4ButtonAccessible::GetRole(PRUint32 *_retval)
347 *_retval = nsIAccessibleRole::ROLE_PUSHBUTTON;
348 return NS_OK;
351 nsresult
352 nsHTML4ButtonAccessible::GetStateInternal(PRUint32 *aState,
353 PRUint32 *aExtraState)
355 nsresult rv = nsHyperTextAccessibleWrap::GetStateInternal(aState,
356 aExtraState);
357 NS_ENSURE_A11Y_SUCCESS(rv, rv);
359 nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mDOMNode));
360 NS_ASSERTION(element, "No element for button's dom node!");
362 *aState |= nsIAccessibleStates::STATE_FOCUSABLE;
364 nsAutoString buttonType;
365 element->GetAttribute(NS_LITERAL_STRING("type"), buttonType);
366 if (buttonType.LowerCaseEqualsLiteral("submit"))
367 *aState |= nsIAccessibleStates::STATE_DEFAULT;
369 return NS_OK;
372 // --- textfield -----
374 nsHTMLTextFieldAccessible::nsHTMLTextFieldAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
375 nsHyperTextAccessibleWrap(aNode, aShell)
379 NS_IMPL_ISUPPORTS_INHERITED3(nsHTMLTextFieldAccessible, nsAccessible, nsHyperTextAccessible, nsIAccessibleText, nsIAccessibleEditableText)
381 NS_IMETHODIMP nsHTMLTextFieldAccessible::GetRole(PRUint32 *aRole)
383 *aRole = nsIAccessibleRole::ROLE_ENTRY;
384 nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
385 if (content &&
386 content->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type,
387 nsAccessibilityAtoms::password, eIgnoreCase)) {
388 *aRole = nsIAccessibleRole::ROLE_PASSWORD_TEXT;
390 return NS_OK;
393 nsresult
394 nsHTMLTextFieldAccessible::GetNameInternal(nsAString& aName)
396 nsresult rv = nsAccessible::GetNameInternal(aName);
397 NS_ENSURE_SUCCESS(rv, rv);
399 if (!aName.IsEmpty())
400 return NS_OK;
402 nsCOMPtr<nsIContent> content = do_QueryInterface(mDOMNode);
403 if (!content->GetBindingParent())
404 return NS_OK;
406 // XXX: bug 459640
407 // There's a binding parent.
408 // This means we're part of another control, so use parent accessible for name.
409 // This ensures that a textbox inside of a XUL widget gets
410 // an accessible name.
411 nsCOMPtr<nsIAccessible> parent;
412 rv = GetParent(getter_AddRefs(parent));
413 return parent ? parent->GetName(aName) : rv;
416 NS_IMETHODIMP nsHTMLTextFieldAccessible::GetValue(nsAString& _retval)
418 PRUint32 state;
419 nsresult rv = GetStateInternal(&state, nsnull);
420 NS_ENSURE_SUCCESS(rv, rv);
422 if (state & nsIAccessibleStates::STATE_PROTECTED) // Don't return password text!
423 return NS_ERROR_FAILURE;
425 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea(do_QueryInterface(mDOMNode));
426 if (textArea) {
427 return textArea->GetValue(_retval);
430 nsCOMPtr<nsIDOMHTMLInputElement> inputElement(do_QueryInterface(mDOMNode));
431 if (inputElement) {
432 return inputElement->GetValue(_retval);
435 return NS_ERROR_FAILURE;
438 nsresult
439 nsHTMLTextFieldAccessible::GetStateInternal(PRUint32 *aState,
440 PRUint32 *aExtraState)
442 nsresult rv = nsHyperTextAccessibleWrap::GetStateInternal(aState,
443 aExtraState);
444 NS_ENSURE_A11Y_SUCCESS(rv, rv);
446 // can be focusable, focused, protected. readonly, unavailable, selected
447 nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
448 NS_ASSERTION(content, "Should not have gotten here if upcalled GetExtState() succeeded");
450 if (content->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type,
451 nsAccessibilityAtoms::password, eIgnoreCase)) {
452 *aState |= nsIAccessibleStates::STATE_PROTECTED;
454 else {
455 nsCOMPtr<nsIAccessible> parent;
456 GetParent(getter_AddRefs(parent));
457 if (nsAccUtils::Role(parent) == nsIAccessibleRole::ROLE_AUTOCOMPLETE)
458 *aState |= nsIAccessibleStates::STATE_HASPOPUP;
461 if (content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::readonly)) {
462 *aState |= nsIAccessibleStates::STATE_READONLY;
465 if (!aExtraState)
466 return NS_OK;
468 nsCOMPtr<nsIDOMHTMLInputElement> htmlInput(do_QueryInterface(mDOMNode));
469 // Is it an <input> or a <textarea> ?
470 if (htmlInput) {
471 *aExtraState |= nsIAccessibleStates::EXT_STATE_SINGLE_LINE;
473 else {
474 *aExtraState |= nsIAccessibleStates::EXT_STATE_MULTI_LINE;
477 if (!(*aExtraState & nsIAccessibleStates::EXT_STATE_EDITABLE))
478 return NS_OK;
480 nsCOMPtr<nsIContent> bindingContent = content->GetBindingParent();
481 if (bindingContent &&
482 bindingContent->NodeInfo()->Equals(nsAccessibilityAtoms::textbox,
483 kNameSpaceID_XUL) &&
484 bindingContent->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type,
485 nsAccessibilityAtoms::autocomplete,
486 eIgnoreCase)) {
487 // If parent is XUL textbox and value of @type attribute is "autocomplete",
488 // then this accessible supports autocompletion.
489 *aExtraState |= nsIAccessibleStates::EXT_STATE_SUPPORTS_AUTOCOMPLETION;
490 } else if (gIsFormFillEnabled && htmlInput &&
491 !(*aState & nsIAccessibleStates::STATE_PROTECTED)) {
492 // Check to see if autocompletion is allowed on this input. We don't expose
493 // it for password fields even though the entire password can be remembered
494 // for a page if the user asks it to be. However, the kind of autocomplete
495 // we're talking here is based on what the user types, where a popup of
496 // possible choices comes up.
497 nsAutoString autocomplete;
498 content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::autocomplete,
499 autocomplete);
501 if (!autocomplete.LowerCaseEqualsLiteral("off")) {
502 nsCOMPtr<nsIDOMHTMLFormElement> form;
503 htmlInput->GetForm(getter_AddRefs(form));
504 nsCOMPtr<nsIContent> formContent(do_QueryInterface(form));
505 if (formContent) {
506 formContent->GetAttr(kNameSpaceID_None,
507 nsAccessibilityAtoms::autocomplete, autocomplete);
510 if (!formContent || !autocomplete.LowerCaseEqualsLiteral("off"))
511 *aExtraState |= nsIAccessibleStates::EXT_STATE_SUPPORTS_AUTOCOMPLETION;
515 return NS_OK;
518 NS_IMETHODIMP nsHTMLTextFieldAccessible::GetNumActions(PRUint8 *_retval)
520 *_retval = 1;
521 return NS_OK;;
524 NS_IMETHODIMP nsHTMLTextFieldAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
526 if (aIndex == eAction_Click) {
527 aName.AssignLiteral("activate");
528 return NS_OK;
530 return NS_ERROR_INVALID_ARG;
533 NS_IMETHODIMP nsHTMLTextFieldAccessible::DoAction(PRUint8 index)
535 if (index == 0) {
536 nsCOMPtr<nsIDOMNSHTMLElement> element(do_QueryInterface(mDOMNode));
537 if ( element ) {
538 return element->Focus();
540 return NS_ERROR_FAILURE;
542 return NS_ERROR_INVALID_ARG;
545 NS_IMETHODIMP nsHTMLTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor)
547 *aEditor = nsnull;
548 nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(mDOMNode));
549 NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE);
551 // nsGenericHTMLElement::GetEditor has a security check.
552 // Make sure we're not restricted by the permissions of
553 // whatever script is currently running.
554 nsCOMPtr<nsIJSContextStack> stack =
555 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
556 PRBool pushed = stack && NS_SUCCEEDED(stack->Push(nsnull));
558 nsCOMPtr<nsIEditor> editor;
559 nsresult rv = editableElt->GetEditor(aEditor);
561 if (pushed) {
562 JSContext* cx;
563 stack->Pop(&cx);
564 NS_ASSERTION(!cx, "context should be null");
567 return rv;
570 // --- groupbox -----
573 * The HTML for this is <fieldset> <legend>box-title</legend> form elements </fieldset>
576 nsHTMLGroupboxAccessible::nsHTMLGroupboxAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
577 nsHyperTextAccessibleWrap(aNode, aShell)
581 NS_IMETHODIMP nsHTMLGroupboxAccessible::GetRole(PRUint32 *_retval)
583 *_retval = nsIAccessibleRole::ROLE_GROUPING;
584 return NS_OK;
587 nsIContent* nsHTMLGroupboxAccessible::GetLegend()
589 nsCOMPtr<nsIContent> content = do_QueryInterface(mDOMNode);
590 NS_ENSURE_TRUE(content, nsnull);
592 nsresult count = 0;
593 nsIContent *testLegendContent;
594 while ((testLegendContent = content->GetChildAt(count ++ )) != nsnull) {
595 if (testLegendContent->NodeInfo()->Equals(nsAccessibilityAtoms::legend,
596 content->GetNameSpaceID())) {
597 // Either XHTML namespace or no namespace
598 return testLegendContent;
602 return nsnull;
605 nsresult
606 nsHTMLGroupboxAccessible::GetNameInternal(nsAString& aName)
608 nsresult rv = nsAccessible::GetNameInternal(aName);
609 NS_ENSURE_SUCCESS(rv, rv);
611 if (!aName.IsEmpty())
612 return NS_OK;
614 nsIContent *legendContent = GetLegend();
615 if (legendContent) {
616 return AppendFlatStringFromSubtree(legendContent, &aName);
619 return NS_OK;
622 NS_IMETHODIMP
623 nsHTMLGroupboxAccessible::GetAccessibleRelated(PRUint32 aRelationType,
624 nsIAccessible **aRelated)
626 if (!mDOMNode) {
627 return NS_ERROR_FAILURE;
629 NS_ENSURE_ARG_POINTER(aRelated);
631 *aRelated = nsnull;
633 nsresult rv = nsHyperTextAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
634 if (NS_FAILED(rv) || *aRelated) {
635 // Either the node is shut down, or another relation mechanism has been used
636 return rv;
639 if (aRelationType == nsIAccessibleRelation::RELATION_LABELLED_BY) {
640 // No override for label, so use <legend> for this <fieldset>
641 nsCOMPtr<nsIDOMNode> legendNode = do_QueryInterface(GetLegend());
642 if (legendNode) {
643 GetAccService()->GetAccessibleInWeakShell(legendNode, mWeakShell, aRelated);
647 return NS_OK;
650 nsHTMLLegendAccessible::nsHTMLLegendAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
651 nsHyperTextAccessibleWrap(aNode, aShell)
655 NS_IMETHODIMP
656 nsHTMLLegendAccessible::GetAccessibleRelated(PRUint32 aRelationType,
657 nsIAccessible **aRelated)
659 *aRelated = nsnull;
661 nsresult rv = nsHyperTextAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
662 if (NS_FAILED(rv) || *aRelated) {
663 // Either the node is shut down, or another relation mechanism has been used
664 return rv;
667 if (aRelationType == nsIAccessibleRelation::RELATION_LABEL_FOR) {
668 // Look for groupbox parent
669 nsCOMPtr<nsIContent> content = do_QueryInterface(mDOMNode);
670 if (!content) {
671 return NS_ERROR_FAILURE; // Node already shut down
673 nsCOMPtr<nsIAccessible> groupboxAccessible = GetParent();
674 if (nsAccUtils::Role(groupboxAccessible) == nsIAccessibleRole::ROLE_GROUPING) {
675 nsCOMPtr<nsIAccessible> testLabelAccessible;
676 groupboxAccessible->GetAccessibleRelated(nsIAccessibleRelation::RELATION_LABELLED_BY,
677 getter_AddRefs(testLabelAccessible));
678 if (testLabelAccessible == this) {
679 // We're the first child of the parent groupbox
680 NS_ADDREF(*aRelated = groupboxAccessible);
685 return NS_OK;