Fix for bug 374790 - clean up accessible event firing, r=aaronlev, ginn.chen

This commit is contained in:
surkov.alexander@gmail.com 2007-04-09 21:37:52 -07:00
parent c3c85c9d19
commit 4fdfeeee01
2 changed files with 142 additions and 168 deletions

View File

@ -100,29 +100,75 @@ NS_IMETHODIMP nsRootAccessibleWrap::GetParent(nsIAccessible ** aParent)
nsresult nsRootAccessibleWrap::HandleEventWithTarget(nsIDOMEvent *aEvent, nsresult nsRootAccessibleWrap::HandleEventWithTarget(nsIDOMEvent *aEvent,
nsIDOMNode *aTargetNode) nsIDOMNode *aTargetNode)
{ {
// first let the cross-platform code dispatch any events, before
// we start doing platform-specific things
nsRootAccessible::HandleEventWithTarget(aEvent, aTargetNode);
nsAutoString eventType; nsAutoString eventType;
aEvent->GetType(eventType); aEvent->GetType(eventType);
nsAutoString localName; nsAutoString localName;
aTargetNode->GetLocalName(localName); aTargetNode->GetLocalName(localName);
if (eventType.LowerCaseEqualsLiteral("pagehide")) { if (eventType.EqualsLiteral("pagehide")) {
// nsRootAccessible::HandleEventWithTarget() has destoryed the accessible object nsRootAccessible::HandleEventWithTarget(aEvent, aTargetNode);
// we don't want to create it again return NS_OK;
return NS_OK;
} }
nsCOMPtr<nsIAccessible> accessible; nsCOMPtr<nsIAccessible> accessible;
nsCOMPtr<nsIAccessibilityService> accService = GetAccService(); nsCOMPtr<nsIAccessibilityService> accService = GetAccService();
accService->GetAccessibleFor(aTargetNode, getter_AddRefs(accessible)); accService->GetAccessibleFor(aTargetNode, getter_AddRefs(accessible));
if (!accessible) if (!accessible)
return NS_OK; return NS_OK;
if (eventType.EqualsLiteral("popupshown")) {
nsRootAccessible::HandleEventWithTarget(aEvent, aTargetNode);
nsCOMPtr<nsIContent> content(do_QueryInterface(aTargetNode));
// 1) Don't fire focus events for tooltips, that wouldn't make any sense.
// 2) Don't fire focus events for autocomplete popups, because they
// come up automatically while the user is typing, and setting focus
// there would interrupt the user.
// If the AT wants to know about these popups it can track the ATK state
// change event we fire for ATK_STATE_INVISIBLE on the popup. This is
// fired as a result of the nsIAccessibleEvent::EVENT_MENUPOPUP_START we
// fire in the nsRootAccessible event handling for all popups.
if (!content->NodeInfo()->Equals(nsAccessibilityAtoms::tooltip,
kNameSpaceID_XUL) &&
!content->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type,
NS_LITERAL_STRING("autocomplete"), eIgnoreCase)) {
FireAccessibleFocusEvent(accessible, aTargetNode, aEvent);
}
return NS_OK;
}
StateChange stateData;
nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible)); nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible));
if (eventType.EqualsLiteral("CheckboxStateChange") || // it's a XUL <checkbox>
eventType.EqualsLiteral("RadioStateChange")) { // it's a XUL <radio>
stateData.state = State(accessible);
// prefPane tab is implemented as list items in A11y, so we need to
// check nsIAccessibleStates::STATE_SELECTED also
stateData.enable = (stateData.state &
(nsIAccessibleStates::STATE_CHECKED |
nsIAccessibleStates::STATE_SELECTED)) != 0;
stateData.state = nsIAccessibleStates::STATE_CHECKED;
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE,
accessible, &stateData);
// only fire focus event for checked radio
if (eventType.EqualsLiteral("RadioStateChange") &&
stateData.enable) {
FireAccessibleFocusEvent(accessible, aTargetNode, aEvent);
}
return NS_OK;
}
if (eventType.EqualsLiteral("OpenStateChange")) {
stateData.state = State(accessible); // collapsed/expanded changed
stateData.enable = (stateData.state & nsIAccessibleStates::STATE_EXPANDED) != 0;
stateData.state = nsIAccessibleStates::STATE_EXPANDED;
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE,
accessible, &stateData);
return NS_OK;
}
#ifdef MOZ_XUL #ifdef MOZ_XUL
// If it's a tree element, need the currently selected item // If it's a tree element, need the currently selected item
nsCOMPtr<nsIAccessible> treeItemAccessible; nsCOMPtr<nsIAccessible> treeItemAccessible;
@ -147,9 +193,8 @@ nsresult nsRootAccessibleWrap::HandleEventWithTarget(nsIDOMEvent *aEvent,
} }
} }
#endif #endif
StateChange stateData; if (eventType.EqualsLiteral("focus")) {
if (eventType.LowerCaseEqualsLiteral("focus")) {
#ifdef MOZ_XUL #ifdef MOZ_XUL
if (treeItemAccessible) { // use focused treeitem if (treeItemAccessible) { // use focused treeitem
privAcc = do_QueryInterface(treeItemAccessible); privAcc = do_QueryInterface(treeItemAccessible);
@ -190,7 +235,7 @@ nsresult nsRootAccessibleWrap::HandleEventWithTarget(nsIDOMEvent *aEvent,
&stateData); &stateData);
} }
} }
else if (eventType.LowerCaseEqualsLiteral("select")) { else if (eventType.EqualsLiteral("select")) {
#ifdef MOZ_XUL #ifdef MOZ_XUL
if (treeItemAccessible) { // it's a XUL <tree> if (treeItemAccessible) { // it's a XUL <tree>
// use EVENT_FOCUS instead of EVENT_SELECTION_CHANGED // use EVENT_FOCUS instead of EVENT_SELECTION_CHANGED
@ -204,65 +249,10 @@ nsresult nsRootAccessibleWrap::HandleEventWithTarget(nsIDOMEvent *aEvent,
// make GOK refresh "UI-Grab" window // make GOK refresh "UI-Grab" window
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_REORDER, accessible, nsnull); privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_REORDER, accessible, nsnull);
} }
} else {
nsRootAccessible::HandleEventWithTarget(aEvent, aTargetNode);
} }
else if (eventType.LowerCaseEqualsLiteral("checkboxstatechange") || // it's a XUL <checkbox>
eventType.LowerCaseEqualsLiteral("radiostatechange")) { // it's a XUL <radio>
stateData.state = State(accessible);
// prefPane tab is implemented as list items in A11y, so we need to
// check nsIAccessibleStates::STATE_SELECTED also
stateData.enable = (stateData.state &
(nsIAccessibleStates::STATE_CHECKED |
nsIAccessibleStates::STATE_SELECTED)) != 0;
stateData.state = nsIAccessibleStates::STATE_CHECKED;
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, accessible, &stateData);
// only fire focus event for checked radio
if (eventType.LowerCaseEqualsLiteral("radiostatechange") &&
stateData.enable) {
FireAccessibleFocusEvent(accessible, aTargetNode, aEvent);
}
}
else if (eventType.LowerCaseEqualsLiteral("openstatechange")) { // collapsed/expanded changed
stateData.state = State(accessible);
stateData.enable = (stateData.state & nsIAccessibleStates::STATE_EXPANDED) != 0;
stateData.state = nsIAccessibleStates::STATE_EXPANDED;
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, accessible, &stateData);
}
else if (eventType.LowerCaseEqualsLiteral("popuphiding")) {
// If accessible focus was inside popup that closes,
// then restore it to true current focus.
// This is the case when we've been getting DOMMenuItemActive events
// inside of a combo box that closes. The real focus is on the combo box.
if (!gLastFocusedNode) {
return NS_OK;
}
nsCOMPtr<nsIDOMNode> parentOfFocus;
gLastFocusedNode->GetParentNode(getter_AddRefs(parentOfFocus));
if (parentOfFocus != aTargetNode) {
return NS_OK;
}
// Focus was inside of popup that's being hidden
FireCurrentFocusEvent();
}
else if (eventType.LowerCaseEqualsLiteral("popupshown")) {
#ifdef MOZ_XUL
nsCOMPtr<nsIContent> content(do_QueryInterface(aTargetNode));
if (content->NodeInfo()->Equals(nsAccessibilityAtoms::tooltip, kNameSpaceID_XUL) ||
content->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type,
NS_LITERAL_STRING("autocomplete"), eIgnoreCase)) {
// 1) Don't fire focus events for tooltips, that wouldn't make any sense.
// 2) Don't fire focus events for autocomplete popups, because they come up
// automatically while the user is typing, and setting focus there would
// interrupt the user.
// ------------------------------------------------------------------------
// If the AT wants to know about these popups it can track the ATK state change
// event we fire for ATK_STATE_INVISIBLE on the popup.
// This is fired as a result of the nsIAccessibleEvent::EVENT_MENUPOPUP_START
// we fire in the nsRootAccessible event handling for all popups.
return NS_OK;
}
#endif
FireAccessibleFocusEvent(accessible, aTargetNode, aEvent);
}
return NS_OK; return NS_OK;
} }

