Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLSelectAccessible.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Kyle Yuan (kyle.yuan@sun.com)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsCOMPtr.h"
00040 #include "nsHTMLSelectAccessible.h"
00041 #include "nsIAccessibilityService.h"
00042 #include "nsIAccessibleEvent.h"
00043 #include "nsIFrame.h"
00044 #include "nsIComboboxControlFrame.h"
00045 #include "nsIDocument.h"
00046 #include "nsIDOMHTMLInputElement.h"
00047 #include "nsIDOMHTMLOptGroupElement.h"
00048 #include "nsIDOMHTMLSelectElement.h"
00049 #include "nsIListControlFrame.h"
00050 #include "nsIServiceManager.h"
00051 #include "nsITextContent.h"
00052 #include "nsArray.h"
00053 
00075 // Helper class
00076 nsHTMLSelectableAccessible::iterator::iterator(nsHTMLSelectableAccessible *aParent, nsIWeakReference *aWeakShell): 
00077   mWeakShell(aWeakShell), mParentSelect(aParent)
00078 {
00079   mLength = mIndex = 0;
00080   mSelCount = 0;
00081 
00082   nsCOMPtr<nsIDOMHTMLSelectElement> htmlSelect(do_QueryInterface(mParentSelect->mDOMNode));
00083   if (htmlSelect) {
00084     htmlSelect->GetOptions(getter_AddRefs(mOptions));
00085     if (mOptions)
00086       mOptions->GetLength(&mLength);
00087   }
00088 }
00089 
00090 PRBool nsHTMLSelectableAccessible::iterator::Advance() 
00091 {
00092   if (mIndex < mLength) {
00093     nsCOMPtr<nsIDOMNode> tempNode;
00094     if (mOptions) {
00095       mOptions->Item(mIndex, getter_AddRefs(tempNode));
00096       mOption = do_QueryInterface(tempNode);
00097     }
00098     mIndex++;
00099     return PR_TRUE;
00100   }
00101   return PR_FALSE;
00102 }
00103 
00104 void nsHTMLSelectableAccessible::iterator::CalcSelectionCount(PRInt32 *aSelectionCount)
00105 {
00106   PRBool isSelected = PR_FALSE;
00107 
00108   if (mOption)
00109     mOption->GetSelected(&isSelected);
00110 
00111   if (isSelected)
00112     (*aSelectionCount)++;
00113 }
00114 
00115 void nsHTMLSelectableAccessible::iterator::AddAccessibleIfSelected(nsIAccessibilityService *aAccService, 
00116                                                                    nsIMutableArray *aSelectedAccessibles, 
00117                                                                    nsPresContext *aContext)
00118 {
00119   PRBool isSelected = PR_FALSE;
00120   nsCOMPtr<nsIAccessible> tempAccess;
00121 
00122   if (mOption) {
00123     mOption->GetSelected(&isSelected);
00124     if (isSelected) {
00125       nsCOMPtr<nsIDOMNode> optionNode(do_QueryInterface(mOption));
00126       aAccService->GetAccessibleInWeakShell(optionNode, mWeakShell, getter_AddRefs(tempAccess));
00127     }
00128   }
00129 
00130   if (tempAccess)
00131     aSelectedAccessibles->AppendElement(NS_STATIC_CAST(nsISupports*, tempAccess), PR_FALSE);
00132 }
00133 
00134 PRBool nsHTMLSelectableAccessible::iterator::GetAccessibleIfSelected(PRInt32 aIndex, 
00135                                                                      nsIAccessibilityService *aAccService, 
00136                                                                      nsPresContext *aContext, 
00137                                                                      nsIAccessible **aAccessible)
00138 {
00139   PRBool isSelected = PR_FALSE;
00140 
00141   *aAccessible = nsnull;
00142 
00143   if (mOption) {
00144     mOption->GetSelected(&isSelected);
00145     if (isSelected) {
00146       if (mSelCount == aIndex) {
00147         nsCOMPtr<nsIDOMNode> optionNode(do_QueryInterface(mOption));
00148         aAccService->GetAccessibleInWeakShell(optionNode, mWeakShell, aAccessible);
00149         return PR_TRUE;
00150       }
00151       mSelCount++;
00152     }
00153   }
00154 
00155   return PR_FALSE;
00156 }
00157 
00158 void nsHTMLSelectableAccessible::iterator::Select(PRBool aSelect)
00159 {
00160   if (mOption)
00161     mOption->SetSelected(aSelect);
00162 }
00163 
00164 nsHTMLSelectableAccessible::nsHTMLSelectableAccessible(nsIDOMNode* aDOMNode, 
00165                                                        nsIWeakReference* aShell):
00166 nsAccessibleWrap(aDOMNode, aShell)
00167 {
00168 }
00169 
00170 NS_IMPL_ISUPPORTS_INHERITED1(nsHTMLSelectableAccessible, nsAccessible, nsIAccessibleSelectable)
00171 
00172 // Helper methods
00173 NS_IMETHODIMP nsHTMLSelectableAccessible::ChangeSelection(PRInt32 aIndex, PRUint8 aMethod, PRBool *aSelState)
00174 {
00175   *aSelState = PR_FALSE;
00176 
00177   nsCOMPtr<nsIDOMHTMLSelectElement> htmlSelect(do_QueryInterface(mDOMNode));
00178   if (!htmlSelect)
00179     return NS_ERROR_FAILURE;
00180 
00181   nsCOMPtr<nsIDOMHTMLOptionsCollection> options;
00182   htmlSelect->GetOptions(getter_AddRefs(options));
00183   if (!options)
00184     return NS_ERROR_FAILURE;
00185 
00186   nsCOMPtr<nsIDOMNode> tempNode;
00187   options->Item(aIndex, getter_AddRefs(tempNode));
00188   nsCOMPtr<nsIDOMHTMLOptionElement> tempOption(do_QueryInterface(tempNode));
00189   if (!tempOption)
00190     return NS_ERROR_FAILURE;
00191 
00192   tempOption->GetSelected(aSelState);
00193   nsresult rv = NS_OK;
00194   if (eSelection_Add == aMethod && !(*aSelState))
00195     rv = tempOption->SetSelected(PR_TRUE);
00196   else if (eSelection_Remove == aMethod && (*aSelState))
00197     rv = tempOption->SetSelected(PR_FALSE);
00198   return rv;
00199 }
00200 
00201 // Interface methods
00202 NS_IMETHODIMP nsHTMLSelectableAccessible::GetSelectedChildren(nsIArray **_retval)
00203 {
00204   *_retval = nsnull;
00205 
00206   nsCOMPtr<nsIAccessibilityService> accService(do_GetService("@mozilla.org/accessibilityService;1"));
00207   if (!accService)
00208     return NS_ERROR_FAILURE;
00209 
00210   nsCOMPtr<nsIMutableArray> selectedAccessibles;
00211   NS_NewArray(getter_AddRefs(selectedAccessibles));
00212   if (!selectedAccessibles)
00213     return NS_ERROR_OUT_OF_MEMORY;
00214   
00215   nsPresContext *context = GetPresContext();
00216   if (!context)
00217     return NS_ERROR_FAILURE;
00218 
00219   nsHTMLSelectableAccessible::iterator iter(this, mWeakShell);
00220   while (iter.Advance())
00221     iter.AddAccessibleIfSelected(accService, selectedAccessibles, context);
00222 
00223   PRUint32 uLength = 0;
00224   selectedAccessibles->GetLength(&uLength); 
00225   if (uLength != 0) { // length of nsIArray containing selected options
00226     *_retval = selectedAccessibles;
00227     NS_ADDREF(*_retval);
00228   }
00229   return NS_OK;
00230 }
00231 
00232 // return the nth selected child's nsIAccessible object
00233 NS_IMETHODIMP nsHTMLSelectableAccessible::RefSelection(PRInt32 aIndex, nsIAccessible **_retval)
00234 {
00235   *_retval = nsnull;
00236 
00237   nsCOMPtr<nsIAccessibilityService> accService(do_GetService("@mozilla.org/accessibilityService;1"));
00238   if (!accService)
00239     return NS_ERROR_FAILURE;
00240 
00241   nsPresContext *context = GetPresContext();
00242   if (!context)
00243     return NS_ERROR_FAILURE;
00244 
00245   nsHTMLSelectableAccessible::iterator iter(this, mWeakShell);
00246   while (iter.Advance())
00247     if (iter.GetAccessibleIfSelected(aIndex, accService, context, _retval))
00248       return NS_OK;
00249   
00250   // No matched item found
00251   return NS_ERROR_FAILURE;
00252 }
00253 
00254 NS_IMETHODIMP nsHTMLSelectableAccessible::GetSelectionCount(PRInt32 *aSelectionCount)
00255 {
00256   *aSelectionCount = 0;
00257 
00258   nsHTMLSelectableAccessible::iterator iter(this, mWeakShell);
00259   while (iter.Advance())
00260     iter.CalcSelectionCount(aSelectionCount);
00261   return NS_OK;
00262 }
00263 
00264 NS_IMETHODIMP nsHTMLSelectableAccessible::AddChildToSelection(PRInt32 aIndex)
00265 {
00266   PRBool isSelected;
00267   return ChangeSelection(aIndex, eSelection_Add, &isSelected);
00268 }
00269 
00270 NS_IMETHODIMP nsHTMLSelectableAccessible::RemoveChildFromSelection(PRInt32 aIndex)
00271 {
00272   PRBool isSelected;
00273   return ChangeSelection(aIndex, eSelection_Remove, &isSelected);
00274 }
00275 
00276 NS_IMETHODIMP nsHTMLSelectableAccessible::IsChildSelected(PRInt32 aIndex, PRBool *_retval)
00277 {
00278   *_retval = PR_FALSE;
00279   return ChangeSelection(aIndex, eSelection_GetState, _retval);
00280 }
00281 
00282 NS_IMETHODIMP nsHTMLSelectableAccessible::ClearSelection()
00283 {
00284   nsHTMLSelectableAccessible::iterator iter(this, mWeakShell);
00285   while (iter.Advance())
00286     iter.Select(PR_FALSE);
00287   return NS_OK;
00288 }
00289 
00290 NS_IMETHODIMP nsHTMLSelectableAccessible::SelectAllSelection(PRBool *_retval)
00291 {
00292   *_retval = PR_FALSE;
00293   
00294   nsCOMPtr<nsIDOMHTMLSelectElement> htmlSelect(do_QueryInterface(mDOMNode));
00295   if (!htmlSelect)
00296     return NS_ERROR_FAILURE;
00297 
00298   htmlSelect->GetMultiple(_retval);
00299   if (*_retval) {
00300     nsHTMLSelectableAccessible::iterator iter(this, mWeakShell);
00301     while (iter.Advance())
00302       iter.Select(PR_TRUE);
00303   }
00304   return NS_OK;
00305 }
00306 
00314 nsHTMLSelectListAccessible::nsHTMLSelectListAccessible(nsIDOMNode* aDOMNode, 
00315                                                        nsIWeakReference* aShell)
00316 :nsHTMLSelectableAccessible(aDOMNode, aShell)
00317 {
00318 }
00319 
00325 NS_IMETHODIMP nsHTMLSelectListAccessible::GetState(PRUint32 *_retval)
00326 {
00327   nsHTMLSelectableAccessible::GetState(_retval);
00328   nsCOMPtr<nsIDOMHTMLSelectElement> select (do_QueryInterface(mDOMNode));
00329   if ( select ) {
00330     PRBool multiple;
00331     select->GetMultiple(&multiple);
00332     if ( multiple )
00333       *_retval |= STATE_MULTISELECTABLE | STATE_EXTSELECTABLE;
00334   }
00335 
00336   return NS_OK;
00337 }
00338 
00339 NS_IMETHODIMP nsHTMLSelectListAccessible::GetRole(PRUint32 *_retval)
00340 {
00341   *_retval = ROLE_LIST;
00342   return NS_OK;
00343 }
00344 
00345 already_AddRefed<nsIAccessible>
00346 nsHTMLSelectListAccessible::AccessibleForOption(nsIAccessibilityService *aAccService,
00347                                                 nsIContent *aContent,
00348                                                 nsIAccessible *aLastGoodAccessible)
00349 {
00350   nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(aContent));
00351   NS_ASSERTION(domNode, "DOM node is null");
00352   // Accessibility service will initialize & cache any accessibles created
00353   nsCOMPtr<nsIAccessible> accessible;
00354   aAccService->GetAccessibleInWeakShell(domNode, mWeakShell, getter_AddRefs(accessible));
00355   nsCOMPtr<nsPIAccessible> privateAccessible(do_QueryInterface(accessible));
00356   if (!privateAccessible) {
00357     return nsnull;
00358   }
00359 
00360   ++ mAccChildCount;
00361   privateAccessible->SetParent(this);
00362   nsCOMPtr<nsPIAccessible> privatePrevAccessible(do_QueryInterface(aLastGoodAccessible));
00363   if (privatePrevAccessible) {
00364     privatePrevAccessible->SetNextSibling(accessible);
00365   }
00366   if (!mFirstChild) {
00367     mFirstChild = accessible;
00368   }
00369   nsIAccessible *returnAccessible = accessible;
00370   NS_ADDREF(returnAccessible);
00371   return returnAccessible;
00372 }
00373 
00374 already_AddRefed<nsIAccessible>
00375 nsHTMLSelectListAccessible::CacheOptSiblings(nsIAccessibilityService *aAccService,
00376                                              nsIContent *aParentContent,
00377                                              nsIAccessible *aLastGoodAccessible)
00378 {
00379   // Recursive helper for CacheChildren()
00380 
00381   PRUint32 numChildren = aParentContent->GetChildCount();
00382   nsCOMPtr<nsIAccessible> lastGoodAccessible(aLastGoodAccessible);
00383   nsCOMPtr<nsIAccessible> newAccessible;
00384 
00385   for (PRUint32 count = 0; count < numChildren; count ++) {
00386     nsIContent *childContent = aParentContent->GetChildAt(count);
00387     if (!childContent->IsContentOfType(nsIContent::eHTML)) {
00388       continue;
00389     }
00390     nsCOMPtr<nsIAtom> tag = childContent->Tag();
00391     if (tag == nsAccessibilityAtoms::option || tag == nsAccessibilityAtoms::optgroup) {
00392       newAccessible = AccessibleForOption(aAccService,
00393                                           childContent,
00394                                           lastGoodAccessible);
00395       if (newAccessible) {
00396         lastGoodAccessible = newAccessible;
00397       }
00398       if (tag == nsAccessibilityAtoms::optgroup) {
00399         newAccessible = CacheOptSiblings(aAccService, childContent,
00400                                          lastGoodAccessible);
00401         if (newAccessible) {
00402           lastGoodAccessible = newAccessible;
00403         }
00404       }
00405     }
00406   }
00407   if (lastGoodAccessible) {
00408     nsCOMPtr<nsPIAccessible> privateLastAcc =
00409       do_QueryInterface(lastGoodAccessible);
00410     privateLastAcc->SetNextSibling(nsnull);
00411     NS_ADDREF(aLastGoodAccessible = lastGoodAccessible);
00412   }
00413   return aLastGoodAccessible;
00414 }
00415 
00422 void nsHTMLSelectListAccessible::CacheChildren(PRBool aWalkAnonContent)
00423 {
00424   // Cache the number of <optgroup> and <option> DOM decendents,
00425   // as well as the accessibles for them. Avoid whitespace text nodes.
00426 
00427   nsCOMPtr<nsIContent> selectContent(do_QueryInterface(mDOMNode));
00428   nsCOMPtr<nsIAccessibilityService> accService(do_GetService("@mozilla.org/accessibilityService;1"));
00429   if (!selectContent || !accService) {
00430     mAccChildCount = eChildCountUninitialized;
00431     return;
00432   }
00433 
00434   mAccChildCount = 0;
00435   nsCOMPtr<nsIAccessible> lastGoodAccessible =
00436     CacheOptSiblings(accService, selectContent, nsnull);
00437 }
00438 
00442 nsHTMLSelectOptionAccessible::nsHTMLSelectOptionAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell):
00443 nsLeafAccessible(aDOMNode, aShell)
00444 {
00445   nsCOMPtr<nsIAccessibilityService> accService(do_GetService("@mozilla.org/accessibilityService;1"));
00446   nsCOMPtr<nsIDOMNode> parentNode;
00447   aDOMNode->GetParentNode(getter_AddRefs(parentNode));
00448   nsCOMPtr<nsIAccessible> parentAccessible;
00449   if (parentNode) {
00450     // If the parent node is a Combobox, then the option's accessible parent
00451     // is nsHTMLComboboxListAccessible, not the nsHTMLComboboxAccessible that
00452     // GetParent would normally return. This is because the 
00453     // nsHTMLComboboxListAccessible is inserted into the accessible hierarchy
00454     // where there is no DOM node for it.
00455     accService->GetAccessibleInWeakShell(parentNode, mWeakShell, getter_AddRefs(parentAccessible));
00456     if (parentAccessible) {
00457       PRUint32 role;
00458       parentAccessible->GetRole(&role);
00459       if (role == ROLE_COMBOBOX) {
00460         nsCOMPtr<nsIAccessible> comboAccessible(parentAccessible);
00461         comboAccessible->GetLastChild(getter_AddRefs(parentAccessible));
00462       }
00463     }
00464   }
00465   SetParent(parentAccessible);
00466 }
00467 
00469 NS_IMETHODIMP nsHTMLSelectOptionAccessible::GetRole(PRUint32 *aRole)
00470 {
00471   *aRole = ROLE_LISTITEM;
00472   return NS_OK;
00473 }
00474 
00478 NS_IMETHODIMP nsHTMLSelectOptionAccessible::GetName(nsAString& aName)
00479 {
00480   // CASE #1 -- great majority of the cases
00481   // find the label attribute - this is what the W3C says we should use
00482   nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(mDOMNode));
00483   NS_ASSERTION(domElement, "No domElement for accessible DOM node!");
00484   nsresult rv = domElement->GetAttribute(NS_LITERAL_STRING("label"), aName) ;
00485 
00486   if (NS_SUCCEEDED(rv) && !aName.IsEmpty() ) {
00487     return NS_OK;
00488   }
00489   
00490   // CASE #2 -- no label parameter, get the first child, 
00491   // use it if it is a text node
00492   nsCOMPtr<nsIDOMNode> child;
00493   mDOMNode->GetFirstChild(getter_AddRefs(child));
00494 
00495   if (child) {
00496     nsCOMPtr<nsITextContent> text(do_QueryInterface(child));
00497     if (text) {
00498       nsAutoString txtValue;
00499       rv = AppendFlatStringFromContentNode(text, &txtValue);
00500       if (NS_SUCCEEDED(rv)) {
00501         // Temp var (txtValue) needed until CompressWhitespace built for nsAString
00502         txtValue.CompressWhitespace();
00503         aName.Assign(txtValue);
00504         return NS_OK;
00505       }
00506     }
00507   }
00508   
00509   return NS_ERROR_FAILURE;
00510 }
00511 
00512 nsIFrame* nsHTMLSelectOptionAccessible::GetBoundsFrame()
00513 {
00514   nsCOMPtr<nsIContent> selectContent(do_QueryInterface(mDOMNode));
00515 
00516   while (selectContent && selectContent->Tag() != nsAccessibilityAtoms::select) {
00517     selectContent = selectContent->GetParent();
00518   }
00519 
00520   nsCOMPtr<nsIDOMNode> selectNode(do_QueryInterface(selectContent));
00521   if (selectNode) {
00522     nsCOMPtr<nsIAccessibilityService> accService(do_GetService("@mozilla.org/accessibilityService;1"));
00523     nsCOMPtr<nsIAccessible> selAcc;
00524     if (NS_SUCCEEDED(accService->GetAccessibleFor(selectNode, 
00525                                                   getter_AddRefs(selAcc)))) {
00526       PRUint32 state;
00527       selAcc->GetFinalState(&state);
00528       if (state & STATE_COLLAPSED) {
00529         nsCOMPtr<nsIPresShell> presShell(GetPresShell());
00530         if (!presShell) {
00531           return nsnull;
00532         }
00533         nsIFrame *selectFrame = nsnull;
00534         presShell->GetPrimaryFrameFor(selectContent, &selectFrame);
00535         return selectFrame;
00536       }
00537     }
00538   }
00539 
00540   return nsAccessible::GetBoundsFrame();
00541 }
00542 
00551 NS_IMETHODIMP nsHTMLSelectOptionAccessible::GetState(PRUint32 *_retval)
00552 {
00553   *_retval = 0;
00554   nsCOMPtr<nsIDOMNode> focusedOptionNode, parentNode;
00555   // Go up to parent <select> element
00556   nsCOMPtr<nsIDOMNode> thisNode(do_QueryInterface(mDOMNode));
00557   do {
00558     thisNode->GetParentNode(getter_AddRefs(parentNode));
00559     nsCOMPtr<nsIDOMHTMLSelectElement> selectControl(do_QueryInterface(parentNode));
00560     if (selectControl) {
00561       break;
00562     }
00563     thisNode = parentNode;
00564   } while (parentNode);
00565   if (!parentNode) {
00566     return NS_ERROR_FAILURE;
00567   }
00568   
00569   // find out if we are the focused node
00570   GetFocusedOptionNode(parentNode, getter_AddRefs(focusedOptionNode));
00571   if (focusedOptionNode == mDOMNode)
00572     *_retval |= STATE_FOCUSED;
00573 
00574   // Are we selected?
00575   nsCOMPtr<nsIDOMHTMLOptionElement> option (do_QueryInterface(mDOMNode));
00576   if ( option ) {
00577     PRBool isSelected = PR_FALSE;
00578     option->GetSelected(&isSelected);
00579     if ( isSelected ) 
00580       *_retval |= STATE_SELECTED;
00581   }
00582 
00583   *_retval |= STATE_SELECTABLE | STATE_FOCUSABLE;
00584   
00585   return NS_OK;
00586 }
00587 
00589 NS_IMETHODIMP nsHTMLSelectOptionAccessible::GetActionName(PRUint8 index, nsAString& _retval)
00590 {
00591   if (index == eAction_Select) {
00592     nsAccessible::GetTranslatedString(NS_LITERAL_STRING("select"), _retval); 
00593     return NS_OK;
00594   }
00595   return NS_ERROR_INVALID_ARG;
00596 }
00597 
00598 NS_IMETHODIMP nsHTMLSelectOptionAccessible::GetNumActions(PRUint8 *_retval)
00599 {
00600   *_retval = eSingle_Action;
00601   return NS_OK;
00602 }
00603 
00604 NS_IMETHODIMP nsHTMLSelectOptionAccessible::DoAction(PRUint8 index)
00605 {
00606   if (index == eAction_Select) {   // default action
00607     nsCOMPtr<nsIDOMHTMLOptionElement> newHTMLOption(do_QueryInterface(mDOMNode));
00608     if (!newHTMLOption) 
00609       return NS_ERROR_FAILURE;
00610     // Clear old selection
00611     nsCOMPtr<nsIDOMNode> oldHTMLOptionNode, selectNode;
00612     nsCOMPtr<nsIAccessible> parent;
00613     GetParent(getter_AddRefs(parent));
00614     nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(parent));
00615     NS_ASSERTION(accessNode, "Unable to QI to nsIAccessNode");
00616     accessNode->GetDOMNode(getter_AddRefs(selectNode));
00617     GetFocusedOptionNode(selectNode, getter_AddRefs(oldHTMLOptionNode));
00618     nsCOMPtr<nsIDOMHTMLOptionElement> oldHTMLOption(do_QueryInterface(oldHTMLOptionNode));
00619     if (oldHTMLOption)
00620       oldHTMLOption->SetSelected(PR_FALSE);
00621     // Set new selection
00622     newHTMLOption->SetSelected(PR_TRUE);
00623 
00624     // If combo box, and open, close it
00625     // First, get the <select> widgets list control frame
00626     nsCOMPtr<nsIDOMNode> testSelectNode;
00627     nsCOMPtr<nsIDOMNode> thisNode(do_QueryInterface(mDOMNode));
00628     do {
00629       thisNode->GetParentNode(getter_AddRefs(testSelectNode));
00630       nsCOMPtr<nsIDOMHTMLSelectElement> selectControl(do_QueryInterface(testSelectNode));
00631       if (selectControl)
00632         break;
00633       thisNode = testSelectNode;
00634     } while (testSelectNode);
00635 
00636     nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
00637     nsCOMPtr<nsIContent> selectContent(do_QueryInterface(testSelectNode));
00638     nsCOMPtr<nsIDOMHTMLOptionElement> option(do_QueryInterface(mDOMNode));
00639 
00640     if (!testSelectNode || !selectContent || !presShell || !option) 
00641       return NS_ERROR_FAILURE;
00642 
00643     nsIFrame *selectFrame = nsnull;
00644     presShell->GetPrimaryFrameFor(selectContent, &selectFrame);
00645     nsIComboboxControlFrame *comboBoxFrame = nsnull;
00646     CallQueryInterface(selectFrame, &comboBoxFrame);
00647     if (comboBoxFrame) {
00648       nsIFrame *listFrame = nsnull;
00649       comboBoxFrame->GetDropDown(&listFrame);
00650       PRBool isDroppedDown;
00651       comboBoxFrame->IsDroppedDown(&isDroppedDown);
00652       if (isDroppedDown && listFrame) {
00653         // use this list control frame to roll up the list
00654         nsIListControlFrame *listControlFrame = nsnull;
00655         listFrame->QueryInterface(NS_GET_IID(nsIListControlFrame), (void**)&listControlFrame);
00656         if (listControlFrame) {
00657           PRInt32 newIndex = 0;
00658           option->GetIndex(&newIndex);
00659           listControlFrame->ComboboxFinish(newIndex);
00660         }
00661       }
00662     }
00663     return NS_OK;
00664   }
00665 
00666   return NS_ERROR_INVALID_ARG;
00667 }
00668 
00674 nsresult nsHTMLSelectOptionAccessible::GetFocusedOptionNode(nsIDOMNode *aListNode, 
00675                                                             nsIDOMNode **aFocusedOptionNode)
00676 {
00677   *aFocusedOptionNode = nsnull;
00678   NS_ASSERTION(aListNode, "Called GetFocusedOptionNode without a valid list node");
00679 
00680   nsCOMPtr<nsIContent> content(do_QueryInterface(aListNode));
00681   nsCOMPtr<nsIDocument> document = content->GetDocument();
00682   nsIPresShell *shell = nsnull;
00683   if (document)
00684     shell = document->GetShellAt(0);
00685   if (!shell)
00686     return NS_ERROR_FAILURE;
00687 
00688   nsIFrame *frame = nsnull;
00689   shell->GetPrimaryFrameFor(content, &frame);
00690   if (!frame)
00691     return NS_ERROR_FAILURE;
00692 
00693   PRInt32 focusedOptionIndex = 0;
00694 
00695   // Get options
00696   nsCOMPtr<nsIDOMHTMLSelectElement> selectElement(do_QueryInterface(aListNode));
00697   NS_ASSERTION(selectElement, "No select element where it should be");
00698 
00699   nsCOMPtr<nsIDOMHTMLOptionsCollection> options;
00700   nsresult rv = selectElement->GetOptions(getter_AddRefs(options));
00701   
00702   if (NS_SUCCEEDED(rv)) {
00703     nsIListControlFrame *listFrame = nsnull;
00704     frame->QueryInterface(NS_GET_IID(nsIListControlFrame), (void**)&listFrame);
00705     if (listFrame) {
00706       // Get what's focused in listbox by asking frame for "selected item". 
00707       // Can't use dom interface for this, because it will always return the first selected item
00708       // when there is more than 1 item selected. We need the focused item, not
00709       // the first selected item.
00710       rv = listFrame->GetSelectedIndex(&focusedOptionIndex);
00711     }
00712     else  // Combo boxes can only have 1 selected option, so they can use the dom interface for this
00713       rv = selectElement->GetSelectedIndex(&focusedOptionIndex);
00714   }
00715 
00716   // Either use options and focused index, or default return null
00717   if (NS_SUCCEEDED(rv) && options && focusedOptionIndex >= 0) {  // Something is focused
00718     rv = options->Item(focusedOptionIndex, aFocusedOptionNode);
00719   }
00720 
00721   return rv;
00722 }
00723 
00724 void nsHTMLSelectOptionAccessible::SelectionChangedIfOption(nsIContent *aPossibleOption)
00725 {
00726   if (!aPossibleOption || aPossibleOption->Tag() != nsAccessibilityAtoms::option ||
00727       !aPossibleOption->IsContentOfType(nsIContent::eHTML)) {
00728     return;
00729   }
00730 
00731   nsCOMPtr<nsIDOMNode> optionNode(do_QueryInterface(aPossibleOption));
00732   NS_ASSERTION(optionNode, "No option node for nsIContent with option tag!");
00733 
00734   nsCOMPtr<nsIAccessible> multiSelect = GetMultiSelectFor(optionNode);
00735   nsCOMPtr<nsPIAccessible> privateMultiSelect = do_QueryInterface(multiSelect);
00736   if (!privateMultiSelect) {
00737     return;
00738   }
00739 
00740   nsCOMPtr<nsIAccessibilityService> accService = 
00741     do_GetService("@mozilla.org/accessibilityService;1");
00742   nsCOMPtr<nsIAccessible> optionAccessible;
00743   accService->GetAccessibleFor(optionNode, getter_AddRefs(optionAccessible));
00744   if (!optionAccessible) {
00745     return;
00746   }
00747 
00748   privateMultiSelect->FireToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
00749                       multiSelect, nsnull);
00750   PRUint32 state;
00751   optionAccessible->GetFinalState(&state);
00752   PRUint32 eventType = (state & STATE_SELECTED) ?
00753                        nsIAccessibleEvent::EVENT_SELECTION_ADD :
00754                        nsIAccessibleEvent::EVENT_SELECTION_REMOVE; 
00755   privateMultiSelect->FireToolkitEvent(eventType, optionAccessible, nsnull);
00756 }
00757 
00761 nsHTMLSelectOptGroupAccessible::nsHTMLSelectOptGroupAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell):
00762 nsHTMLSelectOptionAccessible(aDOMNode, aShell)
00763 {
00764 }
00765 
00766 
00771 NS_IMETHODIMP nsHTMLSelectOptGroupAccessible::GetState(PRUint32 *_retval)
00772 {
00773   nsHTMLSelectOptionAccessible::GetState(_retval);
00774   *_retval &= ~(STATE_FOCUSABLE|STATE_SELECTABLE);
00775   
00776   return NS_OK;
00777 }
00778 
00779 NS_IMETHODIMP nsHTMLSelectOptGroupAccessible::DoAction(PRUint8 index)
00780 {
00781   return NS_ERROR_NOT_IMPLEMENTED;
00782 }
00783 
00784 NS_IMETHODIMP nsHTMLSelectOptGroupAccessible::GetActionName(PRUint8 index, nsAString& _retval)
00785 {
00786   return NS_ERROR_NOT_IMPLEMENTED;
00787 }
00788 
00789 NS_IMETHODIMP nsHTMLSelectOptGroupAccessible::GetNumActions(PRUint8 *_retval)
00790 {
00791   return NS_ERROR_NOT_IMPLEMENTED;
00792 }
00793 
00800 nsHTMLComboboxAccessible::nsHTMLComboboxAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell):
00801 nsHTMLSelectableAccessible(aDOMNode, aShell)
00802 {
00803 }
00804 
00806 NS_IMETHODIMP nsHTMLComboboxAccessible::GetRole(PRUint32 *_retval)
00807 {
00808   *_retval = ROLE_COMBOBOX;
00809   return NS_OK;
00810 }
00811 
00812 NS_IMETHODIMP nsHTMLComboboxAccessible::Shutdown()
00813 {
00814   // Need to hold these locally while we call Shutdown() on them.
00815   nsCOMPtr<nsPIAccessNode> textFieldAcc(do_QueryInterface(mComboboxTextFieldAccessible));
00816   nsCOMPtr<nsPIAccessNode> buttonAcc(do_QueryInterface(mComboboxButtonAccessible));
00817   nsCOMPtr<nsPIAccessNode> listAcc(do_QueryInterface(mComboboxListAccessible));
00818 
00819   if (listAcc) {
00820     listAcc->Shutdown();
00821     NS_ASSERTION(mComboboxTextFieldAccessible == nsnull, "why didn't InvalidateChildren clear this?");
00822   }
00823   if (buttonAcc) {
00824     buttonAcc->Shutdown();
00825   }
00826   if (textFieldAcc) {
00827     textFieldAcc->Shutdown();
00828   }
00829   mComboboxTextFieldAccessible = nsnull;
00830   mComboboxButtonAccessible = nsnull;
00831   mComboboxListAccessible = nsnull;
00832 
00833   return nsHTMLSelectableAccessible::Shutdown();
00834 }
00835 
00836 NS_IMETHODIMP nsHTMLComboboxAccessible::Init()
00837 {
00838   // Hold references
00839   GetFirstChild(getter_AddRefs(mComboboxTextFieldAccessible));
00840   if (mComboboxTextFieldAccessible) {
00841     mComboboxTextFieldAccessible->GetNextSibling(getter_AddRefs(mComboboxButtonAccessible));
00842   }
00843   if (mComboboxButtonAccessible) {
00844     mComboboxButtonAccessible->GetNextSibling(getter_AddRefs(mComboboxListAccessible));
00845   }
00846 
00847   nsHTMLSelectableAccessible::Init();
00848 
00849   return NS_OK;
00850 }
00851 
00855 NS_IMETHODIMP nsHTMLComboboxAccessible::GetChildCount(PRInt32 *_retval)
00856 {
00857   *_retval = 3;
00858   return NS_OK;
00859 }
00860 
00870 NS_IMETHODIMP nsHTMLComboboxAccessible::GetState(PRUint32 *_retval)
00871 {
00872   // Get focus status from base class
00873   nsAccessible::GetState(_retval);
00874 
00875   // we are open or closed
00876   PRBool isOpen = PR_FALSE;
00877   nsIFrame *frame = GetBoundsFrame();
00878   nsIComboboxControlFrame *comboFrame = nsnull;
00879   frame->QueryInterface(NS_GET_IID(nsIComboboxControlFrame), (void**)&comboFrame);
00880   if (comboFrame)
00881     comboFrame->IsDroppedDown(&isOpen);
00882 
00883   if (isOpen)
00884     *_retval |= STATE_EXPANDED;
00885   else
00886     *_retval |= STATE_COLLAPSED;
00887 
00888   *_retval |= STATE_HASPOPUP | STATE_READONLY | STATE_FOCUSABLE;
00889 
00890   return NS_OK;
00891 }
00892 
00896 NS_IMETHODIMP nsHTMLComboboxAccessible::GetLastChild(nsIAccessible **aLastChild)
00897 {
00898   // It goes: textfield, button, list. We're returning the list.
00899 
00900   return GetChildAt(2, aLastChild);
00901 }
00902 
00906 NS_IMETHODIMP nsHTMLComboboxAccessible::GetFirstChild(nsIAccessible **aFirstChild)
00907 {
00908   if (mFirstChild) {
00909     *aFirstChild = mFirstChild;
00910   }
00911   else {
00912     NS_ASSERTION(!mComboboxTextFieldAccessible, "I already have a text field accessible!");
00913     nsHTMLComboboxTextFieldAccessible* accessible = 
00914       new nsHTMLComboboxTextFieldAccessible(this, mDOMNode, mWeakShell);
00915     *aFirstChild = accessible;
00916     if (! *aFirstChild)
00917       return NS_ERROR_FAILURE;
00918     accessible->Init();
00919     SetFirstChild(*aFirstChild);
00920   }
00921   NS_ADDREF(*aFirstChild);
00922   return NS_OK;
00923 }
00924 
00925 NS_IMETHODIMP nsHTMLComboboxAccessible::InvalidateChildren()
00926 {
00927   mComboboxTextFieldAccessible = nsnull;  
00928   mComboboxButtonAccessible = nsnull;  
00929   mComboboxListAccessible = nsnull;  
00930   return nsAccessible::InvalidateChildren();
00931 }
00932 
00933 NS_IMETHODIMP nsHTMLComboboxAccessible::GetDescription(nsAString& aDescription)
00934 {
00935   // Use description of currently focused option
00936   aDescription.Truncate();
00937   nsCOMPtr<nsIAccessible> optionAccessible = GetFocusedOptionAccessible();
00938   NS_ENSURE_TRUE(optionAccessible, NS_ERROR_FAILURE);
00939   return optionAccessible ? optionAccessible->GetDescription(aDescription) : NS_OK;
00940 }
00941 
00942 already_AddRefed<nsIAccessible>
00943 nsHTMLComboboxAccessible::GetFocusedOptionAccessible()
00944 {
00945   if (!mWeakShell) {
00946     return nsnull;  // Shut down
00947   }
00948   nsCOMPtr<nsIComboboxControlFrame> cbxFrame = do_QueryInterface(GetFrame());
00949   if (!cbxFrame) {
00950     return nsnull;
00951   }
00952   nsIFrame *listFrame = nsnull;
00953   cbxFrame->GetDropDown(&listFrame);
00954   if (!listFrame) {
00955     return nsnull;
00956   }
00957   nsCOMPtr<nsIDOMNode> listNode = do_QueryInterface(listFrame->GetContent());
00958   nsCOMPtr<nsIDOMNode> focusedOptionNode;
00959   nsHTMLSelectOptionAccessible::GetFocusedOptionNode(listNode, getter_AddRefs(focusedOptionNode));
00960   nsCOMPtr<nsIAccessibilityService> accService = 
00961     do_GetService("@mozilla.org/accessibilityService;1");
00962   if (!focusedOptionNode || !accService) {
00963     return nsnull;
00964   }
00965 
00966   nsIAccessible *optionAccessible;
00967   accService->GetAccessibleInWeakShell(focusedOptionNode, mWeakShell, 
00968                                        &optionAccessible);
00969   return optionAccessible;
00970 }
00971 
00977 NS_IMETHODIMP nsHTMLComboboxAccessible::GetValue(nsAString& aValue)
00978 {
00979   // Use label of currently focused option
00980   nsCOMPtr<nsIAccessible> optionAccessible = GetFocusedOptionAccessible();
00981   NS_ENSURE_TRUE(optionAccessible, NS_ERROR_FAILURE);
00982   return optionAccessible->GetName(aValue);
00983 }
00984 
00988 nsHTMLComboboxTextFieldAccessible::nsHTMLComboboxTextFieldAccessible(nsIAccessible* aParent, 
00989                                                                      nsIDOMNode* aDOMNode, 
00990                                                                      nsIWeakReference* aShell):
00991 nsLeafAccessible(aDOMNode, aShell)
00992 {
00993   // There is no cache entry for this item. 
00994   // It's generated and ref'd by  nsHTMLComboboxAccessible
00995   SetParent(aParent);
00996 }
00997 
01001 NS_IMETHODIMP nsHTMLComboboxTextFieldAccessible::GetNextSibling(nsIAccessible **aNextSibling)
01002 { 
01003   if (mNextSibling) {
01004     *aNextSibling = mNextSibling;
01005   }
01006   else {
01007     nsHTMLComboboxButtonAccessible* accessible =
01008       new nsHTMLComboboxButtonAccessible(mParent, mDOMNode, mWeakShell);
01009     *aNextSibling = accessible;
01010     if (!*aNextSibling)
01011       return NS_ERROR_FAILURE;
01012     mNextSibling = *aNextSibling;
01013     accessible->Init();
01014   }
01015   NS_ADDREF(*aNextSibling);
01016   return NS_OK;
01017 } 
01018 
01024 NS_IMETHODIMP nsHTMLComboboxTextFieldAccessible::GetValue(nsAString& _retval)
01025 {
01026   nsIFrame* frame = nsAccessible::GetBoundsFrame();
01027   if (!frame)
01028     return NS_ERROR_FAILURE;
01029 
01030   frame = frame->GetFirstChild(nsnull)->GetFirstChild(nsnull);
01031   nsIContent* content = frame->GetContent();
01032 
01033   if (!content) 
01034     return NS_ERROR_FAILURE;
01035 
01036   AppendFlatStringFromSubtree(content, &_retval);
01037 
01038   return NS_OK;
01039 }
01040 
01041 NS_IMETHODIMP nsHTMLComboboxTextFieldAccessible::GetUniqueID(void **aUniqueID)
01042 {
01043   // Since mDOMNode is same for all tree item, use |this| pointer as the unique Id
01044   *aUniqueID = NS_STATIC_CAST(void*, this);
01045   return NS_OK;
01046 }
01047 
01052 void nsHTMLComboboxTextFieldAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame)
01053 {
01054   // get our first child's frame
01055   nsIFrame* frame = nsAccessible::GetBoundsFrame();
01056   if (!frame)
01057     return;
01058 
01059   frame = frame->GetFirstChild(nsnull);
01060   *aBoundingFrame = frame;
01061 
01062   aBounds = frame->GetRect();
01063 }
01064 
01066 NS_IMETHODIMP nsHTMLComboboxTextFieldAccessible::GetParent(nsIAccessible **_retval)
01067 {   
01068     *_retval = mParent;
01069     NS_IF_ADDREF(*_retval);
01070     return NS_OK;
01071 }
01072 
01076 NS_IMETHODIMP nsHTMLComboboxTextFieldAccessible::GetPreviousSibling(nsIAccessible **_retval)
01077 { 
01078   *_retval = nsnull;
01079   return NS_OK;
01080 } 
01081 
01086 NS_IMETHODIMP nsHTMLComboboxTextFieldAccessible::GetRole(PRUint32 *_retval)
01087 {
01088   *_retval = ROLE_STATICTEXT;
01089   return NS_OK;
01090 }
01091 
01098 NS_IMETHODIMP nsHTMLComboboxTextFieldAccessible::GetState(PRUint32 *_retval)
01099 {
01100   // Get focus status from base class
01101   nsAccessible::GetState(_retval);
01102 
01103   *_retval |= STATE_READONLY | STATE_FOCUSABLE;
01104 
01105   return NS_OK;
01106 }
01107 
01108 
01112 nsHTMLComboboxButtonAccessible::nsHTMLComboboxButtonAccessible(nsIAccessible* aParent, 
01113                                                            nsIDOMNode* aDOMNode, 
01114                                                            nsIWeakReference* aShell):
01115 nsLeafAccessible(aDOMNode, aShell)
01116 {
01117   // There is no cache entry for this item. 
01118   // It's generated and ref'd by  nsHTMLComboboxAccessible
01119   SetParent(aParent);
01120 }
01121 
01123 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::GetNumActions(PRUint8 *aNumActions)
01124 {
01125   *aNumActions = eSingle_Action;
01126   return NS_OK;
01127 }
01128 
01134 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::DoAction(PRUint8 aIndex)
01135 {
01136   nsIFrame* frame = nsAccessible::GetBoundsFrame();
01137   nsPresContext *context = GetPresContext();
01138   if (!frame || !context)
01139     return NS_ERROR_FAILURE;
01140 
01141   frame = frame->GetFirstChild(nsnull)->GetNextSibling();
01142 
01143   // We only have one action, click. Any other index is meaningless(wrong)
01144   if (aIndex == eAction_Click) {
01145     nsCOMPtr<nsIDOMHTMLInputElement>
01146       element(do_QueryInterface(frame->GetContent()));
01147     if (element)
01148     {
01149        element->Click();
01150        return NS_OK;
01151     }
01152     return NS_ERROR_FAILURE;
01153   }
01154   return NS_ERROR_INVALID_ARG;
01155 }
01156 
01163 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::GetActionName(PRUint8 aIndex, nsAString& _retval)
01164 {
01165   PRBool isOpen = PR_FALSE;
01166   nsIFrame *boundsFrame = GetBoundsFrame();
01167   nsIComboboxControlFrame* comboFrame;
01168   boundsFrame->QueryInterface(NS_GET_IID(nsIComboboxControlFrame), (void**)&comboFrame);
01169   if (!comboFrame)
01170     return NS_ERROR_FAILURE;
01171   comboFrame->IsDroppedDown(&isOpen);
01172   if (isOpen)
01173     nsAccessible::GetTranslatedString(NS_LITERAL_STRING("close"), _retval); 
01174   else
01175     nsAccessible::GetTranslatedString(NS_LITERAL_STRING("open"), _retval); 
01176 
01177   return NS_OK;
01178 }
01179 
01180 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::GetUniqueID(void **aUniqueID)
01181 {
01182   // Since mDOMNode is same for all tree item, use |this| pointer as the unique Id
01183   *aUniqueID = NS_STATIC_CAST(void*, this);
01184   return NS_OK;
01185 }
01186 
01191 void nsHTMLComboboxButtonAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame)
01192 {
01193   // get our second child's frame
01194   // bounding frame is the ComboboxControlFrame
01195   nsIFrame *frame = nsAccessible::GetBoundsFrame();
01196   *aBoundingFrame = frame;
01197   nsPresContext *context = GetPresContext();
01198   if (!frame || !context)
01199     return;
01200 
01201   aBounds = frame->GetFirstChild(nsnull)->GetNextSibling()->GetRect();
01202     // sibling frame is for the button
01203 }
01204 
01206 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::GetRole(PRUint32 *_retval)
01207 {
01208   *_retval = ROLE_PUSHBUTTON;
01209   return NS_OK;
01210 }
01211 
01213 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::GetParent(nsIAccessible **aParent)
01214 {   
01215   NS_IF_ADDREF(*aParent = mParent);
01216   return NS_OK;
01217 }
01218 
01222 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::GetName(nsAString& _retval)
01223 {
01224   return GetActionName(eAction_Click, _retval);
01225 }
01226 
01233 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::GetState(PRUint32 *_retval)
01234 {
01235   // Get focus status from base class
01236   nsAccessible::GetState(_retval);
01237 
01238   // we are open or closed --> pressed or not
01239   PRBool isOpen = PR_FALSE;
01240   nsIFrame *boundsFrame = GetBoundsFrame();
01241   nsIComboboxControlFrame* comboFrame;
01242   boundsFrame->QueryInterface(NS_GET_IID(nsIComboboxControlFrame), (void**)&comboFrame);
01243   if (!comboFrame)
01244     return NS_ERROR_FAILURE;
01245   comboFrame->IsDroppedDown(&isOpen);
01246   if (isOpen)
01247   *_retval |= STATE_PRESSED;
01248 
01249   *_retval |= STATE_FOCUSABLE;
01250 
01251   return NS_OK;
01252 }
01253 
01257 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::GetNextSibling(nsIAccessible **aNextSibling)
01258 { 
01259   if (mNextSibling) {
01260     *aNextSibling = mNextSibling;
01261   }
01262   else {
01263     nsHTMLComboboxListAccessible* accessible = 
01264       new nsHTMLComboboxListAccessible(mParent, mDOMNode, mWeakShell);
01265     *aNextSibling = accessible;
01266     if (!*aNextSibling)
01267       return NS_ERROR_OUT_OF_MEMORY;
01268     mNextSibling = *aNextSibling;
01269     accessible->Init();
01270   }
01271   NS_ADDREF(*aNextSibling);
01272   return NS_OK;
01273 } 
01274 
01278 NS_IMETHODIMP nsHTMLComboboxButtonAccessible::GetPreviousSibling(nsIAccessible **aAccPrevSibling)
01279 { 
01280   return mParent->GetFirstChild(aAccPrevSibling);
01281 } 
01282 
01283 
01286 nsHTMLComboboxListAccessible::nsHTMLComboboxListAccessible(nsIAccessible *aParent,
01287                                                            nsIDOMNode* aDOMNode, 
01288                                                            nsIWeakReference* aShell):
01289 nsHTMLSelectListAccessible(aDOMNode, aShell)
01290 {
01291   // There is no cache entry for this item. 
01292   // It's generated and ref'd by  nsHTMLComboboxAccessible
01293   SetParent(aParent);
01294 }
01295 
01303 NS_IMETHODIMP nsHTMLComboboxListAccessible::GetState(PRUint32 *aState)
01304 {
01305   // Get focus status from base class
01306   nsAccessible::GetState(aState);
01307 
01308   // we are open or closed
01309   PRBool isOpen = PR_FALSE;
01310   nsIFrame *boundsFrame = GetBoundsFrame();
01311   nsIComboboxControlFrame* comboFrame = nsnull;
01312   boundsFrame->QueryInterface(NS_GET_IID(nsIComboboxControlFrame), (void**)&comboFrame);
01313   if (!comboFrame)
01314     return NS_ERROR_FAILURE;
01315   comboFrame->IsDroppedDown(&isOpen);
01316   if (isOpen)
01317     *aState |= STATE_FLOATING | STATE_FOCUSABLE;
01318   else
01319     *aState |= STATE_INVISIBLE | STATE_FOCUSABLE;
01320   
01321   return NS_OK;
01322 }
01323 
01325 NS_IMETHODIMP nsHTMLComboboxListAccessible::GetParent(nsIAccessible **aParent)
01326 {
01327   NS_IF_ADDREF(*aParent = mParent);
01328   return NS_OK;
01329 }
01330 
01331 NS_IMETHODIMP nsHTMLComboboxListAccessible::GetPreviousSibling(nsIAccessible **aAccPrevSibling)
01332 { 
01333   return mParent->GetChildAt(1, aAccPrevSibling);
01334 } 
01335 
01336 NS_IMETHODIMP nsHTMLComboboxListAccessible::GetUniqueID(void **aUniqueID)
01337 {
01338   // Since mDOMNode is same for all tree item, use |this| pointer as the unique Id
01339   *aUniqueID = NS_STATIC_CAST(void*, this);
01340   return NS_OK;
01341 }
01342 
01347 void nsHTMLComboboxListAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame)
01348 {
01349    // get our first option
01350   nsCOMPtr<nsIDOMNode> child;
01351   mDOMNode->GetFirstChild(getter_AddRefs(child));
01352 
01353   // now get its frame
01354   nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
01355   if (!shell) {
01356     *aBoundingFrame = nsnull;
01357     return;
01358   }
01359 
01360   nsIFrame* frame = nsnull;
01361   nsCOMPtr<nsIContent> content(do_QueryInterface(child));
01362   shell->GetPrimaryFrameFor(content, &frame);
01363   if (!frame) {
01364     *aBoundingFrame = nsnull;
01365     return;
01366   }
01367 
01368   *aBoundingFrame = frame->GetParent();
01369   aBounds = (*aBoundingFrame)->GetRect();
01370 }