mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 673958 - rework accessible focus handling, r=enndeaking, marcoz, tbsaunde, matspal, f=marcoz
--HG-- rename : accessible/tests/mochitest/test_aria_activedescendant.html => accessible/tests/mochitest/events/test_focus_aria_activedescendant.html rename : accessible/tests/mochitest/events/test_focus.html => accessible/tests/mochitest/events/test_focus_dialog.html rename : accessible/tests/mochitest/events/test_focusdoc.html => accessible/tests/mochitest/events/test_focus_doc.html rename : accessible/tests/mochitest/events/test_focus.xul => accessible/tests/mochitest/events/test_focus_general.xul rename : accessible/tests/mochitest/states/test_comboboxes.xul => accessible/tests/mochitest/states/test_expandable.xul rename : accessible/tests/mochitest/test_nsIAccessible_selects.html => accessible/tests/mochitest/states/test_selects.html
This commit is contained in:
parent
616ecf38f9
commit
6a04618807
@ -1241,7 +1241,7 @@ nsAccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
|
||||
g_signal_emit(atkObj, id, 0);
|
||||
|
||||
// Always fire a current focus event after activation.
|
||||
rootAcc->FireCurrentFocusEvent();
|
||||
FocusMgr()->ForceFocusEvent();
|
||||
} break;
|
||||
|
||||
case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE:
|
||||
|
@ -78,9 +78,9 @@ public:
|
||||
// will be emitted.
|
||||
eCoalesceFromSameSubtree,
|
||||
|
||||
// eCoalesceFromSameDocument : For events of the same type from the same
|
||||
// document, only the newest event will be emitted.
|
||||
eCoalesceFromSameDocument,
|
||||
// eCoalesceOfSameType : For events of the same type, only the newest event
|
||||
// will be processed.
|
||||
eCoalesceOfSameType,
|
||||
|
||||
// eRemoveDupes : For repeat events, only the newest event in queue
|
||||
// will be emitted.
|
||||
|
361
accessible/src/base/FocusManager.cpp
Normal file
361
accessible/src/base/FocusManager.cpp
Normal file
@ -0,0 +1,361 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "FocusManager.h"
|
||||
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsRootAccessible.h"
|
||||
|
||||
#include "nsFocusManager.h"
|
||||
|
||||
namespace dom = mozilla::dom;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
FocusManager::FocusManager()
|
||||
{
|
||||
}
|
||||
|
||||
FocusManager::~FocusManager()
|
||||
{
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
FocusManager::FocusedAccessible() const
|
||||
{
|
||||
if (mActiveItem)
|
||||
return mActiveItem;
|
||||
|
||||
nsINode* focusedNode = FocusedDOMNode();
|
||||
if (focusedNode)
|
||||
return GetAccService()->GetAccessibleOrContainer(focusedNode, nsnull);
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
bool
|
||||
FocusManager::IsFocused(const nsAccessible* aAccessible) const
|
||||
{
|
||||
if (mActiveItem)
|
||||
return mActiveItem == aAccessible;
|
||||
|
||||
nsINode* focusedNode = FocusedDOMNode();
|
||||
if (focusedNode) {
|
||||
// XXX: Before getting an accessible for node having a DOM focus make sure
|
||||
// they belong to the same document because it can trigger unwanted document
|
||||
// accessible creation for temporary about:blank document. Without this
|
||||
// peculiarity we would end up with plain implementation based on
|
||||
// FocusedAccessible() method call. Make sure this issue is fixed in
|
||||
// bug 638465.
|
||||
if (focusedNode->GetOwnerDoc() == aAccessible->GetNode()->GetOwnerDoc()) {
|
||||
return aAccessible ==
|
||||
GetAccService()->GetAccessibleOrContainer(focusedNode, nsnull);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
FocusManager::IsFocusWithin(const nsAccessible* aContainer) const
|
||||
{
|
||||
nsAccessible* child = FocusedAccessible();
|
||||
while (child) {
|
||||
if (child == aContainer)
|
||||
return true;
|
||||
|
||||
child = child->Parent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FocusManager::FocusDisposition
|
||||
FocusManager::IsInOrContainsFocus(const nsAccessible* aAccessible) const
|
||||
{
|
||||
nsAccessible* focus = FocusedAccessible();
|
||||
if (!focus)
|
||||
return eNone;
|
||||
|
||||
// If focused.
|
||||
if (focus == aAccessible)
|
||||
return eFocused;
|
||||
|
||||
// If contains the focus.
|
||||
nsAccessible* child = focus->Parent();
|
||||
while (child) {
|
||||
if (child == aAccessible)
|
||||
return eContainsFocus;
|
||||
|
||||
child = child->Parent();
|
||||
}
|
||||
|
||||
// If contained by focus.
|
||||
child = aAccessible->Parent();
|
||||
while (child) {
|
||||
if (child == focus)
|
||||
return eContainedByFocus;
|
||||
|
||||
child = child->Parent();
|
||||
}
|
||||
|
||||
return eNone;
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::NotifyOfDOMFocus(nsISupports* aTarget)
|
||||
{
|
||||
A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET("DOM focus", "DOM focus target",
|
||||
aTarget)
|
||||
|
||||
mActiveItem = nsnull;
|
||||
|
||||
nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
|
||||
if (targetNode) {
|
||||
nsDocAccessible* document =
|
||||
GetAccService()->GetDocAccessible(targetNode->GetOwnerDoc());
|
||||
if (document) {
|
||||
// Set selection listener for focused element.
|
||||
if (targetNode->IsElement()) {
|
||||
nsRootAccessible* root = document->RootAccessible();
|
||||
nsCaretAccessible* caretAcc = root->GetCaretAccessible();
|
||||
caretAcc->SetControlSelectionListener(targetNode->AsElement());
|
||||
}
|
||||
|
||||
document->HandleNotification<FocusManager, nsINode>
|
||||
(this, &FocusManager::ProcessDOMFocus, targetNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::NotifyOfDOMBlur(nsISupports* aTarget)
|
||||
{
|
||||
A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET("DOM blur", "DOM blur target",
|
||||
aTarget)
|
||||
|
||||
mActiveItem = nsnull;
|
||||
|
||||
// If DOM document stays focused then fire accessible focus event to process
|
||||
// the case when no element within this DOM document will be focused.
|
||||
nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
|
||||
if (targetNode && targetNode->GetOwnerDoc() == FocusedDOMDocument()) {
|
||||
nsIDocument* DOMDoc = targetNode->GetOwnerDoc();
|
||||
nsDocAccessible* document =
|
||||
GetAccService()->GetDocAccessible(DOMDoc);
|
||||
if (document) {
|
||||
document->HandleNotification<FocusManager, nsINode>
|
||||
(this, &FocusManager::ProcessDOMFocus, DOMDoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::ActiveItemChanged(nsAccessible* aItem, bool aCheckIfActive)
|
||||
{
|
||||
A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET("active item changed",
|
||||
"Active item", aItem)
|
||||
|
||||
// Nothing changed, happens for XUL trees and HTML selects.
|
||||
if (aItem && aItem == mActiveItem)
|
||||
return;
|
||||
|
||||
mActiveItem = nsnull;
|
||||
|
||||
if (aItem && aCheckIfActive) {
|
||||
nsAccessible* widget = aItem->ContainerWidget();
|
||||
A11YDEBUG_FOCUS_LOG_WIDGET("Active item widget", widget)
|
||||
if (!widget || !widget->IsActiveWidget() || !widget->AreItemsOperable())
|
||||
return;
|
||||
}
|
||||
mActiveItem = aItem;
|
||||
|
||||
// If active item is changed then fire accessible focus event on it, otherwise
|
||||
// if there's no an active item then fire focus event to accessible having
|
||||
// DOM focus.
|
||||
nsAccessible* target = FocusedAccessible();
|
||||
if (target)
|
||||
DispatchFocusEvent(target->GetDocAccessible(), target);
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::ForceFocusEvent()
|
||||
{
|
||||
nsINode* focusedNode = FocusedDOMNode();
|
||||
if (focusedNode) {
|
||||
nsDocAccessible* document =
|
||||
GetAccService()->GetDocAccessible(focusedNode->GetOwnerDoc());
|
||||
if (document) {
|
||||
document->HandleNotification<FocusManager, nsINode>
|
||||
(this, &FocusManager::ProcessDOMFocus, focusedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::DispatchFocusEvent(nsDocAccessible* aDocument,
|
||||
nsAccessible* aTarget)
|
||||
{
|
||||
NS_PRECONDITION(aDocument, "No document for focused accessible!");
|
||||
if (aDocument) {
|
||||
nsRefPtr<AccEvent> event =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, aTarget,
|
||||
eAutoDetect, AccEvent::eCoalesceOfSameType);
|
||||
aDocument->FireDelayedAccessibleEvent(event);
|
||||
|
||||
A11YDEBUG_FOCUS_LOG_ACCTARGET("Focus notification", aTarget)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::ProcessDOMFocus(nsINode* aTarget)
|
||||
{
|
||||
A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET("Process DOM focus",
|
||||
"Notification target", aTarget)
|
||||
|
||||
nsDocAccessible* document =
|
||||
GetAccService()->GetDocAccessible(aTarget->GetOwnerDoc());
|
||||
|
||||
nsAccessible* target = document->GetAccessibleOrContainer(aTarget);
|
||||
if (target) {
|
||||
// Check if still focused. Otherwise we can end up with storing the active
|
||||
// item for control that isn't focused anymore.
|
||||
nsAccessible* DOMFocus =
|
||||
GetAccService()->GetAccessibleOrContainer(FocusedDOMNode(), nsnull);
|
||||
if (target != DOMFocus)
|
||||
return;
|
||||
|
||||
nsAccessible* activeItem = target->CurrentItem();
|
||||
if (activeItem) {
|
||||
mActiveItem = activeItem;
|
||||
target = activeItem;
|
||||
}
|
||||
|
||||
DispatchFocusEvent(document, target);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::ProcessFocusEvent(AccEvent* aEvent)
|
||||
{
|
||||
NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS,
|
||||
"Focus event is expected!");
|
||||
|
||||
EIsFromUserInput fromUserInputFlag = aEvent->IsFromUserInput() ?
|
||||
eFromUserInput : eNoUserInput;
|
||||
|
||||
// Emit focus event if event target is the active item. Otherwise then check
|
||||
// if it's still focused and then update active item and emit focus event.
|
||||
nsAccessible* target = aEvent->GetAccessible();
|
||||
if (target != mActiveItem) {
|
||||
// Check if still focused. Otherwise we can end up with storing the active
|
||||
// item for control that isn't focused anymore.
|
||||
nsAccessible* DOMFocus =
|
||||
GetAccService()->GetAccessibleOrContainer(FocusedDOMNode(), nsnull);
|
||||
if (target != DOMFocus)
|
||||
return;
|
||||
|
||||
nsAccessible* activeItem = target->CurrentItem();
|
||||
if (activeItem) {
|
||||
mActiveItem = activeItem;
|
||||
target = activeItem;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire menu start/end events for ARIA menus.
|
||||
if (target->ARIARole() == nsIAccessibleRole::ROLE_MENUITEM) {
|
||||
// The focus was moved into menu.
|
||||
nsAccessible* ARIAMenubar =
|
||||
nsAccUtils::GetAncestorWithRole(target, nsIAccessibleRole::ROLE_MENUBAR);
|
||||
|
||||
if (ARIAMenubar != mActiveARIAMenubar) {
|
||||
// Leaving ARIA menu. Fire menu_end event on current menubar.
|
||||
if (mActiveARIAMenubar) {
|
||||
nsRefPtr<AccEvent> menuEndEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
|
||||
fromUserInputFlag);
|
||||
nsEventShell::FireEvent(menuEndEvent);
|
||||
}
|
||||
|
||||
mActiveARIAMenubar = ARIAMenubar;
|
||||
|
||||
// Entering ARIA menu. Fire menu_start event.
|
||||
if (mActiveARIAMenubar) {
|
||||
nsRefPtr<AccEvent> menuStartEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
|
||||
mActiveARIAMenubar, fromUserInputFlag);
|
||||
nsEventShell::FireEvent(menuStartEvent);
|
||||
}
|
||||
}
|
||||
} else if (mActiveARIAMenubar) {
|
||||
// Focus left a menu. Fire menu_end event.
|
||||
nsRefPtr<AccEvent> menuEndEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
|
||||
fromUserInputFlag);
|
||||
nsEventShell::FireEvent(menuEndEvent);
|
||||
|
||||
mActiveARIAMenubar = nsnull;
|
||||
}
|
||||
|
||||
A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET("FIRE FOCUS EVENT", "Focus target",
|
||||
target)
|
||||
|
||||
nsRefPtr<AccEvent> focusEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, fromUserInputFlag);
|
||||
nsEventShell::FireEvent(focusEvent);
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
FocusManager::FocusedDOMElm() const
|
||||
{
|
||||
nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
|
||||
return DOMFocusManager->GetFocusedContent();
|
||||
}
|
||||
|
||||
nsIDocument*
|
||||
FocusManager::FocusedDOMDocument() const
|
||||
{
|
||||
nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> focusedWnd;
|
||||
DOMFocusManager->GetFocusedWindow(getter_AddRefs(focusedWnd));
|
||||
if (focusedWnd) {
|
||||
nsCOMPtr<nsIDOMDocument> DOMDoc;
|
||||
focusedWnd->GetDocument(getter_AddRefs(DOMDoc));
|
||||
nsCOMPtr<nsIDocument> DOMDocNode(do_QueryInterface(DOMDoc));
|
||||
return DOMDocNode;
|
||||
}
|
||||
return nsnull;
|
||||
}
|
297
accessible/src/base/FocusManager.h
Normal file
297
accessible/src/base/FocusManager.h
Normal file
@ -0,0 +1,297 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef mozilla_a11y_FocusManager_h_
|
||||
#define mozilla_a11y_FocusManager_h_
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
class AccEvent;
|
||||
class nsAccessible;
|
||||
class nsDocAccessible;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* Manage the accessible focus. Used to fire and process accessible events.
|
||||
*/
|
||||
class FocusManager
|
||||
{
|
||||
public:
|
||||
virtual ~FocusManager();
|
||||
|
||||
/**
|
||||
* Return a focused accessible.
|
||||
*/
|
||||
nsAccessible* FocusedAccessible() const;
|
||||
|
||||
/**
|
||||
* Return true if given accessible is focused.
|
||||
*/
|
||||
bool IsFocused(const nsAccessible* aAccessible) const;
|
||||
|
||||
/**
|
||||
* Return true if the given accessible is an active item, i.e. an item that
|
||||
* is current within the active widget.
|
||||
*/
|
||||
inline bool IsActiveItem(const nsAccessible* aAccessible)
|
||||
{ return aAccessible == mActiveItem; }
|
||||
|
||||
/**
|
||||
* Return true if given DOM node has DOM focus.
|
||||
*/
|
||||
inline bool HasDOMFocus(const nsINode* aNode) const
|
||||
{ return aNode == FocusedDOMNode(); }
|
||||
|
||||
/**
|
||||
* Return true if focused accessible is within the given container.
|
||||
*/
|
||||
bool IsFocusWithin(const nsAccessible* aContainer) const;
|
||||
|
||||
/**
|
||||
* Return whether the given accessible is focused or contains the focus or
|
||||
* contained by focused accessible.
|
||||
*/
|
||||
enum FocusDisposition {
|
||||
eNone,
|
||||
eFocused,
|
||||
eContainsFocus,
|
||||
eContainedByFocus
|
||||
};
|
||||
FocusDisposition IsInOrContainsFocus(const nsAccessible* aAccessible) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Focus notifications and processing (don't use until you know what you do).
|
||||
|
||||
/**
|
||||
* Called when DOM focus event is fired.
|
||||
*/
|
||||
void NotifyOfDOMFocus(nsISupports* aTarget);
|
||||
|
||||
/**
|
||||
* Called when DOM blur event is fired.
|
||||
*/
|
||||
void NotifyOfDOMBlur(nsISupports* aTarget);
|
||||
|
||||
/**
|
||||
* Called when active item is changed. Note: must be called when accessible
|
||||
* tree is up to date.
|
||||
*/
|
||||
void ActiveItemChanged(nsAccessible* aItem, bool aCheckIfActive = true);
|
||||
|
||||
/**
|
||||
* Dispatch delayed focus event for the current focus accessible.
|
||||
*/
|
||||
void ForceFocusEvent();
|
||||
|
||||
/**
|
||||
* Dispatch delayed focus event for the given target.
|
||||
*/
|
||||
void DispatchFocusEvent(nsDocAccessible* aDocument, nsAccessible* aTarget);
|
||||
|
||||
/**
|
||||
* Process DOM focus notification.
|
||||
*/
|
||||
void ProcessDOMFocus(nsINode* aTarget);
|
||||
|
||||
/**
|
||||
* Process the delayed accessible event.
|
||||
* do.
|
||||
*/
|
||||
void ProcessFocusEvent(AccEvent* aEvent);
|
||||
|
||||
protected:
|
||||
FocusManager();
|
||||
|
||||
private:
|
||||
FocusManager(const FocusManager&);
|
||||
FocusManager& operator =(const FocusManager&);
|
||||
|
||||
/**
|
||||
* Return DOM node having DOM focus.
|
||||
*/
|
||||
inline nsINode* FocusedDOMNode() const
|
||||
{
|
||||
nsINode* focusedNode = FocusedDOMElm();
|
||||
if (focusedNode)
|
||||
return focusedNode;
|
||||
return FocusedDOMDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return DOM element having DOM focus.
|
||||
*/
|
||||
nsIContent* FocusedDOMElm() const;
|
||||
|
||||
/**
|
||||
* Return DOM document having DOM focus.
|
||||
*/
|
||||
nsIDocument* FocusedDOMDocument() const;
|
||||
|
||||
private:
|
||||
nsRefPtr<nsAccessible> mActiveItem;
|
||||
nsRefPtr<nsAccessible> mActiveARIAMenubar;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
//#define A11YDEBUG_FOCUS
|
||||
|
||||
#ifdef A11YDEBUG_FOCUS
|
||||
|
||||
// Util macros (don't use them directly)
|
||||
#define A11YDEBUG_FOCUS_STARTBLOCK \
|
||||
printf(" {\n ");
|
||||
|
||||
#define A11YDEBUG_FOCUS_ENDBLOCK \
|
||||
printf("\n }\n");
|
||||
|
||||
#define A11YDEBUG_FOCUS_BLOCKOFFSET \
|
||||
printf(" ");
|
||||
|
||||
#define A11YDEBUG_FOCUS_LOG_TIME \
|
||||
{ \
|
||||
PRIntervalTime time = PR_IntervalNow(); \
|
||||
PRUint32 mins = (PR_IntervalToSeconds(time) / 60) % 60; \
|
||||
PRUint32 secs = PR_IntervalToSeconds(time) % 60; \
|
||||
PRUint32 msecs = PR_IntervalToMilliseconds(time) % 1000; \
|
||||
printf("Time: %2d:%2d.%3d\n", mins, secs, msecs); \
|
||||
}
|
||||
|
||||
#define A11YDEBUG_FOCUS_LOG_DOMNODE(aNode) \
|
||||
if (aNode) { \
|
||||
if (aNode->IsElement()) { \
|
||||
dom::Element* targetElm = aNode->AsElement(); \
|
||||
nsCAutoString tag; \
|
||||
targetElm->Tag()->ToUTF8String(tag); \
|
||||
nsCAutoString id; \
|
||||
nsIAtom* atomid = targetElm->GetID(); \
|
||||
if (atomid) \
|
||||
atomid->ToUTF8String(id); \
|
||||
printf("element %s@id='%s': %p", tag.get(), id.get(), (void*)aNode); \
|
||||
} else if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) { \
|
||||
nsCOMPtr<nsIDocument> document = do_QueryInterface(aNode); \
|
||||
nsIURI* uri = document->GetDocumentURI(); \
|
||||
nsCAutoString spec; \
|
||||
uri->GetSpec(spec); \
|
||||
printf("document: %p; uri: %s", (void*)aNode, spec.get()); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define A11YDEBUG_FOCUS_LOG_ACCESSIBLE(aAccessible) \
|
||||
printf("accessible: %p; ", (void*)aAccessible); \
|
||||
if (aAccessible) { \
|
||||
nsAutoString role; \
|
||||
GetAccService()->GetStringRole(aAccessible->Role(), role); \
|
||||
nsAutoString name; \
|
||||
aAccessible->GetName(name); \
|
||||
printf(" role: %s, name: %s; ", NS_ConvertUTF16toUTF8(role).get(), \
|
||||
NS_ConvertUTF16toUTF8(name).get()); \
|
||||
A11YDEBUG_FOCUS_LOG_DOMNODE(aAccessible->GetNode()) \
|
||||
}
|
||||
|
||||
// Public macros
|
||||
#define A11YDEBUG_FOCUS_LOG_DOMTARGET(aMsg, aTarget) \
|
||||
A11YDEBUG_FOCUS_STARTBLOCK \
|
||||
printf(aMsg "\n"); \
|
||||
if (aTarget) { \
|
||||
A11YDEBUG_FOCUS_BLOCKOFFSET \
|
||||
A11YDEBUG_FOCUS_LOG_DOMNODE(aTarget) \
|
||||
} \
|
||||
A11YDEBUG_FOCUS_ENDBLOCK
|
||||
|
||||
#define A11YDEBUG_FOCUS_LOG_ACCTARGET(aMsg, aTarget) \
|
||||
A11YDEBUG_FOCUS_STARTBLOCK \
|
||||
printf(aMsg "\n"); \
|
||||
A11YDEBUG_FOCUS_BLOCKOFFSET \
|
||||
A11YDEBUG_FOCUS_LOG_ACCESSIBLE(aTarget) \
|
||||
A11YDEBUG_FOCUS_ENDBLOCK
|
||||
|
||||
#define A11YDEBUG_FOCUS_LOG_WIDGET(aMsg, aWidget) \
|
||||
A11YDEBUG_FOCUS_STARTBLOCK \
|
||||
printf(aMsg "\n"); \
|
||||
A11YDEBUG_FOCUS_BLOCKOFFSET \
|
||||
A11YDEBUG_FOCUS_LOG_ACCESSIBLE(aWidget) \
|
||||
printf("; widget is active: %s, has operable items: %s", \
|
||||
(aWidget && aWidget->IsActiveWidget() ? "true" : "false"), \
|
||||
(aWidget && aWidget->AreItemsOperable() ? "true" : "false")); \
|
||||
A11YDEBUG_FOCUS_ENDBLOCK
|
||||
|
||||
#define A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET(aMsg, aTargetMsg, aTarget) \
|
||||
printf("\nA11Y FOCUS: " aMsg ". "); \
|
||||
A11YDEBUG_FOCUS_LOG_TIME \
|
||||
if (aTarget) { \
|
||||
A11YDEBUG_FOCUS_STARTBLOCK \
|
||||
printf(aTargetMsg "\n"); \
|
||||
A11YDEBUG_FOCUS_BLOCKOFFSET \
|
||||
nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget)); \
|
||||
if (targetNode) { \
|
||||
A11YDEBUG_FOCUS_LOG_DOMNODE(targetNode) \
|
||||
} else { \
|
||||
printf("window: %p", (void*)aTarget); \
|
||||
} \
|
||||
A11YDEBUG_FOCUS_ENDBLOCK \
|
||||
}
|
||||
|
||||
#define A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET(aMsg, aTargetMsg, aTarget) \
|
||||
printf("\nA11Y FOCUS: " aMsg ". "); \
|
||||
A11YDEBUG_FOCUS_LOG_TIME \
|
||||
A11YDEBUG_FOCUS_LOG_DOMTARGET(aTargetMsg, aTarget)
|
||||
|
||||
#define A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET(aMsg, aTargetMsg, aTarget) \
|
||||
printf("\nA11Y FOCUS: " aMsg ". "); \
|
||||
A11YDEBUG_FOCUS_LOG_TIME \
|
||||
A11YDEBUG_FOCUS_LOG_ACCTARGET(aTargetMsg, aTarget)
|
||||
|
||||
#define A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE(aMsg, aTarget) \
|
||||
A11YDEBUG_FOCUS_LOG_ACCTARGET("Caused by: " aMsg, aTarget)
|
||||
|
||||
#else
|
||||
#define A11YDEBUG_FOCUS_LOG_DOMTARGET(aMsg, aTarget)
|
||||
#define A11YDEBUG_FOCUS_LOG_ACCTARGET(aMsg, aTarget)
|
||||
#define A11YDEBUG_FOCUS_LOG_WIDGET(aMsg, aWidget)
|
||||
#define A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET(aMsg, aTargetMsg, aTarget)
|
||||
#define A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET(aMsg, aTargetMsg, aTarget)
|
||||
#define A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET(aMsg, aTargetMsg, aTarget)
|
||||
#define A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE(aMsg, aTarget)
|
||||
#endif
|
||||
|
||||
#endif
|
@ -53,6 +53,7 @@ CPPSRCS = \
|
||||
AccGroupInfo.cpp \
|
||||
AccIterator.cpp \
|
||||
filters.cpp \
|
||||
FocusManager.cpp \
|
||||
NotificationController.cpp \
|
||||
nsAccDocManager.cpp \
|
||||
nsAccessNode.cpp \
|
||||
@ -89,6 +90,7 @@ EXPORTS = \
|
||||
EXPORTS_NAMESPACES = mozilla/a11y
|
||||
|
||||
EXPORTS_mozilla/a11y = \
|
||||
FocusManager.h \
|
||||
States.h \
|
||||
$(NULL)
|
||||
|
||||
|
@ -44,9 +44,12 @@
|
||||
#include "nsDocAccessible.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "nsTextAccessible.h"
|
||||
#include "FocusManager.h"
|
||||
#include "TextUpdater.h"
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector
|
||||
@ -182,7 +185,8 @@ NotificationController::IsUpdatePending()
|
||||
return mPresShell->IsLayoutFlushObserver() ||
|
||||
mObservingState == eRefreshProcessingForUpdate ||
|
||||
mContentInsertions.Length() != 0 || mNotifications.Length() != 0 ||
|
||||
mTextHash.Count() != 0;
|
||||
mTextHash.Count() != 0 ||
|
||||
!mDocument->HasLoadState(nsDocAccessible::eTreeConstructed);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -312,8 +316,15 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||
for (PRUint32 idx = 0; idx < eventCount; idx++) {
|
||||
AccEvent* accEvent = events[idx];
|
||||
if (accEvent->mEventRule != AccEvent::eDoNotEmit) {
|
||||
// Dispatch the focus event if target is still focused.
|
||||
if (accEvent->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
|
||||
FocusMgr()->ProcessFocusEvent(accEvent);
|
||||
continue;
|
||||
}
|
||||
|
||||
mDocument->ProcessPendingEvent(accEvent);
|
||||
|
||||
// Fire text change event caused by tree mutation.
|
||||
AccMutationEvent* showOrHideEvent = downcast_accEvent(accEvent);
|
||||
if (showOrHideEvent) {
|
||||
if (showOrHideEvent->mTextChangeEvent)
|
||||
@ -345,14 +356,14 @@ NotificationController::CoalesceEvents()
|
||||
PRInt32 tail = numQueuedEvents - 1;
|
||||
AccEvent* tailEvent = mEvents[tail];
|
||||
|
||||
// No node means this is application accessible (which can be a subject
|
||||
// of reorder events), we do not coalesce events for it currently.
|
||||
if (!tailEvent->mNode)
|
||||
return;
|
||||
|
||||
switch(tailEvent->mEventRule) {
|
||||
case AccEvent::eCoalesceFromSameSubtree:
|
||||
{
|
||||
// No node means this is application accessible (which is a subject of
|
||||
// reorder events), we do not coalesce events for it currently.
|
||||
if (!tailEvent->mNode)
|
||||
return;
|
||||
|
||||
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
|
||||
@ -446,21 +457,18 @@ NotificationController::CoalesceEvents()
|
||||
|
||||
} break; // case eCoalesceFromSameSubtree
|
||||
|
||||
case AccEvent::eCoalesceFromSameDocument:
|
||||
case AccEvent::eCoalesceOfSameType:
|
||||
{
|
||||
// Used for focus event, coalesce more older event since focus event
|
||||
// for accessible can be duplicated by event for its document, we are
|
||||
// interested in focus event for accessible.
|
||||
// Coalesce old events by newer event.
|
||||
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventType == tailEvent->mEventType &&
|
||||
thisEvent->mEventRule == tailEvent->mEventRule &&
|
||||
thisEvent->GetDocAccessible() == tailEvent->GetDocAccessible()) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule) {
|
||||
accEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eCoalesceFromSameDocument
|
||||
} break; // case eCoalesceOfSameType
|
||||
|
||||
case AccEvent::eRemoveDupes:
|
||||
{
|
||||
|
@ -418,6 +418,7 @@ nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
|
||||
}
|
||||
|
||||
NS_LOG_ACCDOCCREATE("document creation finished", aDocument)
|
||||
NS_LOG_ACCDOCCREATE_STACK
|
||||
|
||||
AddListeners(aDocument, isRootDoc);
|
||||
return docAcc;
|
||||
|
@ -165,6 +165,8 @@ private:
|
||||
*/
|
||||
#ifdef DEBUG_ACCDOCMGR
|
||||
|
||||
#include "nsTraceRefcntImpl.h"
|
||||
|
||||
// Enable these to log accessible document loading, creation or destruction.
|
||||
#define DEBUG_ACCDOCMGR_DOCLOAD
|
||||
#define DEBUG_ACCDOCMGR_DOCCREATE
|
||||
@ -405,6 +407,10 @@ private:
|
||||
#define NS_LOG_ACCDOC_TEXT(aMsg) \
|
||||
printf(" " aMsg "\n");
|
||||
|
||||
#define NS_LOG_ACCDOC_STACK \
|
||||
printf(" stack: \n"); \
|
||||
nsTraceRefcntImpl::WalkTheStack(stdout);
|
||||
|
||||
// Accessible document loading macros.
|
||||
#ifdef DEBUG_ACCDOCMGR_DOCLOAD
|
||||
|
||||
@ -515,7 +521,10 @@ private:
|
||||
NS_LOG_ACCDOC_ACCADDRESS(aName, aAcc)
|
||||
|
||||
#define NS_LOG_ACCDOCCREATE_TEXT(aMsg) \
|
||||
NS_LOG_ACCDOC_TEXT(aMsg)
|
||||
NS_LOG_ACCDOC_TEXT(aMsg)
|
||||
|
||||
#define NS_LOG_ACCDOCCREATE_STACK \
|
||||
NS_LOG_ACCDOC_STACK
|
||||
|
||||
#endif // DEBUG_ACCDOCMGR_DOCCREATE
|
||||
|
||||
@ -559,6 +568,7 @@ private:
|
||||
#define NS_LOG_ACCDOCCREATE(aMsg, aDocument)
|
||||
#define NS_LOG_ACCDOCCREATE_ACCADDRESS(aName, aAcc)
|
||||
#define NS_LOG_ACCDOCCREATE_TEXT(aMsg)
|
||||
#define NS_LOG_ACCDOCCREATE_STACK
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_ACCDOCMGR_DOCDESTROY
|
||||
|
@ -75,7 +75,6 @@
|
||||
*/
|
||||
|
||||
nsIStringBundle *nsAccessNode::gStringBundle = 0;
|
||||
nsINode *nsAccessNode::gLastFocusedNode = nsnull;
|
||||
|
||||
PRBool nsAccessNode::gIsFormFillEnabled = PR_FALSE;
|
||||
|
||||
@ -227,7 +226,6 @@ void nsAccessNode::ShutdownXPAccessibility()
|
||||
// at exit of program
|
||||
|
||||
NS_IF_RELEASE(gStringBundle);
|
||||
NS_IF_RELEASE(gLastFocusedNode);
|
||||
|
||||
// Release gApplicationAccessible after everything else is shutdown
|
||||
// so we don't accidently create it again while tearing down root accessibles
|
||||
|
@ -107,11 +107,6 @@ public:
|
||||
*/
|
||||
nsRootAccessible* RootAccessible() const;
|
||||
|
||||
/**
|
||||
* Reference to a node of focused accessible.
|
||||
*/
|
||||
static nsINode *gLastFocusedNode;
|
||||
|
||||
/**
|
||||
* Return focused node within accessible window.
|
||||
*
|
||||
|
@ -43,6 +43,8 @@
|
||||
#include "nsApplicationAccessibleWrap.h"
|
||||
#include "nsARIAGridAccessibleWrap.h"
|
||||
#include "nsARIAMap.h"
|
||||
#include "FocusManager.h"
|
||||
|
||||
#include "nsIContentViewer.h"
|
||||
#include "nsCURILoader.h"
|
||||
#include "nsDocAccessible.h"
|
||||
@ -117,7 +119,8 @@ using namespace mozilla::a11y;
|
||||
nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nsnull;
|
||||
PRBool nsAccessibilityService::gIsShutdown = PR_TRUE;
|
||||
|
||||
nsAccessibilityService::nsAccessibilityService() : nsAccDocManager()
|
||||
nsAccessibilityService::nsAccessibilityService() :
|
||||
nsAccDocManager(), FocusManager()
|
||||
{
|
||||
NS_TIME_FUNCTION;
|
||||
}
|
||||
@ -1852,3 +1855,13 @@ nsAccessibilityService::CreateAccessibleForXULTree(nsIContent* aContent,
|
||||
return accessible;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Services
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
mozilla::a11y::FocusManager*
|
||||
mozilla::a11y::FocusMgr()
|
||||
{
|
||||
return nsAccessibilityService::gAccessibilityService;
|
||||
}
|
||||
|
@ -44,9 +44,23 @@
|
||||
#include "a11yGeneric.h"
|
||||
#include "nsAccDocManager.h"
|
||||
|
||||
#include "mozilla/a11y/FocusManager.h"
|
||||
|
||||
#include "nsIObserver.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* Return focus manager.
|
||||
*/
|
||||
FocusManager* FocusMgr();
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
class nsAccessibilityService : public nsAccDocManager,
|
||||
public mozilla::a11y::FocusManager,
|
||||
public nsIAccessibilityService,
|
||||
public nsIObserver
|
||||
{
|
||||
@ -257,9 +271,9 @@ private:
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Reference for accessibility service.
|
||||
* Reference for accessibility service instance.
|
||||
*/
|
||||
static nsAccessibilityService *gAccessibilityService;
|
||||
static nsAccessibilityService* gAccessibilityService;
|
||||
|
||||
/**
|
||||
* Indicates whether accessibility service was shutdown.
|
||||
@ -276,6 +290,7 @@ private:
|
||||
PRBool HasUniversalAriaProperty(nsIContent *aContent);
|
||||
|
||||
friend nsAccessibilityService* GetAccService();
|
||||
friend mozilla::a11y::FocusManager* mozilla::a11y::FocusMgr();
|
||||
|
||||
friend nsresult NS_GetAccessibilityService(nsIAccessibilityService** aResult);
|
||||
};
|
||||
|
@ -693,14 +693,12 @@ nsAccessible::NativeState()
|
||||
state |= states::UNAVAILABLE;
|
||||
}
|
||||
else if (mContent->IsElement()) {
|
||||
nsIFrame *frame = GetFrame();
|
||||
if (frame && frame->IsFocusable()) {
|
||||
nsIFrame* frame = GetFrame();
|
||||
if (frame && frame->IsFocusable())
|
||||
state |= states::FOCUSABLE;
|
||||
}
|
||||
|
||||
if (gLastFocusedNode == mContent) {
|
||||
if (FocusMgr()->IsFocused(this))
|
||||
state |= states::FOCUSED;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if states::INVISIBLE and
|
||||
@ -746,16 +744,11 @@ nsAccessible::GetFocusedChild(nsIAccessible** aChild)
|
||||
nsAccessible*
|
||||
nsAccessible::FocusedChild()
|
||||
{
|
||||
if (!gLastFocusedNode)
|
||||
return nsnull;
|
||||
if (gLastFocusedNode == mContent)
|
||||
return this;
|
||||
nsAccessible* focus = FocusMgr()->FocusedAccessible();
|
||||
if (focus && (focus == this || focus->Parent() == this))
|
||||
return focus;
|
||||
|
||||
nsAccessible* focusedChild = GetDocAccessible()->GetAccessible(gLastFocusedNode);
|
||||
if (!focusedChild || focusedChild->Parent() != this)
|
||||
return nsnull;
|
||||
|
||||
return focusedChild;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// nsAccessible::ChildAtPoint()
|
||||
@ -1531,7 +1524,7 @@ nsAccessible::State()
|
||||
nsAccessible* relTarget = nsnull;
|
||||
while ((relTarget = rel.Next())) {
|
||||
if (relTarget->Role() == nsIAccessibleRole::ROLE_PROPERTYPAGE &&
|
||||
nsCoreUtils::IsAncestorOf(relTarget->GetNode(), gLastFocusedNode))
|
||||
FocusMgr()->IsFocusWithin(relTarget))
|
||||
state |= states::SELECTED;
|
||||
}
|
||||
}
|
||||
@ -2740,6 +2733,14 @@ nsAccessible::EndOffset()
|
||||
return hyperText ? (hyperText->GetChildOffset(this) + 1) : 0;
|
||||
}
|
||||
|
||||
bool
|
||||
nsAccessible::IsLinkSelected()
|
||||
{
|
||||
NS_PRECONDITION(IsLink(),
|
||||
"IsLinkSelected() called on something that is not a hyper link!");
|
||||
return FocusMgr()->IsFocused(this);
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsAccessible::AnchorCount()
|
||||
{
|
||||
@ -2910,6 +2911,67 @@ nsAccessible::UnselectAll()
|
||||
return success;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Widgets
|
||||
|
||||
bool
|
||||
nsAccessible::IsWidget() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsAccessible::IsActiveWidget() const
|
||||
{
|
||||
return FocusMgr()->IsFocused(this);
|
||||
}
|
||||
|
||||
bool
|
||||
nsAccessible::AreItemsOperable() const
|
||||
{
|
||||
return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant);
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsAccessible::CurrentItem()
|
||||
{
|
||||
// Check for aria-activedescendant, which changes which element has focus.
|
||||
// For activedescendant, the ARIA spec does not require that the user agent
|
||||
// checks whether pointed node is actually a DOM descendant of the element
|
||||
// with the aria-activedescendant attribute.
|
||||
nsAutoString id;
|
||||
if (mContent->GetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::aria_activedescendant, id)) {
|
||||
nsIDocument* DOMDoc = mContent->GetOwnerDoc();
|
||||
dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
|
||||
if (activeDescendantElm) {
|
||||
nsDocAccessible* document = GetDocAccessible();
|
||||
if (document)
|
||||
return document->GetAccessible(activeDescendantElm);
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsAccessible::ContainerWidget() const
|
||||
{
|
||||
nsIAtom* idAttribute = mContent->GetIDAttributeName();
|
||||
if (idAttribute) {
|
||||
if (mContent->HasAttr(kNameSpaceID_None, idAttribute)) {
|
||||
nsAccessible* parent = Parent();
|
||||
do {
|
||||
nsIContent* parentContent = parent->GetContent();
|
||||
if (parentContent &&
|
||||
parentContent->HasAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::aria_activedescendant)) {
|
||||
return parent;
|
||||
}
|
||||
} while ((parent = parent->Parent()));
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccessible protected methods
|
||||
|
@ -402,10 +402,16 @@ public:
|
||||
void TestChildCache(nsAccessible* aCachedChild) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Downcasting
|
||||
// Downcasting and types
|
||||
|
||||
inline bool IsApplication() const { return mFlags & eApplicationAccessible; }
|
||||
|
||||
bool IsAutoComplete() const { return mFlags & eAutoCompleteAccessible; }
|
||||
|
||||
inline bool IsAutoCompletePopup() const { return mFlags & eAutoCompletePopupAccessible; }
|
||||
|
||||
inline bool IsCombobox() const { return mFlags & eComboboxAccessible; }
|
||||
|
||||
inline bool IsDoc() const { return mFlags & eDocAccessible; }
|
||||
nsDocAccessible* AsDoc();
|
||||
|
||||
@ -415,6 +421,12 @@ public:
|
||||
inline bool IsHTMLListItem() const { return mFlags & eHTMLListItemAccessible; }
|
||||
nsHTMLLIAccessible* AsHTMLListItem();
|
||||
|
||||
inline bool IsListControl() const { return mFlags & eListControlAccessible; }
|
||||
|
||||
inline bool IsMenuButton() const { return mFlags & eMenuButtonAccessible; }
|
||||
|
||||
inline bool IsMenuPopup() const { return mFlags & eMenuPopupAccessible; }
|
||||
|
||||
inline bool IsRoot() const { return mFlags & eRootAccessible; }
|
||||
nsRootAccessible* AsRoot();
|
||||
|
||||
@ -475,12 +487,7 @@ public:
|
||||
/**
|
||||
* Return true if the link currently has the focus.
|
||||
*/
|
||||
inline bool IsLinkSelected()
|
||||
{
|
||||
NS_PRECONDITION(IsLink(),
|
||||
"IsLinkSelected() called on something that is not a hyper link!");
|
||||
return gLastFocusedNode == GetNode();
|
||||
}
|
||||
bool IsLinkSelected();
|
||||
|
||||
/**
|
||||
* Return the number of anchors within the link.
|
||||
@ -546,6 +553,38 @@ public:
|
||||
*/
|
||||
virtual bool UnselectAll();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Widgets
|
||||
|
||||
/**
|
||||
* Return true if accessible is a widget, i.e. control or accessible that
|
||||
* manages its items. Note, being a widget the accessible may be a part of
|
||||
* composite widget.
|
||||
*/
|
||||
virtual bool IsWidget() const;
|
||||
|
||||
/**
|
||||
* Return true if the widget is active, i.e. has a focus within it.
|
||||
*/
|
||||
virtual bool IsActiveWidget() const;
|
||||
|
||||
/**
|
||||
* Return true if the widget has items and items are operable by user and
|
||||
* can be activated.
|
||||
*/
|
||||
virtual bool AreItemsOperable() const;
|
||||
|
||||
/**
|
||||
* Return the current item of the widget, i.e. an item that has or will have
|
||||
* keyboard focus when widget gets active.
|
||||
*/
|
||||
virtual nsAccessible* CurrentItem();
|
||||
|
||||
/**
|
||||
* Return container widget this accessible belongs to.
|
||||
*/
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
|
||||
protected:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@ -595,11 +634,17 @@ protected:
|
||||
*/
|
||||
enum AccessibleTypes {
|
||||
eApplicationAccessible = 1 << 2,
|
||||
eDocAccessible = 1 << 3,
|
||||
eHyperTextAccessible = 1 << 4,
|
||||
eHTMLListItemAccessible = 1 << 5,
|
||||
eRootAccessible = 1 << 6,
|
||||
eTextLeafAccessible = 1 << 7
|
||||
eAutoCompleteAccessible = 1 << 3,
|
||||
eAutoCompletePopupAccessible = 1 << 4,
|
||||
eComboboxAccessible = 1 << 5,
|
||||
eDocAccessible = 1 << 6,
|
||||
eHyperTextAccessible = 1 << 7,
|
||||
eHTMLListItemAccessible = 1 << 8,
|
||||
eListControlAccessible = 1 << 9,
|
||||
eMenuButtonAccessible = 1 << 10,
|
||||
eMenuPopupAccessible = 1 << 11,
|
||||
eRootAccessible = 1 << 12,
|
||||
eTextLeafAccessible = 1 << 13
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -174,12 +174,10 @@ nsApplicationAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
nsAccessible*
|
||||
nsApplicationAccessible::FocusedChild()
|
||||
{
|
||||
if (gLastFocusedNode) {
|
||||
nsAccessible* focusedChild =
|
||||
GetAccService()->GetAccessible(gLastFocusedNode);
|
||||
if (focusedChild && focusedChild->Parent() == this)
|
||||
return focusedChild;
|
||||
}
|
||||
nsAccessible* focus = FocusMgr()->FocusedAccessible();
|
||||
if (focus && focus->Parent() == this)
|
||||
return focus;
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
@ -295,18 +295,10 @@ nsDocAccessible::NativeState()
|
||||
PRUint64 state = (mContent->GetCurrentDoc() == mDocument) ?
|
||||
0 : states::STALE;
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
|
||||
if (!xulDoc)
|
||||
#endif
|
||||
{
|
||||
// XXX Need to invent better check to see if doc is focusable,
|
||||
// which it should be if it is scrollable. A XUL document could be focusable.
|
||||
// See bug 376803.
|
||||
state |= states::FOCUSABLE;
|
||||
if (gLastFocusedNode == mDocument)
|
||||
state |= states::FOCUSED;
|
||||
}
|
||||
// Document is always focusable.
|
||||
state |= states::FOCUSABLE;
|
||||
if (FocusMgr()->IsFocused(this))
|
||||
state |= states::FOCUSED;
|
||||
|
||||
// Expose stale state until the document is ready (DOM is loaded and tree is
|
||||
// constructed).
|
||||
@ -357,12 +349,9 @@ nsDocAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
|
||||
nsAccessible*
|
||||
nsDocAccessible::FocusedChild()
|
||||
{
|
||||
// XXXndeakin P3 accessibility shouldn't be caching the focus
|
||||
|
||||
// Return an accessible for the current global focus, which does not have to
|
||||
// be contained within the current document.
|
||||
return gLastFocusedNode ? GetAccService()->GetAccessible(gLastFocusedNode) :
|
||||
nsnull;
|
||||
return FocusMgr()->FocusedAccessible();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDocAccessible::TakeFocus()
|
||||
@ -370,11 +359,6 @@ NS_IMETHODIMP nsDocAccessible::TakeFocus()
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRUint64 state = NativeState();
|
||||
if (0 == (state & states::FOCUSABLE)) {
|
||||
return NS_ERROR_FAILURE; // Not focusable
|
||||
}
|
||||
|
||||
// Focus the document.
|
||||
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
|
||||
NS_ENSURE_STATE(fm);
|
||||
@ -1063,11 +1047,7 @@ nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID
|
||||
|
||||
nsAccessible *multiSelect =
|
||||
nsAccUtils::GetMultiSelectableContainer(aContent);
|
||||
// Multi selects use selection_add and selection_remove
|
||||
// Single select widgets just mirror event_selection for
|
||||
// whatever gets event_focus, which is done in
|
||||
// nsRootAccessible::FireAccessibleFocusEvent()
|
||||
// So right here we make sure only to deal with multi selects
|
||||
// XXX: Multi selects are handled here only (bug 414302).
|
||||
if (multiSelect) {
|
||||
// Need to find the right event to use here, SELECTION_WITHIN would
|
||||
// seem right but we had started using it for something else
|
||||
@ -1118,17 +1098,13 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
||||
return;
|
||||
}
|
||||
|
||||
// The activedescendant universal property redirects accessible focus events
|
||||
// to the element with the id that activedescendant points to. Make sure
|
||||
// the tree up to date before processing.
|
||||
if (aAttribute == nsGkAtoms::aria_activedescendant) {
|
||||
// The activedescendant universal property redirects accessible focus events
|
||||
// to the element with the id that activedescendant points to
|
||||
nsCOMPtr<nsINode> focusedNode = GetCurrentFocus();
|
||||
if (nsCoreUtils::GetRoleContent(focusedNode) == aContent) {
|
||||
nsAccessible* focusedAcc = GetAccService()->GetAccessible(focusedNode);
|
||||
nsRootAccessible* rootAcc = RootAccessible();
|
||||
if (rootAcc && focusedAcc) {
|
||||
rootAcc->FireAccessibleFocusEvent(focusedAcc, nsnull, PR_TRUE);
|
||||
}
|
||||
}
|
||||
mNotificationController->HandleNotification<nsDocAccessible, nsIContent>
|
||||
(this, &nsDocAccessible::ARIAActiveDescendantChanged, aContent);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1200,6 +1176,26 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::ARIAActiveDescendantChanged(nsIContent* aElm)
|
||||
{
|
||||
if (FocusMgr()->HasDOMFocus(aElm)) {
|
||||
nsAutoString id;
|
||||
if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
|
||||
nsIDocument* DOMDoc = aElm->GetOwnerDoc();
|
||||
dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
|
||||
if (activeDescendantElm) {
|
||||
nsAccessible* activeDescendant = GetAccessible(activeDescendantElm);
|
||||
if (activeDescendant) {
|
||||
FocusMgr()->ActiveItemChanged(activeDescendant, false);
|
||||
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("ARIA activedescedant changed",
|
||||
activeDescendant)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent,
|
||||
@ -1362,6 +1358,13 @@ nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible)
|
||||
NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
|
||||
"Unbinding the unbound accessible!");
|
||||
|
||||
// Fire focus event on accessible having DOM focus if active item was removed
|
||||
// from the tree.
|
||||
if (FocusMgr()->IsActiveItem(aAccessible)) {
|
||||
FocusMgr()->ActiveItemChanged(nsnull);
|
||||
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("tree shutdown", aAccessible)
|
||||
}
|
||||
|
||||
// Remove an accessible from node-to-accessible map if it exists there.
|
||||
if (aAccessible->IsPrimaryForNode() &&
|
||||
mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
|
||||
@ -1732,7 +1735,6 @@ nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent)
|
||||
return;
|
||||
|
||||
PRUint32 eventType = aEvent->GetEventType();
|
||||
|
||||
if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
|
||||
nsCOMPtr<nsIAccessibleText> accessibleText = do_QueryObject(accessible);
|
||||
PRInt32 caretOffset;
|
||||
@ -1742,13 +1744,6 @@ nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent)
|
||||
PRUnichar chAtOffset;
|
||||
accessibleText->GetCharacterAtOffset(caretOffset, &chAtOffset);
|
||||
printf("\nCaret moved to %d with char %c", caretOffset, chAtOffset);
|
||||
#endif
|
||||
#ifdef DEBUG_CARET
|
||||
// Test caret line # -- fire an EVENT_ALERT on the focused node so we can watch the
|
||||
// line-number object attribute on it
|
||||
nsAccessible* focusedAcc =
|
||||
GetAccService()->GetAccessible(gLastFocusedNode);
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, focusedAcc);
|
||||
#endif
|
||||
nsRefPtr<AccEvent> caretMoveEvent =
|
||||
new AccCaretMoveEvent(accessible, caretOffset);
|
||||
@ -1948,10 +1943,11 @@ nsDocAccessible::UpdateTreeInternal(nsAccessible* aChild, bool aIsInsert)
|
||||
// while it's focused. Fire focus event on new focused accessible. If
|
||||
// the queue contains focus event for this node then it's suppressed by
|
||||
// this one.
|
||||
if (node == gLastFocusedNode) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
|
||||
node, AccEvent::eCoalesceFromSameDocument);
|
||||
}
|
||||
// XXX: do we really want to send focus to focused DOM node not taking into
|
||||
// account active item?
|
||||
if (FocusMgr()->IsFocused(aChild))
|
||||
FocusMgr()->DispatchFocusEvent(this, aChild);
|
||||
|
||||
} else {
|
||||
// Update the tree for content removal.
|
||||
// The accessible parent may differ from container accessible if
|
||||
|
@ -456,6 +456,11 @@ protected:
|
||||
*/
|
||||
void ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute);
|
||||
|
||||
/**
|
||||
* Process ARIA active-descendant attribute change.
|
||||
*/
|
||||
void ARIAActiveDescendantChanged(nsIContent* aElm);
|
||||
|
||||
/**
|
||||
* Process the event when the queue of pending events is untwisted. Fire
|
||||
* accessible events as result of the processing.
|
||||
|
@ -69,12 +69,6 @@ nsOuterDocAccessible::NativeRole()
|
||||
return nsIAccessibleRole::ROLE_INTERNAL_FRAME;
|
||||
}
|
||||
|
||||
PRUint64
|
||||
nsOuterDocAccessible::NativeState()
|
||||
{
|
||||
return nsAccessible::NativeState() & ~states::FOCUSABLE;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsOuterDocAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild)
|
||||
|
@ -67,7 +67,6 @@ public:
|
||||
|
||||
// nsAccessible
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual PRUint64 NativeState();
|
||||
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
|
||||
virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild);
|
||||
|
@ -61,14 +61,11 @@
|
||||
#include "nsIDOMHTMLSelectElement.h"
|
||||
#include "nsIDOMDataContainerEvent.h"
|
||||
#include "nsIDOMNSEvent.h"
|
||||
#include "nsIDOMXULMenuListElement.h"
|
||||
#include "nsIDOMXULMultSelectCntrlEl.h"
|
||||
#include "nsIDOMXULSelectCntrlItemEl.h"
|
||||
#include "nsIDOMXULPopupElement.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsEventListenerManager.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsMenuFrame.h"
|
||||
#include "nsIHTMLDocument.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsISelectionPrivate.h"
|
||||
@ -86,7 +83,7 @@
|
||||
#include "nsIXULWindow.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
namespace dom = mozilla::dom;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -223,9 +220,6 @@ const char* const docEvents[] = {
|
||||
// debugging a11y objects with event viewers
|
||||
"mouseover",
|
||||
#endif
|
||||
// capture DOM focus and DOM blur events
|
||||
"focus",
|
||||
"blur",
|
||||
// capture Form change events
|
||||
"select",
|
||||
// capture ValueChange events (fired whenever value changes, immediately after, whether focus moves or not)
|
||||
@ -246,6 +240,7 @@ const char* const docEvents[] = {
|
||||
"popuphiding",
|
||||
"DOMMenuInactive",
|
||||
"DOMMenuItemActive",
|
||||
"DOMMenuItemInactive",
|
||||
"DOMMenuBarActive",
|
||||
"DOMMenuBarInactive"
|
||||
};
|
||||
@ -308,125 +303,6 @@ nsRootAccessible::GetCaretAccessible()
|
||||
return mCaretAccessible;
|
||||
}
|
||||
|
||||
void
|
||||
nsRootAccessible::FireAccessibleFocusEvent(nsAccessible* aFocusAccessible,
|
||||
nsIContent* aRealFocusContent,
|
||||
PRBool aForceEvent,
|
||||
EIsFromUserInput aIsFromUserInput)
|
||||
{
|
||||
// Implementors: only fire delayed/async events from this method.
|
||||
|
||||
// Set selection listener for focused element.
|
||||
if (mCaretAccessible && aRealFocusContent)
|
||||
mCaretAccessible->SetControlSelectionListener(aRealFocusContent);
|
||||
|
||||
nsAccessible* focusAccessible = aFocusAccessible;
|
||||
|
||||
// Check for aria-activedescendant, which changes which element has focus.
|
||||
// For activedescendant, the ARIA spec does not require that the user agent
|
||||
// checks whether pointed node is actually a DOM descendant of the element
|
||||
// with the aria-activedescendant attribute.
|
||||
nsIContent* content = focusAccessible->GetContent();
|
||||
if (content) {
|
||||
nsAutoString id;
|
||||
if (content->GetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::aria_activedescendant, id)) {
|
||||
nsIDocument* DOMDoc = content->GetOwnerDoc();
|
||||
nsIContent* activeDescendantContent = DOMDoc->GetElementById(id);
|
||||
|
||||
// If aria-activedescendant is set to nonexistant ID, then treat as focus
|
||||
// on the activedescendant container (which has real DOM focus).
|
||||
if (activeDescendantContent) {
|
||||
nsAccessible* activeDescendant =
|
||||
GetAccService()->GetAccessible(activeDescendantContent);
|
||||
if (activeDescendant) {
|
||||
focusAccessible = activeDescendant;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire focus only if it changes, but always fire focus events when
|
||||
// aForceEvent == PR_TRUE
|
||||
nsINode* focusNode = focusAccessible->GetNode();
|
||||
if (gLastFocusedNode == focusNode && !aForceEvent)
|
||||
return;
|
||||
|
||||
nsDocAccessible* focusDocument = focusAccessible->GetDocAccessible();
|
||||
NS_ASSERTION(focusDocument, "No document while accessible is in document?!");
|
||||
|
||||
// Fire menu start/end events for ARIA menus.
|
||||
if (focusAccessible->ARIARole() == nsIAccessibleRole::ROLE_MENUITEM) {
|
||||
// The focus is inside a menu.
|
||||
if (!mCurrentARIAMenubar) {
|
||||
// Entering ARIA menu. Fire menu start event.
|
||||
nsAccessible* menuBarAccessible =
|
||||
nsAccUtils::GetAncestorWithRole(focusAccessible,
|
||||
nsIAccessibleRole::ROLE_MENUBAR);
|
||||
if (menuBarAccessible) {
|
||||
mCurrentARIAMenubar = menuBarAccessible->GetNode();
|
||||
if (mCurrentARIAMenubar) {
|
||||
nsRefPtr<AccEvent> menuStartEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
|
||||
menuBarAccessible, aIsFromUserInput,
|
||||
AccEvent::eAllowDupes);
|
||||
if (menuStartEvent)
|
||||
focusDocument->FireDelayedAccessibleEvent(menuStartEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mCurrentARIAMenubar) {
|
||||
// Focus left a menu. Fire menu end event.
|
||||
nsRefPtr<AccEvent> menuEndEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mCurrentARIAMenubar,
|
||||
aIsFromUserInput, AccEvent::eAllowDupes);
|
||||
if (menuEndEvent) {
|
||||
focusDocument->FireDelayedAccessibleEvent(menuEndEvent);
|
||||
}
|
||||
mCurrentARIAMenubar = nsnull;
|
||||
}
|
||||
|
||||
NS_IF_RELEASE(gLastFocusedNode);
|
||||
gLastFocusedNode = focusNode;
|
||||
NS_IF_ADDREF(gLastFocusedNode);
|
||||
|
||||
// Coalesce focus events from the same document, because DOM focus event might
|
||||
// be fired for the document node and then for the focused DOM element.
|
||||
nsRefPtr<AccEvent> focusEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, focusAccessible,
|
||||
aIsFromUserInput, AccEvent::eCoalesceFromSameDocument);
|
||||
focusDocument->FireDelayedAccessibleEvent(focusEvent);
|
||||
}
|
||||
|
||||
void
|
||||
nsRootAccessible::FireCurrentFocusEvent()
|
||||
{
|
||||
if (IsDefunct())
|
||||
return;
|
||||
|
||||
// Simulate a focus event so that we can reuse code that fires focus for
|
||||
// container children like treeitems.
|
||||
nsCOMPtr<nsINode> focusedNode = GetCurrentFocus();
|
||||
if (!focusedNode) {
|
||||
return; // No current focus
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
|
||||
if (domDoc) {
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
if (NS_SUCCEEDED(domDoc->CreateEvent(NS_LITERAL_STRING("Events"),
|
||||
getter_AddRefs(event))) &&
|
||||
NS_SUCCEEDED(event->InitEvent(NS_LITERAL_STRING("focus"), PR_TRUE, PR_TRUE))) {
|
||||
|
||||
nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
|
||||
nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(focusedNode));
|
||||
privateEvent->SetTarget(target);
|
||||
HandleEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsRootAccessible::DocumentActivated(nsDocAccessible* aDocument)
|
||||
{
|
||||
@ -497,14 +373,13 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
if (!weakShell)
|
||||
return;
|
||||
|
||||
nsAccessible* accessible =
|
||||
GetAccService()->GetAccessibleOrContainer(origTargetNode, weakShell);
|
||||
|
||||
if (eventType.EqualsLiteral("popuphiding")) {
|
||||
HandlePopupHidingEvent(origTargetNode, accessible);
|
||||
HandlePopupHidingEvent(origTargetNode);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAccessible* accessible =
|
||||
GetAccService()->GetAccessibleOrContainer(origTargetNode, weakShell);
|
||||
if (!accessible)
|
||||
return;
|
||||
|
||||
@ -559,8 +434,10 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
|
||||
nsEventShell::FireEvent(accEvent);
|
||||
|
||||
if (isEnabled)
|
||||
FireAccessibleFocusEvent(accessible, origTargetContent);
|
||||
if (isEnabled) {
|
||||
FocusMgr()->ActiveItemChanged(accessible);
|
||||
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("RadioStateChange", accessible)
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -581,20 +458,9 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
#ifdef MOZ_XUL
|
||||
// If it's a tree element, need the currently selected item
|
||||
if (isTree) {
|
||||
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
|
||||
do_QueryInterface(targetNode);
|
||||
if (multiSelect) {
|
||||
PRInt32 treeIndex = -1;
|
||||
multiSelect->GetCurrentIndex(&treeIndex);
|
||||
if (treeIndex >= 0) {
|
||||
nsRefPtr<nsXULTreeAccessible> treeAcc = do_QueryObject(accessible);
|
||||
if (treeAcc) {
|
||||
treeItemAccessible = treeAcc->GetTreeItemAccessible(treeIndex);
|
||||
if (treeItemAccessible)
|
||||
accessible = treeItemAccessible;
|
||||
}
|
||||
}
|
||||
}
|
||||
treeItemAccessible = accessible->CurrentItem();
|
||||
if (treeItemAccessible)
|
||||
accessible = treeItemAccessible;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -611,7 +477,7 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
|
||||
if (treeItemAccessible && eventType.EqualsLiteral("select")) {
|
||||
// If multiselect tree, we should fire selectionadd or selection removed
|
||||
if (gLastFocusedNode == targetNode) {
|
||||
if (FocusMgr()->HasDOMFocus(targetNode)) {
|
||||
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
|
||||
do_QueryInterface(targetNode);
|
||||
nsAutoString selType;
|
||||
@ -633,41 +499,7 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (eventType.EqualsLiteral("focus")) {
|
||||
// Keep a reference to the target node. We might want to change
|
||||
// it to the individual radio button or selected item, and send
|
||||
// the focus event to that.
|
||||
nsCOMPtr<nsINode> focusedItem = targetNode;
|
||||
if (!treeItemAccessible) {
|
||||
nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
|
||||
do_QueryInterface(targetNode);
|
||||
if (selectControl) {
|
||||
nsCOMPtr<nsIDOMXULMenuListElement> menuList =
|
||||
do_QueryInterface(targetNode);
|
||||
if (!menuList) {
|
||||
// Don't do this for menu lists, the items only get focused
|
||||
// when the list is open, based on DOMMenuitemActive events
|
||||
nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem;
|
||||
selectControl->GetSelectedItem(getter_AddRefs(selectedItem));
|
||||
if (selectedItem)
|
||||
focusedItem = do_QueryInterface(selectedItem);
|
||||
|
||||
if (!focusedItem)
|
||||
return;
|
||||
|
||||
accessible = GetAccService()->GetAccessibleInWeakShell(focusedItem,
|
||||
weakShell);
|
||||
if (!accessible)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
FireAccessibleFocusEvent(accessible, origTargetContent);
|
||||
}
|
||||
else if (eventType.EqualsLiteral("blur")) {
|
||||
NS_IF_RELEASE(gLastFocusedNode);
|
||||
}
|
||||
else if (eventType.EqualsLiteral("AlertActive")) {
|
||||
if (eventType.EqualsLiteral("AlertActive")) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
|
||||
}
|
||||
else if (eventType.EqualsLiteral("popupshown")) {
|
||||
@ -680,70 +512,43 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
}
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
|
||||
PRBool fireFocus = PR_FALSE;
|
||||
if (!treeItemAccessible) {
|
||||
#ifdef MOZ_XUL
|
||||
if (isTree) {
|
||||
return; // Tree with nothing selected
|
||||
}
|
||||
#endif
|
||||
|
||||
nsMenuFrame* menuFrame = do_QueryFrame(accessible->GetFrame());
|
||||
if (menuFrame)
|
||||
fireFocus = PR_TRUE;
|
||||
// QI failed for nsMenuFrame means it's not on menu bar
|
||||
if (menuFrame && menuFrame->IsOnMenuBar() &&
|
||||
!menuFrame->IsOnActiveMenuBar()) {
|
||||
// It is a top level menuitem. Only fire a focus event when the menu bar
|
||||
// is active.
|
||||
return;
|
||||
} else {
|
||||
nsAccessible* container = accessible->Parent();
|
||||
if (!container)
|
||||
return;
|
||||
// It is not top level menuitem
|
||||
// Only fire focus event if it is not inside collapsed popup
|
||||
// and not a listitem of a combo box
|
||||
if (container->State() & states::COLLAPSED) {
|
||||
nsAccessible* containerParent = container->Parent();
|
||||
if (!containerParent)
|
||||
return;
|
||||
if (containerParent->Role() != nsIAccessibleRole::ROLE_COMBOBOX) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!fireFocus) {
|
||||
nsCOMPtr<nsINode> realFocusedNode = GetCurrentFocus();
|
||||
nsIContent* realFocusedContent =
|
||||
realFocusedNode->IsElement() ? realFocusedNode->AsElement() : nsnull;
|
||||
nsIContent* containerContent = targetContent;
|
||||
while (containerContent) {
|
||||
nsCOMPtr<nsIDOMXULPopupElement> popup = do_QueryInterface(containerContent);
|
||||
if (popup || containerContent == realFocusedContent) {
|
||||
// If we're inside the focus or a popup we can fire focus events
|
||||
// for the changed active item
|
||||
fireFocus = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
containerContent = containerContent->GetParent();
|
||||
}
|
||||
}
|
||||
if (fireFocus) {
|
||||
// Always asynch, always from user input.
|
||||
FireAccessibleFocusEvent(accessible, origTargetContent, PR_TRUE,
|
||||
eFromUserInput);
|
||||
FocusMgr()->ActiveItemChanged(accessible);
|
||||
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuItemActive", accessible)
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
|
||||
// Process DOMMenuItemInactive event for autocomplete only because this is
|
||||
// unique widget that may acquire focus from autocomplete popup while popup
|
||||
// stays open and has no active item. In case of XUL tree autocomplete
|
||||
// popup this event is fired for tree accessible.
|
||||
nsAccessible* widget =
|
||||
accessible->IsWidget() ? accessible : accessible->ContainerWidget();
|
||||
if (widget && widget->IsAutoCompletePopup()) {
|
||||
FocusMgr()->ActiveItemChanged(nsnull);
|
||||
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuItemInactive", accessible)
|
||||
}
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
|
||||
accessible, eFromUserInput);
|
||||
|
||||
// Notify of active item change when menubar gets active and if it has
|
||||
// current item. This is a case of mouseover (set current menuitem) and
|
||||
// mouse click (activate the menubar). If menubar doesn't have current item
|
||||
// (can be a case of menubar activation from keyboard) then ignore this
|
||||
// notification because later we'll receive DOMMenuItemActive event after
|
||||
// current menuitem is set.
|
||||
nsAccessible* activeItem = accessible->CurrentItem();
|
||||
if (activeItem) {
|
||||
FocusMgr()->ActiveItemChanged(activeItem);
|
||||
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuBarActive", accessible)
|
||||
}
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
|
||||
accessible, eFromUserInput);
|
||||
FireCurrentFocusEvent();
|
||||
|
||||
FocusMgr()->ActiveItemChanged(nsnull);
|
||||
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuBarInactive", accessible)
|
||||
}
|
||||
else if (eventType.EqualsLiteral("ValueChange")) {
|
||||
targetDocument->
|
||||
@ -769,8 +574,6 @@ nsRootAccessible::Shutdown()
|
||||
if (!mWeakShell)
|
||||
return; // Already shutdown
|
||||
|
||||
mCurrentARIAMenubar = nsnull;
|
||||
|
||||
nsDocAccessibleWrap::Shutdown();
|
||||
}
|
||||
|
||||
@ -886,37 +689,106 @@ nsRootAccessible::HandlePopupShownEvent(nsAccessible* aAccessible)
|
||||
}
|
||||
|
||||
void
|
||||
nsRootAccessible::HandlePopupHidingEvent(nsINode* aNode,
|
||||
nsAccessible* aAccessible)
|
||||
nsRootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
|
||||
{
|
||||
// If accessible focus was on or 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. It's also the case when a popup gets focus in ATK --
|
||||
// when it closes we need to fire an event to restore focus to where it was.
|
||||
// Get popup accessible. There are cases when popup element isn't accessible
|
||||
// but an underlying widget is and behaves like popup, an example is
|
||||
// autocomplete popups.
|
||||
nsDocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode);
|
||||
if (!document)
|
||||
return;
|
||||
|
||||
if (gLastFocusedNode &&
|
||||
nsCoreUtils::IsAncestorOf(aNode, gLastFocusedNode)) {
|
||||
// Focus was on or inside of a popup that's being hidden
|
||||
FireCurrentFocusEvent();
|
||||
nsAccessible* popup = document->GetAccessible(aPopupNode);
|
||||
if (!popup) {
|
||||
nsAccessible* popupContainer = document->GetContainerAccessible(aPopupNode);
|
||||
if (!popupContainer)
|
||||
return;
|
||||
|
||||
PRInt32 childCount = popupContainer->GetChildCount();
|
||||
for (PRInt32 idx = 0; idx < childCount; idx++) {
|
||||
nsAccessible* child = popupContainer->GetChildAt(idx);
|
||||
if (child->IsAutoCompletePopup()) {
|
||||
popup = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No popup no events. Focus is managed by DOM. This is a case for
|
||||
// menupopups of menus on Linux since there are no accessible for popups.
|
||||
if (!popup)
|
||||
return;
|
||||
}
|
||||
|
||||
// Fire expanded state change event for comboboxes and autocompletes.
|
||||
if (!aAccessible ||
|
||||
aAccessible->Role() != nsIAccessibleRole::ROLE_COMBOBOX_LIST)
|
||||
return;
|
||||
// In case of autocompletes and comboboxes fire state change event for
|
||||
// expanded state. Note, HTML form autocomplete isn't a subject of state
|
||||
// change event because they aren't autocompletes strictly speaking.
|
||||
// When popup closes (except nested popups and menus) then fire focus event to
|
||||
// where it was. The focus event is expected even if popup didn't take a focus.
|
||||
|
||||
nsAccessible* combobox = aAccessible->Parent();
|
||||
if (!combobox)
|
||||
return;
|
||||
static const PRUint32 kNotifyOfFocus = 1;
|
||||
static const PRUint32 kNotifyOfState = 2;
|
||||
PRUint32 notifyOf = 0;
|
||||
|
||||
PRUint32 comboboxRole = combobox->Role();
|
||||
if (comboboxRole == nsIAccessibleRole::ROLE_COMBOBOX ||
|
||||
comboboxRole == nsIAccessibleRole::ROLE_AUTOCOMPLETE) {
|
||||
// HTML select is target of popuphidding event. Otherwise get container
|
||||
// widget. No container widget means this is either tooltip or menupopup.
|
||||
// No events in the former case.
|
||||
nsAccessible* widget = nsnull;
|
||||
if (popup->IsCombobox()) {
|
||||
widget = popup;
|
||||
} else {
|
||||
widget = popup->ContainerWidget();
|
||||
if (!widget) {
|
||||
if (!popup->IsMenuPopup())
|
||||
return;
|
||||
|
||||
widget = popup;
|
||||
}
|
||||
}
|
||||
|
||||
if (popup->IsAutoCompletePopup()) {
|
||||
// No focus event for autocomplete because it's managed by
|
||||
// DOMMenuItemInactive events.
|
||||
if (widget->IsAutoComplete())
|
||||
notifyOf = kNotifyOfState;
|
||||
|
||||
} else if (widget->IsCombobox()) {
|
||||
// Fire focus for active combobox, otherwise the focus is managed by DOM
|
||||
// focus notifications. Always fire state change event.
|
||||
if (widget->IsActiveWidget())
|
||||
notifyOf = kNotifyOfFocus;
|
||||
notifyOf |= kNotifyOfState;
|
||||
|
||||
} else if (widget->IsMenuButton()) {
|
||||
// Can be a part of autocomplete.
|
||||
nsAccessible* compositeWidget = widget->ContainerWidget();
|
||||
if (compositeWidget && compositeWidget->IsAutoComplete()) {
|
||||
widget = compositeWidget;
|
||||
notifyOf = kNotifyOfState;
|
||||
}
|
||||
|
||||
// Autocomplete (like searchbar) can be inactive when popup hiddens
|
||||
notifyOf |= kNotifyOfFocus;
|
||||
|
||||
} else if (widget == popup) {
|
||||
// Top level context menus and alerts.
|
||||
// Ignore submenus and menubar. When submenu is closed then sumbenu
|
||||
// container menuitem takes a focus via DOMMenuItemActive notification.
|
||||
// For menubars processing we listen DOMMenubarActive/Inactive
|
||||
// notifications.
|
||||
notifyOf = kNotifyOfFocus;
|
||||
}
|
||||
|
||||
// Restore focus to where it was.
|
||||
if (notifyOf & kNotifyOfFocus) {
|
||||
FocusMgr()->ActiveItemChanged(nsnull);
|
||||
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("popuphiding", popup)
|
||||
}
|
||||
|
||||
// Fire expanded state change event.
|
||||
if (notifyOf & kNotifyOfState) {
|
||||
nsRefPtr<AccEvent> event =
|
||||
new AccStateChangeEvent(combobox, states::EXPANDED, PR_FALSE);
|
||||
if (event)
|
||||
nsEventShell::FireEvent(event);
|
||||
new AccStateChangeEvent(widget, states::EXPANDED, PR_FALSE);
|
||||
document->FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,38 +90,6 @@ public:
|
||||
// nsRootAccessible
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ROOTACCESSIBLE_IMPL_CID)
|
||||
|
||||
/**
|
||||
* Fire an accessible focus event for the focused accessible and attach a new
|
||||
* selection listener to real focused element, if necessary.
|
||||
*
|
||||
* @param aFocusAccessible [in] the accessible which has received focus
|
||||
* @param aRealFocusContent [in] the actual DOM element which has received
|
||||
* focus (see @note section)
|
||||
* @param aForceEvent [in, optional] fire a focus event even if
|
||||
* the last focused item was the same
|
||||
* @param aIsFromUserInput [in, optional] specifies whether the event is
|
||||
* from user input
|
||||
*
|
||||
* @note Use the originally focused node where the selection lives as real
|
||||
* focus node. For example, use the anonymous HTML:input instead of
|
||||
* the containing XUL:textbox. In this case, sometimes it is a later
|
||||
* focus event which points to the actual anonymous child with focus,
|
||||
* so to be safe we need to reset the selection listener every time.
|
||||
* This happens because when some bindings handle focus, they
|
||||
* retarget focus to the appropriate child inside of themselves, but
|
||||
* DOM focus stays outside on that binding parent.
|
||||
*/
|
||||
void FireAccessibleFocusEvent(nsAccessible* aFocusAccessible,
|
||||
nsIContent* aRealFocusContent,
|
||||
PRBool aForceEvent = PR_FALSE,
|
||||
EIsFromUserInput aIsFromUserInput = eAutoDetect);
|
||||
|
||||
/**
|
||||
* Fire an accessible focus event for the current focused node,
|
||||
* if there is a focus.
|
||||
*/
|
||||
void FireCurrentFocusEvent();
|
||||
|
||||
nsCaretAccessible *GetCaretAccessible();
|
||||
|
||||
/**
|
||||
@ -130,7 +98,6 @@ public:
|
||||
virtual void DocumentActivated(nsDocAccessible* aDocument);
|
||||
|
||||
protected:
|
||||
NS_DECL_RUNNABLEMETHOD(nsRootAccessible, FireCurrentFocusEvent)
|
||||
|
||||
/**
|
||||
* Add/remove DOM event listeners.
|
||||
@ -151,7 +118,7 @@ protected:
|
||||
/*
|
||||
* Process "popuphiding" event. Used by HandleEvent().
|
||||
*/
|
||||
void HandlePopupHidingEvent(nsINode* aNode, nsAccessible* aAccessible);
|
||||
void HandlePopupHidingEvent(nsINode* aNode);
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
|
||||
@ -164,7 +131,6 @@ protected:
|
||||
already_AddRefed<nsIDocShellTreeItem>
|
||||
GetContentDocShell(nsIDocShellTreeItem *aStart);
|
||||
nsRefPtr<nsCaretAccessible> mCaretAccessible;
|
||||
nsCOMPtr<nsINode> mCurrentARIAMenubar;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsRootAccessible, NS_ROOTACCESSIBLE_IMPL_CID)
|
||||
|
@ -463,40 +463,29 @@ nsHTMLTextFieldAccessible::NativeState()
|
||||
nsGkAtoms::password, eIgnoreCase)) {
|
||||
state |= states::PROTECTED;
|
||||
}
|
||||
else {
|
||||
nsAccessible* parent = Parent();
|
||||
if (parent && parent->Role() == nsIAccessibleRole::ROLE_AUTOCOMPLETE)
|
||||
state |= states::HASPOPUP;
|
||||
}
|
||||
|
||||
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) {
|
||||
state |= states::READONLY;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> htmlInput(do_QueryInterface(mContent));
|
||||
// Is it an <input> or a <textarea> ?
|
||||
if (htmlInput) {
|
||||
state |= states::SINGLE_LINE;
|
||||
}
|
||||
else {
|
||||
state |= states::MULTI_LINE;
|
||||
}
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> htmlInput(do_QueryInterface(mContent));
|
||||
state |= htmlInput ? states::SINGLE_LINE : states::MULTI_LINE;
|
||||
|
||||
if (!(state & states::EDITABLE))
|
||||
if (!(state & states::EDITABLE) ||
|
||||
(state & (states::PROTECTED | states::MULTI_LINE)))
|
||||
return state;
|
||||
|
||||
nsCOMPtr<nsIContent> bindingContent = mContent->GetBindingParent();
|
||||
if (bindingContent &&
|
||||
bindingContent->NodeInfo()->Equals(nsGkAtoms::textbox,
|
||||
kNameSpaceID_XUL)) {
|
||||
if (bindingContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
||||
nsGkAtoms::autocomplete,
|
||||
eIgnoreCase)) {
|
||||
// If parent is XUL textbox and value of @type attribute is "autocomplete",
|
||||
// then this accessible supports autocompletion.
|
||||
state |= states::SUPPORTS_AUTOCOMPLETION;
|
||||
}
|
||||
} else if (gIsFormFillEnabled && htmlInput && !(state & states::PROTECTED)) {
|
||||
// Expose autocomplete states if this input is part of autocomplete widget.
|
||||
nsAccessible* widget = ContainerWidget();
|
||||
if (widget && widget-IsAutoComplete()) {
|
||||
state |= states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION;
|
||||
return state;
|
||||
}
|
||||
|
||||
// No parent can mean a fake widget created for XUL textbox. If accessible
|
||||
// is unattached from tree then we don't care.
|
||||
if (mParent && gIsFormFillEnabled) {
|
||||
// Check to see if autocompletion is allowed on this input. We don't expose
|
||||
// it for password fields even though the entire password can be remembered
|
||||
// for a page if the user asks it to be. However, the kind of autocomplete
|
||||
@ -575,6 +564,23 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLTextFieldAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsHTMLTextFieldAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsHTMLTextFieldAccessible::ContainerWidget() const
|
||||
{
|
||||
return mParent && mParent->Role() == nsIAccessibleRole::ROLE_AUTOCOMPLETE ?
|
||||
mParent : nsnull;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLGroupboxAccessible
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -166,6 +166,10 @@ public:
|
||||
|
||||
// ActionAccessible
|
||||
virtual PRUint8 ActionCount();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -67,6 +67,7 @@ nsHTMLSelectListAccessible::
|
||||
nsHTMLSelectListAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
|
||||
nsAccessibleWrap(aContent, aShell)
|
||||
{
|
||||
mFlags |= eListControlAccessible;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -76,19 +77,6 @@ PRUint64
|
||||
nsHTMLSelectListAccessible::NativeState()
|
||||
{
|
||||
PRUint64 state = nsAccessibleWrap::NativeState();
|
||||
|
||||
// As a nsHTMLSelectListAccessible we can have the following states:
|
||||
// states::MULTISELECTABLE, states::EXTSELECTABLE
|
||||
|
||||
if (state & states::FOCUSED) {
|
||||
// Treat first focusable option node as actual focus, in order
|
||||
// to avoid confusing JAWS, which needs focus on the option
|
||||
nsCOMPtr<nsIContent> focusedOption =
|
||||
nsHTMLSelectOptionAccessible::GetFocusedOption(mContent);
|
||||
if (focusedOption) { // Clear focused state since it is on option
|
||||
state &= ~states::FOCUSED;
|
||||
}
|
||||
}
|
||||
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple))
|
||||
state |= states::MULTISELECTABLE | states::EXTSELECTABLE;
|
||||
|
||||
@ -127,6 +115,42 @@ nsHTMLSelectListAccessible::UnselectAll()
|
||||
nsAccessibleWrap::UnselectAll() : false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLSelectListAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsHTMLSelectListAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLSelectListAccessible::IsActiveWidget() const
|
||||
{
|
||||
return FocusMgr()->HasDOMFocus(mContent);
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLSelectListAccessible::AreItemsOperable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsHTMLSelectListAccessible::CurrentItem()
|
||||
{
|
||||
nsIListControlFrame* listControlFrame = do_QueryFrame(GetFrame());
|
||||
if (listControlFrame) {
|
||||
nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
|
||||
if (activeOptionNode) {
|
||||
nsDocAccessible* document = GetDocAccessible();
|
||||
if (document)
|
||||
return document->GetAccessible(activeOptionNode);
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLSelectListAccessible: nsAccessible protected
|
||||
|
||||
@ -252,25 +276,12 @@ nsHTMLSelectOptionAccessible::NativeState()
|
||||
|
||||
PRUint64 selectState = 0;
|
||||
nsIContent* selectContent = GetSelectState(&selectState);
|
||||
if (selectState & states::INVISIBLE)
|
||||
if (!selectContent || selectState & states::INVISIBLE)
|
||||
return state;
|
||||
|
||||
NS_ENSURE_TRUE(selectContent, NS_ERROR_FAILURE);
|
||||
|
||||
// Is disabled?
|
||||
if (0 == (state & states::UNAVAILABLE)) {
|
||||
// Focusable and selectable
|
||||
if (!(state & states::UNAVAILABLE))
|
||||
state |= (states::FOCUSABLE | states::SELECTABLE);
|
||||
// When the list is focused but no option is actually focused,
|
||||
// Firefox draws a focus ring around the first non-disabled option.
|
||||
// We need to indicate states::FOCUSED in that case, because it
|
||||
// prevents JAWS from ignoring the list
|
||||
// GetFocusedOption() ensures that an option node is
|
||||
// returned in this case, as long as some focusable option exists
|
||||
// in the listbox
|
||||
nsCOMPtr<nsIContent> focusedOption = GetFocusedOption(selectContent);
|
||||
if (focusedOption == mContent)
|
||||
state |= states::FOCUSED;
|
||||
}
|
||||
|
||||
// Are we selected?
|
||||
PRBool isSelected = PR_FALSE;
|
||||
@ -279,7 +290,6 @@ nsHTMLSelectOptionAccessible::NativeState()
|
||||
option->GetSelected(&isSelected);
|
||||
if (isSelected)
|
||||
state |= states::SELECTED;
|
||||
|
||||
}
|
||||
|
||||
if (selectState & states::OFFSCREEN) {
|
||||
@ -377,62 +387,17 @@ nsHTMLSelectOptionAccessible::ActionCount()
|
||||
return 1;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsHTMLSelectOptionAccessible::DoAction(PRUint8 index)
|
||||
NS_IMETHODIMP
|
||||
nsHTMLSelectOptionAccessible::DoAction(PRUint8 aIndex)
|
||||
{
|
||||
if (index == eAction_Select) { // default action
|
||||
nsCOMPtr<nsIDOMHTMLOptionElement> newHTMLOption(do_QueryInterface(mContent));
|
||||
if (!newHTMLOption)
|
||||
return NS_ERROR_FAILURE;
|
||||
// Clear old selection
|
||||
nsAccessible* parent = Parent();
|
||||
if (!parent)
|
||||
return NS_OK;
|
||||
if (aIndex != eAction_Select)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
nsCOMPtr<nsIContent> oldHTMLOptionContent =
|
||||
GetFocusedOption(parent->GetContent());
|
||||
nsCOMPtr<nsIDOMHTMLOptionElement> oldHTMLOption =
|
||||
do_QueryInterface(oldHTMLOptionContent);
|
||||
if (oldHTMLOption)
|
||||
oldHTMLOption->SetSelected(PR_FALSE);
|
||||
// Set new selection
|
||||
newHTMLOption->SetSelected(PR_TRUE);
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// If combo box, and open, close it
|
||||
// First, get the <select> widgets list control frame
|
||||
nsIContent *selectContent = mContent;
|
||||
do {
|
||||
selectContent = selectContent->GetParent();
|
||||
nsCOMPtr<nsIDOMHTMLSelectElement> selectControl =
|
||||
do_QueryInterface(selectContent);
|
||||
if (selectControl)
|
||||
break;
|
||||
|
||||
} while (selectContent);
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
|
||||
nsCOMPtr<nsIDOMHTMLOptionElement> option(do_QueryInterface(mContent));
|
||||
|
||||
if (!selectContent || !presShell || !option)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsIFrame *selectFrame = selectContent->GetPrimaryFrame();
|
||||
nsIComboboxControlFrame *comboBoxFrame = do_QueryFrame(selectFrame);
|
||||
if (comboBoxFrame) {
|
||||
nsIFrame *listFrame = comboBoxFrame->GetDropDown();
|
||||
if (comboBoxFrame->IsDroppedDown() && listFrame) {
|
||||
// use this list control frame to roll up the list
|
||||
nsIListControlFrame *listControlFrame = do_QueryFrame(listFrame);
|
||||
if (listControlFrame) {
|
||||
PRInt32 newIndex = 0;
|
||||
option->GetIndex(&newIndex);
|
||||
listControlFrame->ComboboxFinish(newIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
DoCommand();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -446,73 +411,24 @@ nsHTMLSelectOptionAccessible::SetSelected(PRBool aSelect)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLSelectOptionAccessible: static methods
|
||||
// nsHTMLSelectOptionAccessible: Widgets
|
||||
|
||||
/**
|
||||
* Helper method for getting the focused DOM Node from our parent(list) node. We
|
||||
* need to use the frame to get the focused option because for some reason we
|
||||
* weren't getting the proper notification when the focus changed using the DOM
|
||||
*/
|
||||
already_AddRefed<nsIContent>
|
||||
nsHTMLSelectOptionAccessible::GetFocusedOption(nsIContent *aListNode)
|
||||
nsAccessible*
|
||||
nsHTMLSelectOptionAccessible::ContainerWidget() const
|
||||
{
|
||||
NS_ASSERTION(aListNode, "Called GetFocusedOptionNode without a valid list node");
|
||||
if (mParent && mParent->IsListControl()) {
|
||||
nsAccessible* grandParent = mParent->Parent();
|
||||
if (grandParent && grandParent->IsCombobox())
|
||||
return grandParent;
|
||||
|
||||
nsIFrame *frame = aListNode->GetPrimaryFrame();
|
||||
if (!frame)
|
||||
return nsnull;
|
||||
|
||||
PRInt32 focusedOptionIndex = 0;
|
||||
|
||||
// Get options
|
||||
nsCOMPtr<nsIDOMHTMLSelectElement> selectElement(do_QueryInterface(aListNode));
|
||||
NS_ASSERTION(selectElement, "No select element where it should be");
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLOptionsCollection> options;
|
||||
nsresult rv = selectElement->GetOptions(getter_AddRefs(options));
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsIListControlFrame *listFrame = do_QueryFrame(frame);
|
||||
if (listFrame) {
|
||||
// Get what's focused in listbox by asking frame for "selected item".
|
||||
// Can't use dom interface for this, because it will always return the first selected item
|
||||
// when there is more than 1 item selected. We need the focused item, not
|
||||
// the first selected item.
|
||||
focusedOptionIndex = listFrame->GetSelectedIndex();
|
||||
if (focusedOptionIndex == -1) {
|
||||
nsCOMPtr<nsIDOMNode> nextOption;
|
||||
while (PR_TRUE) {
|
||||
++ focusedOptionIndex;
|
||||
options->Item(focusedOptionIndex, getter_AddRefs(nextOption));
|
||||
nsCOMPtr<nsIDOMHTMLOptionElement> optionElement = do_QueryInterface(nextOption);
|
||||
if (!optionElement) {
|
||||
break;
|
||||
}
|
||||
PRBool disabled;
|
||||
optionElement->GetDisabled(&disabled);
|
||||
if (!disabled) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Combo boxes can only have 1 selected option, so they can use the dom interface for this
|
||||
rv = selectElement->GetSelectedIndex(&focusedOptionIndex);
|
||||
return mParent;
|
||||
}
|
||||
|
||||
// Either use options and focused index, or default return null
|
||||
if (NS_SUCCEEDED(rv) && options && focusedOptionIndex >= 0) { // Something is focused
|
||||
nsCOMPtr<nsIDOMNode> focusedOptionNode;
|
||||
options->Item(focusedOptionIndex, getter_AddRefs(focusedOptionNode));
|
||||
nsIContent *focusedOption = nsnull;
|
||||
if (focusedOptionNode)
|
||||
CallQueryInterface(focusedOptionNode, &focusedOption);
|
||||
return focusedOption;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLSelectOptionAccessible: static methods
|
||||
|
||||
void
|
||||
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(nsIContent *aPossibleOptionNode)
|
||||
{
|
||||
@ -644,6 +560,7 @@ nsHTMLComboboxAccessible::
|
||||
nsHTMLComboboxAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
|
||||
nsAccessibleWrap(aContent, aShell)
|
||||
{
|
||||
mFlags |= eComboboxAccessible;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -718,16 +635,12 @@ nsHTMLComboboxAccessible::NativeState()
|
||||
|
||||
nsIFrame *frame = GetBoundsFrame();
|
||||
nsIComboboxControlFrame *comboFrame = do_QueryFrame(frame);
|
||||
if (comboFrame && comboFrame->IsDroppedDown()) {
|
||||
if (comboFrame && comboFrame->IsDroppedDown())
|
||||
state |= states::EXPANDED;
|
||||
}
|
||||
else {
|
||||
state &= ~states::FOCUSED; // Focus is on an option
|
||||
else
|
||||
state |= states::COLLAPSED;
|
||||
}
|
||||
|
||||
state |= states::HASPOPUP | states::FOCUSABLE;
|
||||
|
||||
state |= states::HASPOPUP;
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -740,37 +653,17 @@ nsHTMLComboboxAccessible::Description(nsString& aDescription)
|
||||
nsAccessible::Description(aDescription);
|
||||
if (!aDescription.IsEmpty())
|
||||
return;
|
||||
// Use description of currently focused option
|
||||
nsAccessible *option = GetFocusedOptionAccessible();
|
||||
|
||||
// Otherwise use description of selected option.
|
||||
nsAccessible* option = SelectedOption();
|
||||
if (option)
|
||||
option->Description(aDescription);
|
||||
}
|
||||
|
||||
nsAccessible *
|
||||
nsHTMLComboboxAccessible::GetFocusedOptionAccessible()
|
||||
{
|
||||
if (IsDefunct())
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIContent> focusedOption =
|
||||
nsHTMLSelectOptionAccessible::GetFocusedOption(mContent);
|
||||
if (!focusedOption) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
return GetAccService()->GetAccessibleInWeakShell(focusedOption,
|
||||
mWeakShell);
|
||||
}
|
||||
|
||||
/**
|
||||
* MSAA/ATK accessible value != HTML value, especially not in combo boxes.
|
||||
* Our accessible value is the text label for of our ( first ) selected child.
|
||||
* The easiest way to get this is from the first child which is the readonly textfield.
|
||||
*/
|
||||
NS_IMETHODIMP nsHTMLComboboxAccessible::GetValue(nsAString& aValue)
|
||||
{
|
||||
// Use accessible name of currently focused option.
|
||||
nsAccessible *option = GetFocusedOptionAccessible();
|
||||
// Use accessible name of selected option.
|
||||
nsAccessible* option = SelectedOption();
|
||||
return option ? option->GetName(aValue) : NS_OK;
|
||||
}
|
||||
|
||||
@ -780,25 +673,16 @@ nsHTMLComboboxAccessible::ActionCount()
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmaticaly toggle the combo box
|
||||
*/
|
||||
NS_IMETHODIMP nsHTMLComboboxAccessible::DoAction(PRUint8 aIndex)
|
||||
NS_IMETHODIMP
|
||||
nsHTMLComboboxAccessible::DoAction(PRUint8 aIndex)
|
||||
{
|
||||
if (aIndex != nsHTMLComboboxAccessible::eAction_Click) {
|
||||
if (aIndex != eAction_Click)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsIFrame *frame = GetFrame();
|
||||
if (!frame) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsIComboboxControlFrame *comboFrame = do_QueryFrame(frame);
|
||||
if (!comboFrame) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Reverse whether it's dropped down or not
|
||||
comboFrame->ShowDropDown(!comboFrame->IsDroppedDown());
|
||||
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
DoCommand();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -829,6 +713,63 @@ NS_IMETHODIMP nsHTMLComboboxAccessible::GetActionName(PRUint8 aIndex, nsAString&
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLComboboxAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsHTMLComboboxAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLComboboxAccessible::IsActiveWidget() const
|
||||
{
|
||||
return FocusMgr()->HasDOMFocus(mContent);
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLComboboxAccessible::AreItemsOperable() const
|
||||
{
|
||||
nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(GetFrame());
|
||||
return comboboxFrame && comboboxFrame->IsDroppedDown();
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsHTMLComboboxAccessible::CurrentItem()
|
||||
{
|
||||
// No current item for collapsed combobox.
|
||||
return SelectedOption(true);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLComboboxAccessible: protected
|
||||
|
||||
nsAccessible*
|
||||
nsHTMLComboboxAccessible::SelectedOption(bool aIgnoreIfCollapsed) const
|
||||
{
|
||||
nsIFrame* frame = GetFrame();
|
||||
nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(frame);
|
||||
if (comboboxFrame) {
|
||||
if (aIgnoreIfCollapsed && !comboboxFrame->IsDroppedDown())
|
||||
return nsnull;
|
||||
|
||||
frame = comboboxFrame->GetDropDown();
|
||||
}
|
||||
|
||||
nsIListControlFrame* listControlFrame = do_QueryFrame(frame);
|
||||
if (listControlFrame) {
|
||||
nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
|
||||
if (activeOptionNode) {
|
||||
nsDocAccessible* document = GetDocAccessible();
|
||||
if (document)
|
||||
return document->GetAccessible(activeOptionNode);
|
||||
}
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLComboboxListAccessible
|
||||
|
@ -80,6 +80,12 @@ public:
|
||||
virtual bool SelectAll();
|
||||
virtual bool UnselectAll();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
virtual nsAccessible* CurrentItem();
|
||||
|
||||
protected:
|
||||
|
||||
// nsAccessible
|
||||
@ -121,10 +127,8 @@ public:
|
||||
// ActionAccessible
|
||||
virtual PRUint8 ActionCount();
|
||||
|
||||
/**
|
||||
* Return focused option if any.
|
||||
*/
|
||||
static already_AddRefed<nsIContent> GetFocusedOption(nsIContent *aListNode);
|
||||
// Widgets
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
|
||||
static void SelectionChangedIfOption(nsIContent *aPossibleOption);
|
||||
|
||||
@ -202,16 +206,20 @@ public:
|
||||
// ActionAccessible
|
||||
virtual PRUint8 ActionCount();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
virtual nsAccessible* CurrentItem();
|
||||
|
||||
protected:
|
||||
// nsAccessible
|
||||
virtual void CacheChildren();
|
||||
|
||||
// nsHTMLComboboxAccessible
|
||||
|
||||
/**
|
||||
* Return focused option accessible.
|
||||
* Return selected option.
|
||||
*/
|
||||
nsAccessible *GetFocusedOptionAccessible();
|
||||
nsAccessible* SelectedOption(bool aIgnoreIfCollapsed = false) const;
|
||||
|
||||
private:
|
||||
nsRefPtr<nsHTMLComboboxListAccessible> mListAccessible;
|
||||
|
@ -1242,7 +1242,7 @@ nsHyperTextAccessible::GetAttributesInternal(nsIPersistentProperties *aAttribute
|
||||
oldValueUnused);
|
||||
}
|
||||
|
||||
if (gLastFocusedNode == GetNode()) {
|
||||
if (FocusMgr()->IsFocused(this)) {
|
||||
PRInt32 lineNumber = GetCaretLineNumber();
|
||||
if (lineNumber >= 1) {
|
||||
nsAutoString strLineNumber;
|
||||
@ -1628,13 +1628,9 @@ nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
|
||||
|
||||
// No caret if the focused node is not inside this DOM node and this DOM node
|
||||
// is not inside of focused node.
|
||||
|
||||
nsINode* thisNode = GetNode();
|
||||
PRBool isInsideOfFocusedNode =
|
||||
nsCoreUtils::IsAncestorOf(gLastFocusedNode, thisNode);
|
||||
|
||||
if (!isInsideOfFocusedNode && thisNode != gLastFocusedNode &&
|
||||
!nsCoreUtils::IsAncestorOf(thisNode, gLastFocusedNode))
|
||||
FocusManager::FocusDisposition focusDisp =
|
||||
FocusMgr()->IsInOrContainsFocus(this);
|
||||
if (focusDisp == FocusManager::eNone)
|
||||
return NS_OK;
|
||||
|
||||
// Turn the focus node and offset of the selection into caret hypretext
|
||||
@ -1655,10 +1651,11 @@ nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
|
||||
// No caret if this DOM node is inside of focused node but the selection's
|
||||
// focus point is not inside of this DOM node.
|
||||
nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDOMNode));
|
||||
if (isInsideOfFocusedNode) {
|
||||
if (focusDisp == FocusManager::eContainedByFocus) {
|
||||
nsINode *resultNode =
|
||||
nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
|
||||
|
||||
nsINode* thisNode = GetNode();
|
||||
if (resultNode != thisNode &&
|
||||
!nsCoreUtils::IsAncestorOf(thisNode, resultNode))
|
||||
return NS_OK;
|
||||
|
@ -72,4 +72,6 @@ LOCAL_INCLUDES = \
|
||||
-I$(srcdir) \
|
||||
-I$(srcdir)/../base \
|
||||
-I$(srcdir)/../html \
|
||||
-I$(srcdir)/../../../layout/generic \
|
||||
-I$(srcdir)/../../../layout/xul/base/src \
|
||||
$(NULL)
|
||||
|
@ -73,3 +73,21 @@ nsXULAlertAccessible::GetName(nsAString& aName)
|
||||
aName.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Widgets
|
||||
|
||||
bool
|
||||
nsXULAlertAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsXULAlertAccessible::ContainerWidget() const
|
||||
{
|
||||
// If a part of colorpicker widget.
|
||||
if (mParent && mParent->IsMenuButton())
|
||||
return mParent;
|
||||
return nsnull;
|
||||
}
|
||||
|
@ -57,6 +57,10 @@ public:
|
||||
// nsAccessible
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual PRUint64 NativeState();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "nsDocAccessible.h"
|
||||
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsMenuPopupFrame.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
@ -85,27 +86,30 @@ nsXULColorPickerTileAccessible::NativeRole()
|
||||
PRUint64
|
||||
nsXULColorPickerTileAccessible::NativeState()
|
||||
{
|
||||
// Possible states: focused, focusable, selected.
|
||||
PRUint64 state = nsAccessibleWrap::NativeState();
|
||||
if (!(state & states::UNAVAILABLE))
|
||||
state |= states::FOCUSABLE | states::SELECTABLE;
|
||||
|
||||
// get focus and disable status from base class
|
||||
PRUint64 states = nsAccessibleWrap::NativeState();
|
||||
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::selected))
|
||||
state |= states::SELECTED;
|
||||
|
||||
states |= states::FOCUSABLE;
|
||||
|
||||
// Focused?
|
||||
PRBool isFocused = mContent->HasAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::hover);
|
||||
if (isFocused)
|
||||
states |= states::FOCUSED;
|
||||
|
||||
PRBool isSelected = mContent->HasAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::selected);
|
||||
if (isSelected)
|
||||
states |= states::SELECTED;
|
||||
|
||||
return states;
|
||||
return state;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULColorPickerTileAccessible: Widgets
|
||||
|
||||
nsAccessible*
|
||||
nsXULColorPickerTileAccessible::ContainerWidget() const
|
||||
{
|
||||
nsAccessible* parent = Parent();
|
||||
if (parent) {
|
||||
nsAccessible* grandParent = parent->Parent();
|
||||
if (grandParent && grandParent->IsMenuButton())
|
||||
return grandParent;
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULColorPickerAccessible
|
||||
@ -115,6 +119,7 @@ nsXULColorPickerAccessible::
|
||||
nsXULColorPickerAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
|
||||
nsXULColorPickerTileAccessible(aContent, aShell)
|
||||
{
|
||||
mFlags |= eMenuButtonAccessible;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -139,6 +144,32 @@ nsXULColorPickerAccessible::NativeRole()
|
||||
return nsIAccessibleRole::ROLE_BUTTONDROPDOWNGRID;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULColorPickerAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsXULColorPickerAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULColorPickerAccessible::IsActiveWidget() const
|
||||
{
|
||||
return FocusMgr()->HasDOMFocus(mContent);
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULColorPickerAccessible::AreItemsOperable() const
|
||||
{
|
||||
nsAccessible* menuPopup = mChildren.SafeElementAt(0, nsnull);
|
||||
if (menuPopup) {
|
||||
nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
|
||||
return menuPopupFrame && menuPopupFrame->IsOpen();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULColorPickerAccessible: protected nsAccessible
|
||||
|
||||
|
@ -39,7 +39,6 @@
|
||||
#ifndef _nsXULColorPickerAccessible_H_
|
||||
#define _nsXULColorPickerAccessible_H_
|
||||
|
||||
// NOTE: alphabetically ordered
|
||||
#include "nsAccessibleWrap.h"
|
||||
|
||||
/**
|
||||
@ -57,6 +56,9 @@ public:
|
||||
// nsAccessible
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual PRUint64 NativeState();
|
||||
|
||||
// Widgets
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
};
|
||||
|
||||
|
||||
@ -72,6 +74,11 @@ public:
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual PRUint64 NativeState();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
|
||||
protected:
|
||||
|
||||
// nsAccessible
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsCoreUtils.h"
|
||||
|
||||
#include "nsIAutoCompleteInput.h"
|
||||
#include "nsIDOMXULMenuListElement.h"
|
||||
#include "nsIDOMXULSelectCntrlItemEl.h"
|
||||
|
||||
@ -57,13 +58,17 @@ nsXULComboboxAccessible::
|
||||
nsXULComboboxAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
|
||||
nsAccessibleWrap(aContent, aShell)
|
||||
{
|
||||
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
||||
nsGkAtoms::autocomplete, eIgnoreCase))
|
||||
mFlags |= eAutoCompleteAccessible;
|
||||
else
|
||||
mFlags |= eComboboxAccessible;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsXULComboboxAccessible::NativeRole()
|
||||
{
|
||||
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
||||
nsGkAtoms::autocomplete, eIgnoreCase))
|
||||
if (IsAutoComplete())
|
||||
return nsIAccessibleRole::ROLE_AUTOCOMPLETE;
|
||||
return nsIAccessibleRole::ROLE_COMBOBOX;
|
||||
}
|
||||
@ -206,3 +211,48 @@ nsXULComboboxAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Widgets
|
||||
|
||||
bool
|
||||
nsXULComboboxAccessible::IsActiveWidget() const
|
||||
{
|
||||
if (IsAutoComplete() ||
|
||||
mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
|
||||
nsGkAtoms::_true, eIgnoreCase)) {
|
||||
PRInt32 childCount = mChildren.Length();
|
||||
for (PRInt32 idx = 0; idx < childCount; idx++) {
|
||||
nsAccessible* child = mChildren[idx];
|
||||
if (child->Role() == nsIAccessibleRole::ROLE_ENTRY)
|
||||
return FocusMgr()->HasDOMFocus(child->GetContent());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return FocusMgr()->HasDOMFocus(mContent);
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULComboboxAccessible::AreItemsOperable() const
|
||||
{
|
||||
if (IsAutoComplete()) {
|
||||
nsCOMPtr<nsIAutoCompleteInput> autoCompleteInputElm =
|
||||
do_QueryInterface(mContent);
|
||||
if (autoCompleteInputElm) {
|
||||
PRBool isOpen = PR_FALSE;
|
||||
autoCompleteInputElm->GetPopupOpen(&isOpen);
|
||||
return isOpen;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = do_QueryInterface(mContent);
|
||||
if (menuListElm) {
|
||||
PRBool isOpen = PR_FALSE;
|
||||
menuListElm->GetOpen(&isOpen);
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -40,7 +40,6 @@
|
||||
#ifndef __nsXULComboboxAccessible_h__
|
||||
#define __nsXULComboboxAccessible_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsXULMenuAccessible.h"
|
||||
|
||||
/**
|
||||
@ -66,6 +65,10 @@ public:
|
||||
|
||||
// ActionAccessible
|
||||
virtual PRUint8 ActionCount();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include "nsIFrame.h"
|
||||
#include "nsINameSpaceManager.h"
|
||||
#include "nsITextControlFrame.h"
|
||||
#include "nsMenuPopupFrame.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
@ -72,6 +73,8 @@ nsXULButtonAccessible::
|
||||
nsXULButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
|
||||
nsAccessibleWrap(aContent, aShell)
|
||||
{
|
||||
if (ContainsMenu())
|
||||
mFlags |= eMenuButtonAccessible;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -164,6 +167,42 @@ nsXULButtonAccessible::NativeState()
|
||||
return state;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULButtonAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsXULButtonAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULButtonAccessible::IsActiveWidget() const
|
||||
{
|
||||
return FocusMgr()->HasDOMFocus(mContent);
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULButtonAccessible::AreItemsOperable() const
|
||||
{
|
||||
if (IsMenuButton()) {
|
||||
nsAccessible* menuPopup = mChildren.SafeElementAt(0, nsnull);
|
||||
if (menuPopup) {
|
||||
nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
|
||||
return menuPopupFrame->IsOpen();
|
||||
}
|
||||
}
|
||||
return false; // no items
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsXULButtonAccessible::ContainerWidget() const
|
||||
{
|
||||
if (IsMenuButton() && mParent && mParent->IsAutoComplete())
|
||||
return mParent;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULButtonAccessible: nsAccessible protected
|
||||
|
||||
@ -464,19 +503,19 @@ nsXULRadioButtonAccessible::
|
||||
{
|
||||
}
|
||||
|
||||
/** We are Focusable and can be Checked and focused */
|
||||
PRUint64
|
||||
nsXULRadioButtonAccessible::NativeState()
|
||||
{
|
||||
PRUint64 state = nsFormControlAccessible::NativeState();
|
||||
|
||||
state |= states::CHECKABLE;
|
||||
|
||||
PRBool selected = PR_FALSE; // Radio buttons can be selected
|
||||
|
||||
if (!(state & states::UNAVAILABLE))
|
||||
state |= states::FOCUSABLE;
|
||||
|
||||
nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
|
||||
do_QueryInterface(mContent);
|
||||
if (radioButton) {
|
||||
PRBool selected = PR_FALSE; // Radio buttons can be selected
|
||||
radioButton->GetSelected(&selected);
|
||||
if (selected) {
|
||||
state |= states::CHECKED;
|
||||
@ -494,6 +533,15 @@ nsXULRadioButtonAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet,
|
||||
aSetSize);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULRadioButtonAccessible: Widgets
|
||||
|
||||
nsAccessible*
|
||||
nsXULRadioButtonAccessible::ContainerWidget() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULRadioGroupAccessible
|
||||
@ -529,7 +577,28 @@ nsXULRadioGroupAccessible::NativeState()
|
||||
return nsAccessible::NativeState() & ~(states::FOCUSABLE | states::FOCUSED);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULRadioGroupAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsXULRadioGroupAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULRadioGroupAccessible::IsActiveWidget() const
|
||||
{
|
||||
return FocusMgr()->HasDOMFocus(mContent);
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULRadioGroupAccessible::AreItemsOperable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULStatusBarAccessible
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -719,9 +788,6 @@ nsXULTextFieldAccessible::NativeState()
|
||||
|
||||
state |= tempAccessible->NativeState();
|
||||
|
||||
if (gLastFocusedNode == mContent)
|
||||
state |= states::FOCUSED;
|
||||
|
||||
nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
|
||||
if (menuList) {
|
||||
// <xul:menulist droppable="false">
|
||||
|
@ -77,6 +77,12 @@ public:
|
||||
// ActionAccessible
|
||||
virtual PRUint8 ActionCount();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
|
||||
protected:
|
||||
|
||||
// nsAccessible
|
||||
@ -159,6 +165,9 @@ public:
|
||||
virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
|
||||
PRInt32 *aSetSize);
|
||||
virtual PRUint64 NativeState();
|
||||
|
||||
// Widgets
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -172,6 +181,11 @@ public:
|
||||
// nsAccessible
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual PRUint64 NativeState();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -44,11 +44,14 @@
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccUtils.h"
|
||||
|
||||
#include "nsIDOMXULPopupElement.h"
|
||||
#include "nsIDOMXULMultSelectCntrlEl.h"
|
||||
#include "nsIDOMXULSelectCntrlItemEl.h"
|
||||
#include "nsIDOMNodeList.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIAutoCompleteInput.h"
|
||||
#include "nsIAutoCompletePopup.h"
|
||||
#include "nsIDOMXULMenuListElement.h"
|
||||
#include "nsIDOMXULMultSelectCntrlEl.h"
|
||||
#include "nsIDOMNodeList.h"
|
||||
#include "nsIDOMXULPopupElement.h"
|
||||
#include "nsIDOMXULSelectCntrlItemEl.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
@ -131,6 +134,13 @@ nsXULListboxAccessible::
|
||||
nsXULListboxAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
|
||||
nsXULSelectableAccessible(aContent, aShell)
|
||||
{
|
||||
nsIContent* parentContent = mContent->GetParent();
|
||||
if (parentContent) {
|
||||
nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
|
||||
do_QueryInterface(parentContent);
|
||||
if (autoCompletePopupElm)
|
||||
mFlags |= eAutoCompletePopupAccessible;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsXULListboxAccessible, nsXULSelectableAccessible)
|
||||
@ -825,6 +835,72 @@ nsXULListboxAccessible::IsProbablyForLayout(PRBool *aIsProbablyForLayout)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULListboxAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsXULListboxAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULListboxAccessible::IsActiveWidget() const
|
||||
{
|
||||
if (IsAutoCompletePopup()) {
|
||||
nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
|
||||
do_QueryInterface(mContent->GetParent());
|
||||
|
||||
if (autoCompletePopupElm) {
|
||||
PRBool isOpen = PR_FALSE;
|
||||
autoCompletePopupElm->GetPopupOpen(&isOpen);
|
||||
return isOpen;
|
||||
}
|
||||
}
|
||||
return FocusMgr()->HasDOMFocus(mContent);
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULListboxAccessible::AreItemsOperable() const
|
||||
{
|
||||
if (IsAutoCompletePopup()) {
|
||||
nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
|
||||
do_QueryInterface(mContent->GetParent());
|
||||
|
||||
if (autoCompletePopupElm) {
|
||||
PRBool isOpen = PR_FALSE;
|
||||
autoCompletePopupElm->GetPopupOpen(&isOpen);
|
||||
return isOpen;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsXULListboxAccessible::ContainerWidget() const
|
||||
{
|
||||
if (IsAutoCompletePopup()) {
|
||||
// This works for XUL autocompletes. It doesn't work for HTML forms
|
||||
// autocomplete because of potential crossprocess calls (when autocomplete
|
||||
// lives in content process while popup lives in chrome process). If that's
|
||||
// a problem then rethink Widgets interface.
|
||||
nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
|
||||
do_QueryInterface(mContent->GetParent());
|
||||
if (menuListElm) {
|
||||
nsCOMPtr<nsIDOMNode> inputElm;
|
||||
menuListElm->GetInputField(getter_AddRefs(inputElm));
|
||||
if (inputElm) {
|
||||
nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm);
|
||||
if (inputNode) {
|
||||
nsAccessible* input = GetAccService()->GetAccessible(inputNode);
|
||||
return input ? input->ContainerWidget() : nsnull;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULListitemAccessible
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -930,9 +1006,8 @@ nsXULListitemAccessible::NativeState()
|
||||
if (isSelected)
|
||||
states |= states::SELECTED;
|
||||
|
||||
if (gLastFocusedNode == mContent)
|
||||
if (FocusMgr()->IsFocused(this))
|
||||
states |= states::FOCUSED;
|
||||
|
||||
}
|
||||
|
||||
return states;
|
||||
@ -969,6 +1044,15 @@ nsXULListitemAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet,
|
||||
aSetSize);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULListitemAccessible: Widgets
|
||||
|
||||
nsAccessible*
|
||||
nsXULListitemAccessible::ContainerWidget() const
|
||||
{
|
||||
return Parent();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULListCellAccessible
|
||||
|
@ -105,6 +105,13 @@ public:
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual PRUint64 NativeState();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
|
||||
protected:
|
||||
PRBool IsMulticolumn();
|
||||
};
|
||||
@ -135,6 +142,9 @@ public:
|
||||
PRInt32 *aSetSize);
|
||||
virtual PRBool GetAllowsAnonChildAccessibles();
|
||||
|
||||
// Widgets
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Return listbox accessible for the listitem.
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsDocAccessible.h"
|
||||
#include "nsXULFormControlAccessible.h"
|
||||
#include "States.h"
|
||||
|
||||
@ -54,6 +55,8 @@
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsMenuBarFrame.h"
|
||||
#include "nsMenuPopupFrame.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
@ -269,6 +272,35 @@ nsXULSelectableAccessible::SelectAll()
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULSelectableAccessible: Widgets
|
||||
|
||||
nsAccessible*
|
||||
nsXULSelectableAccessible::CurrentItem()
|
||||
{
|
||||
if (!mSelectControl)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIDOMXULSelectControlItemElement> currentItemElm;
|
||||
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
|
||||
do_QueryInterface(mSelectControl);
|
||||
if (multiSelectControl)
|
||||
multiSelectControl->GetCurrentItem(getter_AddRefs(currentItemElm));
|
||||
else
|
||||
mSelectControl->GetSelectedItem(getter_AddRefs(currentItemElm));
|
||||
|
||||
nsCOMPtr<nsINode> DOMNode;
|
||||
if (currentItemElm)
|
||||
DOMNode = do_QueryInterface(currentItemElm);
|
||||
|
||||
if (DOMNode) {
|
||||
nsDocAccessible* document = GetDocAccessible();
|
||||
if (document)
|
||||
return document->GetAccessible(DOMNode);
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULMenuitemAccessible
|
||||
@ -285,10 +317,6 @@ nsXULMenuitemAccessible::NativeState()
|
||||
{
|
||||
PRUint64 state = nsAccessible::NativeState();
|
||||
|
||||
// Focused?
|
||||
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::menuactive))
|
||||
state |= states::FOCUSED;
|
||||
|
||||
// Has Popup?
|
||||
if (mContent->NodeInfo()->Equals(nsGkAtoms::menu, kNameSpaceID_XUL)) {
|
||||
state |= states::HASPOPUP;
|
||||
@ -362,7 +390,10 @@ nsXULMenuitemAccessible::NativeState()
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
state |= (states::FOCUSABLE | states::SELECTABLE);
|
||||
if (FocusMgr()->IsFocused(this))
|
||||
state |= states::FOCUSED;
|
||||
|
||||
return state;
|
||||
}
|
||||
@ -563,6 +594,56 @@ nsXULMenuitemAccessible::ActionCount()
|
||||
return 1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULMenuitemAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsXULMenuitemAccessible::IsActiveWidget() const
|
||||
{
|
||||
// Parent menu item is a widget, it's active when its popup is open.
|
||||
nsIContent* menuPopupContent = mContent->GetFirstChild();
|
||||
if (menuPopupContent) {
|
||||
nsMenuPopupFrame* menuPopupFrame =
|
||||
do_QueryFrame(menuPopupContent->GetPrimaryFrame());
|
||||
return menuPopupFrame && menuPopupFrame->IsOpen();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULMenuitemAccessible::AreItemsOperable() const
|
||||
{
|
||||
// Parent menu item is a widget, its items are operable when its popup is open.
|
||||
nsIContent* menuPopupContent = mContent->GetFirstChild();
|
||||
if (menuPopupContent) {
|
||||
nsMenuPopupFrame* menuPopupFrame =
|
||||
do_QueryFrame(menuPopupContent->GetPrimaryFrame());
|
||||
return menuPopupFrame && menuPopupFrame->IsOpen();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsXULMenuitemAccessible::ContainerWidget() const
|
||||
{
|
||||
nsMenuFrame* menuFrame = do_QueryFrame(GetFrame());
|
||||
if (menuFrame) {
|
||||
nsMenuParent* menuParent = menuFrame->GetMenuParent();
|
||||
if (menuParent) {
|
||||
if (menuParent->IsMenuBar()) // menubar menu
|
||||
return mParent;
|
||||
|
||||
// a menupoup or parent menu item
|
||||
if (menuParent->IsMenu())
|
||||
return mParent;
|
||||
|
||||
// otherwise it's different kind of popups (like panel or tooltip), it
|
||||
// shouldn't be a real case.
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULMenuSeparatorAccessible
|
||||
@ -617,7 +698,11 @@ nsXULMenuSeparatorAccessible::ActionCount()
|
||||
nsXULMenupopupAccessible::
|
||||
nsXULMenupopupAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
|
||||
nsXULSelectableAccessible(aContent, aShell)
|
||||
{
|
||||
{
|
||||
nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
|
||||
if (menuPopupFrame && menuPopupFrame->IsMenu())
|
||||
mFlags |= eMenuPopupAccessible;
|
||||
|
||||
// May be the anonymous <menupopup> inside <menulist> (a combobox)
|
||||
mSelectControl = do_QueryInterface(mContent->GetParent());
|
||||
}
|
||||
@ -689,6 +774,65 @@ nsXULMenupopupAccessible::NativeRole()
|
||||
return nsIAccessibleRole::ROLE_MENUPOPUP;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULMenupopupAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsXULMenupopupAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULMenupopupAccessible::IsActiveWidget() const
|
||||
{
|
||||
// If menupopup is a widget (the case of context menus) then active when open.
|
||||
nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
|
||||
return menuPopupFrame && menuPopupFrame->IsOpen();
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULMenupopupAccessible::AreItemsOperable() const
|
||||
{
|
||||
nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
|
||||
return menuPopupFrame && menuPopupFrame->IsOpen();
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsXULMenupopupAccessible::ContainerWidget() const
|
||||
{
|
||||
nsDocAccessible* document = GetDocAccessible();
|
||||
|
||||
nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
|
||||
while (menuPopupFrame) {
|
||||
nsAccessible* menuPopup =
|
||||
document->GetAccessible(menuPopupFrame->GetContent());
|
||||
if (!menuPopup) // shouldn't be a real case
|
||||
return nsnull;
|
||||
|
||||
nsMenuFrame* menuFrame = menuPopupFrame->GetParentMenu();
|
||||
if (!menuFrame) // context menu or popups
|
||||
return nsnull;
|
||||
|
||||
nsMenuParent* menuParent = menuFrame->GetMenuParent();
|
||||
if (!menuParent) // menulist or menubutton
|
||||
return menuPopup->Parent();
|
||||
|
||||
if (menuParent->IsMenuBar()) { // menubar menu
|
||||
nsMenuBarFrame* menuBarFrame = static_cast<nsMenuBarFrame*>(menuParent);
|
||||
return document->GetAccessible(menuBarFrame->GetContent());
|
||||
}
|
||||
|
||||
// different kind of popups like panel or tooltip
|
||||
if (!menuParent->IsMenu())
|
||||
return nsnull;
|
||||
|
||||
menuPopupFrame = static_cast<nsMenuPopupFrame*>(menuParent);
|
||||
}
|
||||
|
||||
NS_NOTREACHED("Shouldn't be a real case.");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULMenubarAccessible
|
||||
@ -724,3 +868,32 @@ nsXULMenubarAccessible::NativeRole()
|
||||
return nsIAccessibleRole::ROLE_MENUBAR;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULMenubarAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsXULMenubarAccessible::IsActiveWidget() const
|
||||
{
|
||||
nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
|
||||
return menuBarFrame && menuBarFrame->IsActive();
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULMenubarAccessible::AreItemsOperable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsXULMenubarAccessible::CurrentItem()
|
||||
{
|
||||
nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
|
||||
if (menuBarFrame) {
|
||||
nsMenuFrame* menuFrame = menuBarFrame->GetCurrentMenuItem();
|
||||
if (menuFrame) {
|
||||
nsIContent* menuItemNode = menuFrame->GetContent();
|
||||
return GetAccService()->GetAccessible(menuItemNode);
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
@ -65,6 +65,9 @@ public:
|
||||
virtual bool SelectAll();
|
||||
virtual bool UnselectAll();
|
||||
|
||||
// Widgets
|
||||
virtual nsAccessible* CurrentItem();
|
||||
|
||||
protected:
|
||||
// nsIDOMXULMultiSelectControlElement inherits from this, so we'll always have
|
||||
// one of these if the widget is valid and not defunct
|
||||
@ -100,6 +103,11 @@ public:
|
||||
virtual PRUint8 ActionCount();
|
||||
virtual KeyBinding AccessKey() const;
|
||||
virtual KeyBinding KeyboardShortcut() const;
|
||||
|
||||
// Widgets
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -136,6 +144,13 @@ public:
|
||||
virtual nsresult GetNameInternal(nsAString& aName);
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual PRUint64 NativeState();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -150,6 +165,11 @@ public:
|
||||
virtual nsresult GetNameInternal(nsAString& aName);
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual PRUint64 NativeState();
|
||||
|
||||
// Widget
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
virtual nsAccessible* CurrentItem();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
#include "nsXULSliderAccessible.h"
|
||||
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "States.h"
|
||||
|
||||
#include "nsIDOMDocument.h"
|
||||
@ -82,7 +83,7 @@ nsXULSliderAccessible::NativeState()
|
||||
if (frame && frame->IsFocusable())
|
||||
states |= states::FOCUSABLE;
|
||||
|
||||
if (gLastFocusedNode == mContent)
|
||||
if (FocusMgr()->IsFocused(this))
|
||||
states |= states::FOCUSED;
|
||||
|
||||
return states;
|
||||
|
@ -46,13 +46,16 @@
|
||||
#include "Relation.h"
|
||||
#include "States.h"
|
||||
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIAccessibleRelation.h"
|
||||
#include "nsIAutoCompleteInput.h"
|
||||
#include "nsIAutoCompletePopup.h"
|
||||
#include "nsIDOMXULElement.h"
|
||||
#include "nsIDOMXULMenuListElement.h"
|
||||
#include "nsIDOMXULMultSelectCntrlEl.h"
|
||||
#include "nsIDOMXULTreeElement.h"
|
||||
#include "nsITreeSelection.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
@ -70,6 +73,14 @@ nsXULTreeAccessible::
|
||||
|
||||
NS_ASSERTION(mTree && mTreeView, "Can't get mTree or mTreeView!\n");
|
||||
|
||||
nsIContent* parentContent = mContent->GetParent();
|
||||
if (parentContent) {
|
||||
nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
|
||||
do_QueryInterface(parentContent);
|
||||
if (autoCompletePopupElm)
|
||||
mFlags |= eAutoCompletePopupAccessible;
|
||||
}
|
||||
|
||||
mAccessibleCache.Init(kDefaultTreeCacheSize);
|
||||
}
|
||||
|
||||
@ -107,9 +118,6 @@ nsXULTreeAccessible::NativeState()
|
||||
// readonly state
|
||||
state |= states::READONLY;
|
||||
|
||||
// remove focusable and focused states since tree items are focusable for AT
|
||||
state &= ~(states::FOCUSABLE | states::FOCUSED);
|
||||
|
||||
// multiselectable state.
|
||||
nsCOMPtr<nsITreeSelection> selection;
|
||||
mTreeView->GetSelection(getter_AddRefs(selection));
|
||||
@ -201,27 +209,6 @@ nsXULTreeAccessible::NativeRole()
|
||||
static_cast<PRUint32>(nsIAccessibleRole::ROLE_LIST);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULTreeAccessible: nsIAccessible implementation
|
||||
|
||||
nsAccessible*
|
||||
nsXULTreeAccessible::FocusedChild()
|
||||
{
|
||||
if (gLastFocusedNode != mContent)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
|
||||
do_QueryInterface(mContent);
|
||||
if (multiSelect) {
|
||||
PRInt32 row = -1;
|
||||
multiSelect->GetCurrentIndex(&row);
|
||||
if (row >= 0)
|
||||
return GetTreeItemAccessible(row);
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULTreeAccessible: nsAccessible implementation (DON'T put methods here)
|
||||
|
||||
@ -277,6 +264,21 @@ nsXULTreeAccessible::IsSelect()
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsXULTreeAccessible::CurrentItem()
|
||||
{
|
||||
nsCOMPtr<nsITreeSelection> selection;
|
||||
mTreeView->GetSelection(getter_AddRefs(selection));
|
||||
if (selection) {
|
||||
PRInt32 currentIndex = -1;
|
||||
selection->GetCurrentIndex(¤tIndex);
|
||||
if (currentIndex >= 0)
|
||||
return GetTreeItemAccessible(currentIndex);
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIArray>
|
||||
nsXULTreeAccessible::SelectedItems()
|
||||
{
|
||||
@ -452,6 +454,72 @@ nsXULTreeAccessible::GetChildCount()
|
||||
return childCount;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULTreeAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsXULTreeAccessible::IsWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULTreeAccessible::IsActiveWidget() const
|
||||
{
|
||||
if (IsAutoCompletePopup()) {
|
||||
nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
|
||||
do_QueryInterface(mContent->GetParent());
|
||||
|
||||
if (autoCompletePopupElm) {
|
||||
PRBool isOpen = PR_FALSE;
|
||||
autoCompletePopupElm->GetPopupOpen(&isOpen);
|
||||
return isOpen;
|
||||
}
|
||||
}
|
||||
return FocusMgr()->HasDOMFocus(mContent);
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULTreeAccessible::AreItemsOperable() const
|
||||
{
|
||||
if (IsAutoCompletePopup()) {
|
||||
nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
|
||||
do_QueryInterface(mContent->GetParent());
|
||||
|
||||
if (autoCompletePopupElm) {
|
||||
PRBool isOpen = PR_FALSE;
|
||||
autoCompletePopupElm->GetPopupOpen(&isOpen);
|
||||
return isOpen;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsXULTreeAccessible::ContainerWidget() const
|
||||
{
|
||||
if (IsAutoCompletePopup()) {
|
||||
// This works for XUL autocompletes. It doesn't work for HTML forms
|
||||
// autocomplete because of potential crossprocess calls (when autocomplete
|
||||
// lives in content process while popup lives in chrome process). If that's
|
||||
// a problem then rethink Widgets interface.
|
||||
nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
|
||||
do_QueryInterface(mContent->GetParent());
|
||||
if (menuListElm) {
|
||||
nsCOMPtr<nsIDOMNode> inputElm;
|
||||
menuListElm->GetInputField(getter_AddRefs(inputElm));
|
||||
if (inputElm) {
|
||||
nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm);
|
||||
if (inputNode) {
|
||||
nsAccessible* input = GetAccService()->GetAccessible(inputNode);
|
||||
return input ? input->ContainerWidget() : nsnull;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULTreeAccessible: public implementation
|
||||
|
||||
@ -662,20 +730,7 @@ NS_IMPL_RELEASE_INHERITED(nsXULTreeItemAccessibleBase, nsAccessible)
|
||||
nsAccessible*
|
||||
nsXULTreeItemAccessibleBase::FocusedChild()
|
||||
{
|
||||
if (gLastFocusedNode != mContent)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
|
||||
do_QueryInterface(mContent);
|
||||
|
||||
if (multiSelect) {
|
||||
PRInt32 row = -1;
|
||||
multiSelect->GetCurrentIndex(&row);
|
||||
if (row == mRow)
|
||||
return this;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
return FocusMgr()->FocusedAccessible() == this ? this : nsnull;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -944,15 +999,8 @@ nsXULTreeItemAccessibleBase::NativeState()
|
||||
}
|
||||
|
||||
// focused state
|
||||
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
|
||||
do_QueryInterface(mContent);
|
||||
if (multiSelect) {
|
||||
PRInt32 currentIndex;
|
||||
multiSelect->GetCurrentIndex(¤tIndex);
|
||||
if (currentIndex == mRow) {
|
||||
state |= states::FOCUSED;
|
||||
}
|
||||
}
|
||||
if (FocusMgr()->IsFocused(this))
|
||||
state |= states::FOCUSED;
|
||||
|
||||
// invisible state
|
||||
PRInt32 firstVisibleRow, lastVisibleRow;
|
||||
@ -970,6 +1018,15 @@ nsXULTreeItemAccessibleBase::IndexInParent() const
|
||||
return mParent ? mParent->ContentChildCount() + mRow : -1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULTreeItemAccessibleBase: Widgets
|
||||
|
||||
nsAccessible*
|
||||
nsXULTreeItemAccessibleBase::ContainerWidget() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULTreeItemAccessibleBase: nsAccessible protected methods
|
||||
|
||||
|
@ -86,7 +86,6 @@ public:
|
||||
virtual PRUint64 NativeState();
|
||||
virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild);
|
||||
virtual nsAccessible* FocusedChild();
|
||||
|
||||
virtual nsAccessible* GetChildAt(PRUint32 aIndex);
|
||||
virtual PRInt32 GetChildCount();
|
||||
@ -102,6 +101,14 @@ public:
|
||||
virtual bool SelectAll();
|
||||
virtual bool UnselectAll();
|
||||
|
||||
// Widgets
|
||||
virtual bool IsWidget() const;
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
virtual nsAccessible* CurrentItem();
|
||||
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
|
||||
// nsXULTreeAccessible
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULTREEACCESSIBLE_IMPL_CID)
|
||||
@ -209,6 +216,9 @@ public:
|
||||
// ActionAccessible
|
||||
virtual PRUint8 ActionCount();
|
||||
|
||||
// Widgets
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
|
||||
// nsXULTreeItemAccessibleBase
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULTREEITEMBASEACCESSIBLE_IMPL_CID)
|
||||
|
||||
|
@ -72,19 +72,18 @@ _TEST_FILES =\
|
||||
longdesc_src.html \
|
||||
actions.js \
|
||||
attributes.js \
|
||||
autocomplete.js \
|
||||
common.js \
|
||||
events.js \
|
||||
grid.js \
|
||||
layout.js \
|
||||
name.js \
|
||||
nsIAccessible_selects.js \
|
||||
relations.js \
|
||||
role.js \
|
||||
selectable.js \
|
||||
states.js \
|
||||
table.js \
|
||||
value.js \
|
||||
test_aria_activedescendant.html \
|
||||
test_aria_role_article.html \
|
||||
test_aria_role_equation.html \
|
||||
test_aria_roles.html \
|
||||
@ -98,7 +97,6 @@ _TEST_FILES =\
|
||||
test_elm_listbox.xul \
|
||||
test_elm_nsApplicationAcc.html \
|
||||
test_elm_plugin.html \
|
||||
test_nsIAccessible_selects.html \
|
||||
test_nsIAccessibleDocument.html \
|
||||
test_nsIAccessibleImage.html \
|
||||
test_nsIAccessNode_utils.html \
|
||||
|
@ -55,6 +55,7 @@ _TEST_FILES =\
|
||||
test_keys.html \
|
||||
test_link.html \
|
||||
test_media.html \
|
||||
test_select.html \
|
||||
test_tree.xul \
|
||||
test_treegrid.xul \
|
||||
$(NULL)
|
||||
|
103
accessible/tests/mochitest/actions/test_select.html
Normal file
103
accessible/tests/mochitest/actions/test_select.html
Normal file
@ -0,0 +1,103 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>nsIAccessible actions testing for HTML select</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../actions.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
//gA11yEventDumpToConsole = true; // debugging
|
||||
function doTest()
|
||||
{
|
||||
var actionsArray = [
|
||||
{
|
||||
ID: "lb_apple",
|
||||
actionIndex: 0,
|
||||
actionName: "select",
|
||||
events: CLICK_EVENTS,
|
||||
eventSeq: [
|
||||
new focusChecker("lb_apple")
|
||||
]
|
||||
},
|
||||
{
|
||||
ID: "combobox",
|
||||
actionIndex: 0,
|
||||
actionName: "open",
|
||||
events: CLICK_EVENTS,
|
||||
eventSeq: [
|
||||
new focusChecker("cb_orange")
|
||||
]
|
||||
},
|
||||
{
|
||||
ID: "cb_apple",
|
||||
actionIndex: 0,
|
||||
actionName: "select",
|
||||
events: CLICK_EVENTS,
|
||||
eventSeq: [
|
||||
new focusChecker("combobox")
|
||||
]
|
||||
},
|
||||
{
|
||||
ID: "combobox",
|
||||
actionIndex: 0,
|
||||
actionName: "open",
|
||||
events: CLICK_EVENTS,
|
||||
eventSeq: [
|
||||
new focusChecker("cb_apple")
|
||||
]
|
||||
},
|
||||
{
|
||||
ID: "combobox",
|
||||
actionIndex: 0,
|
||||
actionName: "close",
|
||||
events: CLICK_EVENTS,
|
||||
eventSeq: [
|
||||
new focusChecker("combobox")
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
testActions(actionsArray);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
|
||||
title="Rework accessible focus handling">
|
||||
Mozilla Bug 673958
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<select id="listbox" size="2">
|
||||
<option id="lb_orange">orange</option>
|
||||
<option id="lb_apple">apple</option>
|
||||
</select>
|
||||
|
||||
<select id="combobox">
|
||||
<option id="cb_orange">orange</option>
|
||||
<option id="cb_apple">apple</option>
|
||||
</select>
|
||||
</body>
|
||||
</html>
|
@ -26,14 +26,10 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Accessible tree testers
|
||||
|
||||
function focusChecker(aAcc, aStates)
|
||||
function stateFocusChecker(aAcc, aStates)
|
||||
{
|
||||
this.type = EVENT_FOCUS;
|
||||
this.target = aAcc;
|
||||
this.getID = function focusChecker_getID()
|
||||
{
|
||||
return "focus handling";
|
||||
}
|
||||
this.__proto__ = new focusChecker(aAcc);
|
||||
|
||||
this.check = function focusChecker_check(aEvent)
|
||||
{
|
||||
var states = aStates ? aStates : 0;
|
||||
@ -45,6 +41,7 @@
|
||||
// Test
|
||||
|
||||
// gA11yEventDumpID = "debug";
|
||||
//gA11yEventDumpToConsole = true; // debug
|
||||
|
||||
function doTestActions()
|
||||
{
|
||||
@ -64,7 +61,7 @@
|
||||
events: CLICK_EVENTS,
|
||||
targetID: treeBodyNode,
|
||||
eventSeq: [
|
||||
new focusChecker(expandedTreeItem, STATE_EXPANDED)
|
||||
new stateFocusChecker(expandedTreeItem, STATE_EXPANDED)
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -70,6 +70,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
function doTest()
|
||||
{
|
||||
|
216
accessible/tests/mochitest/autocomplete.js
Normal file
216
accessible/tests/mochitest/autocomplete.js
Normal file
@ -0,0 +1,216 @@
|
||||
|
||||
const nsISupports = Components.interfaces.nsISupports;
|
||||
const nsIAutoCompleteResult = Components.interfaces.nsIAutoCompleteResult;
|
||||
const nsIAutoCompleteSearch = Components.interfaces.nsIAutoCompleteSearch;
|
||||
const nsIFactory = Components.interfaces.nsIFactory;
|
||||
const nsIUUIDGenerator = Components.interfaces.nsIUUIDGenerator;
|
||||
const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
|
||||
|
||||
var gDefaultAutoCompleteSearch = null;
|
||||
|
||||
/**
|
||||
* Register 'test-a11y-search' AutoCompleteSearch.
|
||||
*
|
||||
* @param aValues [in] set of possible results values
|
||||
* @param aComments [in] set of possible results descriptions
|
||||
*/
|
||||
function initAutoComplete(aValues, aComments)
|
||||
{
|
||||
var allResults = new ResultsHeap(aValues, aComments);
|
||||
gDefaultAutoCompleteSearch =
|
||||
new AutoCompleteSearch("test-a11y-search", allResults);
|
||||
registerAutoCompleteSearch(gDefaultAutoCompleteSearch,
|
||||
"Accessibility Test AutoCompleteSearch");
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister 'test-a11y-search' AutoCompleteSearch.
|
||||
*/
|
||||
function shutdownAutoComplete()
|
||||
{
|
||||
unregisterAutoCompleteSearch(gDefaultAutoCompleteSearch);
|
||||
gDefaultAutoCompleteSearch.cid = null;
|
||||
gDefaultAutoCompleteSearch = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register the given AutoCompleteSearch.
|
||||
*
|
||||
* @param aSearch [in] AutoCompleteSearch object
|
||||
* @param aDescription [in] description of the search object
|
||||
*/
|
||||
function registerAutoCompleteSearch(aSearch, aDescription)
|
||||
{
|
||||
var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
|
||||
|
||||
var uuidGenerator = Components.classes["@mozilla.org/uuid-generator;1"].
|
||||
getService(nsIUUIDGenerator);
|
||||
var cid = uuidGenerator.generateUUID();
|
||||
|
||||
var componentManager = Components.manager.QueryInterface(nsIComponentRegistrar);
|
||||
componentManager.registerFactory(cid, aDescription, name, aSearch);
|
||||
|
||||
// Keep the id on the object so we can unregister later.
|
||||
aSearch.cid = cid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the given AutoCompleteSearch.
|
||||
*/
|
||||
function unregisterAutoCompleteSearch(aSearch)
|
||||
{
|
||||
var componentManager = Components.manager.QueryInterface(nsIComponentRegistrar);
|
||||
componentManager.unregisterFactory(aSearch.cid, aSearch);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A container to keep all possible results of autocomplete search.
|
||||
*/
|
||||
function ResultsHeap(aValues, aComments)
|
||||
{
|
||||
this.values = aValues;
|
||||
this.comments = aComments;
|
||||
}
|
||||
|
||||
ResultsHeap.prototype =
|
||||
{
|
||||
constructor: ResultsHeap,
|
||||
|
||||
/**
|
||||
* Return AutoCompleteResult for the given search string.
|
||||
*/
|
||||
getAutoCompleteResultFor: function(aSearchString)
|
||||
{
|
||||
var values = [], comments = [];
|
||||
for (var idx = 0; idx < this.values.length; idx++) {
|
||||
if (this.values[idx].indexOf(aSearchString) != -1) {
|
||||
values.push(this.values[idx]);
|
||||
comments.push(this.comments[idx]);
|
||||
}
|
||||
}
|
||||
return new AutoCompleteResult(values, comments);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nsIAutoCompleteSearch implementation.
|
||||
*
|
||||
* @param aName [in] the name of autocomplete search
|
||||
* @param aAllResults [in] ResultsHeap object
|
||||
*/
|
||||
function AutoCompleteSearch(aName, aAllResults)
|
||||
{
|
||||
this.name = aName;
|
||||
this.allResults = aAllResults;
|
||||
}
|
||||
|
||||
AutoCompleteSearch.prototype =
|
||||
{
|
||||
constructor: AutoCompleteSearch,
|
||||
|
||||
// nsIAutoCompleteSearch implementation
|
||||
startSearch: function(aSearchString, aSearchParam, aPreviousResult,
|
||||
aListener)
|
||||
{
|
||||
var result = this.allResults.getAutoCompleteResultFor(aSearchString);
|
||||
aListener.onSearchResult(this, result);
|
||||
},
|
||||
|
||||
stopSearch: function() {},
|
||||
|
||||
// nsISupports implementation
|
||||
QueryInterface: function(iid)
|
||||
{
|
||||
if (iid.equals(nsISupports) ||
|
||||
iid.equals(nsIFactory) ||
|
||||
iid.equals(nsIAutoCompleteSearch))
|
||||
return this;
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
// nsIFactory implementation
|
||||
createInstance: function(outer, iid)
|
||||
{
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
||||
// Search name. Used by AutoCompleteController.
|
||||
name: null,
|
||||
|
||||
// Results heap.
|
||||
allResults: null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nsIAutoCompleteResult implementation.
|
||||
*/
|
||||
function AutoCompleteResult(aValues, aComments)
|
||||
{
|
||||
this.values = aValues;
|
||||
this.comments = aComments;
|
||||
|
||||
if (this.values.length > 0)
|
||||
this.searchResult = nsIAutoCompleteResult.RESULT_SUCCESS;
|
||||
else
|
||||
this.searchResult = nsIAutoCompleteResult.NOMATCH;
|
||||
}
|
||||
|
||||
AutoCompleteResult.prototype =
|
||||
{
|
||||
constructor: AutoCompleteResult,
|
||||
|
||||
searchString: "",
|
||||
searchResult: null,
|
||||
|
||||
defaultIndex: 0,
|
||||
|
||||
get matchCount()
|
||||
{
|
||||
return this.values.length;
|
||||
},
|
||||
|
||||
getValueAt: function(aIndex)
|
||||
{
|
||||
return this.values[aIndex];
|
||||
},
|
||||
|
||||
getLabelAt: function(aIndex)
|
||||
{
|
||||
return this.getValueAt(aIndex);
|
||||
},
|
||||
|
||||
getCommentAt: function(aIndex)
|
||||
{
|
||||
return this.comments[aIndex];
|
||||
},
|
||||
|
||||
getStyleAt: function(aIndex)
|
||||
{
|
||||
return null;
|
||||
},
|
||||
|
||||
getImageAt: function(aIndex)
|
||||
{
|
||||
return "";
|
||||
},
|
||||
|
||||
removeValueAt: function (aRowIndex, aRemoveFromDb) {},
|
||||
|
||||
// nsISupports implementation
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(nsISupports) ||
|
||||
iid.equals(nsIAutoCompleteResult))
|
||||
return this;
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
// Data
|
||||
values: null,
|
||||
comments: null
|
||||
}
|
@ -13,16 +13,14 @@ const nsIAccessibleTextChangeEvent =
|
||||
|
||||
const nsIAccessibleStates = Components.interfaces.nsIAccessibleStates;
|
||||
const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
|
||||
const nsIAccessibleTypes = Components.interfaces.nsIAccessibleTypes;
|
||||
const nsIAccessibleScrollType = Components.interfaces.nsIAccessibleScrollType;
|
||||
const nsIAccessibleCoordinateType = Components.interfaces.nsIAccessibleCoordinateType;
|
||||
|
||||
const nsIAccessibleRelation = Components.interfaces.nsIAccessibleRelation;
|
||||
|
||||
const nsIAccessNode = Components.interfaces.nsIAccessNode;
|
||||
const nsIAccessible = Components.interfaces.nsIAccessible;
|
||||
|
||||
const nsIAccessibleCoordinateType =
|
||||
Components.interfaces.nsIAccessibleCoordinateType;
|
||||
|
||||
const nsIAccessibleDocument = Components.interfaces.nsIAccessibleDocument;
|
||||
const nsIAccessibleApplication = Components.interfaces.nsIAccessibleApplication;
|
||||
|
||||
@ -62,6 +60,8 @@ const WIN = (navigator.platform.indexOf("Win") != -1)? true : false;
|
||||
|
||||
const STATE_BUSY = nsIAccessibleStates.STATE_BUSY;
|
||||
|
||||
const SCROLL_TYPE_ANYWHERE = nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE;
|
||||
|
||||
const kEmbedChar = String.fromCharCode(0xfffc);
|
||||
|
||||
const kDiscBulletText = String.fromCharCode(0x2022) + " ";
|
||||
@ -69,9 +69,10 @@ const kCircleBulletText = String.fromCharCode(0x25e6) + " ";
|
||||
const kSquareBulletText = String.fromCharCode(0x25aa) + " ";
|
||||
|
||||
/**
|
||||
* nsIAccessibleRetrieval, initialized when test is loaded.
|
||||
* nsIAccessibleRetrieval service.
|
||||
*/
|
||||
var gAccRetrieval = null;
|
||||
var gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
|
||||
getService(nsIAccessibleRetrieval);
|
||||
|
||||
/**
|
||||
* Invokes the given function when document is loaded and focused. Preferable
|
||||
@ -80,14 +81,15 @@ var gAccRetrieval = null;
|
||||
*
|
||||
* @param aFunc the function to invoke
|
||||
*/
|
||||
function addA11yLoadEvent(aFunc)
|
||||
function addA11yLoadEvent(aFunc, aWindow)
|
||||
{
|
||||
function waitForDocLoad()
|
||||
{
|
||||
window.setTimeout(
|
||||
function()
|
||||
{
|
||||
var accDoc = getAccessible(document);
|
||||
var targetDocument = aWindow ? aWindow.document : document;
|
||||
var accDoc = getAccessible(targetDocument);
|
||||
var state = {};
|
||||
accDoc.getState(state, {});
|
||||
if (state.value & STATE_BUSY)
|
||||
@ -99,7 +101,7 @@ function addA11yLoadEvent(aFunc)
|
||||
);
|
||||
}
|
||||
|
||||
SimpleTest.waitForFocus(waitForDocLoad);
|
||||
SimpleTest.waitForFocus(waitForDocLoad, aWindow);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -589,21 +591,15 @@ function prettyName(aIdentifier)
|
||||
msg += "defunct";
|
||||
}
|
||||
|
||||
if (acc) {
|
||||
var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
|
||||
var match = exp.exec(acc.valueOf());
|
||||
if (match)
|
||||
msg += ", address: " + match[1];
|
||||
else
|
||||
msg += ", address: " + acc.valueOf();
|
||||
}
|
||||
if (acc)
|
||||
msg += ", address: " + getObjAddress(acc);
|
||||
msg += "]";
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (aIdentifier instanceof nsIDOMNode)
|
||||
return getNodePrettyName(aIdentifier);
|
||||
return "[ " + getNodePrettyName(aIdentifier) + " ]";
|
||||
|
||||
return " '" + aIdentifier + "' ";
|
||||
}
|
||||
@ -615,27 +611,30 @@ function prettyName(aIdentifier)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Accessible general
|
||||
|
||||
function initialize()
|
||||
{
|
||||
gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
|
||||
getService(nsIAccessibleRetrieval);
|
||||
}
|
||||
|
||||
addLoadEvent(initialize);
|
||||
|
||||
function getNodePrettyName(aNode)
|
||||
{
|
||||
try {
|
||||
if (aNode.nodeType == nsIDOMNode.DOCUMENT_NODE)
|
||||
return " 'document node' ";
|
||||
var tag = "";
|
||||
if (aNode.nodeType == nsIDOMNode.DOCUMENT_NODE) {
|
||||
tag = "document";
|
||||
} else {
|
||||
tag = aNode.localName;
|
||||
if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
|
||||
tag += "@id=\"" + aNode.getAttribute("id") + "\"";
|
||||
}
|
||||
|
||||
var name = " '" + aNode.localName;
|
||||
if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
|
||||
name += "@id='" + aNode.getAttribute("id") + "'";
|
||||
|
||||
name += " node' "
|
||||
return name;
|
||||
return "'" + tag + " node', address: " + getObjAddress(aNode);
|
||||
} catch (e) {
|
||||
return "' no node info '";
|
||||
}
|
||||
}
|
||||
|
||||
function getObjAddress(aObj)
|
||||
{
|
||||
var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
|
||||
var match = exp.exec(aObj.valueOf());
|
||||
if (match)
|
||||
return match[1];
|
||||
|
||||
return aObj.valueOf();
|
||||
}
|
||||
|
@ -168,6 +168,9 @@ const DO_NOT_FINISH_TEST = 1;
|
||||
* // * DOM event phase (false - bubbling). *
|
||||
* // phase getter: function() {},
|
||||
* //
|
||||
* // * Callback, called to match handled event. *
|
||||
* // match : function() {},
|
||||
* //
|
||||
* // * Callback, called when event is handled
|
||||
* // check: function(aEvent) {},
|
||||
* //
|
||||
@ -175,7 +178,13 @@ const DO_NOT_FINISH_TEST = 1;
|
||||
* // getID: function() {},
|
||||
* //
|
||||
* // * Event that don't have predefined order relative other events. *
|
||||
* // async getter: function() {}
|
||||
* // async getter: function() {},
|
||||
* //
|
||||
* // * Event that is not expected. *
|
||||
* // unexpected getter: function() {},
|
||||
* //
|
||||
* // * No other event of the same type is not allowed. *
|
||||
* // unique getter: function() {}
|
||||
* // };
|
||||
* eventSeq getter() {},
|
||||
*
|
||||
@ -316,7 +325,7 @@ function eventQueue(aEventType)
|
||||
}
|
||||
|
||||
// Check in timeout invoker didn't fire registered events.
|
||||
window.setTimeout(function(aQueue) { aQueue.processNextInvoker(); }, 500,
|
||||
window.setTimeout(function(aQueue) { aQueue.processNextInvoker(); }, 100,
|
||||
this);
|
||||
}
|
||||
|
||||
@ -344,11 +353,11 @@ function eventQueue(aEventType)
|
||||
var idx = 0;
|
||||
for (; idx < this.mEventSeq.length; idx++) {
|
||||
if (this.isEventExpected(idx) && (invoker.wasCaught[idx] == true) &&
|
||||
this.isAlreadyCaught(idx, aEvent)) {
|
||||
this.isSameEvent(idx, aEvent)) {
|
||||
|
||||
var msg = "Doubled event { event type: " +
|
||||
this.getEventTypeAsString(idx) + ", target: " +
|
||||
prettyName(this.getEventTarget(idx)) + "} in test with ID = '" +
|
||||
this.getEventTargetDescr(idx) + "} in test with ID = '" +
|
||||
this.getEventID(idx) + "'.";
|
||||
ok(false, msg);
|
||||
}
|
||||
@ -445,16 +454,49 @@ function eventQueue(aEventType)
|
||||
|
||||
this.setEventHandler = function eventQueue_setEventHandler(aInvoker)
|
||||
{
|
||||
// Create unique event sequence concatenating expected and unexpected
|
||||
// Create unified event sequence concatenating expected and unexpected
|
||||
// events.
|
||||
this.mEventSeq = ("eventSeq" in aInvoker) ?
|
||||
aInvoker.eventSeq :
|
||||
[ new invokerChecker(this.mDefEventType, aInvoker.DOMNode) ];
|
||||
|
||||
for (var idx = 0; idx < this.mEventSeq.length; idx++) {
|
||||
this.mEventSeq[idx].unexpected = false;
|
||||
var len = this.mEventSeq.length;
|
||||
for (var idx = 0; idx < len; idx++) {
|
||||
var seqItem = this.mEventSeq[idx];
|
||||
// Allow unexpected events in primary event sequence.
|
||||
if (!("unexpected" in this.mEventSeq[idx]))
|
||||
seqItem.unexpected = false;
|
||||
|
||||
if (!("async" in this.mEventSeq[idx]))
|
||||
this.mEventSeq[idx].async = false;
|
||||
seqItem.async = false;
|
||||
|
||||
// If the event is of unique type (regardless whether it's expected or
|
||||
// not) then register additional unexpected event that matches to any
|
||||
// event of the same type with any target different from registered
|
||||
// expected events.
|
||||
if (("unique" in seqItem) && seqItem.unique) {
|
||||
var uniquenessChecker = {
|
||||
type: seqItem.type,
|
||||
unexpected: true,
|
||||
match: function uniquenessChecker_match(aEvent)
|
||||
{
|
||||
// The handled event is matched if its target doesn't match to any
|
||||
// registered expected event.
|
||||
var matched = true;
|
||||
for (var idx = 0; idx < this.queue.mEventSeq.length; idx++) {
|
||||
if (this.queue.isEventExpected(idx) &&
|
||||
this.queue.compareEvents(idx, aEvent)) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return matched;
|
||||
},
|
||||
targetDescr: "any target different from expected events",
|
||||
queue: this
|
||||
};
|
||||
this.mEventSeq.push(uniquenessChecker);
|
||||
}
|
||||
}
|
||||
|
||||
var unexpectedSeq = aInvoker.unexpectedEventSeq;
|
||||
@ -483,7 +525,7 @@ function eventQueue(aEventType)
|
||||
msg += " unexpected";
|
||||
|
||||
msg += ": event type: " + this.getEventTypeAsString(idx) +
|
||||
", target: " + this.getEventTargetDescr(idx);
|
||||
", target: " + this.getEventTargetDescr(idx, true);
|
||||
|
||||
gLogger.logToConsole(msg);
|
||||
gLogger.logToDOM(msg, true);
|
||||
@ -492,6 +534,10 @@ function eventQueue(aEventType)
|
||||
if (typeof eventType == "string") {
|
||||
// DOM event
|
||||
var target = this.getEventTarget(idx);
|
||||
if (!target) {
|
||||
ok(false, "no target for DOM event!");
|
||||
return;
|
||||
}
|
||||
var phase = this.getEventPhase(idx);
|
||||
target.ownerDocument.addEventListener(eventType, this, phase);
|
||||
|
||||
@ -540,10 +586,19 @@ function eventQueue(aEventType)
|
||||
return this.mEventSeq[aIdx].target;
|
||||
}
|
||||
|
||||
this.getEventTargetDescr = function eventQueue_getEventTargetDescr(aIdx)
|
||||
this.getEventTargetDescr =
|
||||
function eventQueue_getEventTargetDescr(aIdx, aDontForceTarget)
|
||||
{
|
||||
var descr = this.mEventSeq[aIdx].targetDescr;
|
||||
return descr ? descr : "no target description";
|
||||
if (descr)
|
||||
return descr;
|
||||
|
||||
if (aDontForceTarget)
|
||||
return "no target description";
|
||||
|
||||
var target = ("target" in this.mEventSeq[aIdx]) ?
|
||||
this.mEventSeq[aIdx].target : null;
|
||||
return prettyName(target);
|
||||
}
|
||||
|
||||
this.getEventPhase = function eventQueue_getEventPhase(aIdx)
|
||||
@ -574,16 +629,25 @@ function eventQueue(aEventType)
|
||||
return !this.mEventSeq[aIdx].unexpected;
|
||||
}
|
||||
|
||||
this.compareEvents = function eventQueue_compareEvents(aIdx, aEvent)
|
||||
this.compareEventTypes = function eventQueue_compareEventTypes(aIdx, aEvent)
|
||||
{
|
||||
var eventType1 = this.getEventType(aIdx);
|
||||
|
||||
var eventType2 = (aEvent instanceof nsIDOMEvent) ?
|
||||
aEvent.type : aEvent.eventType;
|
||||
|
||||
if (eventType1 != eventType2)
|
||||
return eventType1 == eventType2;
|
||||
}
|
||||
|
||||
this.compareEvents = function eventQueue_compareEvents(aIdx, aEvent)
|
||||
{
|
||||
if (!this.compareEventTypes(aIdx, aEvent))
|
||||
return false;
|
||||
|
||||
// If checker provides "match" function then allow the checker to decide
|
||||
// whether event is matched.
|
||||
if ("match" in this.mEventSeq[aIdx])
|
||||
return this.mEventSeq[aIdx].match(aEvent);
|
||||
|
||||
var target1 = this.getEventTarget(aIdx);
|
||||
if (target1 instanceof nsIAccessible) {
|
||||
var target2 = (aEvent instanceof nsIDOMEvent) ?
|
||||
@ -599,7 +663,7 @@ function eventQueue(aEventType)
|
||||
return target1 == target2;
|
||||
}
|
||||
|
||||
this.isAlreadyCaught = function eventQueue_isAlreadyCaught(aIdx, aEvent)
|
||||
this.isSameEvent = function eventQueue_isSameEvent(aIdx, aEvent)
|
||||
{
|
||||
// We don't have stored info about handled event other than its type and
|
||||
// target, thus we should filter text change and state change events since
|
||||
@ -662,11 +726,11 @@ function eventQueue(aEventType)
|
||||
var emphText = "matched ";
|
||||
|
||||
var currType = this.getEventTypeAsString(aExpectedEventIdx);
|
||||
var currTarget = this.getEventTarget(aExpectedEventIdx);
|
||||
var currTargetDescr = this.getEventTargetDescr(aExpectedEventIdx);
|
||||
var consoleMsg = "*****\nEQ matched: " + currType + "\n*****";
|
||||
gLogger.logToConsole(consoleMsg);
|
||||
|
||||
msg += " event, type: " + currType + ", target: " + prettyName(currTarget);
|
||||
msg += " event, type: " + currType + ", target: " + currTargetDescr;
|
||||
|
||||
gLogger.logToDOM(msg, true, emphText);
|
||||
}
|
||||
@ -736,31 +800,44 @@ function sequence()
|
||||
// Event queue invokers
|
||||
|
||||
/**
|
||||
* Invokers defined below take a checker object (or array of checker objects)
|
||||
* implementing 'check' method which will be called when proper event is
|
||||
* handled. Invokers listen default event type registered in event queue object
|
||||
* until it is passed explicetly.
|
||||
* Invokers defined below take a checker object (or array of checker objects).
|
||||
* An invoker listens for default event type registered in event queue object
|
||||
* until its checker is provided.
|
||||
*
|
||||
* Note, checker object or array of checker objects is optional.
|
||||
* Note, you don't need to initialize 'target' and 'type' members of checker
|
||||
* object. The 'target' member will be initialized by invoker object and you are
|
||||
* free to use it in 'check' method.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Click invoker.
|
||||
*/
|
||||
function synthClick(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthClick(aNodeOrID, aCheckerOrEventSeq, aArgs)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq);
|
||||
|
||||
this.invoke = function synthClick_invoke()
|
||||
{
|
||||
// Scroll the node into view, otherwise synth click may fail.
|
||||
if (this.DOMNode instanceof nsIDOMNSHTMLElement)
|
||||
this.DOMNode.scrollIntoView(true);
|
||||
var targetNode = this.DOMNode;
|
||||
if (targetNode instanceof nsIDOMDocument) {
|
||||
targetNode =
|
||||
this.DOMNode.body ? this.DOMNode.body : this.DOMNode.documentElement;
|
||||
}
|
||||
|
||||
synthesizeMouse(this.DOMNode, 1, 1, {});
|
||||
// Scroll the node into view, otherwise synth click may fail.
|
||||
if (targetNode instanceof nsIDOMNSHTMLElement) {
|
||||
targetNode.scrollIntoView(true);
|
||||
} else if (targetNode instanceof nsIDOMXULElement) {
|
||||
var targetAcc = getAccessible(targetNode);
|
||||
targetAcc.scrollTo(SCROLL_TYPE_ANYWHERE);
|
||||
}
|
||||
|
||||
var x = 1, y = 1;
|
||||
if (aArgs && ("where" in aArgs) && aArgs.where == "right") {
|
||||
if (targetNode instanceof nsIDOMNSHTMLElement)
|
||||
x = targetNode.offsetWidth - 1;
|
||||
else if (targetNode instanceof nsIDOMXULElement)
|
||||
x = targetNode.boxObject.width - 1;
|
||||
}
|
||||
synthesizeMouse(targetNode, x, y, aArgs ? aArgs : {});
|
||||
}
|
||||
|
||||
this.finalCheck = function synthClick_finalCheck()
|
||||
@ -771,16 +848,16 @@ function synthClick(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
|
||||
this.getID = function synthClick_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " click";
|
||||
return prettyName(aNodeOrID) + " click";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse move invoker.
|
||||
*/
|
||||
function synthMouseMove(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthMouseMove(aID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
|
||||
this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
|
||||
|
||||
this.invoke = function synthMouseMove_invoke()
|
||||
{
|
||||
@ -790,111 +867,163 @@ function synthMouseMove(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
|
||||
this.getID = function synthMouseMove_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " mouse move";
|
||||
return prettyName(aID) + " mouse move";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General key press invoker.
|
||||
*/
|
||||
function synthKey(aNodeOrID, aKey, aArgs, aCheckerOrEventSeq, aEventType)
|
||||
function synthKey(aNodeOrID, aKey, aArgs, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq);
|
||||
|
||||
this.invoke = function synthKey_invoke()
|
||||
{
|
||||
synthesizeKey(this.mKey, this.mArgs);
|
||||
synthesizeKey(this.mKey, this.mArgs, this.mWindow);
|
||||
}
|
||||
|
||||
this.getID = function synthKey_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " '" + this.mKey + "' key";
|
||||
var key = this.mKey;
|
||||
switch (this.mKey) {
|
||||
case "VK_TAB":
|
||||
key = "tab";
|
||||
break;
|
||||
case "VK_DOWN":
|
||||
key = "down";
|
||||
break;
|
||||
case "VK_UP":
|
||||
key = "up";
|
||||
break;
|
||||
case "VK_LEFT":
|
||||
key = "left";
|
||||
break;
|
||||
case "VK_RIGHT":
|
||||
key = "right";
|
||||
break;
|
||||
case "VK_HOME":
|
||||
key = "home";
|
||||
break;
|
||||
case "VK_ESCAPE":
|
||||
key = "escape";
|
||||
break;
|
||||
case "VK_RETURN":
|
||||
key = "enter";
|
||||
break;
|
||||
}
|
||||
if (aArgs) {
|
||||
if (aArgs.shiftKey)
|
||||
key += " shift";
|
||||
if (aArgs.ctrlKey)
|
||||
key += " ctrl";
|
||||
if (aArgs.altKey)
|
||||
key += " alt";
|
||||
}
|
||||
return prettyName(aNodeOrID) + " '" + key + " ' key";
|
||||
}
|
||||
|
||||
this.mKey = aKey;
|
||||
this.mArgs = aArgs ? aArgs : {};
|
||||
this.mWindow = aArgs ? aArgs.window : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tab key invoker.
|
||||
*/
|
||||
function synthTab(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthTab(aNodeOrID, aCheckerOrEventSeq, aWindow)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_TAB", { shiftKey: false },
|
||||
aCheckerOrEventSeq, aEventType);
|
||||
|
||||
this.getID = function synthTab_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " tab";
|
||||
}
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_TAB",
|
||||
{ shiftKey: false, window: aWindow },
|
||||
aCheckerOrEventSeq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift tab key invoker.
|
||||
*/
|
||||
function synthShiftTab(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthShiftTab(aNodeOrID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_TAB", { shiftKey: true },
|
||||
aCheckerOrEventSeq, aEventType);
|
||||
aCheckerOrEventSeq);
|
||||
}
|
||||
|
||||
this.getID = function synthTabTest_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " shift tab";
|
||||
}
|
||||
/**
|
||||
* Escape key invoker.
|
||||
*/
|
||||
function synthEscapeKey(aNodeOrID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_ESCAPE", null,
|
||||
aCheckerOrEventSeq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Down arrow key invoker.
|
||||
*/
|
||||
function synthDownKey(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthDownKey(aNodeOrID, aCheckerOrEventSeq, aArgs)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_DOWN", null, aCheckerOrEventSeq,
|
||||
aEventType);
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_DOWN", aArgs,
|
||||
aCheckerOrEventSeq);
|
||||
}
|
||||
|
||||
this.getID = function synthDownKey_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " key down";
|
||||
}
|
||||
/**
|
||||
* Up arrow key invoker.
|
||||
*/
|
||||
function synthUpKey(aNodeOrID, aCheckerOrEventSeq, aArgs)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_UP", aArgs,
|
||||
aCheckerOrEventSeq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Right arrow key invoker.
|
||||
*/
|
||||
function synthRightKey(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthRightKey(aNodeOrID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aCheckerOrEventSeq,
|
||||
aEventType);
|
||||
|
||||
this.getID = function synthRightKey_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " key right";
|
||||
}
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aCheckerOrEventSeq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Home key invoker.
|
||||
*/
|
||||
function synthHomeKey(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthHomeKey(aNodeOrID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_HOME", null, aCheckerOrEventSeq,
|
||||
aEventType);
|
||||
|
||||
this.getID = function synthHomeKey_getID()
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_HOME", null, aCheckerOrEventSeq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter key invoker
|
||||
*/
|
||||
function synthEnterKey(aID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthKey(aID, "VK_RETURN", null, aCheckerOrEventSeq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synth alt + down arrow to open combobox.
|
||||
*/
|
||||
function synthOpenComboboxKey(aID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthDownKey(aID, aCheckerOrEventSeq, { altKey: true });
|
||||
|
||||
this.getID = function synthOpenComboboxKey_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " key home";
|
||||
return "open combobox (atl + down arrow) " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus invoker.
|
||||
*/
|
||||
function synthFocus(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthFocus(aNodeOrID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
|
||||
var checkerOfEventSeq =
|
||||
aCheckerOrEventSeq ? aCheckerOrEventSeq : new focusChecker(aNodeOrID);
|
||||
this.__proto__ = new synthAction(aNodeOrID, checkerOfEventSeq);
|
||||
|
||||
this.invoke = function synthFocus_invoke()
|
||||
{
|
||||
if (this.DOMNode instanceof Components.interfaces.nsIDOMNSEditableElement ||
|
||||
if (this.DOMNode instanceof Components.interfaces.nsIDOMNSEditableElement &&
|
||||
this.DOMNode.editor ||
|
||||
this.DOMNode instanceof Components.interfaces.nsIDOMXULTextBoxElement) {
|
||||
this.DOMNode.selectionStart = this.DOMNode.selectionEnd = this.DOMNode.value.length;
|
||||
}
|
||||
@ -910,28 +1039,164 @@ function synthFocus(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
/**
|
||||
* Focus invoker. Focus the HTML body of content document of iframe.
|
||||
*/
|
||||
function synthFocusOnFrame(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthFocusOnFrame(aNodeOrID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthAction(getNode(aNodeOrID).contentDocument,
|
||||
aCheckerOrEventSeq, aEventType);
|
||||
|
||||
var frameDoc = getNode(aNodeOrID).contentDocument;
|
||||
var checkerOrEventSeq =
|
||||
aCheckerOrEventSeq ? aCheckerOrEventSeq : new focusChecker(frameDoc);
|
||||
this.__proto__ = new synthAction(frameDoc, checkerOrEventSeq);
|
||||
|
||||
this.invoke = function synthFocus_invoke()
|
||||
{
|
||||
this.DOMNode.body.focus();
|
||||
}
|
||||
|
||||
|
||||
this.getID = function synthFocus_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " frame document focus";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the current item when the widget doesn't have a focus.
|
||||
*/
|
||||
function changeCurrentItem(aID, aItemID)
|
||||
{
|
||||
this.eventSeq = [ new nofocusChecker() ];
|
||||
|
||||
this.invoke = function changeCurrentItem_invoke()
|
||||
{
|
||||
var controlNode = getNode(aID);
|
||||
var itemNode = getNode(aItemID);
|
||||
|
||||
// HTML
|
||||
if (controlNode.localName == "input") {
|
||||
if (controlNode.checked)
|
||||
this.reportError();
|
||||
|
||||
controlNode.checked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (controlNode.localName == "select") {
|
||||
if (controlNode.selectedIndex == itemNode.index)
|
||||
this.reportError();
|
||||
|
||||
controlNode.selectedIndex = itemNode.index;
|
||||
return;
|
||||
}
|
||||
|
||||
// XUL
|
||||
if (controlNode.localName == "tree") {
|
||||
if (controlNode.currentIndex == aItemID)
|
||||
this.reportError();
|
||||
|
||||
controlNode.currentIndex = aItemID;
|
||||
return;
|
||||
}
|
||||
|
||||
if (controlNode.localName == "menulist") {
|
||||
if (controlNode.selectedItem == itemNode)
|
||||
this.reportError();
|
||||
|
||||
controlNode.selectedItem = itemNode;
|
||||
return;
|
||||
}
|
||||
|
||||
if (controlNode.currentItem == itemNode)
|
||||
ok(false, "Error in test: proposed current item is already current" + prettyName(aID));
|
||||
|
||||
controlNode.currentItem = itemNode;
|
||||
}
|
||||
|
||||
this.getID = function changeCurrentItem_getID()
|
||||
{
|
||||
return "current item change for " + prettyName(aID);
|
||||
}
|
||||
|
||||
this.reportError = function changeCurrentItem_reportError()
|
||||
{
|
||||
ok(false,
|
||||
"Error in test: proposed current item '" + aItemID + "' is already current");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle top menu invoker.
|
||||
*/
|
||||
function toggleTopMenu(aID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthKey(aID, "VK_ALT", null,
|
||||
aCheckerOrEventSeq);
|
||||
|
||||
this.getID = function toggleTopMenu_getID()
|
||||
{
|
||||
return "toggle top menu on " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Context menu invoker.
|
||||
*/
|
||||
function synthContextMenu(aID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthClick(aID, aCheckerOrEventSeq,
|
||||
{ button: 0, type: "contextmenu" });
|
||||
|
||||
this.getID = function synthContextMenu_getID()
|
||||
{
|
||||
return "context menu on " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open combobox, autocomplete and etc popup, check expandable states.
|
||||
*/
|
||||
function openCombobox(aComboboxID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new stateChangeChecker(STATE_EXPANDED, false, true, aComboboxID)
|
||||
];
|
||||
|
||||
this.invoke = function openCombobox_invoke()
|
||||
{
|
||||
getNode(aComboboxID).focus();
|
||||
synthesizeKey("VK_DOWN", { altKey: true });
|
||||
}
|
||||
|
||||
this.getID = function openCombobox_getID()
|
||||
{
|
||||
return "open combobox " + prettyName(aComboboxID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close combobox, autocomplete and etc popup, check expandable states.
|
||||
*/
|
||||
function closeCombobox(aComboboxID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new stateChangeChecker(STATE_EXPANDED, false, false, aComboboxID)
|
||||
];
|
||||
|
||||
this.invoke = function closeCombobox_invoke()
|
||||
{
|
||||
synthesizeKey("VK_ESCAPE", { });
|
||||
}
|
||||
|
||||
this.getID = function closeCombobox_getID()
|
||||
{
|
||||
return "close combobox " + prettyName(aComboboxID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Select all invoker.
|
||||
*/
|
||||
function synthSelectAll(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthSelectAll(aNodeOrID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq);
|
||||
|
||||
this.invoke = function synthSelectAll_invoke()
|
||||
{
|
||||
@ -970,6 +1235,8 @@ function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg, aIsAsync)
|
||||
{
|
||||
if (typeof this.mTarget == "function")
|
||||
return this.mTarget.call(null, this.mTargetFuncArg);
|
||||
if (typeof this.mTarget == "string")
|
||||
return getNode(this.mTarget);
|
||||
|
||||
return this.mTarget;
|
||||
}
|
||||
@ -1003,6 +1270,25 @@ function asyncInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
|
||||
aTargetFuncArg, true);
|
||||
}
|
||||
|
||||
function focusChecker(aTargetOrFunc, aTargetFuncArg)
|
||||
{
|
||||
this.__proto__ = new invokerChecker(EVENT_FOCUS, aTargetOrFunc,
|
||||
aTargetFuncArg, false);
|
||||
|
||||
this.unique = true; // focus event must be unique for invoker action
|
||||
|
||||
this.check = function focusChecker_check(aEvent)
|
||||
{
|
||||
testStates(aEvent.accessible, STATE_FOCUSED);
|
||||
}
|
||||
}
|
||||
|
||||
function nofocusChecker(aID)
|
||||
{
|
||||
this.__proto__ = new focusChecker(aID);
|
||||
this.unexpected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Text inserted/removed events checker.
|
||||
*/
|
||||
@ -1032,8 +1318,11 @@ function textChangeChecker(aID, aStart, aEnd, aTextOrFunc, aIsInserted)
|
||||
/**
|
||||
* Caret move events checker.
|
||||
*/
|
||||
function caretMoveChecker(aCaretOffset)
|
||||
function caretMoveChecker(aCaretOffset, aTargetOrFunc, aTargetFuncArg)
|
||||
{
|
||||
this.__proto__ = new invokerChecker(EVENT_TEXT_CARET_MOVED,
|
||||
aTargetOrFunc, aTargetFuncArg);
|
||||
|
||||
this.check = function caretMoveChecker_check(aEvent)
|
||||
{
|
||||
is(aEvent.QueryInterface(nsIAccessibleCaretMoveEvent).caretOffset,
|
||||
@ -1368,28 +1657,18 @@ function sequenceItem(aProcessor, aEventType, aTarget, aItemID)
|
||||
/**
|
||||
* Invoker base class for prepare an action.
|
||||
*/
|
||||
function synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType)
|
||||
function synthAction(aNodeOrID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.DOMNode = getNode(aNodeOrID);
|
||||
|
||||
this.checker = null;
|
||||
if (aCheckerOrEventSeq) {
|
||||
if (aCheckerOrEventSeq instanceof Array) {
|
||||
this.eventSeq = aCheckerOrEventSeq;
|
||||
} else {
|
||||
this.checker = aCheckerOrEventSeq;
|
||||
this.checker.target = this.DOMNode;
|
||||
this.eventSeq = [ aCheckerOrEventSeq ];
|
||||
}
|
||||
}
|
||||
|
||||
if (aEventType)
|
||||
this.eventSeq = [ new invokerChecker(aEventType, this.DOMNode) ];
|
||||
|
||||
this.check = function synthAction_check(aEvent)
|
||||
{
|
||||
if (this.checker)
|
||||
this.checker.check(aEvent);
|
||||
}
|
||||
|
||||
this.getID = function synthAction_getID() { return aNodeOrID + " action"; }
|
||||
this.getID = function synthAction_getID()
|
||||
{ return prettyName(aNodeOrID) + " action"; }
|
||||
}
|
||||
|
@ -63,11 +63,20 @@ _TEST_FILES =\
|
||||
test_docload.xul \
|
||||
test_dragndrop.html \
|
||||
test_flush.html \
|
||||
test_focus.html \
|
||||
test_focus.xul \
|
||||
test_focus_aria_activedescendant.html \
|
||||
test_focus_autocomplete.xul \
|
||||
test_focus_browserui.xul \
|
||||
test_focus_contextmenu.xul \
|
||||
test_focus_controls.html \
|
||||
test_focus_dialog.html \
|
||||
test_focus_doc.html \
|
||||
test_focus_general.html \
|
||||
test_focus_general.xul \
|
||||
test_focus_listcontrols.xul \
|
||||
test_focus_menu.xul \
|
||||
test_focus_name.html \
|
||||
test_focus_selects.html \
|
||||
test_focus_tree.xul \
|
||||
test_focusdoc.html \
|
||||
test_menu.xul \
|
||||
test_mutation.html \
|
||||
test_mutation.xhtml \
|
||||
|
@ -22,12 +22,17 @@
|
||||
const kViaDisplayStyle = 0;
|
||||
const kViaVisibilityStyle = 1;
|
||||
|
||||
function focusMenu(aMenuBarID, aMenuID)
|
||||
function focusMenu(aMenuBarID, aMenuID, aActiveMenuBarID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_MENU_START, getNode(aMenuBarID)),
|
||||
new invokerChecker(EVENT_FOCUS, getNode(aMenuID)),
|
||||
];
|
||||
this.eventSeq = [];
|
||||
|
||||
if (aActiveMenuBarID) {
|
||||
this.eventSeq.push(new invokerChecker(EVENT_MENU_END,
|
||||
getNode(aActiveMenuBarID)));
|
||||
}
|
||||
|
||||
this.eventSeq.push(new invokerChecker(EVENT_MENU_START, getNode(aMenuBarID)));
|
||||
this.eventSeq.push(new invokerChecker(EVENT_FOCUS, getNode(aMenuID)));
|
||||
|
||||
this.invoke = function focusMenu_invoke()
|
||||
{
|
||||
@ -143,14 +148,15 @@
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
//gA11yEventDumpID = "eventdump";
|
||||
//gA11yEventDumpToConsole = true;
|
||||
//gA11yEventDumpID = "eventdump"; // debuging
|
||||
//gA11yEventDumpToConsole = true; // debuging
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new focusMenu("menubar", "menu-file"));
|
||||
gQueue.push(new focusMenu("menubar2", "menu-help"));
|
||||
gQueue.push(new focusMenu("menubar", "menu-file", "menubar2"));
|
||||
gQueue.push(new showMenu("menupopup-file", "menu-file", kViaDisplayStyle));
|
||||
gQueue.push(new closeMenu("menupopup-file", "menu-file", kViaDisplayStyle));
|
||||
gQueue.push(new showMenu("menupopup-edit", "menu-edit", kViaVisibilityStyle));
|
||||
@ -183,6 +189,11 @@
|
||||
title="Clean up FireAccessibleFocusEvent">
|
||||
Mozilla Bug 615189
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
|
||||
title="Rework accessible focus handling">
|
||||
Mozilla Bug 673958
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
@ -205,6 +216,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="menubar2" role="menubar">
|
||||
<div id="menu-help" role="menuitem" tabindex="0">
|
||||
Help
|
||||
<div id="menupopup-help" role="menu" style="display: none;">
|
||||
<div id="menuitem-about" role="menuitem" tabindex="0">About</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div tabindex="0" id="outsidemenu">outsidemenu</div>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
|
@ -23,8 +23,11 @@
|
||||
/**
|
||||
* Test "event-from-input" object attribute.
|
||||
*/
|
||||
function checker(aValue, aNoTargetID)
|
||||
function eventFromInputChecker(aEventType, aID, aValue, aNoTargetID)
|
||||
{
|
||||
this.type = aEventType;
|
||||
this.target = getAccessible(aID);
|
||||
|
||||
this.noTarget = getNode(aNoTargetID);
|
||||
|
||||
this.check = function checker_check(aEvent)
|
||||
@ -42,16 +45,21 @@
|
||||
var gQueue = null;
|
||||
|
||||
// gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
var id = "textbox", noTargetId = "textarea";
|
||||
gQueue.push(new synthFocus(id, new checker("false", noTargetId), EVENT_FOCUS));
|
||||
|
||||
var checker =
|
||||
new eventFromInputChecker(EVENT_FOCUS, id, "false", noTargetId);
|
||||
gQueue.push(new synthFocus(id, checker));
|
||||
|
||||
if (!MAC) { // Mac failure is bug 541093
|
||||
gQueue.push(new synthHomeKey(id, new checker("false", noTargetId), EVENT_TEXT_CARET_MOVED));
|
||||
var checker =
|
||||
new eventFromInputChecker(EVENT_TEXT_CARET_MOVED, id, "false", noTargetId);
|
||||
gQueue.push(new synthHomeKey(id, checker));
|
||||
}
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
|
@ -20,9 +20,9 @@
|
||||
/**
|
||||
* Click checker.
|
||||
*/
|
||||
function clickChecker(aCaretOffset, aExtraNodeOrID, aExtraCaretOffset)
|
||||
function clickChecker(aCaretOffset, aID, aExtraNodeOrID, aExtraCaretOffset)
|
||||
{
|
||||
this.__proto__ = new caretMoveChecker(aCaretOffset);
|
||||
this.__proto__ = new caretMoveChecker(aCaretOffset, aID);
|
||||
|
||||
this.extraNode = getNode(aExtraNodeOrID);
|
||||
|
||||
@ -62,30 +62,33 @@
|
||||
testCaretOffset("p", -1);
|
||||
|
||||
// test caret move events and caret offsets
|
||||
gQueue = new eventQueue(EVENT_TEXT_CARET_MOVED);
|
||||
gQueue = new eventQueue();
|
||||
|
||||
var id = "textbox";
|
||||
gQueue.push(new synthFocus(id, new caretMoveChecker(5)));
|
||||
gQueue.push(new synthSelectAll(id, new caretMoveChecker(5)));
|
||||
gQueue.push(new synthClick(id, new caretMoveChecker(0)));
|
||||
gQueue.push(new synthRightKey(id, new caretMoveChecker(1)));
|
||||
gQueue.push(new synthFocus(id, new caretMoveChecker(5, id)));
|
||||
gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, id)));
|
||||
gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
|
||||
gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
|
||||
|
||||
id = "textarea";
|
||||
gQueue.push(new synthClick(id, new caretMoveChecker(0)));
|
||||
gQueue.push(new synthRightKey(id, new caretMoveChecker(1)));
|
||||
gQueue.push(new synthDownKey(id, new caretMoveChecker(12)));
|
||||
gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
|
||||
gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
|
||||
gQueue.push(new synthDownKey(id, new caretMoveChecker(12, id)));
|
||||
|
||||
id = "p";
|
||||
gQueue.push(new synthClick(id, new caretMoveChecker(0)));
|
||||
gQueue.push(new synthRightKey(id, new caretMoveChecker(1)));
|
||||
gQueue.push(new synthDownKey(id, new caretMoveChecker(6)));
|
||||
gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
|
||||
gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
|
||||
gQueue.push(new synthDownKey(id, new caretMoveChecker(6, id)));
|
||||
|
||||
gQueue.push(new synthClick("p1_in_div",
|
||||
new clickChecker(0, "p2_in_div", -1)));
|
||||
id = "p1_in_div";
|
||||
gQueue.push(new synthClick(id, new clickChecker(0, id, "p2_in_div", -1)));
|
||||
|
||||
gQueue.push(new synthShiftTab("p", new caretMoveChecker(0)));
|
||||
gQueue.push(new synthShiftTab("textarea", new caretMoveChecker(12)));
|
||||
gQueue.push(new synthTab("p", new caretMoveChecker(0)));
|
||||
id = "p";
|
||||
gQueue.push(new synthShiftTab(id, new caretMoveChecker(0, id)));
|
||||
id = "textarea";
|
||||
gQueue.push(new synthShiftTab(id, new caretMoveChecker(12, id)));
|
||||
id = "p";
|
||||
gQueue.push(new synthTab(id, new caretMoveChecker(0, id)));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
*/
|
||||
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
gA11yEventDumpToConsole = true;
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
@ -31,10 +31,10 @@
|
||||
gQueue = new eventQueue(EVENT_TEXT_CARET_MOVED);
|
||||
|
||||
var id = "textbox";
|
||||
gQueue.push(new synthFocus(id, new caretMoveChecker(5)));
|
||||
gQueue.push(new synthSelectAll(id, new caretMoveChecker(5)));
|
||||
gQueue.push(new synthHomeKey(id, new caretMoveChecker(0)));
|
||||
gQueue.push(new synthRightKey(id, new caretMoveChecker(1)));
|
||||
gQueue.push(new synthFocus(id, new caretMoveChecker(5, id)));
|
||||
gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, id)));
|
||||
gQueue.push(new synthHomeKey(id, new caretMoveChecker(0, id)));
|
||||
gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
@ -1,99 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Accessible focus event testing">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
<script type="application/javascript"
|
||||
src="../events.js" />
|
||||
|
||||
<script type="application/javascript">
|
||||
/**
|
||||
* Click menu item invoker.
|
||||
*/
|
||||
function clickMenuItem(aNodeOrID, aFocusNodeOrID)
|
||||
{
|
||||
this.DOMNode = getNode(aFocusNodeOrID);
|
||||
|
||||
this.invoke = function clickMenuItem_invoke()
|
||||
{
|
||||
synthesizeMouse(getNode(aNodeOrID), 1, 1, {});
|
||||
}
|
||||
|
||||
this.getID = function clickMenuItem_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " click menu item";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
*/
|
||||
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
function doTests()
|
||||
{
|
||||
// Test focus events.
|
||||
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
|
||||
|
||||
gQueue.push(new synthFocus("textbox"));
|
||||
gQueue.push(new synthFocus("scale"));
|
||||
gQueue.push(new synthFocusOnFrame("editabledoc"));
|
||||
gQueue.push(new synthClick("menu"));
|
||||
gQueue.push(new synthMouseMove("menuitem"));
|
||||
gQueue.push(new clickMenuItem("menuitem",
|
||||
getNode("editabledoc").contentDocument));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
|
||||
<hbox flex="1" style="overflow: auto;">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=492518"
|
||||
title="xul:slider accessible of xul:scale is accessible illegally">
|
||||
Mozilla Bug 492518
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=552368"
|
||||
title=" fire focus event on document accessible whenever the root or body element is focused">
|
||||
Mozilla Bug 552368
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<vbox flex="1">
|
||||
<textbox id="textbox" value="hello"/>
|
||||
<scale id="scale" min="0" max="9" value="5"/>
|
||||
<menubar>
|
||||
<menu id="menu" label="menu">
|
||||
<menupopup>
|
||||
<menuitem id="menuitem" label="menuitem"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
</menubar>
|
||||
<iframe id="editabledoc" src="focus.html"/>
|
||||
|
||||
<vbox id="eventdump"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</window>
|
@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=429547
|
||||
-->
|
||||
<head>
|
||||
<title>aria-activedescendant focus tests</title>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
//gA11yEventDumpToConsole = true; // debugging
|
||||
|
||||
function changeARIAActiveDescendant(aID, aItemID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new focusChecker(aItemID)
|
||||
];
|
||||
|
||||
this.invoke = function changeARIAActiveDescendant_invoke()
|
||||
{
|
||||
getNode(aID).setAttribute("aria-activedescendant", aItemID);
|
||||
}
|
||||
|
||||
this.getID = function changeARIAActiveDescendant_getID()
|
||||
{
|
||||
return "change aria-activedescendant on " + aItemID;
|
||||
}
|
||||
}
|
||||
|
||||
function insertItemNFocus(aID, aNewItemID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, aNewItemID),
|
||||
new focusChecker(aNewItemID)
|
||||
];
|
||||
|
||||
this.invoke = function insertItemNFocus_invoke()
|
||||
{
|
||||
var container = getNode(aID);
|
||||
var itemNode = document.createElement("div");
|
||||
itemNode.setAttribute("id", aNewItemID);
|
||||
itemNode.textContent = "item3";
|
||||
container.appendChild(itemNode);
|
||||
|
||||
container.setAttribute("aria-activedescendant", aNewItemID);
|
||||
}
|
||||
|
||||
this.getID = function insertItemNFocus_getID()
|
||||
{
|
||||
return "insert new node and focus it with ID: " + aNewItemID;
|
||||
}
|
||||
}
|
||||
|
||||
var gQueue = null;
|
||||
function doTest()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new synthFocus("container", new focusChecker("item1")));
|
||||
gQueue.push(new changeARIAActiveDescendant("container", "item2"));
|
||||
todo(false, "No focus for inserted element, bug 687011");
|
||||
//gQueue.push(new insertItemNFocus("container", "item3"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=429547"
|
||||
title="Support aria-activedescendant usage in nsIAccesible::TakeFocus()">
|
||||
Mozilla Bug 429547
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div role="listbox" aria-activedescendant="item1" id="container" tabindex="1">
|
||||
<div role="listitem" id="item1">item1</div>
|
||||
<div role="listitem" id="item2">item2</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
463
accessible/tests/mochitest/events/test_focus_autocomplete.xul
Normal file
463
accessible/tests/mochitest/events/test_focus_autocomplete.xul
Normal file
@ -0,0 +1,463 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
|
||||
<?xml-stylesheet href="chrome://browser/content/browser.css"
|
||||
type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Accessible focus event testing">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
<script type="application/javascript"
|
||||
src="../states.js" />
|
||||
<script type="application/javascript"
|
||||
src="../events.js" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../autocomplete.js" />
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Hacky stuffs
|
||||
|
||||
// This is the hack needed for searchbar work outside of browser.
|
||||
function getBrowser()
|
||||
{
|
||||
return {
|
||||
mCurrentBrowser: { engines: new Array() }
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
function loadFormAutoComplete(aIFrameID)
|
||||
{
|
||||
this.iframeNode = getNode(aIFrameID);
|
||||
this.iframe = getAccessible(aIFrameID);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.iframe)
|
||||
];
|
||||
|
||||
this.invoke = function loadFormAutoComplete_invoke()
|
||||
{
|
||||
var url = "data:text/html,<html><body><form id='form'>" +
|
||||
"<input id='input' name='a11ytest-formautocomplete'>" +
|
||||
"</form></body></html>";
|
||||
this.iframeNode.setAttribute("src", url);
|
||||
}
|
||||
|
||||
this.getID = function loadFormAutoComplete_getID()
|
||||
{
|
||||
return "load form autocomplete page";
|
||||
}
|
||||
}
|
||||
|
||||
function initFormAutoCompleteBy(aIFrameID, aAutoCompleteValue)
|
||||
{
|
||||
this.iframe = getAccessible(aIFrameID);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.iframe)
|
||||
];
|
||||
|
||||
this.invoke = function initFormAutoCompleteBy_invoke()
|
||||
{
|
||||
var iframeDOMDoc = getFormAutoCompleteDOMDoc(aIFrameID);
|
||||
|
||||
var inputNode = iframeDOMDoc.getElementById("input");
|
||||
inputNode.value = aAutoCompleteValue;
|
||||
var formNode = iframeDOMDoc.getElementById("form");
|
||||
formNode.submit();
|
||||
}
|
||||
|
||||
this.getID = function initFormAutoCompleteBy_getID()
|
||||
{
|
||||
return "init form autocomplete by '" + aAutoCompleteValue + "'";
|
||||
}
|
||||
}
|
||||
|
||||
function removeChar(aID, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
|
||||
|
||||
this.invoke = function removeChar_invoke()
|
||||
{
|
||||
synthesizeKey("VK_LEFT", { shiftKey: true });
|
||||
synthesizeKey("VK_DELETE", {});
|
||||
}
|
||||
|
||||
this.getID = function removeChar_getID()
|
||||
{
|
||||
return "remove char on " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
function replaceOnChar(aID, aChar, aCheckerOrEventSeq)
|
||||
{
|
||||
this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
|
||||
|
||||
this.invoke = function replaceOnChar_invoke()
|
||||
{
|
||||
this.DOMNode.select();
|
||||
synthesizeKey(aChar, {});
|
||||
}
|
||||
|
||||
this.getID = function replaceOnChar_getID()
|
||||
{
|
||||
return "replace on char '" + aChar + "' for" + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
function focusOnMouseOver(aIDFunc, aIDFuncArg)
|
||||
{
|
||||
this.eventSeq = [ new focusChecker(aIDFunc, aIDFuncArg) ];
|
||||
|
||||
this.invoke = function focusOnMouseOver_invoke()
|
||||
{
|
||||
this.id = aIDFunc.call(null, aIDFuncArg);
|
||||
this.node = getNode(this.id);
|
||||
this.window = this.node.ownerDocument.defaultView;
|
||||
|
||||
if (this.node.localName == "tree") {
|
||||
var tree = getAccessible(this.node);
|
||||
var accessible = getAccessible(this.id);
|
||||
if (tree != accessible) {
|
||||
var itemX = {}, itemY = {}, treeX = {}, treeY = {};
|
||||
accessible.getBounds(itemX, itemY, {}, {});
|
||||
tree.getBounds(treeX, treeY, {}, {});
|
||||
this.x = itemX.value - treeX.value;
|
||||
this.y = itemY.value - treeY.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate mouse move events in timeouts until autocomplete popup list
|
||||
// doesn't have it, the reason is do that because autocomplete popup
|
||||
// ignores mousemove events firing in too short range.
|
||||
synthesizeMouse(this.node, this.x, this.y, { type: "mousemove" });
|
||||
this.doMouseMoveFlood(this);
|
||||
}
|
||||
|
||||
this.finalCheck = function focusOnMouseOver_getID()
|
||||
{
|
||||
this.isFlooding = false;
|
||||
}
|
||||
|
||||
this.getID = function focusOnMouseOver_getID()
|
||||
{
|
||||
return "mouse over on " + prettyName(aIDFunc.call(null, aIDFuncArg));
|
||||
}
|
||||
|
||||
this.doMouseMoveFlood = function focusOnMouseOver_doMouseMoveFlood(aThis)
|
||||
{
|
||||
synthesizeMouse(aThis.node, aThis.x + 1, aThis.y + 1,
|
||||
{ type: "mousemove" }, aThis.window);
|
||||
|
||||
if (aThis.isFlooding)
|
||||
aThis.window.setTimeout(aThis.doMouseMoveFlood, 0, aThis);
|
||||
}
|
||||
|
||||
this.id = null;
|
||||
this.node = null;
|
||||
this.window = null;
|
||||
|
||||
this.isFlooding = true;
|
||||
this.x = 1;
|
||||
this.y = 1;
|
||||
}
|
||||
|
||||
function selectByClick(aIDFunc, aIDFuncArg,
|
||||
aFocusTargetFunc, aFocusTargetFuncArg)
|
||||
{
|
||||
this.eventSeq = [ new focusChecker(aFocusTargetFunc, aFocusTargetFuncArg) ];
|
||||
|
||||
this.invoke = function selectByClick_invoke()
|
||||
{
|
||||
var id = aIDFunc.call(null, aIDFuncArg);
|
||||
var node = getNode(id);
|
||||
var targetWindow = node.ownerDocument.defaultView;
|
||||
|
||||
var x = 0, y = 0;
|
||||
if (node.localName == "tree") {
|
||||
var tree = getAccessible(node);
|
||||
var accessible = getAccessible(id);
|
||||
if (tree != accessible) {
|
||||
var itemX = {}, itemY = {}, treeX = {}, treeY = {};
|
||||
accessible.getBounds(itemX, itemY, {}, {});
|
||||
tree.getBounds(treeX, treeY, {}, {});
|
||||
x = itemX.value - treeX.value;
|
||||
y = itemY.value - treeY.value;
|
||||
}
|
||||
}
|
||||
|
||||
synthesizeMouse(node, x + 1, y + 1, {}, targetWindow);
|
||||
}
|
||||
|
||||
this.getID = function selectByClick_getID()
|
||||
{
|
||||
return "select by click " + prettyName(aIDFunc.call(null, aIDFuncArg));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Target getters
|
||||
|
||||
function getItem(aItemObj)
|
||||
{
|
||||
var autocomplete = aItemObj.autocomplete;
|
||||
var autocompleteNode = aItemObj.autocompleteNode;
|
||||
|
||||
// XUL searchbar
|
||||
if (autocompleteNode.localName == "searchbar") {
|
||||
var popupNode = autocompleteNode._popup;
|
||||
if (popupNode) {
|
||||
var list = getAccessible(popupNode);
|
||||
return list.getChildAt(aItemObj.index);
|
||||
}
|
||||
}
|
||||
|
||||
// XUL autocomplete
|
||||
var popupNode = autocompleteNode.popup;
|
||||
if (!popupNode) {
|
||||
// HTML form autocomplete
|
||||
var controller = Components.classes["@mozilla.org/autocomplete/controller;1"].
|
||||
getService(Components.interfaces.nsIAutoCompleteController);
|
||||
popupNode = controller.input.popup.QueryInterface(nsIDOMNode);
|
||||
}
|
||||
|
||||
if (popupNode) {
|
||||
if ("richlistbox" in popupNode) {
|
||||
var list = getAccessible(popupNode.richlistbox);
|
||||
return list.getChildAt(aItemObj.index);
|
||||
}
|
||||
|
||||
var list = getAccessible(popupNode.tree);
|
||||
return list.getChildAt(aItemObj.index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
function getTextEntry(aID)
|
||||
{
|
||||
// For form autocompletes the autocomplete widget and text entry widget
|
||||
// is the same widget, for XUL autocompletes the text entry is a first
|
||||
// child.
|
||||
var localName = getNode(aID).localName;
|
||||
|
||||
// XUL autocomplete
|
||||
if (localName == "textbox")
|
||||
return getAccessible(aID).firstChild;
|
||||
|
||||
// HTML form autocomplete
|
||||
if (localName == "input")
|
||||
return getAccessible(aID);
|
||||
|
||||
// XUL searchbar
|
||||
if (localName == "searchbar")
|
||||
return getAccessible(getNode(aID).textbox.inputField);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function itemObj(aID, aIdx)
|
||||
{
|
||||
this.autocompleteNode = getNode(aID);
|
||||
|
||||
this.autocomplete = this.autocompleteNode.localName == "searchbar" ?
|
||||
getAccessible(this.autocompleteNode.textbox) :
|
||||
getAccessible(this.autocompleteNode);
|
||||
|
||||
this.index = aIdx;
|
||||
}
|
||||
|
||||
function getFormAutoCompleteDOMDoc(aIFrameID)
|
||||
{
|
||||
return getNode(aIFrameID).contentDocument;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Test helpers
|
||||
|
||||
function queueAutoCompleteTests(aID)
|
||||
{
|
||||
// focus autocomplete text entry
|
||||
gQueue.push(new synthFocus(aID, new focusChecker(getTextEntry, aID)));
|
||||
|
||||
// open autocomplete popup
|
||||
gQueue.push(new synthDownKey(aID, new nofocusChecker()));
|
||||
|
||||
// select second option ('hi' option), focus on it
|
||||
gQueue.push(new synthUpKey(aID,
|
||||
new focusChecker(getItem, new itemObj(aID, 1))));
|
||||
|
||||
// choose selected option, focus on text entry
|
||||
gQueue.push(new synthEnterKey(aID, new focusChecker(getTextEntry, aID)));
|
||||
|
||||
// remove char, autocomplete popup appears
|
||||
gQueue.push(new removeChar(aID, new nofocusChecker()));
|
||||
|
||||
// select first option ('hello' option), focus on it
|
||||
gQueue.push(new synthDownKey(aID,
|
||||
new focusChecker(getItem, new itemObj(aID, 0))));
|
||||
|
||||
// mouse move on second option ('hi' option), focus on it
|
||||
gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 1)));
|
||||
|
||||
// autocomplete popup updated (no selected item), focus on textentry
|
||||
gQueue.push(new synthKey(aID, "e", null, new focusChecker(getTextEntry, aID)));
|
||||
|
||||
// select first option ('hello' option), focus on it
|
||||
gQueue.push(new synthDownKey(aID,
|
||||
new focusChecker(getItem, new itemObj(aID, 0))));
|
||||
|
||||
// popup gets hidden, focus on textentry
|
||||
gQueue.push(new synthRightKey(aID, new focusChecker(getTextEntry, aID)));
|
||||
|
||||
// popup gets open, no focus
|
||||
gQueue.push(new synthOpenComboboxKey(aID, new nofocusChecker()));
|
||||
|
||||
// select first option again ('hello' option), focus on it
|
||||
gQueue.push(new synthDownKey(aID,
|
||||
new focusChecker(getItem, new itemObj(aID, 0))));
|
||||
|
||||
// no option is selected, focus on text entry
|
||||
gQueue.push(new synthUpKey(aID, new focusChecker(getTextEntry, aID)));
|
||||
|
||||
// close popup, no focus
|
||||
gQueue.push(new synthEscapeKey(aID, new nofocusChecker()));
|
||||
|
||||
// autocomplete popup appears (no selected item), focus stays on textentry
|
||||
gQueue.push(new replaceOnChar(aID, "h", new nofocusChecker()));
|
||||
|
||||
// mouse move on first option ('hello' option), focus on it
|
||||
gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 0)));
|
||||
|
||||
// click first option ('hello' option), popup closes, focus on text entry
|
||||
gQueue.push(new selectByClick(getItem, new itemObj(aID, 0), getTextEntry, aID));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Tests
|
||||
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
var gInitQueue = null;
|
||||
function initTests()
|
||||
{
|
||||
// register 'test-a11y-search' autocomplete search
|
||||
initAutoComplete([ "hello", "hi" ],
|
||||
[ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
|
||||
|
||||
gInitQueue = new eventQueue();
|
||||
gInitQueue.push(new loadFormAutoComplete("iframe"));
|
||||
gInitQueue.push(new initFormAutoCompleteBy("iframe", "hello"));
|
||||
gInitQueue.push(new initFormAutoCompleteBy("iframe", "hi"));
|
||||
gInitQueue.onFinish = function initQueue_onFinish()
|
||||
{
|
||||
SimpleTest.executeSoon(doTests);
|
||||
return DO_NOT_FINISH_TEST;
|
||||
}
|
||||
gInitQueue.invoke();
|
||||
}
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
// Test focus events.
|
||||
gQueue = new eventQueue();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// tree popup autocomplete tests
|
||||
queueAutoCompleteTests("autocomplete");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// richlistbox popup autocomplete tests
|
||||
queueAutoCompleteTests("richautocomplete");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// HTML form autocomplete tests
|
||||
queueAutoCompleteTests(getFormAutoCompleteDOMDoc("iframe").getElementById("input"));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// searchbar tests
|
||||
|
||||
// focus searchbar, focus on text entry
|
||||
gQueue.push(new synthFocus("searchbar",
|
||||
new focusChecker(getTextEntry, "searchbar")));
|
||||
// open search engine popup, no focus
|
||||
gQueue.push(new synthOpenComboboxKey("searchbar", new nofocusChecker()));
|
||||
// select first item, focus on it
|
||||
gQueue.push(new synthDownKey("searchbar",
|
||||
new focusChecker(getItem, new itemObj("searchbar", 0))));
|
||||
// mouse over on second item, focus on it
|
||||
gQueue.push(new focusOnMouseOver(getItem, new itemObj("searchbar", 1)));
|
||||
// press enter key, focus on text entry
|
||||
gQueue.push(new synthEnterKey("searchbar",
|
||||
new focusChecker(getTextEntry, "searchbar")));
|
||||
// click on search button, open popup, focus goes to document
|
||||
var searchBtn = getAccessible(getNode("searchbar").searchButton);
|
||||
gQueue.push(new synthClick(searchBtn, new focusChecker(document)));
|
||||
// select first item, focus on it
|
||||
gQueue.push(new synthDownKey("searchbar",
|
||||
new focusChecker(getItem, new itemObj("searchbar", 0))));
|
||||
// close popup, focus goes on document
|
||||
gQueue.push(new synthEscapeKey("searchbar", new focusChecker(document)));
|
||||
|
||||
gQueue.onFinish = function()
|
||||
{
|
||||
// unregister 'test-a11y-search' autocomplete search
|
||||
shutdownAutoComplete();
|
||||
}
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(initTests);
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<hbox flex="1" style="overflow: auto;">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=383759"
|
||||
title="Focus event inconsistent for search box autocomplete">
|
||||
Mozilla Bug 383759
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
|
||||
title="Rework accessible focus handling">
|
||||
Mozilla Bug 673958
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<vbox flex="1">
|
||||
<textbox id="autocomplete" type="autocomplete"
|
||||
autocompletesearch="test-a11y-search"/>
|
||||
|
||||
<textbox id="richautocomplete" type="autocomplete"
|
||||
autocompletesearch="test-a11y-search"
|
||||
autocompletepopup="richpopup"/>
|
||||
<panel id="richpopup" type="autocomplete-richlistbox" noautofocus="true"/>
|
||||
|
||||
<iframe id="iframe"/>
|
||||
|
||||
<searchbar id="searchbar"/>
|
||||
|
||||
<vbox id="eventdump"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</window>
|
178
accessible/tests/mochitest/events/test_focus_browserui.xul
Normal file
178
accessible/tests/mochitest/events/test_focus_browserui.xul
Normal file
@ -0,0 +1,178 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Accessibility Loading Document Events Test.">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
|
||||
function tabBrowser()
|
||||
{
|
||||
return gBrowserWnd.gBrowser;
|
||||
}
|
||||
|
||||
function currentBrowser()
|
||||
{
|
||||
return tabBrowser().selectedBrowser;
|
||||
}
|
||||
|
||||
function currentTabDocument()
|
||||
{
|
||||
return currentBrowser().contentDocument;
|
||||
}
|
||||
|
||||
function inputInDocument()
|
||||
{
|
||||
var tabdoc = currentTabDocument();
|
||||
return tabdoc.getElementById("input");
|
||||
}
|
||||
|
||||
function urlbarInput()
|
||||
{
|
||||
return gBrowserWnd.document.getElementById("urlbar").inputField;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
function loadURI(aURI)
|
||||
{
|
||||
this.invoke = function loadURI_invoke()
|
||||
{
|
||||
tabBrowser().loadURI(aURI);
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new focusChecker(currentTabDocument)
|
||||
];
|
||||
|
||||
this.getID = function loadURI_getID()
|
||||
{
|
||||
return "load uri " + aURI;
|
||||
}
|
||||
}
|
||||
|
||||
function goBack()
|
||||
{
|
||||
this.invoke = function goBack_invoke()
|
||||
{
|
||||
tabBrowser().goBack();
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new focusChecker(inputInDocument)
|
||||
];
|
||||
|
||||
this.getID = function goBack_getID()
|
||||
{
|
||||
return "go back one page in history ";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Testing
|
||||
|
||||
var gInputDocURI = "data:text/html,<html><input id='input'></html>";
|
||||
var gButtonDocURI = "data:text/html,<html><input id='input' type='button' value='button'></html>";
|
||||
|
||||
var gBrowserWnd = null;
|
||||
function loadBrowser()
|
||||
{
|
||||
gBrowserWnd = window.openDialog("chrome://browser/content/", "_blank",
|
||||
"chrome,all,dialog=no", gInputDocURI);
|
||||
|
||||
addA11yLoadEvent(startTests, gBrowserWnd);
|
||||
}
|
||||
|
||||
function startTests()
|
||||
{
|
||||
// Wait for tab load.
|
||||
var browser = gBrowserWnd.gBrowser.selectedBrowser;
|
||||
addA11yLoadEvent(doTests, browser.contentWindow);
|
||||
}
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debug
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
var tabDocument = currentTabDocument();
|
||||
var input = inputInDocument();
|
||||
|
||||
// move focus to input inside tab document
|
||||
gQueue.push(new synthTab(tabDocument, new focusChecker(input), gBrowserWnd));
|
||||
|
||||
// open new url, focus moves to new document
|
||||
gQueue.push(new loadURI(gButtonDocURI));
|
||||
|
||||
// back one page in history, moves moves on input of tab document
|
||||
gQueue.push(new goBack());
|
||||
|
||||
// open new tab, focus moves to urlbar
|
||||
gQueue.push(new synthKey(tabDocument, "t", { ctrlKey: true, window: gBrowserWnd },
|
||||
new focusChecker(urlbarInput)));
|
||||
|
||||
// close open tab, focus goes on input of tab document
|
||||
gQueue.push(new synthKey(tabDocument, "w", { ctrlKey: true, window: gBrowserWnd },
|
||||
new focusChecker(inputInDocument)));
|
||||
|
||||
gQueue.onFinish = function()
|
||||
{
|
||||
gBrowserWnd.close();
|
||||
}
|
||||
gQueue.invoke();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(loadBrowser);
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<vbox flex="1" style="overflow: auto;">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=644452"
|
||||
title="Focus not set when switching to cached document with back or forward if anything other than the document was last focused">
|
||||
Mozilla Bug 644452
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=665412"
|
||||
title="Broken focus when returning to editable text field after closing a tab while focused in the Navigation toolbar">
|
||||
Mozilla Bug 665412
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
|
||||
title="Rework accessible focus handling">
|
||||
Mozilla Bug 673958
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<vbox id="eventdump"></vbox>
|
||||
</vbox>
|
||||
</window>
|
78
accessible/tests/mochitest/events/test_focus_contextmenu.xul
Normal file
78
accessible/tests/mochitest/events/test_focus_contextmenu.xul
Normal file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Context nenu focus testing">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
<script type="application/javascript"
|
||||
src="../states.js" />
|
||||
<script type="application/javascript"
|
||||
src="../events.js" />
|
||||
|
||||
<script type="application/javascript">
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
// Test focus events.
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new synthFocus("button"));
|
||||
gQueue.push(new synthContextMenu("button",
|
||||
new invokerChecker(EVENT_MENUPOPUP_START, "contextmenu")));
|
||||
gQueue.push(new synthEscapeKey("contextmenu", new focusChecker("button")));
|
||||
|
||||
gQueue.push(new synthContextMenu("button",
|
||||
new invokerChecker(EVENT_MENUPOPUP_START, "contextmenu")));
|
||||
gQueue.push(new synthDownKey("contextmenu", new focusChecker("item1")));
|
||||
gQueue.push(new synthDownKey("item1", new focusChecker("item2")));
|
||||
gQueue.push(new synthRightKey("item2", new focusChecker("item2.1")));
|
||||
gQueue.push(new synthEscapeKey("item2.1", new focusChecker("item2")));
|
||||
gQueue.push(new synthEscapeKey("item2", new focusChecker("button")));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
|
||||
<hbox flex="1" style="overflow: auto;">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
|
||||
title="Rework accessible focus handling">
|
||||
Mozilla Bug 673958
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<vbox flex="1">
|
||||
<button id="button" context="contextmenu" label="button"/>
|
||||
<menupopup id="contextmenu">
|
||||
<menuitem id="item1" label="item1"/>
|
||||
<menu id="item2" label="item2">
|
||||
<menupopup>
|
||||
<menuitem id="item2.1" label="item2.1"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
</menupopup>
|
||||
|
||||
<vbox id="eventdump"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</window>
|
73
accessible/tests/mochitest/events/test_focus_controls.html
Normal file
73
accessible/tests/mochitest/events/test_focus_controls.html
Normal file
@ -0,0 +1,73 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible focus testing on HTML controls</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue(EVENT_FOCUS);
|
||||
|
||||
gQueue.push(new synthFocus("textbox"));
|
||||
gQueue.push(new synthFocus("textarea"));
|
||||
gQueue.push(new synthFocus("button1"));
|
||||
gQueue.push(new synthFocus("button2"));
|
||||
gQueue.push(new synthFocus("checkbox"));
|
||||
gQueue.push(new synthFocus("radio1"));
|
||||
gQueue.push(new synthDownKey("radio1", new focusChecker("radio2")));
|
||||
|
||||
// no focus events for checkbox or radio inputs when they are checked
|
||||
// programmatically
|
||||
gQueue.push(new changeCurrentItem("checkbox"));
|
||||
gQueue.push(new changeCurrentItem("radio1"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
|
||||
title="Rework accessible focus handling">
|
||||
Mozilla Bug 673958
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<input id="textbox">
|
||||
<textarea id="textarea"></textarea>
|
||||
|
||||
<input id="button1" type="button" value="button">
|
||||
<button id="button2">button</button>
|
||||
<input id="checkbox" type="checkbox">
|
||||
<input id="radio1" type="radio" name="radiogroup">
|
||||
<input id="radio2" type="radio" name="radiogroup">
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
@ -15,11 +15,15 @@
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function openCloseDialog(aID)
|
||||
{
|
||||
this.DOMNode = getNode(aID);
|
||||
this.eventSeq = [
|
||||
new focusChecker(getNode(aID))
|
||||
];
|
||||
|
||||
this.invoke = function openCloseDialog_invoke()
|
||||
{
|
||||
@ -29,30 +33,47 @@
|
||||
|
||||
this.getID = function openCloseDialog_getID()
|
||||
{
|
||||
return "Open close dialog on " + prettyName(aID);
|
||||
return "Open close dialog while focus on " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
function focusElmWhileSubdocIsFocused(aID)
|
||||
var gDialogWnd = null;
|
||||
function getDialogDocument()
|
||||
{
|
||||
this.DOMNode = getNode(aID);
|
||||
return gDialogWnd.document;
|
||||
}
|
||||
|
||||
this.invoke = function focusElmWhileSubdocIsFocused_invoke()
|
||||
function openDialog(aID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new focusChecker(getDialogDocument)
|
||||
];
|
||||
|
||||
this.invoke = function openDialog_invoke()
|
||||
{
|
||||
this.DOMNode.focus();
|
||||
gDialogWnd = window.open("focus.html");
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_FOCUS, this.DOMNode)
|
||||
];
|
||||
|
||||
this.unexpectedEventSeq = [
|
||||
new invokerChecker(EVENT_FOCUS, this.DOMNode.ownerDocument)
|
||||
];
|
||||
|
||||
this.getID = function focusElmWhileSubdocIsFocused_getID()
|
||||
this.getID = function openDialog_getID()
|
||||
{
|
||||
return "Focus element while subdocument is focused " + prettyName(aID);
|
||||
return "Open dialog while focus on " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
function closeDialog(aID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new focusChecker(aID)
|
||||
];
|
||||
|
||||
this.invoke = function closeDialog_invoke()
|
||||
{
|
||||
gDialogWnd.close();
|
||||
}
|
||||
|
||||
this.getID = function closeDialog_getID()
|
||||
{
|
||||
return "Close dialog while focus on " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +90,7 @@
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, this.DOMNode),
|
||||
new invokerChecker(EVENT_FOCUS, this.DOMNode)
|
||||
new focusChecker(this.DOMNode)
|
||||
];
|
||||
|
||||
this.getID = function showNFocusAlertDialog_getID()
|
||||
@ -83,6 +104,7 @@
|
||||
*/
|
||||
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
@ -90,18 +112,14 @@
|
||||
{
|
||||
gQueue = new eventQueue(EVENT_FOCUS);
|
||||
|
||||
gQueue.push(new synthFocus("editablearea"));
|
||||
gQueue.push(new synthFocus("textbox"));
|
||||
|
||||
gQueue.push(new synthFocus("button"));
|
||||
gQueue.push(new openCloseDialog("button"));
|
||||
gQueue.push(new openDialog("button"));
|
||||
gQueue.push(new closeDialog("button"));
|
||||
|
||||
var frameNode = getNode("editabledoc");
|
||||
gQueue.push(new synthFocusOnFrame(frameNode));
|
||||
gQueue.push(new openCloseDialog(frameNode.contentDocument));
|
||||
|
||||
gQueue.push(new focusElmWhileSubdocIsFocused("button"));
|
||||
|
||||
gQueue.push(new showNFocusAlertDialog());
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
@ -119,11 +137,6 @@
|
||||
title="focus is not fired for focused document when switching between windows">
|
||||
Mozilla Bug 551679
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=352220"
|
||||
title=" Inconsistent focus events when returning to a document frame">
|
||||
Mozilla Bug 352220
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=580464"
|
||||
title="Accessible focus incorrect after JS focus() but correct after switching apps or using menu bar">
|
||||
@ -134,8 +147,6 @@
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="editablearea" contentEditable="true">editable area</div>
|
||||
<input id="textbox">
|
||||
<button id="button">button</button>
|
||||
<iframe id="editabledoc" src="focus.html"></iframe>
|
||||
|
@ -7,8 +7,10 @@
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
@ -17,34 +19,10 @@
|
||||
src="../states.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
/**
|
||||
* Focus invoker.
|
||||
*/
|
||||
function takeFocus(aAcc)
|
||||
{
|
||||
this.DOMNode = aAcc; // xxx rename this expected property in events.js
|
||||
|
||||
this.invoke = function takeFocus_invoke()
|
||||
{
|
||||
this.DOMNode.takeFocus();
|
||||
};
|
||||
|
||||
this.check = function takeFocus_check()
|
||||
{
|
||||
testStates(this.DOMNode, STATE_FOCUSABLE | STATE_FOCUSED);
|
||||
};
|
||||
|
||||
this.getID = function takeFocus_getID() { return aAcc.name + " focus"; };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
*/
|
||||
var gQueue = null;
|
||||
|
||||
//var gA11yEventDumpID = "eventdump";
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
function doTests()
|
||||
{
|
||||
@ -54,14 +32,23 @@
|
||||
var frameDocAcc = getAccessible(frameDoc, [nsIAccessibleDocument]);
|
||||
var buttonAcc = getAccessible("b1");
|
||||
|
||||
var frame2Doc = document.getElementById("iframe2").contentDocument;
|
||||
var frame2Input = frame2Doc.getElementById("input");
|
||||
var frame2DocAcc = getAccessible(frame2Doc);
|
||||
var frame2InputAcc = getAccessible(frame2Input);
|
||||
|
||||
// Test focus events.
|
||||
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
|
||||
gQueue = new eventQueue();
|
||||
|
||||
// try to give focus to contentEditable frame twice to cover bug 512059
|
||||
gQueue.push(new takeFocus(buttonAcc));
|
||||
gQueue.push(new takeFocus(frameDocAcc));
|
||||
gQueue.push(new takeFocus(buttonAcc));
|
||||
gQueue.push(new takeFocus(frameDocAcc));
|
||||
gQueue.push(new synthFocus(buttonAcc));
|
||||
gQueue.push(new synthTab(frameDocAcc, new focusChecker(frameDocAcc)));
|
||||
gQueue.push(new synthFocus(buttonAcc));
|
||||
gQueue.push(new synthTab(frameDocAcc, new focusChecker(frameDocAcc)));
|
||||
|
||||
// focus on not editable document
|
||||
gQueue.push(new synthFocus(frame2InputAcc));
|
||||
gQueue.push(new synthShiftTab(frame2DocAcc, new focusChecker(frame2DocAcc)));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
@ -83,6 +70,11 @@
|
||||
title="Accessibility focus event never fired for designMode document after the first focus">
|
||||
Mozilla Bug 512059
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=618046"
|
||||
title="No focus change event when Shift+Tab at top of screen">
|
||||
Mozilla Bug 618046
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
@ -95,6 +87,7 @@
|
||||
<button id="b1">a button</button>
|
||||
<iframe id="iframe" src="about:blank"></iframe>
|
||||
<button id="b2">a button</button>
|
||||
<iframe id="iframe2" src="data:text/html,<html><input id='input'></html>"></iframe>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
150
accessible/tests/mochitest/events/test_focus_general.html
Normal file
150
accessible/tests/mochitest/events/test_focus_general.html
Normal file
@ -0,0 +1,150 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible focus testing</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function focusElmWhileSubdocIsFocused(aID)
|
||||
{
|
||||
this.DOMNode = getNode(aID);
|
||||
|
||||
this.invoke = function focusElmWhileSubdocIsFocused_invoke()
|
||||
{
|
||||
this.DOMNode.focus();
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new focusChecker(this.DOMNode)
|
||||
];
|
||||
|
||||
this.unexpectedEventSeq = [
|
||||
new invokerChecker(EVENT_FOCUS, this.DOMNode.ownerDocument)
|
||||
];
|
||||
|
||||
this.getID = function focusElmWhileSubdocIsFocused_getID()
|
||||
{
|
||||
return "Focus element while subdocument is focused " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
function topMenuChecker()
|
||||
{
|
||||
this.type = EVENT_FOCUS;
|
||||
this.match = function topMenuChecker_match(aEvent)
|
||||
{
|
||||
return aEvent.accessible.role == ROLE_PARENT_MENUITEM;
|
||||
}
|
||||
}
|
||||
|
||||
function contextMenuChecker()
|
||||
{
|
||||
this.type = EVENT_MENUPOPUP_START;
|
||||
this.match = function contextMenuChecker_match(aEvent)
|
||||
{
|
||||
return aEvent.accessible.role == ROLE_MENUPOPUP;
|
||||
}
|
||||
}
|
||||
|
||||
function focusContextMenuItemChecker()
|
||||
{
|
||||
this.__proto__ = new focusChecker();
|
||||
|
||||
this.match = function focusContextMenuItemChecker_match(aEvent)
|
||||
{
|
||||
return aEvent.accessible.role == ROLE_MENUITEM;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
*/
|
||||
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
function doTests()
|
||||
{
|
||||
var frameDoc = document.getElementById("iframe").contentDocument;
|
||||
|
||||
var editableDoc = document.getElementById('editabledoc').contentDocument;
|
||||
editableDoc.designMode = 'on';
|
||||
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new synthFocus("editablearea"));
|
||||
gQueue.push(new synthFocus("navarea"));
|
||||
gQueue.push(new synthTab("navarea", new focusChecker(frameDoc)));
|
||||
gQueue.push(new focusElmWhileSubdocIsFocused("link"));
|
||||
|
||||
gQueue.push(new synthTab(editableDoc, new focusChecker(editableDoc)));
|
||||
if (WIN) {
|
||||
// Alt key is used to active menubar and focus menu item on Windows,
|
||||
// other platforms requires setting a ui.key.menuAccessKeyFocuses
|
||||
// preference.
|
||||
gQueue.push(new toggleTopMenu(editableDoc, new topMenuChecker()));
|
||||
gQueue.push(new toggleTopMenu(editableDoc, new focusChecker(editableDoc)));
|
||||
}
|
||||
gQueue.push(new synthContextMenu(editableDoc, new contextMenuChecker()));
|
||||
gQueue.push(new synthDownKey(editableDoc, new focusContextMenuItemChecker()));
|
||||
gQueue.push(new synthEscapeKey(editableDoc, new focusChecker(editableDoc)));
|
||||
|
||||
todo(false, "shift+tab doesn't issue the focus, see bug 684818");
|
||||
//gQuee.push(new synthShiftTab("link", new focusChecker("link")));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=352220"
|
||||
title="Inconsistent focus events when returning to a document frame">
|
||||
Mozilla Bug 352220
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=550338"
|
||||
title="Broken focus when returning to editable documents from menus">
|
||||
Mozilla Bug 550338
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
|
||||
title="Rework accessible focus handling">
|
||||
Mozilla Bug 673958
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="editablearea" contentEditable="true">editable area</div>
|
||||
<div id="navarea" tabindex="0">navigable area</div>
|
||||
<iframe id="iframe" src="data:text/html,<html></html>"></iframe>
|
||||
<a id="link" href="">link</a>
|
||||
<iframe id="editabledoc" src="about:blank"></iframe>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
171
accessible/tests/mochitest/events/test_focus_general.xul
Normal file
171
accessible/tests/mochitest/events/test_focus_general.xul
Normal file
@ -0,0 +1,171 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Accessible focus event testing">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
<script type="application/javascript"
|
||||
src="../states.js" />
|
||||
<script type="application/javascript"
|
||||
src="../events.js" />
|
||||
|
||||
<script type="application/javascript">
|
||||
function getColorBtn(aBtnObj)
|
||||
{
|
||||
var colorpicker = aBtnObj.colorpicker;
|
||||
var container = colorpicker.firstChild;
|
||||
var btn = container.getChildAt(aBtnObj.btnIndex);
|
||||
return btn;
|
||||
}
|
||||
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
// Test focus events.
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new synthFocus("textbox"));
|
||||
gQueue.push(new synthFocus("textbox_multiline"));
|
||||
gQueue.push(new synthFocus("scale"));
|
||||
gQueue.push(new synthFocusOnFrame("editabledoc"));
|
||||
gQueue.push(new synthFocus("radioclothes",
|
||||
new focusChecker("radiosweater")));
|
||||
gQueue.push(new synthDownKey("radiosweater",
|
||||
new focusChecker("radiojacket")));
|
||||
gQueue.push(new synthFocus("checkbox"));
|
||||
gQueue.push(new synthFocus("button"));
|
||||
gQueue.push(new synthFocus("checkbutton"));
|
||||
gQueue.push(new synthFocus("radiobutton"));
|
||||
|
||||
// focus menubutton
|
||||
gQueue.push(new synthFocus("menubutton"));
|
||||
// click menubutton, open popup, focus stays on menu button
|
||||
gQueue.push(new synthClick("menubutton", new nofocusChecker()));
|
||||
// select first menu item ("item 1"), focus on menu item
|
||||
gQueue.push(new synthDownKey("menubutton", new focusChecker("mb_item1")));
|
||||
// choose select menu item, focus gets back to menubutton
|
||||
gQueue.push(new synthEnterKey("mb_item1", new focusChecker("menubutton")));
|
||||
// press enter to open popup, focus stays on menubutton
|
||||
gQueue.push(new synthEnterKey("menubutton", new nofocusChecker()));
|
||||
// select second menu item ("item 2"), focus on menu item
|
||||
gQueue.push(new synthUpKey("menubutton", new focusChecker("mb_item2")));
|
||||
|
||||
// clicking on button having associated popup doesn't change a focus
|
||||
gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
|
||||
// select first menu item ("item 1"), focus on menu item
|
||||
gQueue.push(new synthDownKey("popupbutton", new focusChecker("bp_item1")));
|
||||
// choose select menu item, focus gets back to menubutton
|
||||
gQueue.push(new synthEnterKey("bp_item1", new focusChecker("menubutton")));
|
||||
// show popup again for the next test
|
||||
gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
|
||||
|
||||
// click menubutton of the 'menubutton' button while popup of button open.
|
||||
gQueue.push(new synthClick("mbb", new focusChecker("mbb"), { where: "right" }));
|
||||
// close popup, focus stays on menubutton, fire focus event
|
||||
gQueue.push(new synthEscapeKey("mbb", new focusChecker("mbb")));
|
||||
// click menubutton, open popup, focus stays on menubutton
|
||||
gQueue.push(new synthClick("mbb", new nofocusChecker(), { where: "right" }));
|
||||
// select first menu item ("item 1"), focus on menu item
|
||||
gQueue.push(new synthDownKey("mbb", new focusChecker("mbb_item1")));
|
||||
// choose select menu item, focus gets back to menubutton
|
||||
gQueue.push(new synthEnterKey("mbb_item1", new focusChecker("mbb")));
|
||||
// open popup, focus stays on menubutton
|
||||
gQueue.push(new synthOpenComboboxKey("mbb", new nofocusChecker()));
|
||||
// select second menu item ("item 2"), focus on menu item
|
||||
gQueue.push(new synthUpKey("menubutton", new focusChecker("mbb_item2")));
|
||||
// click on menu item of menubutton menu, focus menubutton
|
||||
gQueue.push(new synthClick("mbb_item2", new focusChecker("mbb")));
|
||||
|
||||
// focus colorpicker button
|
||||
gQueue.push(new synthFocus("colorpicker"));
|
||||
// click on button, open popup, focus goes to current color button
|
||||
var btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 0 };
|
||||
var checker = new focusChecker(getColorBtn, btnObj);
|
||||
gQueue.push(new synthClick("colorpicker", checker));
|
||||
// select sibling color button, focus on it
|
||||
btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 1 };
|
||||
var checker = new focusChecker(getColorBtn, btnObj);
|
||||
gQueue.push(new synthRightKey("colorpicker", checker));
|
||||
// choose selected color button, close popup, focus on colorpicker button
|
||||
gQueue.push(new synthEnterKey("colorpicker", new focusChecker("colorpicker")));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
|
||||
<hbox flex="1" style="overflow: auto;">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=492518"
|
||||
title="xul:slider accessible of xul:scale is accessible illegally">
|
||||
Mozilla Bug 492518
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=552368"
|
||||
title=" fire focus event on document accessible whenever the root or body element is focused">
|
||||
Mozilla Bug 552368
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<vbox flex="1">
|
||||
<textbox id="textbox" value="hello"/>
|
||||
<textbox id="textbox_multiline" multiline="true" value="hello"/>
|
||||
<scale id="scale" min="0" max="9" value="5"/>
|
||||
<iframe id="editabledoc" src="focus.html"/>
|
||||
<radiogroup id="radioclothes">
|
||||
<radio id="radiosweater" label="radiosweater"/>
|
||||
<radio id="radiocap" label="radiocap" disabled="true"/>
|
||||
<radio id="radiojacket" label="radiojacket"/>
|
||||
</radiogroup>
|
||||
<checkbox id="checkbox" label="checkbox"/>
|
||||
<button id="button" label="button"/>
|
||||
<button id="checkbutton" type="checkbox" label="checkbutton"/>
|
||||
<button id="radiobutton" type="radio" group="rbgroup" label="radio1"/>
|
||||
|
||||
<button id="menubutton" type="menu" label="menubutton">
|
||||
<menupopup>
|
||||
<menuitem id="mb_item1" label="item1"/>
|
||||
<menuitem id="mb_item2" label="item2"/>
|
||||
</menupopup>
|
||||
</button>
|
||||
<button id="mbb" type="menu-button" label="menubutton button">
|
||||
<menupopup>
|
||||
<menuitem id="mbb_item1" label="item1"/>
|
||||
<menuitem id="mbb_item2" label="item2"/>
|
||||
</menupopup>
|
||||
</button>
|
||||
|
||||
<colorpicker id="colorpicker" type="button" label="color picker"
|
||||
color="#FFFFFF"/>
|
||||
|
||||
<popupset>
|
||||
<menupopup id="backpopup" position="after_start">
|
||||
<menuitem id="bp_item1" label="Page 1"/>
|
||||
<menuitem id="bp_item2" label="Page 2"/>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
<button id="popupbutton" label="Pop Me Up" popup="backpopup"/>
|
||||
|
||||
<vbox id="eventdump"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</window>
|
179
accessible/tests/mochitest/events/test_focus_listcontrols.xul
Normal file
179
accessible/tests/mochitest/events/test_focus_listcontrols.xul
Normal file
@ -0,0 +1,179 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Accessible focus event testing">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
<script type="application/javascript"
|
||||
src="../states.js" />
|
||||
<script type="application/javascript"
|
||||
src="../events.js" />
|
||||
|
||||
<script type="application/javascript">
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
// Test focus events.
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new synthFocus("listbox", new focusChecker("lb_item1")));
|
||||
gQueue.push(new synthDownKey("lb_item1", new focusChecker("lb_item2")));
|
||||
gQueue.push(new synthTab("lb_item2", new focusChecker("mslb_item1")));
|
||||
gQueue.push(new synthDownKey("mslb_item1", new focusChecker("mslb_item2"), { shiftKey: true }));
|
||||
gQueue.push(new synthTab("mslb_item2", new focusChecker("emptylistbox")));
|
||||
gQueue.push(new synthFocus("mcolumnlistbox", new focusChecker("mclb_item1")));
|
||||
gQueue.push(new synthDownKey("mclb_item1", new focusChecker("mclb_item2")));
|
||||
gQueue.push(new synthFocus("headerlistbox", new focusChecker("hlb_item1")));
|
||||
gQueue.push(new synthDownKey("hlb_item1", new focusChecker("hlb_item2")));
|
||||
|
||||
gQueue.push(new synthFocus("richlistbox", new focusChecker("rlb_item1")));
|
||||
gQueue.push(new synthDownKey("rlb_item1", new focusChecker("rlb_item2")));
|
||||
gQueue.push(new synthFocus("multiselrichlistbox", new focusChecker("msrlb_item1")));
|
||||
gQueue.push(new synthDownKey("msrlb_item1", new focusChecker("msrlb_item2"), { shiftKey: true }));
|
||||
gQueue.push(new synthFocus("emptyrichlistbox", new focusChecker("emptyrichlistbox")));
|
||||
|
||||
gQueue.push(new synthFocus("menulist"));
|
||||
gQueue.push(new synthClick("menulist", new focusChecker("ml_tangerine")));
|
||||
gQueue.push(new synthDownKey("ml_tangerine", new focusChecker("ml_marmalade")));
|
||||
gQueue.push(new synthEscapeKey("ml_marmalade", new focusChecker("menulist")));
|
||||
gQueue.push(new synthDownKey("menulist", new nofocusChecker("ml_marmalade")));
|
||||
gQueue.push(new synthOpenComboboxKey("menulist", new focusChecker("ml_marmalade")));
|
||||
gQueue.push(new synthEnterKey("ml_marmalade", new focusChecker("menulist")));
|
||||
|
||||
var textentry = getAccessible("emenulist").firstChild;
|
||||
gQueue.push(new synthFocus("emenulist", new focusChecker(textentry)));
|
||||
gQueue.push(new synthDownKey(textentry, new nofocusChecker("eml_tangerine")));
|
||||
gQueue.push(new synthUpKey(textentry, new focusChecker("eml_marmalade")));
|
||||
gQueue.push(new synthEnterKey("eml_marmalade", new focusChecker(textentry)));
|
||||
gQueue.push(new synthOpenComboboxKey("emenulist", new focusChecker("eml_marmalade")));
|
||||
gQueue.push(new synthEscapeKey("eml_marmalade", new focusChecker(textentry)));
|
||||
|
||||
// no focus events for unfocused list controls when current item is
|
||||
// changed.
|
||||
gQueue.push(new synthFocus("emptylistbox"));
|
||||
|
||||
gQueue.push(new changeCurrentItem("listbox", "lb_item1"));
|
||||
gQueue.push(new changeCurrentItem("richlistbox", "rlb_item1"));
|
||||
gQueue.push(new changeCurrentItem("menulist", "ml_tangerine"));
|
||||
gQueue.push(new changeCurrentItem("emenulist", "eml_tangerine"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
|
||||
<hbox flex="1" style="overflow: auto;">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=433418"
|
||||
title="Accessibles for focused HTML Select elements are not getting focused state">
|
||||
Mozilla Bug 433418
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=474893"
|
||||
title="List controls should fire a focus event on the selected child when tabbing or when the selected child changes while the list is focused">
|
||||
Mozilla Bug 474893
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=552368"
|
||||
title=" fire focus event on document accessible whenever the root or body element is focused">
|
||||
Mozilla Bug 552368
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<vbox flex="1">
|
||||
<listbox id="listbox" rows="3">
|
||||
<listitem id="lb_item1" label="item1"/>
|
||||
<listitem id="lb_item2" label="item1"/>
|
||||
</listbox>
|
||||
<listbox id="multisellistbox" rows="3" seltype="multiple">
|
||||
<listitem id="mslb_item1" label="item1"/>
|
||||
<listitem id="mslb_item2" label="item1"/>
|
||||
</listbox>
|
||||
<listbox id="emptylistbox" rows="3"/>
|
||||
<listbox id="mcolumnlistbox" rows="3">
|
||||
<listcols>
|
||||
<listcol/>
|
||||
<listcol/>
|
||||
</listcols>
|
||||
<listitem id="mclb_item1">
|
||||
<listcell label="George"/>
|
||||
<listcell label="House Painter"/>
|
||||
</listitem>
|
||||
<listitem id="mclb_item2">
|
||||
<listcell label="Mary Ellen"/>
|
||||
<listcell label="Candle Maker"/>
|
||||
</listitem>
|
||||
</listbox>
|
||||
<listbox id="headerlistbox" rows="3">
|
||||
<listhead>
|
||||
<listheader label="Name"/>
|
||||
<listheader label="Occupation"/>
|
||||
</listhead>
|
||||
<listcols>
|
||||
<listcol/>
|
||||
<listcol flex="1"/>
|
||||
</listcols>
|
||||
<listitem id="hlb_item1">
|
||||
<listcell label="George"/>
|
||||
<listcell label="House Painter"/>
|
||||
</listitem>
|
||||
<listitem id="hlb_item2">
|
||||
<listcell label="Mary Ellen"/>
|
||||
<listcell label="Candle Maker"/>
|
||||
</listitem>
|
||||
</listbox>
|
||||
|
||||
<richlistbox id="richlistbox">
|
||||
<richlistitem id="rlb_item1">
|
||||
<description>A XUL Description!</description>
|
||||
</richlistitem>
|
||||
<richlistitem id="rlb_item2">
|
||||
<button label="A XUL Button"/>
|
||||
</richlistitem>
|
||||
</richlistbox>
|
||||
<richlistbox id="multiselrichlistbox" seltype="multiple">
|
||||
<richlistitem id="msrlb_item1">
|
||||
<description>A XUL Description!</description>
|
||||
</richlistitem>
|
||||
<richlistitem id="msrlb_item2">
|
||||
<button label="A XUL Button"/>
|
||||
</richlistitem>
|
||||
</richlistbox>
|
||||
<richlistbox id="emptyrichlistbox" seltype="multiple"/>
|
||||
|
||||
<menulist id="menulist">
|
||||
<menupopup>
|
||||
<menuitem id="ml_tangerine" label="tangerine trees"/>
|
||||
<menuitem id="ml_marmalade" label="marmalade skies"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<menulist id="emenulist" editable="true">
|
||||
<menupopup>
|
||||
<menuitem id="eml_tangerine" label="tangerine trees"/>
|
||||
<menuitem id="eml_marmalade" label="marmalade skies"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
|
||||
<vbox id="eventdump"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</window>
|
115
accessible/tests/mochitest/events/test_focus_menu.xul
Normal file
115
accessible/tests/mochitest/events/test_focus_menu.xul
Normal file
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Menu focus testing">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
<script type="application/javascript"
|
||||
src="../states.js" />
|
||||
<script type="application/javascript"
|
||||
src="../events.js" />
|
||||
|
||||
<script type="application/javascript">
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
// Test focus events.
|
||||
gQueue = new eventQueue();
|
||||
|
||||
if (WIN) {
|
||||
gQueue.push(new toggleTopMenu("fruit", new focusChecker("fruit")));
|
||||
gQueue.push(new synthRightKey("fruit", new focusChecker("vehicle")));
|
||||
gQueue.push(new synthEscapeKey("vehicle", new focusChecker(document)));
|
||||
}
|
||||
|
||||
// mouse move activate items but no focus event until menubar is active
|
||||
gQueue.push(new synthMouseMove("fruit", new nofocusChecker("apple")));
|
||||
|
||||
// mouseover and click on menuitem makes it active before menubar is
|
||||
// active
|
||||
gQueue.push(new synthClick("fruit", new focusChecker("fruit")));
|
||||
|
||||
// mouseover on menuitem when menubar is active
|
||||
gQueue.push(new synthMouseMove("apple", new focusChecker("apple")));
|
||||
|
||||
// keydown on disabled menuitem (disabled items are skipped on linux)
|
||||
if (WIN)
|
||||
gQueue.push(new synthDownKey("apple", new focusChecker("orange")));
|
||||
|
||||
// menu and menuitem are both active
|
||||
// XXX: intermitent failure because two focus events may be coalesced,
|
||||
// think to workaround or fix this issue, when done enable queue invoker
|
||||
// below and remove next two.
|
||||
//gQueue.push(new synthRightKey("apple",
|
||||
// [ new focusChecker("vehicle"),
|
||||
// new focusChecker("cycle")]));
|
||||
gQueue.push(new synthClick("vehicle", new focusChecker("vehicle")));
|
||||
gQueue.push(new synthMouseMove("cycle", new focusChecker("cycle")));
|
||||
|
||||
// open submenu
|
||||
gQueue.push(new synthRightKey("cycle", new focusChecker("tricycle")));
|
||||
|
||||
// move to first menu in cycle, DOMMenuItemActive is fired for fruit,
|
||||
// cycle and apple menuitems (bug 685191)
|
||||
todo(false, "focus is fired for 'cycle' menuitem");
|
||||
//gQueue.push(new synthRightKey("vehicle", new focusChecker("apple")));
|
||||
|
||||
// click menuitem to close menu, focus gets back to document
|
||||
gQueue.push(new synthClick("tricycle", new focusChecker(document)));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
|
||||
<hbox flex="1" style="overflow: auto;">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
|
||||
title="Rework accessible focus handling">
|
||||
Mozilla Bug 673958
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<vbox flex="1">
|
||||
<menubar>
|
||||
<menu id="fruit" label="Fruit">
|
||||
<menupopup>
|
||||
<menuitem id="apple" label="Apple"/>
|
||||
<menuitem id="orange" label="Orange" disabled="true"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menu id="vehicle" label="Vehicle">
|
||||
<menupopup>
|
||||
<menu id="cycle" label="cycle">
|
||||
<menupopup>
|
||||
<menuitem id="tricycle" label="tricycle"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuitem id="car" label="Car" disabled="true"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
</menubar>
|
||||
|
||||
<vbox id="eventdump"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</window>
|
@ -20,8 +20,10 @@
|
||||
/**
|
||||
* Checker for invokers.
|
||||
*/
|
||||
function actionChecker(aDescription)
|
||||
function actionChecker(aID, aDescription)
|
||||
{
|
||||
this.__proto__ = new invokerChecker(EVENT_FOCUS, aID);
|
||||
|
||||
this.check = function actionChecker_check(aEvent)
|
||||
{
|
||||
var target = aEvent.accessible;
|
||||
@ -57,6 +59,7 @@
|
||||
*/
|
||||
|
||||
// gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
@ -86,9 +89,9 @@
|
||||
gTextboxElm.removeEventListener("blur", gBlurHandler, false);
|
||||
}
|
||||
|
||||
var checker = new actionChecker("It's a tooltip");
|
||||
gQueue.push(new synthFocus("button", checker));
|
||||
gQueue.push(new synthTab("textbox", checker));
|
||||
var descr = "It's a tooltip";
|
||||
gQueue.push(new synthFocus("button", new actionChecker("button", descr)));
|
||||
gQueue.push(new synthTab("textbox", new actionChecker("textbox", descr)));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
109
accessible/tests/mochitest/events/test_focus_selects.html
Normal file
109
accessible/tests/mochitest/events/test_focus_selects.html
Normal file
@ -0,0 +1,109 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible focus testing</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
// first item is focused until there's selection
|
||||
gQueue.push(new synthFocus("list", new focusChecker("orange")));
|
||||
|
||||
// item is selected and stays focused
|
||||
gQueue.push(new synthDownKey("list", new nofocusChecker()));
|
||||
|
||||
// last selected item is focused
|
||||
gQueue.push(new synthDownKey("list", new focusChecker("apple"), { shiftKey: true }));
|
||||
|
||||
// no focus event if nothing is changed
|
||||
gQueue.push(new synthDownKey("list", new nofocusChecker("apple")));
|
||||
|
||||
// current item is focused
|
||||
gQueue.push(new synthUpKey("list", new focusChecker("orange"), { ctrlKey: true }));
|
||||
|
||||
// focus on empty list (no items to be focused)
|
||||
gQueue.push(new synthTab("list", new focusChecker("emptylist")));
|
||||
|
||||
// current item is focused
|
||||
gQueue.push(new synthShiftTab("emptylist", new focusChecker("orange")));
|
||||
|
||||
// collapsed combobox keeps a focus
|
||||
gQueue.push(new synthFocus("combobox", new focusChecker("combobox")));
|
||||
gQueue.push(new synthDownKey("combobox", new nofocusChecker("combobox")));
|
||||
|
||||
// selected item is focused for expanded combobox
|
||||
gQueue.push(new synthOpenComboboxKey("combobox", new focusChecker("cb_apple")));
|
||||
gQueue.push(new synthUpKey("combobox", new focusChecker("cb_orange")));
|
||||
|
||||
// collapsed combobx keeps a focus
|
||||
gQueue.push(new synthEscapeKey("combobox", new focusChecker("combobox")));
|
||||
|
||||
// no focus events for unfocused list controls when current item is
|
||||
// changed
|
||||
gQueue.push(new synthFocus("emptylist"));
|
||||
|
||||
gQueue.push(new changeCurrentItem("list", "orange"));
|
||||
gQueue.push(new changeCurrentItem("combobox", "cb_apple"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=433418"
|
||||
title="Accessibles for focused HTML Select elements are not getting focused state">
|
||||
Mozilla Bug 433418
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=474893"
|
||||
title="List controls should fire a focus event on the selected child when tabbing or when the selected child changes while the list is focused">
|
||||
Mozilla Bug 474893
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<select id="list" size="5" multiple="">
|
||||
<option id="orange">Orange</option>
|
||||
<option id="apple">Apple</option>
|
||||
</select>
|
||||
|
||||
<select id="emptylist" size="5"></select>
|
||||
|
||||
<select id="combobox">
|
||||
<option id="cb_orange">Orange</option>
|
||||
<option id="cb_apple">Apple</option>
|
||||
</select>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
@ -18,6 +18,8 @@
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
<script type="application/javascript"
|
||||
src="../states.js" />
|
||||
<script type="application/javascript"
|
||||
src="../events.js" />
|
||||
|
||||
@ -46,13 +48,13 @@
|
||||
|
||||
function focusTree(aTreeID)
|
||||
{
|
||||
var checker = new invokerChecker(EVENT_FOCUS, getFirstTreeItem, aTreeID);
|
||||
var checker = new focusChecker(getFirstTreeItem, aTreeID);
|
||||
this.__proto__ = new synthFocus(aTreeID, [ checker ]);
|
||||
}
|
||||
|
||||
function moveToNextItem(aTreeID)
|
||||
{
|
||||
var checker = new invokerChecker(EVENT_FOCUS, getSecondTreeItem, aTreeID);
|
||||
var checker = new focusChecker(getSecondTreeItem, aTreeID);
|
||||
this.__proto__ = new synthDownKey(aTreeID, [ checker ]);
|
||||
}
|
||||
|
||||
@ -83,6 +85,10 @@
|
||||
gQueue.push(new setTreeView("tree", new nsTableTreeView(5)));
|
||||
gQueue.push(new focusTree("tree"));
|
||||
gQueue.push(new moveToNextItem("tree"));
|
||||
gQueue.push(new synthFocus("emptytree"));
|
||||
|
||||
// no focus event for changed current item for unfocused tree
|
||||
gQueue.push(new changeCurrentItem("tree", 0));
|
||||
|
||||
gQueue.invoke();
|
||||
}
|
||||
@ -98,7 +104,12 @@
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=386821"
|
||||
title="Need better solution for firing delayed event against xul tree">
|
||||
Mozilla Bug 386821
|
||||
</a><br/>
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=406308"
|
||||
title="Don't fire accessible focus events if widget is not actually in focus, confuses screen readers">
|
||||
Mozilla Bug 406308
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
@ -114,6 +125,13 @@
|
||||
</treecols>
|
||||
<treechildren id="treechildren"/>
|
||||
</tree>
|
||||
<tree id="emptytree" flex="1">
|
||||
<treecols>
|
||||
<treecol id="emptytree_col1" flex="1" primary="true" label="column"/>
|
||||
<treecol id="emptytree_col2" flex="1" label="column 2"/>
|
||||
</treecols>
|
||||
<treechildren id="emptytree_treechildren"/>
|
||||
</tree>
|
||||
</hbox>
|
||||
|
||||
</window>
|
||||
|
@ -77,8 +77,8 @@
|
||||
function focusFileMenu()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_MENU_START, getNode("menubar")),
|
||||
new invokerChecker(EVENT_FOCUS, getNode("menuitem-file"))
|
||||
new invokerChecker(EVENT_MENU_START, getNode("menubar"))
|
||||
// new invokerChecker(EVENT_FOCUS, getNode("menuitem-file")) //intermitent failure
|
||||
];
|
||||
|
||||
this.invoke = function focusFileMenu_invoke()
|
||||
@ -132,7 +132,7 @@
|
||||
*/
|
||||
|
||||
//gA11yEventDumpID = "eventdump";
|
||||
gA11yEventDumpToConsole = true;
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
|
@ -43,7 +43,7 @@
|
||||
offset + str.length, str,
|
||||
isInserted);
|
||||
|
||||
if (eventItem[3] == kUnexpected)
|
||||
if (event[3] == kUnexpected)
|
||||
this.unexpectedEventSeq.push(checker);
|
||||
else
|
||||
this.eventSeq.push(checker);
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
<script type="application/javascript"
|
||||
@ -80,7 +82,7 @@
|
||||
// gets a time for initialization.
|
||||
|
||||
gQueue = new eventQueue();
|
||||
gQueue.push(new synthFocus("input", null, EVENT_FOCUS));
|
||||
gQueue.push(new synthFocus("input"));
|
||||
gQueue.push(new spelledTextInvoker("input"));
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
@ -26,18 +26,13 @@
|
||||
{
|
||||
this.accessible = getAccessible(aID);
|
||||
|
||||
this.eventSeq = [ new invokerChecker(EVENT_FOCUS, this.accessible) ];
|
||||
this.eventSeq = [ new focusChecker(this.accessible) ];
|
||||
|
||||
this.invoke = function takeFocusInvoker_invoke()
|
||||
{
|
||||
this.accessible.takeFocus();
|
||||
}
|
||||
|
||||
this.finalCheck = function takeFocusInvoker()
|
||||
{
|
||||
testStates(this.accessible, STATE_FOCUSED);
|
||||
}
|
||||
|
||||
this.getID = function takeFocusInvoker_getID()
|
||||
{
|
||||
return "takeFocus for " + prettyName(aID);
|
||||
@ -51,6 +46,9 @@
|
||||
gQueue.push(new takeFocusInvoker("aria-link"));
|
||||
gQueue.push(new takeFocusInvoker("aria-link2"));
|
||||
gQueue.push(new takeFocusInvoker("link"));
|
||||
gQueue.push(new takeFocusInvoker("item2"));
|
||||
gQueue.push(new takeFocusInvoker("plugin"));
|
||||
gQueue.push(new takeFocusInvoker(document));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
@ -62,11 +60,21 @@
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=429547"
|
||||
title="Support aria-activedescendant usage in nsIAccesible::TakeFocus()">
|
||||
Mozilla Bug 429547
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=452710"
|
||||
title="nsIAccessible::takeFocus testing">
|
||||
Mozilla Bug 452710
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=646361"
|
||||
title="No focus event fired on document when focus is set to the document while focused in a plugin">
|
||||
Mozilla Bug 646361
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
@ -77,5 +85,12 @@
|
||||
|
||||
<a id="link" href="">link</span>
|
||||
|
||||
<div role="listbox" aria-activedescendant="item1" id="container" tabindex="1">
|
||||
<div role="listitem" id="item1">item1</div>
|
||||
<div role="listitem" id="item2">item2</div>
|
||||
<div role="listitem" id="item3">item3</div>
|
||||
</div>
|
||||
|
||||
<embed id="plugin" type="application/x-test" width="200" height="200" wmode="window"></embed>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,111 +0,0 @@
|
||||
/**
|
||||
* Tests an accessible and its children.
|
||||
* The accessible is one of the following:
|
||||
* - HTML:select (ROLE_COMBOBOX)
|
||||
* - HTML:select @size (ROLE_LIST)
|
||||
* - HTML:label containing either of the above (ROLE_LABEL)
|
||||
*
|
||||
* @param aID The ID of the base element to test.
|
||||
* @param aNames The array of expected names for the accessible and
|
||||
* its children.
|
||||
* @param aRoles The array of expected roles for the accessible and
|
||||
* its children.
|
||||
* @param aStates The array of states to test for on each accessible.
|
||||
* @param aUndesiredStates Array of states we don't want to have for each
|
||||
* accessible.
|
||||
*
|
||||
* @note Each of the three arrays must have an equal number of elements. The
|
||||
* order of elements corresponds to the order in which the tree is walked from
|
||||
* the base accessible downwards.
|
||||
*/
|
||||
function testSelect(aID, aNames, aRoles, aStates, aUndesiredStates)
|
||||
{
|
||||
// used to walk the tree starting at the aID's accessible.
|
||||
var acc = getAccessible(aID);
|
||||
if (!acc) {
|
||||
return;
|
||||
}
|
||||
|
||||
testThis(aID, acc, aNames, aRoles, aStates, aUndesiredStates, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a single accessible.
|
||||
* Recursively calls itself until it reaches the last option item.
|
||||
* Called first time from the testSelect function.
|
||||
*
|
||||
* @param aID @see testSelect
|
||||
* @param aAcc The accessible to test.
|
||||
* @param aNames @see testSelect
|
||||
* @param aRoles @see testSelect
|
||||
* @param aStates @see testSelect
|
||||
* @param aUndesiredStates @see testSelect
|
||||
* @param aIndex The index in the three arrays to use. Used for both
|
||||
* the error message and for walking the tree downwards
|
||||
* from the base accessible.
|
||||
*/
|
||||
function testThis(aID, aAcc, aNames, aRoles, aStates, aUndesiredStates, aIndex)
|
||||
{
|
||||
if (aIndex >= aNames.length)
|
||||
return; // End of test
|
||||
else if (!aAcc) {
|
||||
ok(false, "No accessible for " + aID + " at index " + aIndex + "!");
|
||||
return;
|
||||
}
|
||||
|
||||
is(aAcc.name, aNames[aIndex],
|
||||
"wrong name for " + aID + " at index " + aIndex + "!");
|
||||
var role = getRole(aAcc);
|
||||
is(role, aRoles[aIndex],
|
||||
"Wrong role for " + aID + " at index " + aIndex + "!");
|
||||
testStates(aID, aAcc, aStates, aUndesiredStates, aIndex);
|
||||
switch(role) {
|
||||
case ROLE_COMBOBOX:
|
||||
case ROLE_COMBOBOX_LIST:
|
||||
case ROLE_LABEL:
|
||||
case ROLE_LIST:
|
||||
// All of these expect the next item to be the first child of the current
|
||||
// accessible.
|
||||
var acc = null;
|
||||
try {
|
||||
acc = aAcc.firstChild;
|
||||
} catch(e) {}
|
||||
testThis(aID, acc, aNames, aRoles, aStates, aUndesiredStates, ++aIndex);
|
||||
break;
|
||||
case ROLE_COMBOBOX_OPTION:
|
||||
case ROLE_OPTION:
|
||||
case ROLE_TEXT_LEAF:
|
||||
// All of these expect the next item's accessible to be the next sibling.
|
||||
var acc = null;
|
||||
try {
|
||||
acc = aAcc.nextSibling;
|
||||
} catch(e) {}
|
||||
testThis(aID, acc, aNames, aRoles, aStates, aUndesiredStates, ++aIndex);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the states for the given accessible.
|
||||
* Does not test for extraStates since we don't need these.
|
||||
*
|
||||
* @param aID the ID of the base element.
|
||||
* @param aAcc the current accessible from the recursive algorithm.
|
||||
* @param aStates the states array passed down from the testThis function.
|
||||
* @param aUndesiredStates the array of states we don't want to see, if any.
|
||||
* @param aIndex the index of the item to test, determined and passed in from
|
||||
* the testThis function.
|
||||
*/
|
||||
function testStates(aID, aAcc, aStates, aUndesiredStates, aIndex)
|
||||
{
|
||||
var state = {}, extraState = {};
|
||||
aAcc.getState(state, extraState);
|
||||
if (aStates[aIndex] != 0)
|
||||
is(state.value & aStates[aIndex], aStates[aIndex],
|
||||
"Wrong state bits for " + aID + " at index " + aIndex + "!");
|
||||
if (aUndesiredStates[aIndex] != 0)
|
||||
is(state.value & aUndesiredStates[aIndex], 0,
|
||||
"Wrong undesired state bits for " + aID + " at index " + aIndex + "!");
|
||||
}
|
@ -89,6 +89,16 @@ function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState,
|
||||
|
||||
// Additional test.
|
||||
|
||||
// focused/focusable
|
||||
if (state & STATE_FOCUSED)
|
||||
isState(state & STATE_FOCUSABLE, STATE_FOCUSABLE, false,
|
||||
"Focussed " + id + " must be focusable!");
|
||||
|
||||
if (aAbsentState && (aAbsentState & STATE_FOCUSABLE)) {
|
||||
isState(state & STATE_FOCUSED, 0, false,
|
||||
"Not focusable " + id + " must be not focused!");
|
||||
}
|
||||
|
||||
// readonly/editable
|
||||
if (state & STATE_READONLY)
|
||||
isState(extraState & EXT_STATE_EDITABLE, 0, true,
|
||||
@ -110,7 +120,7 @@ function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState,
|
||||
// expanded/collapsed/expandable
|
||||
if (state & STATE_COLLAPSED || state & STATE_EXPANDED)
|
||||
isState(extraState & EXT_STATE_EXPANDABLE, EXT_STATE_EXPANDABLE, true,
|
||||
"Collapsed or expanded " + id + " should be expandable!");
|
||||
"Collapsed or expanded " + id + " must be expandable!");
|
||||
|
||||
if (state & STATE_COLLAPSED)
|
||||
isState(state & STATE_EXPANDED, 0, false,
|
||||
@ -120,6 +130,16 @@ function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState,
|
||||
isState(state & STATE_COLLAPSED, 0, false,
|
||||
"Expanded " + id + " cannot be collapsed!");
|
||||
|
||||
if (aAbsentState && (extraState & EXT_STATE_EXPANDABLE)) {
|
||||
if (aAbsentState & STATE_EXPANDED) {
|
||||
isState(state & STATE_COLLAPSED, STATE_COLLAPSED, false,
|
||||
"Not expanded " + id + " must be collapsed!");
|
||||
} else if (aAbsentState & STATE_COLLAPSED) {
|
||||
isState(state & STATE_EXPANDED, STATE_EXPANDED, false,
|
||||
"Not collapsed " + id + " must be expanded!");
|
||||
}
|
||||
}
|
||||
|
||||
// checked/mixed/checkable
|
||||
if (state & STATE_CHECKED || state & STATE_MIXED)
|
||||
isState(state & STATE_CHECKABLE, STATE_CHECKABLE, false,
|
||||
@ -136,7 +156,7 @@ function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState,
|
||||
// selected/selectable
|
||||
if (state & STATE_SELECTED) {
|
||||
isState(state & STATE_SELECTABLE, STATE_SELECTABLE, false,
|
||||
"Selected element should be selectable!");
|
||||
"Selected element must be selectable!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,15 +49,16 @@ _TEST_FILES =\
|
||||
test_aria.html \
|
||||
test_aria_imgmap.html \
|
||||
test_aria_tabs.html \
|
||||
test_comboboxes.xul \
|
||||
test_doc.html \
|
||||
test_docarticle.html \
|
||||
test_editablebody.html \
|
||||
test_expandable.xul \
|
||||
test_frames.html \
|
||||
test_inputs.html \
|
||||
test_inputs.xul \
|
||||
test_link.html \
|
||||
test_popup.xul \
|
||||
test_selects.html \
|
||||
test_stale.html \
|
||||
test_textbox.xul \
|
||||
test_tree.xul \
|
||||
|
@ -6,56 +6,42 @@
|
||||
type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="nsIAccessible interface for xul:menulist test.">
|
||||
title="Expanded state change events tests for comboboxes and autocompletes.">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../autocomplete.js" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
<script type="application/javascript"
|
||||
src="../states.js" />
|
||||
<script type="application/javascript"
|
||||
src="../events.js" />
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
function openHideCombobox(aComboboxNodeOrID, aIsOpen)
|
||||
{
|
||||
this.invoke = function invoke()
|
||||
{
|
||||
synthesizeMouse(this.DOMNode, 5, 5, {});
|
||||
}
|
||||
this.check = function check(aEvent)
|
||||
{
|
||||
aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
|
||||
|
||||
var id = this.getID();
|
||||
is(aEvent.state, nsIAccessibleStates.STATE_EXPANDED,
|
||||
"Wrong state change event is handled in test '" + id + "'.");
|
||||
is(aEvent.isEnabled(), this.mIsOpen,
|
||||
"Wrong value of state expanded in test '" + id + "'.");
|
||||
}
|
||||
this.getID = function getID()
|
||||
{
|
||||
if (this.mIsOpen)
|
||||
return this.DOMNodeOrID + " open combobox";
|
||||
return this.DOMNodeOrID + " close combobox";
|
||||
}
|
||||
|
||||
this.DOMNodeOrID = aComboboxNodeOrID;
|
||||
this.DOMNode = getNode(aComboboxNodeOrID);
|
||||
this.mIsOpen = aIsOpen;
|
||||
}
|
||||
//gA11yEventDumpToConsole = true; // debuggin
|
||||
|
||||
var gQueue = null;
|
||||
function doTest()
|
||||
{
|
||||
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_STATE_CHANGE);
|
||||
// register 'test-a11y-search' autocomplete search
|
||||
initAutoComplete([ "hello", "hi" ],
|
||||
[ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
|
||||
|
||||
var ID = "menulist";
|
||||
gQueue.push(new openHideCombobox(ID, true));
|
||||
gQueue.push(new openHideCombobox(ID, false));
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new openCombobox("menulist"));
|
||||
gQueue.push(new closeCombobox("menulist"));
|
||||
|
||||
todo(false, "Autocompletes don't fire expanded state change events when popup open. See bug 688480!");
|
||||
//gQueue.push(new openCombobox("autocomplete"));
|
||||
//gQueue.push(new closeCombobox("autocomplete"));
|
||||
|
||||
// XXX: searchbar doesn't fire state change events because accessible
|
||||
// parent of combobox_list accessible is pushbutton accessible.
|
||||
@ -64,6 +50,12 @@
|
||||
//gQueue.push(new openHideCombobox(searchbar, false));
|
||||
todo(false, "Enable states test for XUL searchbar widget!");
|
||||
|
||||
gQueue.onFinish = function()
|
||||
{
|
||||
// unregister 'test-a11y-search' autocomplete search
|
||||
shutdownAutoComplete();
|
||||
}
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -103,6 +95,9 @@
|
||||
</menupopup>
|
||||
</menulist>
|
||||
|
||||
<textbox id="autocomplete" type="autocomplete"
|
||||
autocompletesearch="test-a11y-search"/>
|
||||
|
||||
<searchbar id="searchbar"/>
|
||||
</vbox>
|
||||
</hbox>
|
81
accessible/tests/mochitest/states/test_selects.html
Normal file
81
accessible/tests/mochitest/states/test_selects.html
Normal file
@ -0,0 +1,81 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>HTML selects accessible states tests</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function doTest()
|
||||
{
|
||||
// combobox
|
||||
var combobox = getAccessible("combobox");
|
||||
testStates(combobox,
|
||||
STATE_HASPOPUP | STATE_COLLAPSED | STATE_FOCUSABLE, 0,
|
||||
STATE_FOCUSED, 0);
|
||||
|
||||
var comboboxList = combobox.firstChild;
|
||||
testStates(comboboxList, 0, 0, STATE_FOCUSABLE, 0);
|
||||
|
||||
var opt1 = comboboxList.firstChild;
|
||||
testStates(opt1, STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE, 0,
|
||||
STATE_FOCUSED, 0);
|
||||
|
||||
var opt2 = comboboxList.lastChild;
|
||||
testStates(opt2, STATE_SELECTABLE | STATE_FOCUSABLE, 0, STATE_SELECTED, 0,
|
||||
STATE_FOCUSED, 0);
|
||||
|
||||
// listbox
|
||||
var listbox = getAccessible("listbox");
|
||||
testStates(listbox, STATE_FOCUSABLE, 0,
|
||||
STATE_HASPOPUP | STATE_COLLAPSED | STATE_FOCUSED, 0);
|
||||
|
||||
testStates(listbox.firstChild, STATE_SELECTABLE, 0,
|
||||
STATE_SELECTED | STATE_FOCUSED | STATE_FOCUSED, 0);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=443889"
|
||||
title="mochitest for selects and lists">
|
||||
Mozilla Bug 443889
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=640716"
|
||||
title="mochitest for selects and lists">
|
||||
Mozilla Bug 640716
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<select id="combobox">
|
||||
<option>item 1</option>
|
||||
<option>item 2</option>
|
||||
</select>
|
||||
|
||||
<select id="listbox" name="component" size="3">
|
||||
<option>Build</option>
|
||||
<option>Disability Access APIs</option>
|
||||
<option>General</option>
|
||||
<option>UI</option>
|
||||
</select>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,70 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=429547
|
||||
-->
|
||||
<head>
|
||||
<title>aria-activedescendant property chrome tests</title>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="common.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
const ELEMENT_NODE = nsIDOMNode.ELEMENT_NODE;
|
||||
|
||||
var gContainerFocused = false;
|
||||
|
||||
function doTest()
|
||||
{
|
||||
var focusHandler = {
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
var target = aEvent.target;
|
||||
if (target.nodeType == ELEMENT_NODE &&
|
||||
target.getAttribute("id") == "container")
|
||||
gContainerFocused = true;
|
||||
}
|
||||
};
|
||||
|
||||
var container = document.getElementById("container");
|
||||
container.addEventListener("focus", focusHandler, false);
|
||||
|
||||
var item2Acc = getAccessible("item2");
|
||||
if (item2Acc) {
|
||||
item2Acc.takeFocus();
|
||||
|
||||
ok(gContainerFocused,
|
||||
"Container with active descendant didn't receive the focus.");
|
||||
}
|
||||
|
||||
container.removeEventListener("focus", focusHandler, false);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=429547"
|
||||
title="Support aria-activedescendant usage in nsIAccesible::TakeFocus()">
|
||||
Mozilla Bug 429547
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div aria-activedescendant="item1" id="container" tabindex="1">
|
||||
<div id="item1">item1</div>
|
||||
<div id="item2">item2</div>
|
||||
<div id="item3">item3</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,206 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>nsIAccessible selects tests</title>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="states.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="nsIAccessible_selects.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function doTest()
|
||||
{
|
||||
// Label and combo, separate tags
|
||||
var names = [
|
||||
"Foo:", // combobox
|
||||
"Foo:", // combobox list
|
||||
"item 1", // first item
|
||||
"item 2" // second item
|
||||
];
|
||||
var roles = [
|
||||
ROLE_COMBOBOX, // root
|
||||
ROLE_COMBOBOX_LIST, // list accessible
|
||||
ROLE_COMBOBOX_OPTION, // first option
|
||||
ROLE_COMBOBOX_OPTION // second option
|
||||
];
|
||||
var states = [
|
||||
(STATE_FOCUSABLE | STATE_HASPOPUP | STATE_COLLAPSED), // combobox
|
||||
(0), // combobox_list
|
||||
(STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE | STATE_FOCUSED),
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE) // second item, not focused
|
||||
];
|
||||
var undesiredStates = [
|
||||
(STATE_FOCUSED), // combobox
|
||||
(STATE_FOCUSED), // combobox_list
|
||||
(0), // first item
|
||||
(STATE_SELECTED | STATE_FOCUSED) // second, not currently focused, item
|
||||
];
|
||||
testSelect("combo1", names, roles, states, undesiredStates);
|
||||
|
||||
// Select nested within label element.
|
||||
names = [
|
||||
"Search in: Newsticker", // label
|
||||
"Search in:", // text leaf
|
||||
"Search in:", // combobox
|
||||
"Search in: Newsticker", // combobox_list
|
||||
"Newsticker", // option 1
|
||||
"Entire site" // Option 2
|
||||
];
|
||||
roles = [
|
||||
ROLE_LABEL, // root
|
||||
ROLE_TEXT_LEAF, // inner text
|
||||
ROLE_COMBOBOX, // combobox accessible
|
||||
ROLE_COMBOBOX_LIST, // list accessible
|
||||
ROLE_COMBOBOX_OPTION, // first option
|
||||
ROLE_COMBOBOX_OPTION // second option
|
||||
];
|
||||
states = [
|
||||
(0), // label
|
||||
(0), // text leaf
|
||||
(STATE_FOCUSABLE | STATE_HASPOPUP | STATE_COLLAPSED), // combobox
|
||||
(0), // combobox_list
|
||||
(STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE | STATE_FOCUSED),
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE) // second item, not focused
|
||||
];
|
||||
undesiredStates = [
|
||||
(0), // label
|
||||
(0), // text leaf
|
||||
(STATE_FOCUSED), // combobox
|
||||
(STATE_FOCUSED), // combobox_list
|
||||
(0), // first item
|
||||
(STATE_SELECTED | STATE_FOCUSED) // second, not currently focused, item
|
||||
];
|
||||
testSelect("combo2", names, roles, states, undesiredStates);
|
||||
|
||||
// select @size with label as separate tags.
|
||||
names = [
|
||||
"Component:", // list
|
||||
"Build", // item 1
|
||||
"Disability Access APIs", // item 2
|
||||
"General", // item 3
|
||||
"UI" // item 4
|
||||
];
|
||||
roles = [
|
||||
ROLE_LISTBOX, // root
|
||||
ROLE_OPTION, // item 1
|
||||
ROLE_OPTION, // item 2
|
||||
ROLE_OPTION, // item 4
|
||||
ROLE_OPTION // item 4
|
||||
];
|
||||
states = [
|
||||
(STATE_FOCUSABLE), // list
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE | STATE_FOCUSED),
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE), // second item, not focused
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE), // third item, not focused
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE) // fourth item, not focused
|
||||
];
|
||||
undesiredStates = [
|
||||
(STATE_FOCUSED), // listbox
|
||||
(STATE_SELECTED), // first item
|
||||
(STATE_SELECTED | STATE_FOCUSED), // second, not currently focused, item
|
||||
(STATE_SELECTED | STATE_FOCUSED), // third, not currently focused, item
|
||||
(STATE_SELECTED | STATE_FOCUSED) // fourth, not currently focused, item
|
||||
];
|
||||
testSelect("list1", names, roles, states, undesiredStates);
|
||||
|
||||
// Select @size nested within label element.
|
||||
names = [
|
||||
"Version:", // label
|
||||
"Version:", // text leaf
|
||||
"Version:", // list
|
||||
"2.0", // option 1
|
||||
"3.0", // Option 2
|
||||
"3.1", // Option 3
|
||||
"trunk" // Option 4
|
||||
];
|
||||
roles = [
|
||||
ROLE_LABEL, // root
|
||||
ROLE_TEXT_LEAF, // inner text
|
||||
ROLE_LISTBOX, // listbox accessible
|
||||
ROLE_OPTION, // first option
|
||||
ROLE_OPTION, // second option
|
||||
ROLE_OPTION, // third option
|
||||
ROLE_OPTION // fourth option
|
||||
];
|
||||
states = [
|
||||
(0), // label
|
||||
(0), // text leaf
|
||||
(STATE_FOCUSABLE), // listbox
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE | STATE_FOCUSED), // Option 1
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE), // second item, not focused
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE), // third item, not focused
|
||||
(STATE_SELECTABLE | STATE_FOCUSABLE) // fourth item, not focused
|
||||
];
|
||||
undesiredStates = [
|
||||
(0), // label
|
||||
(0), // text leaf
|
||||
(STATE_FOCUSED | STATE_HASPOPUP | STATE_COLLAPSED), // listbox
|
||||
(STATE_SELECTED), // first item
|
||||
(STATE_SELECTED | STATE_FOCUSED), // second, not currently focused, item
|
||||
(STATE_SELECTED | STATE_FOCUSED), // third, not currently focused, item
|
||||
(STATE_SELECTED | STATE_FOCUSED) // fourth, not currently focused, item
|
||||
];
|
||||
testSelect("list2", names, roles, states, undesiredStates);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=443889"
|
||||
title="mochitest for selects and lists">
|
||||
Mozilla Bug 443889
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<form action="post.php" method="post">
|
||||
<!-- Label and select separate tags -->
|
||||
<label for="combo1">Foo:</label>
|
||||
<select id="combo1" name="combo1">
|
||||
<option>item 1</option>
|
||||
<option>item 2</option>
|
||||
</select><br />
|
||||
|
||||
<!-- Select embedded in label -->
|
||||
<label id="combo2">Search in:<select name="search">
|
||||
<option>Newsticker</option>
|
||||
<option>Entire site</option>
|
||||
</select></label><br />
|
||||
|
||||
<!-- Label and select @size -->
|
||||
<label for="list1">Component:</label>
|
||||
<select id="list1" name="component" size="3">
|
||||
<option>Build</option>
|
||||
<option>Disability Access APIs</option>
|
||||
<option>General</option>
|
||||
<option>UI</option>
|
||||
</select><br />
|
||||
|
||||
<!-- Select @size nested within label -->
|
||||
<label id="list2">Version:<select name="version" size="3">
|
||||
<option>2.0</option>
|
||||
<option>3.0</option>
|
||||
<option>3.1</option>
|
||||
<option>trunk</option>
|
||||
</select></label><br />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
@ -31,8 +31,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=441519
|
||||
if (outerDocAcc) {
|
||||
testRole(outerDocAcc, ROLE_INTERNAL_FRAME);
|
||||
|
||||
// check if it is focusable, not desired.
|
||||
testStates(outerDocAcc, 0, 0, STATE_FOCUSABLE);
|
||||
// check if it is focusable.
|
||||
testStates(outerDocAcc, STATE_FOCUSABLE, 0);
|
||||
|
||||
// see bug 428954: No name wanted for internal frame
|
||||
is(outerDocAcc.name, null, "Wrong name for internal frame!");
|
||||
|
@ -50,6 +50,10 @@
|
||||
testValue("aria_main_link", href);
|
||||
testValue("aria_navigation_link", href);
|
||||
|
||||
// HTML controls
|
||||
testValue("combobox1", "item1");
|
||||
testValue("combobox2", "item2");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -84,5 +88,13 @@
|
||||
<!-- strange edge case: please don't do this in the wild -->
|
||||
<a id="aria_link_link" role="link" href="foo">link</a>
|
||||
|
||||
<select id="combobox1">
|
||||
<option id="cb1_item1">item1</option>
|
||||
<option id="cb1_item2">item2</option>
|
||||
</select>
|
||||
<select id="combobox2">
|
||||
<option id="cb2_item1">item1</option>
|
||||
<option id="cb2_item2" selected="true">item2</option>
|
||||
</select>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -90,6 +90,10 @@
|
||||
#include "nsIDOMXULMenuListElement.h"
|
||||
#endif
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
#include "nsAccessibilityService.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -1892,6 +1896,16 @@ nsFocusManager::SendFocusOrBlurEvent(PRUint32 aType,
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
nsAccessibilityService* accService = GetAccService();
|
||||
if (accService) {
|
||||
if (aType == NS_FOCUS_CONTENT)
|
||||
accService->NotifyOfDOMFocus(aTarget);
|
||||
else
|
||||
accService->NotifyOfDOMBlur(aTarget);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsContentUtils::AddScriptRunner(
|
||||
new FocusBlurEvent(aTarget, aType, aPresShell->GetPresContext(),
|
||||
aWindowRaised, aIsRefocus));
|
||||
|
@ -69,6 +69,12 @@ public:
|
||||
*/
|
||||
virtual PRInt32 GetSelectedIndex() = 0;
|
||||
|
||||
/**
|
||||
* Return current option. The current option is the option displaying
|
||||
* the focus ring when the listbox is focused.
|
||||
*/
|
||||
virtual already_AddRefed<nsIContent> GetCurrentOption() = 0;
|
||||
|
||||
/**
|
||||
* Initiates mouse capture for the listbox
|
||||
*
|
||||
|
@ -240,78 +240,15 @@ void nsListControlFrame::PaintFocus(nsRenderingContext& aRC, nsPoint aPt)
|
||||
{
|
||||
if (mFocused != this) return;
|
||||
|
||||
// The mEndSelectionIndex is what is currently being selected
|
||||
// use the selected index if this is kNothingSelected
|
||||
PRInt32 focusedIndex;
|
||||
if (mEndSelectionIndex == kNothingSelected) {
|
||||
focusedIndex = GetSelectedIndex();
|
||||
} else {
|
||||
focusedIndex = mEndSelectionIndex;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
|
||||
nsIFrame* containerFrame = GetOptionsContainer();
|
||||
if (!containerFrame) return;
|
||||
|
||||
nsIFrame * childframe = nsnull;
|
||||
nsresult result = NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIContent> focusedContent;
|
||||
|
||||
nsRefPtr<nsHTMLSelectElement> selectElement =
|
||||
nsHTMLSelectElement::FromContent(mContent);
|
||||
NS_ASSERTION(selectElement, "Can't be null");
|
||||
|
||||
// If we have a selected index then get that child frame
|
||||
if (focusedIndex != kNothingSelected) {
|
||||
focusedContent = GetOptionContent(focusedIndex);
|
||||
// otherwise we find the focusedContent's frame and scroll to it
|
||||
if (focusedContent) {
|
||||
childframe = focusedContent->GetPrimaryFrame();
|
||||
}
|
||||
} else {
|
||||
// Since there isn't a selected item we need to show a focus ring around the first
|
||||
// non-disabled item and skip all the option group elements (nodes)
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
|
||||
PRUint32 length;
|
||||
selectElement->GetLength(&length);
|
||||
if (length) {
|
||||
// find the first non-disabled item
|
||||
PRBool isDisabled = PR_TRUE;
|
||||
for (PRUint32 i = 0; i < length && isDisabled; i++) {
|
||||
if (NS_FAILED(selectElement->Item(i, getter_AddRefs(node))) || !node) {
|
||||
break;
|
||||
}
|
||||
if (NS_FAILED(selectElement->IsOptionDisabled(i, &isDisabled))) {
|
||||
break;
|
||||
}
|
||||
if (isDisabled) {
|
||||
node = nsnull;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we found a node use it, if not get the first child (this is for empty selects)
|
||||
if (node) {
|
||||
focusedContent = do_QueryInterface(node);
|
||||
childframe = focusedContent->GetPrimaryFrame();
|
||||
}
|
||||
if (!childframe) {
|
||||
// Failing all else, try the first thing we have, but only if
|
||||
// it's an element. Text frames need not apply.
|
||||
childframe = containerFrame->GetFirstPrincipalChild();
|
||||
if (childframe && !childframe->GetContent()->IsElement()) {
|
||||
childframe = nsnull;
|
||||
}
|
||||
result = NS_OK;
|
||||
}
|
||||
nsIFrame* childframe = nsnull;
|
||||
nsCOMPtr<nsIContent> focusedContent = GetCurrentOption();
|
||||
if (focusedContent) {
|
||||
childframe = focusedContent->GetPrimaryFrame();
|
||||
}
|
||||
|
||||
nsRect fRect;
|
||||
@ -820,9 +757,20 @@ nsListControlFrame::SingleSelection(PRInt32 aClickedIndex, PRBool aDoToggle)
|
||||
PR_TRUE, PR_TRUE);
|
||||
}
|
||||
ScrollToIndex(aClickedIndex);
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
bool isCurrentOptionChanged = mEndSelectionIndex != aClickedIndex;
|
||||
#endif
|
||||
mStartSelectionIndex = aClickedIndex;
|
||||
mEndSelectionIndex = aClickedIndex;
|
||||
InvalidateFocus();
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
if (isCurrentOptionChanged) {
|
||||
FireMenuItemActiveEvent();
|
||||
}
|
||||
#endif
|
||||
|
||||
return wasChanged;
|
||||
}
|
||||
|
||||
@ -915,11 +863,18 @@ nsListControlFrame::PerformSelection(PRInt32 aClickedIndex,
|
||||
|
||||
if (mStartSelectionIndex == kNothingSelected) {
|
||||
mStartSelectionIndex = aClickedIndex;
|
||||
mEndSelectionIndex = aClickedIndex;
|
||||
} else {
|
||||
mEndSelectionIndex = aClickedIndex;
|
||||
}
|
||||
#ifdef ACCESSIBILITY
|
||||
bool isCurrentOptionChanged = mEndSelectionIndex != aClickedIndex;
|
||||
#endif
|
||||
mEndSelectionIndex = aClickedIndex;
|
||||
InvalidateFocus();
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
if (isCurrentOptionChanged) {
|
||||
FireMenuItemActiveEvent();
|
||||
}
|
||||
#endif
|
||||
} else if (aIsControl) {
|
||||
wasChanged = SingleSelection(aClickedIndex, PR_TRUE);
|
||||
} else {
|
||||
@ -1321,6 +1276,55 @@ nsListControlFrame::GetSelectedIndex()
|
||||
return aIndex;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIContent>
|
||||
nsListControlFrame::GetCurrentOption()
|
||||
{
|
||||
// The mEndSelectionIndex is what is currently being selected. Use
|
||||
// the selected index if this is kNothingSelected.
|
||||
PRInt32 focusedIndex = (mEndSelectionIndex == kNothingSelected) ?
|
||||
GetSelectedIndex() : mEndSelectionIndex;
|
||||
|
||||
if (focusedIndex != kNothingSelected) {
|
||||
return GetOptionContent(focusedIndex);
|
||||
}
|
||||
|
||||
nsRefPtr<nsHTMLSelectElement> selectElement =
|
||||
nsHTMLSelectElement::FromContent(mContent);
|
||||
NS_ASSERTION(selectElement, "Can't be null");
|
||||
|
||||
// There is no a selected item return the first non-disabled item and skip all
|
||||
// the option group elements.
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
|
||||
PRUint32 length;
|
||||
selectElement->GetLength(&length);
|
||||
if (length) {
|
||||
PRBool isDisabled = PR_TRUE;
|
||||
for (PRUint32 i = 0; i < length && isDisabled; i++) {
|
||||
if (NS_FAILED(selectElement->Item(i, getter_AddRefs(node))) || !node) {
|
||||
break;
|
||||
}
|
||||
if (NS_FAILED(selectElement->IsOptionDisabled(i, &isDisabled))) {
|
||||
break;
|
||||
}
|
||||
if (isDisabled) {
|
||||
node = nsnull;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!node) {
|
||||
return nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
if (node) {
|
||||
nsCOMPtr<nsIContent> focusedOption = do_QueryInterface(node);
|
||||
return focusedOption.forget();
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsListControlFrame::IsInDropDownMode() const
|
||||
{
|
||||
@ -1983,19 +1987,7 @@ nsListControlFrame::FireMenuItemActiveEvent()
|
||||
return;
|
||||
}
|
||||
|
||||
// The mEndSelectionIndex is what is currently being selected
|
||||
// use the selected index if this is kNothingSelected
|
||||
PRInt32 focusedIndex;
|
||||
if (mEndSelectionIndex == kNothingSelected) {
|
||||
focusedIndex = GetSelectedIndex();
|
||||
} else {
|
||||
focusedIndex = mEndSelectionIndex;
|
||||
}
|
||||
if (focusedIndex == kNothingSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> optionContent = GetOptionContent(focusedIndex);
|
||||
nsCOMPtr<nsIContent> optionContent = GetCurrentOption();
|
||||
if (!optionContent) {
|
||||
return;
|
||||
}
|
||||
@ -2104,11 +2096,6 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent)
|
||||
mButtonDown = PR_TRUE;
|
||||
CaptureMouseEvents(PR_TRUE);
|
||||
mChangesSinceDragStart = HandleListSelection(aMouseEvent, selectedIndex);
|
||||
#ifdef ACCESSIBILITY
|
||||
if (mChangesSinceDragStart) {
|
||||
FireMenuItemActiveEvent();
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// NOTE: the combo box is responsible for dropping it down
|
||||
if (mComboboxFrame) {
|
||||
@ -2601,9 +2588,6 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
|
||||
if (!UpdateSelection()) {
|
||||
return NS_OK;
|
||||
}
|
||||
#ifdef ACCESSIBILITY
|
||||
FireMenuItemActiveEvent(); // Only fire if new item reached
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2633,6 +2617,10 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
|
||||
mEndSelectionIndex = newIndex;
|
||||
InvalidateFocus();
|
||||
ScrollToIndex(newIndex);
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
FireMenuItemActiveEvent();
|
||||
#endif
|
||||
} else if (mControlSelectMode && charcode == ' ') {
|
||||
wasChanged = SingleSelection(newIndex, PR_TRUE);
|
||||
} else {
|
||||
@ -2644,11 +2632,6 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
#ifdef ACCESSIBILITY
|
||||
if (charcode != ' ') {
|
||||
FireMenuItemActiveEvent();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -152,7 +152,8 @@ public:
|
||||
|
||||
// nsIListControlFrame
|
||||
virtual void SetComboboxFrame(nsIFrame* aComboboxFrame);
|
||||
virtual PRInt32 GetSelectedIndex();
|
||||
virtual PRInt32 GetSelectedIndex();
|
||||
virtual already_AddRefed<nsIContent> GetCurrentOption();
|
||||
|
||||
/**
|
||||
* Gets the text of the currently selected item.
|
||||
|
@ -78,6 +78,10 @@ NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||
|
||||
NS_IMPL_FRAMEARENA_HELPERS(nsMenuBarFrame)
|
||||
|
||||
NS_QUERYFRAME_HEAD(nsMenuBarFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsMenuBarFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
|
||||
|
||||
//
|
||||
// nsMenuBarFrame cntr
|
||||
//
|
||||
|
@ -60,6 +60,8 @@ nsIFrame* NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||
class nsMenuBarFrame : public nsBoxFrame, public nsMenuParent
|
||||
{
|
||||
public:
|
||||
NS_DECL_QUERYFRAME_TARGET(nsMenuBarFrame)
|
||||
NS_DECL_QUERYFRAME
|
||||
NS_DECL_FRAMEARENA_HELPERS
|
||||
|
||||
nsMenuBarFrame(nsIPresShell* aShell, nsStyleContext* aContext);
|
||||
|
@ -104,6 +104,10 @@ NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||
|
||||
NS_IMPL_FRAMEARENA_HELPERS(nsMenuPopupFrame)
|
||||
|
||||
NS_QUERYFRAME_HEAD(nsMenuPopupFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsMenuPopupFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
|
||||
|
||||
//
|
||||
// nsMenuPopupFrame ctor
|
||||
//
|
||||
|
@ -130,6 +130,8 @@ class nsMenuPopupFrame;
|
||||
class nsMenuPopupFrame : public nsBoxFrame, public nsMenuParent
|
||||
{
|
||||
public:
|
||||
NS_DECL_QUERYFRAME_TARGET(nsMenuPopupFrame)
|
||||
NS_DECL_QUERYFRAME
|
||||
NS_DECL_FRAMEARENA_HELPERS
|
||||
|
||||
nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext);
|
||||
|
@ -672,7 +672,7 @@ NS_IMETHODIMP nsTreeSelection::SetCurrentIndex(PRInt32 aIndex)
|
||||
if (aIndex != -1)
|
||||
mTree->InvalidateRow(aIndex);
|
||||
|
||||
// Fire DOMMenuItemActive event for tree
|
||||
// Fire DOMMenuItemActive or DOMMenuItemInactive event for tree.
|
||||
nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
|
||||
NS_ASSERTION(boxObject, "no box object!");
|
||||
if (!boxObject)
|
||||
@ -683,12 +683,13 @@ NS_IMETHODIMP nsTreeSelection::SetCurrentIndex(PRInt32 aIndex)
|
||||
nsCOMPtr<nsINode> treeDOMNode(do_QueryInterface(treeElt));
|
||||
NS_ENSURE_STATE(treeDOMNode);
|
||||
|
||||
nsRefPtr<nsPLDOMEvent> event =
|
||||
new nsPLDOMEvent(treeDOMNode, NS_LITERAL_STRING("DOMMenuItemActive"),
|
||||
PR_TRUE, PR_FALSE);
|
||||
if (!event)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_NAMED_LITERAL_STRING(DOMMenuItemActive, "DOMMenuItemActive");
|
||||
NS_NAMED_LITERAL_STRING(DOMMenuItemInactive, "DOMMenuItemInactive");
|
||||
|
||||
nsRefPtr<nsPLDOMEvent> event =
|
||||
new nsPLDOMEvent(treeDOMNode,
|
||||
(aIndex != -1 ? DOMMenuItemActive : DOMMenuItemInactive),
|
||||
PR_TRUE, PR_FALSE);
|
||||
return event->PostDOMEvent();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user