View File

@ -568,7 +568,7 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
aTargetNode->GetLocalName(localName); aTargetNode->GetLocalName(localName);
#ifdef DEBUG_A11Y #ifdef DEBUG_A11Y
// Very useful for debugging, please leave this here. // Very useful for debugging, please leave this here.
if (eventType.LowerCaseEqualsLiteral("alertactive")) { if (eventType.EqualsLiteral("AlertActive")) {
printf("\ndebugging %s events for %s", NS_ConvertUTF16toUTF8(eventType).get(), NS_ConvertUTF16toUTF8(localName).get()); printf("\ndebugging %s events for %s", NS_ConvertUTF16toUTF8(eventType).get(), NS_ConvertUTF16toUTF8(localName).get());
} }
if (localName.LowerCaseEqualsLiteral("textbox")) { if (localName.LowerCaseEqualsLiteral("textbox")) {
@ -584,7 +584,7 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
nsIAccessibilityService *accService = GetAccService(); nsIAccessibilityService *accService = GetAccService();
NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE); NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
if (eventType.LowerCaseEqualsLiteral("pagehide")) { if (eventType.EqualsLiteral("pagehide")) {
// pagehide event can be fired under several conditions, such as HTML // pagehide event can be fired under several conditions, such as HTML
// document going away, closing a window/dialog, and wizard page changing. // document going away, closing a window/dialog, and wizard page changing.
// We only destroy the accessible object when it's a document accessible, // We only destroy the accessible object when it's a document accessible,
@ -601,7 +601,7 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
return NS_OK; return NS_OK;
} }
if (eventType.LowerCaseEqualsLiteral("popupshown")) { if (eventType.EqualsLiteral("popupshown")) {
// Fire menupopupstart events after a delay so that ancestor views // Fire menupopupstart events after a delay so that ancestor views
// are visible, otherwise an accessible cannot be created for the // are visible, otherwise an accessible cannot be created for the
// popup and the accessibility toolkit event can't be fired. // popup and the accessibility toolkit event can't be fired.
@ -620,7 +620,7 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
} }
} }
if (eventType.LowerCaseEqualsLiteral("domcontentloaded")) { if (eventType.EqualsLiteral("DOMContentLoaded")) {
// Don't create the doc accessible until load scripts have a chance to set // Don't create the doc accessible until load scripts have a chance to set
// role attribute for <body> or <html> element, because the value of // role attribute for <body> or <html> element, because the value of
// role attribute will be cached when the doc accessible is Init()'d // role attribute will be cached when the doc accessible is Init()'d
@ -642,9 +642,9 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
if (!accessible) if (!accessible)
return NS_OK; return NS_OK;
nsCOMPtr<nsIAccessible> treeItemAccessible;
#ifdef MOZ_XUL #ifdef MOZ_XUL
// If it's a tree element, need the currently selected item // If it's a tree element, need the currently selected item
nsCOMPtr<nsIAccessible> treeItemAccessible;
if (localName.EqualsLiteral("tree")) { if (localName.EqualsLiteral("tree")) {
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect = nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
do_QueryInterface(aTargetNode); do_QueryInterface(aTargetNode);
@ -669,83 +669,68 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible)); nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible));
#ifndef MOZ_ACCESSIBILITY_ATK
#ifdef MOZ_XUL #ifdef MOZ_XUL
// tree event // tree event
if (eventType.LowerCaseEqualsLiteral("checkboxstatechange") || if (eventType.EqualsLiteral("CheckboxStateChange") ||
eventType.LowerCaseEqualsLiteral("openstatechange")) { eventType.EqualsLiteral("OpenStateChange")) {
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, return privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE,
accessible, nsnull); accessible, nsnull);
return NS_OK;
}
else if (treeItemAccessible) {
if (eventType.LowerCaseEqualsLiteral("focus")) {
FireAccessibleFocusEvent(accessible, aTargetNode, aEvent); // Tree has focus
}
else if (eventType.LowerCaseEqualsLiteral("dommenuitemactive")) {
FireAccessibleFocusEvent(treeItemAccessible, aTargetNode, aEvent, PR_TRUE);
}
else if (eventType.LowerCaseEqualsLiteral("namechange")) {
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
accessible, nsnull);
}
else if (eventType.LowerCaseEqualsLiteral("select")) {
// If multiselect tree, we should fire selectionadd or selection removed
if (gLastFocusedNode == aTargetNode) {
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
do_QueryInterface(aTargetNode);
nsAutoString selType;
multiSel->GetSelType(selType);
if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
accessible, nsnull);
// XXX We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
// for each tree item. Perhaps each tree item will need to
// cache its selection state and fire an event after a DOM "select"
// event when that state changes.
// nsXULTreeAccessible::UpdateTreeSelection();
}
else {
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION,
treeItemAccessible, nsnull);
}
}
}
return NS_OK;
} }
else else if (treeItemAccessible && eventType.EqualsLiteral("select")) {
// If multiselect tree, we should fire selectionadd or selection removed
if (gLastFocusedNode == aTargetNode) {
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
do_QueryInterface(aTargetNode);
nsAutoString selType;
multiSel->GetSelType(selType);
if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
// XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
// for each tree item. Perhaps each tree item will need to cache its
// selection state and fire an event after a DOM "select" event when
// that state changes. nsXULTreeAccessible::UpdateTreeSelection();
return privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
accessible, nsnull);
}
return privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION,
treeItemAccessible, nsnull);
}
}
else
#endif #endif
if (eventType.LowerCaseEqualsLiteral("focus")) { if (eventType.EqualsLiteral("focus")) {
nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
do_QueryInterface(aTargetNode);
// Keep a reference to the target node. We might want to change // Keep a reference to the target node. We might want to change
// it to the individual radio button or selected item, and send // it to the individual radio button or selected item, and send
// the focus event to that. // the focus event to that.
nsCOMPtr<nsIDOMNode> focusedItem(aTargetNode); nsCOMPtr<nsIDOMNode> focusedItem(aTargetNode);
if (selectControl) {
nsCOMPtr<nsIDOMXULMenuListElement> menuList = if (!treeItemAccessible) {
nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
do_QueryInterface(aTargetNode); do_QueryInterface(aTargetNode);
if (!menuList) { if (selectControl) {
// Don't do this for menu lists, the items only get focused nsCOMPtr<nsIDOMXULMenuListElement> menuList =
// when the list is open, based on DOMMenuitemActive events do_QueryInterface(aTargetNode);
nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem; if (!menuList) {
selectControl->GetSelectedItem(getter_AddRefs(selectedItem)); // Don't do this for menu lists, the items only get focused
if (selectedItem) { // when the list is open, based on DOMMenuitemActive events
focusedItem = do_QueryInterface(selectedItem); nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem;
selectControl->GetSelectedItem(getter_AddRefs(selectedItem));
if (selectedItem)
focusedItem = do_QueryInterface(selectedItem);
if (!focusedItem)
return NS_OK;
accService->GetAccessibleInShell(focusedItem, eventShell,
getter_AddRefs(accessible));
if (!accessible)
return NS_OK;
} }
if (!focusedItem)
return NS_OK;
accService->GetAccessibleInShell(focusedItem, eventShell,
getter_AddRefs(accessible));
if (!accessible)
return NS_OK;
} }
} }
FireAccessibleFocusEvent(accessible, focusedItem, aEvent); FireAccessibleFocusEvent(accessible, focusedItem, aEvent);
} }
else if (eventType.LowerCaseEqualsLiteral("namechange")) { else if (eventType.EqualsLiteral("NameChange")) {
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
accessible, nsnull); accessible, nsnull);
} }
@ -753,7 +738,7 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_ALERT, privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_ALERT,
accessible, nsnull); accessible, nsnull);
} }
else if (eventType.LowerCaseEqualsLiteral("radiostatechange") ) { else if (eventType.EqualsLiteral("RadioStateChange")) {
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE,
accessible, nsnull); accessible, nsnull);
PRUint32 finalState = State(accessible); PRUint32 finalState = State(accessible);
@ -762,7 +747,7 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
FireAccessibleFocusEvent(accessible, aTargetNode, aEvent); FireAccessibleFocusEvent(accessible, aTargetNode, aEvent);
} }
} }
else if (eventType.LowerCaseEqualsLiteral("popuphiding")) { else if (eventType.EqualsLiteral("popuphiding")) {
// If accessible focus was inside popup that closes, // If accessible focus was inside popup that closes,
// then restore it to true current focus. // then restore it to true current focus.
// This is the case when we've been getting DOMMenuItemActive events // This is the case when we've been getting DOMMenuItemActive events
@ -785,38 +770,37 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
accessible, nsnull); accessible, nsnull);
} }
} }
else else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
#endif if (!treeItemAccessible) {
if (eventType.LowerCaseEqualsLiteral("dommenuitemactive")) { nsCOMPtr<nsIAccessible> containerAccessible;
nsCOMPtr<nsIAccessible> containerAccessible; accessible->GetParent(getter_AddRefs(containerAccessible));
accessible->GetParent(getter_AddRefs(containerAccessible)); NS_ENSURE_TRUE(containerAccessible, NS_OK);
NS_ENSURE_TRUE(containerAccessible, NS_OK); if (Role(containerAccessible) == nsIAccessibleRole::ROLE_MENUBAR) {
if (Role(containerAccessible) == nsIAccessibleRole::ROLE_MENUBAR) { nsCOMPtr<nsPIAccessNode> menuBarAccessNode(do_QueryInterface(containerAccessible));
nsCOMPtr<nsPIAccessNode> menuBarAccessNode(do_QueryInterface(containerAccessible)); NS_ENSURE_TRUE(menuBarAccessNode, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(menuBarAccessNode, NS_ERROR_FAILURE); nsCOMPtr<nsIMenuParent> menuParent = do_QueryInterface(menuBarAccessNode->GetFrame());
nsCOMPtr<nsIMenuParent> menuParent = do_QueryInterface(menuBarAccessNode->GetFrame()); NS_ENSURE_TRUE(menuParent, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(menuParent, NS_ERROR_FAILURE); PRBool isActive;
PRBool isActive; menuParent->GetIsActive(isActive);
menuParent->GetIsActive(isActive); if (!isActive) {
if (!isActive) { // It is a top level menuitem. Only fire focus event the menu bar
// It is a top level menuitem // is active.
// Only fire focus event the menu bar is active return NS_OK;
return NS_OK; }
} else {
// It is not top level menuitem
// Only fire focus event if it is not inside collapsed popup
if (State(containerAccessible) & nsIAccessibleStates::STATE_COLLAPSED)
return NS_OK;
} }
} }
else {
// It is not top level menuitem
// Only fire focus event if it is not inside collapsed popup
if (State(containerAccessible) & nsIAccessibleStates::STATE_COLLAPSED)
return NS_OK;
}
FireAccessibleFocusEvent(accessible, aTargetNode, aEvent, PR_TRUE); FireAccessibleFocusEvent(accessible, aTargetNode, aEvent, PR_TRUE);
} }
else if (eventType.LowerCaseEqualsLiteral("dommenubaractive")) { else if (eventType.EqualsLiteral("DOMMenuBarActive")) {
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENU_START, privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENU_START,
accessible, nsnull); accessible, nsnull);
} }
else if (eventType.LowerCaseEqualsLiteral("dommenubarinactive")) { else if (eventType.EqualsLiteral("DOMMenuBarInactive")) {
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENU_END, privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENU_END,
accessible, nsnull); accessible, nsnull);
FireCurrentFocusEvent(); FireCurrentFocusEvent();