зеркало из https://github.com/mozilla/gecko-dev.git
Bug 414302 - Incorrect selection events in HTML, XUL and ARIA, r=tbsaunde, f=marcoz
This commit is contained in:
Родитель
a09e8acf9a
Коммит
3f07ab58a4
|
@ -59,7 +59,7 @@ interface nsIDOMNode;
|
|||
* if (NS_SUCCEEDED(rv))
|
||||
* rv = observerService->AddObserver(this, "accessible-event", PR_TRUE);
|
||||
*/
|
||||
[scriptable, uuid(fd1378c5-c606-4a5e-a321-8e7fc107e5cf)]
|
||||
[scriptable, uuid(7f66a33a-9ed7-4fd4-87a8-e431b0f43368)]
|
||||
interface nsIAccessibleEvent : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -280,7 +280,12 @@ interface nsIAccessibleEvent : nsISupports
|
|||
const unsigned long EVENT_DOCUMENT_CONTENT_CHANGED = 0x002B;
|
||||
|
||||
const unsigned long EVENT_PROPERTY_CHANGED = 0x002C;
|
||||
const unsigned long EVENT_SELECTION_CHANGED = 0x002D;
|
||||
|
||||
/**
|
||||
* A slide changed in a presentation document or a page boundary was
|
||||
* crossed in a word processing document.
|
||||
*/
|
||||
const unsigned long EVENT_PAGE_CHANGED = 0x002D;
|
||||
|
||||
/**
|
||||
* A text object's attributes changed.
|
||||
|
@ -436,16 +441,10 @@ interface nsIAccessibleEvent : nsISupports
|
|||
*/
|
||||
const unsigned long EVENT_OBJECT_ATTRIBUTE_CHANGED = 0x0055;
|
||||
|
||||
/**
|
||||
* A slide changed in a presentation document or a page boundary was
|
||||
* crossed in a word processing document.
|
||||
*/
|
||||
const unsigned long EVENT_PAGE_CHANGED = 0x0056;
|
||||
|
||||
/**
|
||||
* Help make sure event map does not get out-of-line.
|
||||
*/
|
||||
const unsigned long EVENT_LAST_ENTRY = 0x0057;
|
||||
const unsigned long EVENT_LAST_ENTRY = 0x0056;
|
||||
|
||||
/**
|
||||
* The type of event, based on the enumerated event values
|
||||
|
|
|
@ -1089,10 +1089,24 @@ nsAccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
|
|||
}
|
||||
} break;
|
||||
|
||||
case nsIAccessibleEvent::EVENT_SELECTION_CHANGED:
|
||||
case nsIAccessibleEvent::EVENT_SELECTION:
|
||||
case nsIAccessibleEvent::EVENT_SELECTION_ADD:
|
||||
case nsIAccessibleEvent::EVENT_SELECTION_REMOVE:
|
||||
{
|
||||
// XXX: dupe events may be fired
|
||||
MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
|
||||
AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent);
|
||||
g_signal_emit_by_name(nsAccessibleWrap::GetAtkObject(selChangeEvent->Widget()),
|
||||
"selection_changed");
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIAccessibleEvent::EVENT_SELECTION_WITHIN:
|
||||
{
|
||||
MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
|
||||
g_signal_emit_by_name(atkObj, "selection_changed");
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
|
||||
MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_SELECTION_CHANGED\n"));
|
||||
|
|
|
@ -330,6 +330,28 @@ AccCaretMoveEvent::CreateXPCOMObject()
|
|||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccSelChangeEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccSelChangeEvent::
|
||||
AccSelChangeEvent(nsAccessible* aWidget, nsAccessible* aItem,
|
||||
SelChangeType aSelChangeType) :
|
||||
AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange),
|
||||
mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType),
|
||||
mPreceedingCount(0), mPackedEvent(nsnull)
|
||||
{
|
||||
if (aSelChangeType == eSelectionAdd) {
|
||||
if (mWidget->GetSelectedItem(1))
|
||||
mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
|
||||
else
|
||||
mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
} else {
|
||||
mEventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccTableChangeEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -82,6 +82,9 @@ public:
|
|||
// will be processed.
|
||||
eCoalesceOfSameType,
|
||||
|
||||
// eCoalesceSelectionChange: coalescence of selection change events.
|
||||
eCoalesceSelectionChange,
|
||||
|
||||
// eRemoveDupes : For repeat events, only the newest event in queue
|
||||
// will be emitted.
|
||||
eRemoveDupes,
|
||||
|
@ -125,6 +128,7 @@ public:
|
|||
eHideEvent,
|
||||
eShowEvent,
|
||||
eCaretMoveEvent,
|
||||
eSelectionChangeEvent,
|
||||
eTableChangeEvent
|
||||
};
|
||||
|
||||
|
@ -327,10 +331,37 @@ private:
|
|||
/**
|
||||
* Accessible widget selection change event.
|
||||
*/
|
||||
class AccSelectionChangeEvent : public AccEvent
|
||||
class AccSelChangeEvent : public AccEvent
|
||||
{
|
||||
public:
|
||||
enum SelChangeType {
|
||||
eSelectionAdd,
|
||||
eSelectionRemove
|
||||
};
|
||||
|
||||
AccSelChangeEvent(nsAccessible* aWidget, nsAccessible* aItem,
|
||||
SelChangeType aSelChangeType);
|
||||
|
||||
virtual ~AccSelChangeEvent() { }
|
||||
|
||||
// AccEvent
|
||||
static const EventGroup kEventGroup = eSelectionChangeEvent;
|
||||
virtual unsigned int GetEventGroups() const
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eSelectionChangeEvent);
|
||||
}
|
||||
|
||||
// AccSelChangeEvent
|
||||
nsAccessible* Widget() const { return mWidget; }
|
||||
|
||||
private:
|
||||
nsRefPtr<nsAccessible> mWidget;
|
||||
nsRefPtr<nsAccessible> mItem;
|
||||
SelChangeType mSelChangeType;
|
||||
PRUint32 mPreceedingCount;
|
||||
AccSelChangeEvent* mPackedEvent;
|
||||
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -51,6 +51,10 @@
|
|||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
// Defines the number of selection add/remove events in the queue when they
|
||||
// aren't packed into single selection within event.
|
||||
const unsigned int kSelChangeCountToPack = 5;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -491,6 +495,26 @@ NotificationController::CoalesceEvents()
|
|||
}
|
||||
} break; // case eRemoveDupes
|
||||
|
||||
case AccEvent::eCoalesceSelectionChange:
|
||||
{
|
||||
AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
|
||||
PRInt32 index = tail - 1;
|
||||
for (; index >= 0; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule == tailEvent->mEventRule) {
|
||||
AccSelChangeEvent* thisSelChangeEvent =
|
||||
downcast_accEvent(thisEvent);
|
||||
|
||||
// Coalesce selection change events within same control.
|
||||
if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
|
||||
CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} break; // eCoalesceSelectionChange
|
||||
|
||||
default:
|
||||
break; // case eAllowDupes, eDoNotEmit
|
||||
} // switch
|
||||
|
@ -511,6 +535,86 @@ NotificationController::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
PRInt32 aThisIndex)
|
||||
{
|
||||
aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
|
||||
|
||||
// Pack all preceding events into single selection within event
|
||||
// when we receive too much selection add/remove events.
|
||||
if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
|
||||
aTailEvent->mAccessible = aTailEvent->mWidget;
|
||||
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
// Do not emit any preceding selection events for same widget if they
|
||||
// weren't coalesced yet.
|
||||
if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
|
||||
for (PRInt32 jdx = aThisIndex - 1; jdx >= 0; jdx--) {
|
||||
AccEvent* prevEvent = mEvents[jdx];
|
||||
if (prevEvent->mEventRule == aTailEvent->mEventRule) {
|
||||
AccSelChangeEvent* prevSelChangeEvent =
|
||||
downcast_accEvent(prevEvent);
|
||||
if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
|
||||
prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Pack sequential selection remove and selection add events into
|
||||
// single selection change event.
|
||||
if (aTailEvent->mPreceedingCount == 1 &&
|
||||
aTailEvent->mItem != aThisEvent->mItem) {
|
||||
if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
||||
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
||||
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
aTailEvent->mPackedEvent = aThisEvent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
||||
aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
||||
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
aThisEvent->mPackedEvent = aThisEvent;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack the packed selection change event because we've got one
|
||||
// more selection add/remove.
|
||||
if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
|
||||
if (aThisEvent->mPackedEvent) {
|
||||
aThisEvent->mPackedEvent->mEventType =
|
||||
aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
||||
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
||||
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
|
||||
aThisEvent->mPackedEvent->mEventRule =
|
||||
AccEvent::eCoalesceSelectionChange;
|
||||
|
||||
aThisEvent->mPackedEvent = nsnull;
|
||||
}
|
||||
|
||||
aThisEvent->mEventType =
|
||||
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
||||
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
||||
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert into selection add since control has single selection but other
|
||||
// selection events for this control are queued.
|
||||
if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
|
||||
AccHideEvent* aThisEvent)
|
||||
|
|
|
@ -251,11 +251,11 @@ private:
|
|||
AccEvent::EEventRule aEventRule);
|
||||
|
||||
/**
|
||||
* Do not emit one of two given reorder events fired for DOM nodes in the case
|
||||
* when one DOM node is in parent chain of second one.
|
||||
* Coalesce two selection change events within the same select control.
|
||||
*/
|
||||
void CoalesceReorderEventsFromSameTree(AccEvent* aAccEvent,
|
||||
AccEvent* aDescendantAccEvent);
|
||||
void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
PRInt32 aThisIndex);
|
||||
|
||||
/**
|
||||
* Coalesce text change events caused by sibling hide events.
|
||||
|
|
|
@ -473,7 +473,7 @@ static const char kEventTypeNames[][40] = {
|
|||
"document attributes changed", // EVENT_DOCUMENT_ATTRIBUTES_CHANGED
|
||||
"document content changed", // EVENT_DOCUMENT_CONTENT_CHANGED
|
||||
"property changed", // EVENT_PROPERTY_CHANGED
|
||||
"selection changed", // EVENT_SELECTION_CHANGED
|
||||
"page changed", // EVENT_PAGE_CHANGED
|
||||
"text attribute changed", // EVENT_TEXT_ATTRIBUTE_CHANGED
|
||||
"text caret moved", // EVENT_TEXT_CARET_MOVED
|
||||
"text changed", // EVENT_TEXT_CHANGED
|
||||
|
@ -514,7 +514,6 @@ static const char kEventTypeNames[][40] = {
|
|||
"hypertext changed", // EVENT_HYPERTEXT_CHANGED
|
||||
"hypertext links count changed", // EVENT_HYPERTEXT_NLINKS_CHANGED
|
||||
"object attribute changed", // EVENT_OBJECT_ATTRIBUTE_CHANGED
|
||||
"page changed" // EVENT_PAGE_CHANGED
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1042,34 +1042,25 @@ nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID
|
|||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::selected ||
|
||||
aAttribute == nsGkAtoms::aria_selected) {
|
||||
// ARIA or XUL selection
|
||||
if ((aContent->IsXUL() && aAttribute == nsGkAtoms::selected) ||
|
||||
aAttribute == nsGkAtoms::aria_selected) {
|
||||
nsAccessible* item = GetAccessible(aContent);
|
||||
nsAccessible* widget =
|
||||
nsAccUtils::GetSelectableContainer(item, item->State());
|
||||
if (widget) {
|
||||
AccSelChangeEvent::SelChangeType selChangeType =
|
||||
aContent->AttrValueIs(aNameSpaceID, aAttribute,
|
||||
nsGkAtoms::_true, eCaseMatters) ?
|
||||
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
|
||||
|
||||
nsAccessible *multiSelect =
|
||||
nsAccUtils::GetMultiSelectableContainer(aContent);
|
||||
// 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
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
|
||||
multiSelect->GetNode(),
|
||||
AccEvent::eAllowDupes);
|
||||
|
||||
static nsIContent::AttrValuesArray strings[] =
|
||||
{&nsGkAtoms::_empty, &nsGkAtoms::_false, nsnull};
|
||||
if (aContent->FindAttrValueIn(kNameSpaceID_None, aAttribute,
|
||||
strings, eCaseMatters) >= 0) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_REMOVE,
|
||||
aContent);
|
||||
nsRefPtr<AccEvent> event =
|
||||
new AccSelChangeEvent(widget, item, selChangeType);
|
||||
FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_ADD,
|
||||
aContent);
|
||||
}
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::contenteditable) {
|
||||
nsRefPtr<AccEvent> editableChangeEvent =
|
||||
new AccStateChangeEvent(aContent, states::EDITABLE);
|
||||
|
@ -1209,7 +1200,18 @@ void nsDocAccessible::ContentStateChanged(nsIDocument* aDocument,
|
|||
nsEventStates aStateMask)
|
||||
{
|
||||
if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
|
||||
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent);
|
||||
nsAccessible* item = GetAccessible(aContent);
|
||||
if (item) {
|
||||
nsAccessible* widget = item->ContainerWidget();
|
||||
if (widget && widget->IsSelect()) {
|
||||
AccSelChangeEvent::SelChangeType selChangeType =
|
||||
aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
|
||||
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
|
||||
nsRefPtr<AccEvent> event = new AccSelChangeEvent(widget, item,
|
||||
selChangeType);
|
||||
FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
|
||||
|
|
|
@ -473,6 +473,8 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
|||
}
|
||||
|
||||
if (treeItemAccessible && eventType.EqualsLiteral("select")) {
|
||||
// XXX: We shouldn't be based on DOM select event which doesn't provide us
|
||||
// any context info. We should integrate into nsTreeSelection instead.
|
||||
// If multiselect tree, we should fire selectionadd or selection removed
|
||||
if (FocusMgr()->HasDOMFocus(targetNode)) {
|
||||
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
|
||||
|
|
|
@ -416,59 +416,7 @@ nsHTMLSelectOptionAccessible::SetSelected(bool aSelect)
|
|||
nsAccessible*
|
||||
nsHTMLSelectOptionAccessible::ContainerWidget() const
|
||||
{
|
||||
if (mParent && mParent->IsListControl()) {
|
||||
nsAccessible* grandParent = mParent->Parent();
|
||||
if (grandParent && grandParent->IsCombobox())
|
||||
return grandParent;
|
||||
|
||||
return mParent;
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLSelectOptionAccessible: static methods
|
||||
|
||||
void
|
||||
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(nsIContent *aPossibleOptionNode)
|
||||
{
|
||||
if (!aPossibleOptionNode ||
|
||||
aPossibleOptionNode->Tag() != nsGkAtoms::option ||
|
||||
!aPossibleOptionNode->IsHTML()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAccessible *multiSelect =
|
||||
nsAccUtils::GetMultiSelectableContainer(aPossibleOptionNode);
|
||||
if (!multiSelect)
|
||||
return;
|
||||
|
||||
nsAccessible *option = GetAccService()->GetAccessible(aPossibleOptionNode);
|
||||
if (!option)
|
||||
return;
|
||||
|
||||
|
||||
nsRefPtr<AccEvent> selWithinEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN, multiSelect);
|
||||
|
||||
if (!selWithinEvent)
|
||||
return;
|
||||
|
||||
option->GetDocAccessible()->FireDelayedAccessibleEvent(selWithinEvent);
|
||||
|
||||
PRUint64 state = option->State();
|
||||
PRUint32 eventType;
|
||||
if (state & states::SELECTED) {
|
||||
eventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
|
||||
}
|
||||
else {
|
||||
eventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
}
|
||||
|
||||
nsRefPtr<AccEvent> selAddRemoveEvent = new AccEvent(eventType, option);
|
||||
|
||||
if (selAddRemoveEvent)
|
||||
option->GetDocAccessible()->FireDelayedAccessibleEvent(selAddRemoveEvent);
|
||||
return mParent && mParent->IsListControl() ? mParent : nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -738,26 +686,22 @@ nsHTMLComboboxAccessible::AreItemsOperable() const
|
|||
nsAccessible*
|
||||
nsHTMLComboboxAccessible::CurrentItem()
|
||||
{
|
||||
// No current item for collapsed combobox.
|
||||
return SelectedOption(true);
|
||||
return AreItemsOperable() ? mListAccessible->CurrentItem() : nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLComboboxAccessible: protected
|
||||
|
||||
nsAccessible*
|
||||
nsHTMLComboboxAccessible::SelectedOption(bool aIgnoreIfCollapsed) const
|
||||
nsHTMLComboboxAccessible::SelectedOption() const
|
||||
{
|
||||
nsIFrame* frame = GetFrame();
|
||||
nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(frame);
|
||||
if (comboboxFrame) {
|
||||
if (aIgnoreIfCollapsed && !comboboxFrame->IsDroppedDown())
|
||||
if (!comboboxFrame)
|
||||
return nsnull;
|
||||
|
||||
frame = comboboxFrame->GetDropDown();
|
||||
}
|
||||
|
||||
nsIListControlFrame* listControlFrame = do_QueryFrame(frame);
|
||||
nsIListControlFrame* listControlFrame =
|
||||
do_QueryFrame(comboboxFrame->GetDropDown());
|
||||
if (listControlFrame) {
|
||||
nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
|
||||
if (activeOptionNode) {
|
||||
|
@ -858,3 +802,19 @@ void nsHTMLComboboxListAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aBo
|
|||
*aBoundingFrame = frame->GetParent();
|
||||
aBounds = (*aBoundingFrame)->GetRect();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLComboboxListAccessible: Widgets
|
||||
|
||||
bool
|
||||
nsHTMLComboboxListAccessible::IsActiveWidget() const
|
||||
{
|
||||
return mParent && mParent->IsActiveWidget();
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLComboboxListAccessible::AreItemsOperable() const
|
||||
{
|
||||
return mParent && mParent->AreItemsOperable();
|
||||
}
|
||||
|
||||
|
|
|
@ -130,8 +130,6 @@ public:
|
|||
// Widgets
|
||||
virtual nsAccessible* ContainerWidget() const;
|
||||
|
||||
static void SelectionChangedIfOption(nsIContent *aPossibleOption);
|
||||
|
||||
protected:
|
||||
// nsAccessible
|
||||
virtual nsIFrame* GetBoundsFrame();
|
||||
|
@ -219,7 +217,7 @@ protected:
|
|||
/**
|
||||
* Return selected option.
|
||||
*/
|
||||
nsAccessible* SelectedOption(bool aIgnoreIfCollapsed = false) const;
|
||||
nsAccessible* SelectedOption() const;
|
||||
|
||||
private:
|
||||
nsRefPtr<nsHTMLComboboxListAccessible> mListAccessible;
|
||||
|
@ -246,6 +244,10 @@ public:
|
|||
// nsAccessible
|
||||
virtual PRUint64 NativeState();
|
||||
virtual void GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame);
|
||||
|
||||
// Widgets
|
||||
virtual bool IsActiveWidget() const;
|
||||
virtual bool AreItemsOperable() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -90,7 +90,7 @@ static const PRUint32 gWinEventMap[] = {
|
|||
IA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_DOCUMENT_ATTRIBUTES_CHANGED
|
||||
IA2_EVENT_DOCUMENT_CONTENT_CHANGED, // nsIAccessibleEvent::EVENT_DOCUMENT_CONTENT_CHANGED
|
||||
kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_PROPERTY_CHANGED
|
||||
kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_SELECTION_CHANGED
|
||||
IA2_EVENT_PAGE_CHANGED, // nsIAccessibleEvent::IA2_EVENT_PAGE_CHANGED
|
||||
IA2_EVENT_TEXT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED
|
||||
IA2_EVENT_TEXT_CARET_MOVED, // nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED
|
||||
IA2_EVENT_TEXT_CHANGED, // nsIAccessibleEvent::EVENT_TEXT_CHANGED
|
||||
|
@ -131,7 +131,6 @@ static const PRUint32 gWinEventMap[] = {
|
|||
IA2_EVENT_HYPERTEXT_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_CHANGED
|
||||
IA2_EVENT_HYPERTEXT_NLINKS_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_NLINKS_CHANGED
|
||||
IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
|
||||
IA2_EVENT_PAGE_CHANGED, // nsIAccessibleEvent::EVENT_PAGE_CHANGED
|
||||
kEVENT_LAST_ENTRY // nsIAccessibleEvent::EVENT_LAST_ENTRY
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@ const EVENT_MENUPOPUP_END = nsIAccessibleEvent.EVENT_MENUPOPUP_END;
|
|||
const EVENT_OBJECT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_OBJECT_ATTRIBUTE_CHANGED;
|
||||
const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
|
||||
const EVENT_SCROLLING_START = nsIAccessibleEvent.EVENT_SCROLLING_START;
|
||||
const EVENT_SELECTION = nsIAccessibleEvent.EVENT_SELECTION;
|
||||
const EVENT_SELECTION_ADD = nsIAccessibleEvent.EVENT_SELECTION_ADD;
|
||||
const EVENT_SELECTION_REMOVE = nsIAccessibleEvent.EVENT_SELECTION_REMOVE;
|
||||
const EVENT_SELECTION_WITHIN = nsIAccessibleEvent.EVENT_SELECTION_WITHIN;
|
||||
const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
|
||||
const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;
|
||||
|
|
|
@ -82,7 +82,9 @@ _TEST_FILES =\
|
|||
test_mutation.html \
|
||||
test_mutation.xhtml \
|
||||
test_scroll.xul \
|
||||
test_selection_aria.html \
|
||||
test_selection.html \
|
||||
test_selection.xul \
|
||||
test_statechange.html \
|
||||
test_text_alg.html \
|
||||
test_text.html \
|
||||
|
|
|
@ -22,39 +22,45 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
function addSelection(aNode, aOption)
|
||||
{
|
||||
this.DOMNode = aNode;
|
||||
this.optionNode = aOption;
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.DOMNode)),
|
||||
new invokerChecker(EVENT_SELECTION_ADD, getAccessible(this.optionNode))
|
||||
];
|
||||
|
||||
this.invoke = function addselection_invoke() {
|
||||
synthesizeMouse(this.optionNode, 1, 1, {});
|
||||
};
|
||||
|
||||
this.getID = function addselection_getID() {
|
||||
return prettyName(this.optionNode) + " added to selection";
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debuggin
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
//var gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
var select = document.getElementById("toppings");
|
||||
var option = document.getElementById("onions");
|
||||
gQueue.push(new addSelection(select, option));
|
||||
// open combobox
|
||||
gQueue.push(new synthClick("combobox",
|
||||
new invokerChecker(EVENT_FOCUS, "cb1_item1")));
|
||||
gQueue.push(new synthDownKey("cb1_item1",
|
||||
new invokerChecker(EVENT_SELECTION, "cb1_item2")));
|
||||
|
||||
// closed combobox
|
||||
gQueue.push(new synthEscapeKey("combobox",
|
||||
new invokerChecker(EVENT_FOCUS, "combobox")));
|
||||
gQueue.push(new synthDownKey("cb1_item2",
|
||||
new invokerChecker(EVENT_SELECTION, "cb1_item3")));
|
||||
|
||||
// listbox
|
||||
gQueue.push(new synthClick("lb1_item1",
|
||||
new invokerChecker(EVENT_SELECTION, "lb1_item1")));
|
||||
gQueue.push(new synthDownKey("lb1_item1",
|
||||
new invokerChecker(EVENT_SELECTION, "lb1_item2")));
|
||||
|
||||
// multiselectable listbox
|
||||
gQueue.push(new synthClick("lb2_item1",
|
||||
new invokerChecker(EVENT_SELECTION, "lb2_item1")));
|
||||
gQueue.push(new synthDownKey("lb2_item1",
|
||||
new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
|
||||
{ shiftKey: true }));
|
||||
gQueue.push(new synthUpKey("lb2_item2",
|
||||
new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
|
||||
{ shiftKey: true }));
|
||||
gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
|
||||
new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
@ -67,9 +73,9 @@
|
|||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=569653"
|
||||
title="Make selection events async">
|
||||
Mozilla Bug 569653
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
|
||||
title="Incorrect selection events in HTML, XUL and ARIA">
|
||||
Mozilla Bug 414302
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
|
@ -77,18 +83,31 @@
|
|||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<p>Pizza</p>
|
||||
<select id="toppings" name="toppings" multiple size=5>
|
||||
<option value="mushrooms">mushrooms
|
||||
<option value="greenpeppers">green peppers
|
||||
<option value="onions" id="onions">onions
|
||||
<option value="tomatoes">tomatoes
|
||||
<option value="olives">olives
|
||||
<select id="combobox">
|
||||
<option id="cb1_item1" value="mushrooms">mushrooms
|
||||
<option id="cb1_item2" value="greenpeppers">green peppers
|
||||
<option id="cb1_item3" value="onions" id="onions">onions
|
||||
<option id="cb1_item4" value="tomatoes">tomatoes
|
||||
<option id="cb1_item5" value="olives">olives
|
||||
</select>
|
||||
|
||||
<select id="listbox" size=5>
|
||||
<option id="lb1_item1" value="mushrooms">mushrooms
|
||||
<option id="lb1_item2" value="greenpeppers">green peppers
|
||||
<option id="lb1_item3" value="onions" id="onions">onions
|
||||
<option id="lb1_item4" value="tomatoes">tomatoes
|
||||
<option id="lb1_item5" value="olives">olives
|
||||
</select>
|
||||
|
||||
<p>Pizza</p>
|
||||
<select id="listbox2" multiple size=5>
|
||||
<option id="lb2_item1" value="mushrooms">mushrooms
|
||||
<option id="lb2_item2" value="greenpeppers">green peppers
|
||||
<option id="lb2_item3" value="onions" id="onions">onions
|
||||
<option id="lb2_item4" value="tomatoes">tomatoes
|
||||
<option id="lb2_item5" value="olives">olives
|
||||
</select>
|
||||
|
||||
<div id="testContainer">
|
||||
<iframe id="iframe"></iframe>
|
||||
</div>
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
<?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="Selection event tests">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js" />
|
||||
<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 advanceTab(aTabsID, aDirection, aNextTabID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SELECTION, aNextTabID)
|
||||
];
|
||||
|
||||
this.invoke = function advanceTab_invoke()
|
||||
{
|
||||
getNode(aTabsID).advanceSelectedTab(aDirection, true);
|
||||
}
|
||||
|
||||
this.getID = function synthFocus_getID()
|
||||
{
|
||||
return "advanceTab on " + prettyName(aTabsID) + " to " + prettyName(aNextTabID);
|
||||
}
|
||||
}
|
||||
|
||||
function select4FirstItems(aID)
|
||||
{
|
||||
this.listboxNode = getNode(aID);
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0)),
|
||||
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(1)),
|
||||
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(2)),
|
||||
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(3))
|
||||
];
|
||||
|
||||
this.invoke = function select4FirstItems_invoke()
|
||||
{
|
||||
synthesizeKey("VK_DOWN", { shiftKey: true }); // selects two items
|
||||
synthesizeKey("VK_DOWN", { shiftKey: true });
|
||||
synthesizeKey("VK_DOWN", { shiftKey: true });
|
||||
}
|
||||
|
||||
this.getID = function select4FirstItems_getID()
|
||||
{
|
||||
return "select 4 first items for " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
function unselect4FirstItems(aID)
|
||||
{
|
||||
this.listboxNode = getNode(aID);
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(3)),
|
||||
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(2)),
|
||||
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(1)),
|
||||
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0))
|
||||
];
|
||||
|
||||
this.invoke = function unselect4FirstItems_invoke()
|
||||
{
|
||||
synthesizeKey("VK_UP", { shiftKey: true });
|
||||
synthesizeKey("VK_UP", { shiftKey: true });
|
||||
synthesizeKey("VK_UP", { shiftKey: true });
|
||||
synthesizeKey(" ", { ctrlKey: true }); // unselect first item
|
||||
}
|
||||
|
||||
this.getID = function unselect4FirstItems_getID()
|
||||
{
|
||||
return "unselect 4 first items for " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
function selectAllItems(aID)
|
||||
{
|
||||
this.listboxNode = getNode(aID);
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
|
||||
];
|
||||
|
||||
this.invoke = function selectAllItems_invoke()
|
||||
{
|
||||
synthesizeKey("VK_END", { shiftKey: true });
|
||||
}
|
||||
|
||||
this.getID = function selectAllItems_getID()
|
||||
{
|
||||
return "select all items for " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
function unselectAllItemsButFirst(aID)
|
||||
{
|
||||
this.listboxNode = getNode(aID);
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
|
||||
];
|
||||
|
||||
this.invoke = function unselectAllItemsButFirst_invoke()
|
||||
{
|
||||
synthesizeKey("VK_HOME", { shiftKey: true });
|
||||
}
|
||||
|
||||
this.getID = function unselectAllItemsButFirst_getID()
|
||||
{
|
||||
return "unselect all items for " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
function unselectSelectItem(aID)
|
||||
{
|
||||
this.listboxNode = getNode(aID);
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0)),
|
||||
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0))
|
||||
];
|
||||
|
||||
this.invoke = function unselectSelectItem_invoke()
|
||||
{
|
||||
synthesizeKey(" ", { ctrlKey: true }); // select item
|
||||
synthesizeKey(" ", { ctrlKey: true }); // unselect item
|
||||
}
|
||||
|
||||
this.getID = function unselectSelectItem_getID()
|
||||
{
|
||||
return "unselect and then select first item for " + prettyName(aID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
*/
|
||||
var gQueue = null;
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debuggin
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// tabbox
|
||||
gQueue.push(new advanceTab("tabs", 1, "tab3"));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// listbox
|
||||
gQueue.push(new synthClick("lb1_item1",
|
||||
new invokerChecker(EVENT_SELECTION, "lb1_item1")));
|
||||
gQueue.push(new synthDownKey("lb1_item1",
|
||||
new invokerChecker(EVENT_SELECTION, "lb1_item2")));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// multiselectable listbox
|
||||
gQueue.push(new synthClick("lb2_item1",
|
||||
new invokerChecker(EVENT_SELECTION, "lb2_item1")));
|
||||
gQueue.push(new synthDownKey("lb2_item1",
|
||||
new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
|
||||
{ shiftKey: true }));
|
||||
gQueue.push(new synthUpKey("lb2_item2",
|
||||
new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
|
||||
{ shiftKey: true }));
|
||||
gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
|
||||
new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// selection event coalescence
|
||||
|
||||
// fire 4 selection_add events
|
||||
gQueue.push(new select4FirstItems("listbox2"));
|
||||
// fire 4 selection_remove events
|
||||
gQueue.push(new unselect4FirstItems("listbox2"));
|
||||
// fire selection_within event
|
||||
gQueue.push(new selectAllItems("listbox2"));
|
||||
// fire selection_within event
|
||||
gQueue.push(new unselectAllItemsButFirst("listbox2"));
|
||||
// fire selection_remove/add events
|
||||
gQueue.push(new unselectSelectItem("listbox2"));
|
||||
|
||||
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=414302"
|
||||
title="Incorrect selection events in HTML, XUL and ARIA">
|
||||
Mozilla Bug 414302
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<tabbox id="tabbox" selectedIndex="1">
|
||||
<tabs id="tabs">
|
||||
<tab id="tab1" label="tab1"/>
|
||||
<tab id="tab2" label="tab2"/>
|
||||
<tab id="tab3" label="tab3"/>
|
||||
<tab id="tab4" label="tab4"/>
|
||||
</tabs>
|
||||
<tabpanels>
|
||||
<tabpanel><!-- tabpanel First elements go here --></tabpanel>
|
||||
<tabpanel><button id="b1" label="b1"/></tabpanel>
|
||||
<tabpanel><button id="b2" label="b2"/></tabpanel>
|
||||
<tabpanel></tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
|
||||
<listbox id="listbox">
|
||||
<listitem id="lb1_item1" label="item1"/>
|
||||
<listitem id="lb1_item2" label="item2"/>
|
||||
</listbox>
|
||||
|
||||
<listbox id="listbox2" seltype="multiple">
|
||||
<listitem id="lb2_item1" label="item1"/>
|
||||
<listitem id="lb2_item2" label="item2"/>
|
||||
<listitem id="lb2_item3" label="item3"/>
|
||||
<listitem id="lb2_item4" label="item4"/>
|
||||
<listitem id="lb2_item5" label="item5"/>
|
||||
<listitem id="lb2_item6" label="item6"/>
|
||||
<listitem id="lb2_item7" label="item7"/>
|
||||
</listbox>
|
||||
|
||||
</hbox>
|
||||
</window>
|
|
@ -0,0 +1,112 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>ARIA selection event 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">
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
function selectTreeItem(aTreeID, aItemID)
|
||||
{
|
||||
this.treeNode = getNode(aTreeID);
|
||||
this.itemNode = getNode(aItemID);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SELECTION, aItemID)
|
||||
];
|
||||
|
||||
this.invoke = function selectTreeItem_invoke() {
|
||||
var itemNode = this.treeNode.querySelector("*[aria-selected='true']");
|
||||
if (itemNode)
|
||||
itemNode.removeAttribute("aria-selected", "true");
|
||||
|
||||
this.itemNode.setAttribute("aria-selected", "true");
|
||||
}
|
||||
|
||||
this.getID = function selectTreeItem_getID()
|
||||
{
|
||||
return "selectTreeItem " + prettyName(aItemID);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
//var gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new selectTreeItem("tree", "treeitem1"));
|
||||
gQueue.push(new selectTreeItem("tree", "treeitem1a"));
|
||||
gQueue.push(new selectTreeItem("tree", "treeitem1a1"));
|
||||
|
||||
gQueue.push(new selectTreeItem("tree2", "tree2item1"));
|
||||
gQueue.push(new selectTreeItem("tree2", "tree2item1a"));
|
||||
gQueue.push(new selectTreeItem("tree2", "tree2item1a1"));
|
||||
|
||||
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=569653"
|
||||
title="Make selection events async">
|
||||
Mozilla Bug 569653
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="tree" role="tree">
|
||||
<div id="treeitem1" role="treeitem">Canada
|
||||
<div id="treeitem1a" role="treeitem">- Ontario
|
||||
<div id="treeitem1a1" role="treeitem">-- Toronto</div>
|
||||
</div>
|
||||
<div id="treeitem1b" role="treeitem">- Manitoba</div>
|
||||
</div>
|
||||
<div id="treeitem2" role="treeitem">Germany</div>
|
||||
<div id="treeitem3" role="treeitem">Russia</div>
|
||||
</div>
|
||||
|
||||
<div id="tree2" role="tree" aria-multiselectable="true">
|
||||
<div id="tree2item1" role="treeitem">Canada
|
||||
<div id="tree2item1a" role="treeitem">- Ontario
|
||||
<div id="tree2item1a1" role="treeitem">-- Toronto</div>
|
||||
</div>
|
||||
<div id="tree2item1b" role="treeitem">- Manitoba</div>
|
||||
</div>
|
||||
<div id="tree2item2" role="treeitem">Germany</div>
|
||||
<div id="tree2item3" role="treeitem">Russia</div>
|
||||
</div>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче