diff options
Diffstat (limited to 'WebCore/accessibility')
17 files changed, 1053 insertions, 147 deletions
diff --git a/WebCore/accessibility/AXObjectCache.cpp b/WebCore/accessibility/AXObjectCache.cpp index 0b758e6..55199a3 100644 --- a/WebCore/accessibility/AXObjectCache.cpp +++ b/WebCore/accessibility/AXObjectCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,6 +36,7 @@ #include "AccessibilityListBox.h" #include "AccessibilityListBoxOption.h" #include "AccessibilityImageMapLink.h" +#include "AccessibilityMediaControls.h" #include "AccessibilityRenderObject.h" #include "AccessibilitySlider.h" #include "AccessibilityTable.h" @@ -43,8 +44,14 @@ #include "AccessibilityTableColumn.h" #include "AccessibilityTableHeaderContainer.h" #include "AccessibilityTableRow.h" -#include "InputElement.h" +#include "FocusController.h" +#include "Frame.h" #include "HTMLNames.h" +#if ENABLE(VIDEO) +#include "MediaControlElements.h" +#endif +#include "InputElement.h" +#include "Page.h" #include "RenderObject.h" #include "RenderView.h" @@ -73,6 +80,32 @@ AXObjectCache::~AXObjectCache() } } +AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page) +{ + // get the focused node in the page + Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document(); + Node* focusedNode = focusedDocument->focusedNode(); + if (!focusedNode) + focusedNode = focusedDocument; + + RenderObject* focusedNodeRenderer = focusedNode->renderer(); + if (!focusedNodeRenderer) + return 0; + + AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer); + + if (obj->shouldFocusActiveDescendant()) { + if (AccessibilityObject* descendant = obj->activeDescendant()) + obj = descendant; + } + + // the HTML element, for example, is focusable but has an AX object that is ignored + if (obj->accessibilityIsIgnored()) + obj = obj->parentObjectUnignored(); + + return obj; +} + AccessibilityObject* AXObjectCache::get(RenderObject* renderer) { if (!renderer) @@ -108,7 +141,7 @@ AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer) RefPtr<AccessibilityObject> newObj = 0; if (renderer->isListBox()) newObj = AccessibilityListBox::create(renderer); - else if (node && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag))) + else if (node && (nodeIsAriaType(node, "list") || node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag))) newObj = AccessibilityList::create(renderer); // aria tables @@ -127,6 +160,12 @@ AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer) else if (renderer->isTableCell()) newObj = AccessibilityTableCell::create(renderer); +#if ENABLE(VIDEO) + // media controls + else if (renderer->node() && renderer->node()->isMediaControlElement()) + newObj = AccessibilityMediaControl::create(renderer); +#endif + // input type=range else if (renderer->isSlider()) newObj = AccessibilitySlider::create(renderer); @@ -213,6 +252,23 @@ void AXObjectCache::remove(RenderObject* renderer) m_renderObjectMapping.remove(renderer); } +#if !PLATFORM(WIN) +AXID AXObjectCache::platformGenerateAXID() const +{ + static AXID lastUsedID = 0; + + // Generate a new ID. + AXID objID = lastUsedID; + do { + ++objID; + } while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID)); + + lastUsedID = objID; + + return objID; +} +#endif + AXID AXObjectCache::getAXID(AccessibilityObject* obj) { // check for already-assigned ID @@ -221,15 +277,10 @@ AXID AXObjectCache::getAXID(AccessibilityObject* obj) ASSERT(m_idsInUse.contains(objID)); return objID; } - - // generate a new ID - static AXID lastUsedID = 0; - objID = lastUsedID; - do - ++objID; - while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID)); + + objID = platformGenerateAXID(); + m_idsInUse.add(objID); - lastUsedID = objID; obj->setAXObjectID(objID); return objID; @@ -288,7 +339,7 @@ void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*) } #if HAVE(ACCESSIBILITY) -void AXObjectCache::postNotification(RenderObject* renderer, const String& message, bool postToElement) +void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement) { // Notifications for text input objects are sent to that object. // All others are sent to the top WebArea. @@ -316,14 +367,14 @@ void AXObjectCache::postNotification(RenderObject* renderer, const String& messa if (!obj) return; - m_notificationsToPost.append(make_pair(obj, message)); + m_notificationsToPost.append(make_pair(obj, notification)); if (!m_notificationPostTimer.isActive()) m_notificationPostTimer.startOneShot(0); } void AXObjectCache::selectedChildrenChanged(RenderObject* renderer) { - postNotification(renderer, "AXSelectedChildrenChanged", true); + postNotification(renderer, AXSelectedChildrenChanged, true); } #endif diff --git a/WebCore/accessibility/AXObjectCache.h b/WebCore/accessibility/AXObjectCache.h index 7a808dd..5a75f84 100644 --- a/WebCore/accessibility/AXObjectCache.h +++ b/WebCore/accessibility/AXObjectCache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -42,13 +42,11 @@ class WebCoreTextMarker; namespace WebCore { + class Node; + class Page; class RenderObject; class String; class VisiblePosition; - class AccessibilityObject; - class Node; - - typedef unsigned AXID; struct TextMarkerData { AXID axID; @@ -61,7 +59,9 @@ namespace WebCore { public: AXObjectCache(); ~AXObjectCache(); - + + static AccessibilityObject* focusedUIElementForPage(const Page*); + // to be used with render objects AccessibilityObject* getOrCreate(RenderObject*); @@ -76,16 +76,13 @@ namespace WebCore { void detachWrapper(AccessibilityObject*); void attachWrapper(AccessibilityObject*); - void postNotification(RenderObject*, const String&, bool postToElement); - void postPlatformNotification(AccessibilityObject*, const String&); void childrenChanged(RenderObject*); void selectedChildrenChanged(RenderObject*); void handleActiveDescendantChanged(RenderObject*); void handleAriaRoleChanged(RenderObject*); - void handleFocusedUIElementChanged(); -#if PLATFORM(GTK) - void handleFocusedUIElementChangedWithRenderers(RenderObject*, RenderObject*); -#endif + void handleFocusedUIElementChanged(RenderObject* oldFocusedRenderer, RenderObject* newFocusedRenderer); + void handleScrolledToAnchor(const Node* anchorNode); + static void enableAccessibility() { gAccessibilityEnabled = true; } static void enableEnhancedUserInterfaceAccessibility() { gAccessibilityEnhancedUserInterfaceEnabled = true; } @@ -94,11 +91,29 @@ namespace WebCore { void removeAXID(AccessibilityObject*); bool isIDinUse(AXID id) const { return m_idsInUse.contains(id); } + AXID platformGenerateAXID() const; + AccessibilityObject* objectFromAXID(AXID id) const { return m_objects.get(id).get(); } // Text marker utilities. static void textMarkerDataForVisiblePosition(TextMarkerData&, const VisiblePosition&); static VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&); - + + enum AXNotification { + AXCheckedStateChanged, + AXFocusedUIElementChanged, + AXLayoutComplete, + AXLoadComplete, + AXSelectedChildrenChanged, + AXSelectedTextChanged, + AXValueChanged, + AXScrolledToAnchor, + }; + + void postNotification(RenderObject*, AXNotification, bool postToElement); + + protected: + void postPlatformNotification(AccessibilityObject*, AXNotification); + private: HashMap<AXID, RefPtr<AccessibilityObject> > m_objects; HashMap<RenderObject*, AXID> m_renderObjectMapping; @@ -108,7 +123,7 @@ namespace WebCore { HashSet<AXID> m_idsInUse; Timer<AXObjectCache> m_notificationPostTimer; - Vector<pair<RefPtr<AccessibilityObject>, const String> > m_notificationsToPost; + Vector<pair<RefPtr<AccessibilityObject>, AXNotification> > m_notificationsToPost; void notificationPostTimerFired(Timer<AXObjectCache>*); AXID getAXID(AccessibilityObject*); @@ -118,15 +133,13 @@ namespace WebCore { #if !HAVE(ACCESSIBILITY) inline void AXObjectCache::handleActiveDescendantChanged(RenderObject*) { } inline void AXObjectCache::handleAriaRoleChanged(RenderObject*) { } - inline void AXObjectCache::handleFocusedUIElementChanged() { } inline void AXObjectCache::detachWrapper(AccessibilityObject*) { } inline void AXObjectCache::attachWrapper(AccessibilityObject*) { } inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { } - inline void AXObjectCache::postNotification(RenderObject*, const String&, bool postToElement) { } - inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, const String&) { } -#if PLATFORM(GTK) - inline void AXObjectCache::handleFocusedUIElementChangedWithRenderers(RenderObject*, RenderObject*) { } -#endif + inline void AXObjectCache::postNotification(RenderObject*, AXNotification, bool postToElement) { } + inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, AXNotification) { } + inline void AXObjectCache::handleFocusedUIElementChanged(RenderObject*, RenderObject*) { } + inline void AXObjectCache::handleScrolledToAnchor(const Node*) { } #endif } diff --git a/WebCore/accessibility/AccessibilityList.cpp b/WebCore/accessibility/AccessibilityList.cpp index 3b7c7a4..95239b0 100644 --- a/WebCore/accessibility/AccessibilityList.cpp +++ b/WebCore/accessibility/AccessibilityList.cpp @@ -69,6 +69,13 @@ bool AccessibilityList::isUnorderedList() const return false; Node* node = m_renderer->node(); + + // The ARIA spec says the "list" role is supposed to mimic a UL or OL tag. + // Since it can't be both, it's probably OK to say that it's an un-ordered list. + // On the Mac, there's no distinction to the client. + if (ariaRoleAttribute() == ListRole) + return true; + return node && node->hasTagName(ulTag); } diff --git a/WebCore/accessibility/AccessibilityMediaControls.cpp b/WebCore/accessibility/AccessibilityMediaControls.cpp new file mode 100644 index 0000000..7200de9 --- /dev/null +++ b/WebCore/accessibility/AccessibilityMediaControls.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" + +#if ENABLE(VIDEO) + +#include "AccessibilityMediaControls.h" + +#include "AXObjectCache.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "LocalizedStrings.h" +#include "MediaControlElements.h" +#include "RenderObject.h" +#include "RenderSlider.h" + +namespace WebCore { + +using namespace HTMLNames; + + +AccessibilityMediaControl::AccessibilityMediaControl(RenderObject* renderer) + : AccessibilityRenderObject(renderer) +{ +} + +PassRefPtr<AccessibilityObject> AccessibilityMediaControl::create(RenderObject* renderer) +{ + ASSERT(renderer->node() && renderer->node()->isMediaControlElement()); + + Node* node = renderer->node(); + MediaControlElementType controlType; + + if (node->hasTagName(inputTag)) + controlType = static_cast<MediaControlInputElement*>(node)->displayType(); + else + controlType = static_cast<MediaControlElement*>(node)->displayType(); + + PassRefPtr<AccessibilityObject> obj; + switch (controlType) { + case MediaSlider: + obj = AccessibilityMediaTimeline::create(renderer); + break; + + case MediaCurrentTimeDisplay: + case MediaTimeRemainingDisplay: + obj = AccessibilityMediaTimeDisplay::create(renderer); + break; + + case MediaControlsPanel: + obj = AccessibilityMediaControlsContainer::create(renderer); + break; + + default: + obj = adoptRef(new AccessibilityMediaControl(renderer)); + break; + } + + return obj; +} + +MediaControlElementType AccessibilityMediaControl::controlType() const +{ + if (!renderer() || !renderer()->node()) + return MediaTimelineContainer; // Timeline container is not accessible. + + Node* node = renderer()->node(); + + if (node->hasTagName(inputTag)) + return static_cast<MediaControlInputElement*>(node)->displayType(); + + return static_cast<MediaControlElement*>(node)->displayType(); +} + +String AccessibilityMediaControl::controlTypeName() const +{ + DEFINE_STATIC_LOCAL(const String, mediaFullscreenButtonName, ("FullscreenButton")); + DEFINE_STATIC_LOCAL(const String, mediaMuteButtonName, ("MuteButton")); + DEFINE_STATIC_LOCAL(const String, mediaPlayButtonName, ("PlayButton")); + DEFINE_STATIC_LOCAL(const String, mediaSeekBackButtonName, ("SeekBackButton")); + DEFINE_STATIC_LOCAL(const String, mediaSeekForwardButtonName, ("SeekForwardButton")); + DEFINE_STATIC_LOCAL(const String, mediaRewindButtonName, ("RewindButton")); + DEFINE_STATIC_LOCAL(const String, mediaReturnToRealtimeButtonName, ("ReturnToRealtimeButton")); + DEFINE_STATIC_LOCAL(const String, mediaUnMuteButtonName, ("UnMuteButton")); + DEFINE_STATIC_LOCAL(const String, mediaPauseButtonName, ("PauseButton")); + DEFINE_STATIC_LOCAL(const String, mediaStatusDisplayName, ("StatusDisplay")); + DEFINE_STATIC_LOCAL(const String, mediaCurrentTimeDisplay, ("CurrentTimeDisplay")); + DEFINE_STATIC_LOCAL(const String, mediaTimeRemainingDisplay, ("TimeRemainingDisplay")); + + switch (controlType()) { + case MediaFullscreenButton: + return mediaFullscreenButtonName; + case MediaMuteButton: + return mediaMuteButtonName; + case MediaPlayButton: + return mediaPlayButtonName; + case MediaSeekBackButton: + return mediaSeekBackButtonName; + case MediaSeekForwardButton: + return mediaSeekForwardButtonName; + case MediaRewindButton: + return mediaRewindButtonName; + case MediaReturnToRealtimeButton: + return mediaReturnToRealtimeButtonName; + case MediaUnMuteButton: + return mediaUnMuteButtonName; + case MediaPauseButton: + return mediaPauseButtonName; + case MediaStatusDisplay: + return mediaStatusDisplayName; + case MediaCurrentTimeDisplay: + return mediaCurrentTimeDisplay; + case MediaTimeRemainingDisplay: + return mediaTimeRemainingDisplay; + + default: + break; + } + + return String(); +} + +String AccessibilityMediaControl::title() const +{ + DEFINE_STATIC_LOCAL(const String, controlsPanel, ("ControlsPanel")); + + if (controlType() == MediaControlsPanel) + return localizedMediaControlElementString(controlsPanel); + + return AccessibilityRenderObject::title(); +} + +String AccessibilityMediaControl::accessibilityDescription() const +{ + return localizedMediaControlElementString(controlTypeName()); +} + +String AccessibilityMediaControl::helpText() const +{ + return localizedMediaControlElementHelpText(controlTypeName()); +} + +bool AccessibilityMediaControl::accessibilityIsIgnored() const +{ + if (!m_renderer || !m_renderer->style() || m_renderer->style()->visibility() != VISIBLE || controlType() == MediaTimelineContainer) + return true; + + return false; +} + +AccessibilityRole AccessibilityMediaControl::roleValue() const +{ + switch (controlType()) { + case MediaFullscreenButton: + case MediaMuteButton: + case MediaPlayButton: + case MediaSeekBackButton: + case MediaSeekForwardButton: + case MediaRewindButton: + case MediaReturnToRealtimeButton: + case MediaUnMuteButton: + case MediaPauseButton: + return ButtonRole; + + case MediaStatusDisplay: + return StaticTextRole; + + case MediaTimelineContainer: + return GroupRole; + + default: + break; + } + + return UnknownRole; +} + + + +// +// AccessibilityMediaControlsContainer + +AccessibilityMediaControlsContainer::AccessibilityMediaControlsContainer(RenderObject* renderer) + : AccessibilityMediaControl(renderer) +{ +} + +PassRefPtr<AccessibilityObject> AccessibilityMediaControlsContainer::create(RenderObject* renderer) +{ + return adoptRef(new AccessibilityMediaControlsContainer(renderer)); +} + +String AccessibilityMediaControlsContainer::accessibilityDescription() const +{ + return localizedMediaControlElementString(elementTypeName()); +} + +String AccessibilityMediaControlsContainer::helpText() const +{ + return localizedMediaControlElementHelpText(elementTypeName()); +} + +bool AccessibilityMediaControlsContainer::controllingVideoElement() const +{ + if (!m_renderer->node()) + return true; + + MediaControlTimeDisplayElement* element = static_cast<MediaControlTimeDisplayElement*>(m_renderer->node()); + + return element->mediaElement()->isVideo(); +} + +const String AccessibilityMediaControlsContainer::elementTypeName() const +{ + DEFINE_STATIC_LOCAL(const String, videoElement, ("VideoElement")); + DEFINE_STATIC_LOCAL(const String, audioElement, ("AudioElement")); + + if (controllingVideoElement()) + return videoElement; + return audioElement; +} + + +// +// AccessibilityMediaTimeline + +AccessibilityMediaTimeline::AccessibilityMediaTimeline(RenderObject* renderer) + : AccessibilitySlider(renderer) +{ +} + +PassRefPtr<AccessibilityObject> AccessibilityMediaTimeline::create(RenderObject* renderer) +{ + return adoptRef(new AccessibilityMediaTimeline(renderer)); +} + +String AccessibilityMediaTimeline::valueDescription() const +{ + ASSERT(m_renderer->node()->hasTagName(inputTag)); + + float time = static_cast<HTMLInputElement*>(m_renderer->node())->value().toFloat(); + return localizedMediaTimeDescription(time); +} + +String AccessibilityMediaTimeline::helpText() const +{ + DEFINE_STATIC_LOCAL(const String, slider, ("Slider")); + return localizedMediaControlElementHelpText(slider); +} + + +// +// AccessibilityMediaTimeDisplay + +AccessibilityMediaTimeDisplay::AccessibilityMediaTimeDisplay(RenderObject* renderer) + : AccessibilityMediaControl(renderer) +{ +} + +PassRefPtr<AccessibilityObject> AccessibilityMediaTimeDisplay::create(RenderObject* renderer) +{ + return adoptRef(new AccessibilityMediaTimeDisplay(renderer)); +} + +bool AccessibilityMediaTimeDisplay::accessibilityIsIgnored() const +{ + if (!m_renderer || !m_renderer->style() || m_renderer->style()->visibility() != VISIBLE) + return true; + + return !m_renderer->style()->width().value(); +} + +String AccessibilityMediaTimeDisplay::accessibilityDescription() const +{ + DEFINE_STATIC_LOCAL(const String, currentTimeDisplay, ("CurrentTimeDisplay")); + DEFINE_STATIC_LOCAL(const String, timeRemainingDisplay, ("TimeRemainingDisplay")); + + if (controlType() == MediaCurrentTimeDisplay) + return localizedMediaControlElementString(currentTimeDisplay); + + return localizedMediaControlElementString(timeRemainingDisplay); +} + +String AccessibilityMediaTimeDisplay::stringValue() const +{ + if (!m_renderer || !m_renderer->node()) + return String(); + + MediaControlTimeDisplayElement* element = static_cast<MediaControlTimeDisplayElement*>(m_renderer->node()); + float time = element->currentValue(); + return localizedMediaTimeDescription(fabsf(time)); +} + +} // namespace WebCore + +#endif // ENABLE(VIDEO) diff --git a/WebCore/accessibility/AccessibilityMediaControls.h b/WebCore/accessibility/AccessibilityMediaControls.h new file mode 100644 index 0000000..9b306fd --- /dev/null +++ b/WebCore/accessibility/AccessibilityMediaControls.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef AccessibilityMediaControls_h +#define AccessibilityMediaControls_h + +#if ENABLE(VIDEO) + +#include "AccessibilitySlider.h" +#include "MediaControlElements.h" + +namespace WebCore { + + class AccessibilityMediaControl : public AccessibilityRenderObject { + + public: + static PassRefPtr<AccessibilityObject> create(RenderObject*); + virtual ~AccessibilityMediaControl() { } + + virtual AccessibilityRole roleValue() const; + virtual bool accessibilityIsIgnored() const; + + virtual String title() const; + virtual String accessibilityDescription() const; + virtual String helpText() const; + + protected: + AccessibilityMediaControl(RenderObject*); + MediaControlElementType controlType() const; + String controlTypeName() const; + }; + + + class AccessibilityMediaTimeline : public AccessibilitySlider { + + public: + static PassRefPtr<AccessibilityObject> create(RenderObject*); + virtual ~AccessibilityMediaTimeline() { } + + virtual bool isMediaTimeline() const { return true; } + + virtual String helpText() const; + virtual String valueDescription() const; + const AtomicString& getAttribute(const QualifiedName& attribute) const; + + private: + AccessibilityMediaTimeline(RenderObject*); + }; + + + class AccessibilityMediaControlsContainer : public AccessibilityMediaControl { + + public: + static PassRefPtr<AccessibilityObject> create(RenderObject*); + virtual ~AccessibilityMediaControlsContainer() { } + + virtual AccessibilityRole roleValue() const { return ToolbarRole; } + virtual bool accessibilityIsIgnored() const { return false; } + + virtual String helpText() const; + virtual String accessibilityDescription() const; + + private: + AccessibilityMediaControlsContainer(RenderObject*); + bool controllingVideoElement() const; + const String elementTypeName() const; + }; + + + class AccessibilityMediaTimeDisplay : public AccessibilityMediaControl { + + public: + static PassRefPtr<AccessibilityObject> create(RenderObject*); + virtual ~AccessibilityMediaTimeDisplay() { } + + virtual AccessibilityRole roleValue() const { return StaticTextRole; } + virtual bool accessibilityIsIgnored() const; + + virtual String stringValue() const; + virtual String accessibilityDescription() const; + + private: + AccessibilityMediaTimeDisplay(RenderObject*); + }; + + +} // namespace WebCore + +#endif // ENABLE(VIDEO) + +#endif // AccessibilityMediaControls_h diff --git a/WebCore/accessibility/AccessibilityObject.cpp b/WebCore/accessibility/AccessibilityObject.cpp index d6fd969..d7093e4 100644 --- a/WebCore/accessibility/AccessibilityObject.cpp +++ b/WebCore/accessibility/AccessibilityObject.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,6 +41,7 @@ #include "NotImplemented.h" #include "Page.h" #include "RenderImage.h" +#include "RenderListItem.h" #include "RenderListMarker.h" #include "RenderMenuList.h" #include "RenderTextControl.h" @@ -89,6 +90,35 @@ AccessibilityObject* AccessibilityObject::parentObjectUnignored() const return parent; } +AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node) +{ + ASSERT(AXObjectCache::accessibilityEnabled()); + + if (!node) + return 0; + + Document* document = node->document(); + if (!document) + return 0; + + AXObjectCache* cache = document->axObjectCache(); + + AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer()); + while (accessibleObject && accessibleObject->accessibilityIsIgnored()) { + node = node->traverseNextNode(); + + while (node && !node->renderer()) + node = node->traverseNextSibling(); + + if (!node) + return 0; + + accessibleObject = cache->getOrCreate(node->renderer()); + } + + return accessibleObject; +} + bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole) { return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole; @@ -363,6 +393,49 @@ static bool replacedNodeNeedsCharacter(Node* replacedNode) return true; } +// Finds a RenderListItem parent give a node. +RenderListItem* AccessibilityObject::renderListItemContainerForNode(Node* node) const +{ + for (Node* stringNode = node; stringNode; stringNode = stringNode->parent()) { + RenderObject* renderObject = stringNode->renderer(); + if (!renderObject || !renderObject->isListItem()) + continue; + + return toRenderListItem(renderObject); + } + + return 0; +} + +// Returns the text associated with a list marker if this node is contained within a list item. +String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const +{ + // If the range does not contain the start of the line, the list marker text should not be included. + if (!isStartOfLine(visiblePositionStart)) + return String(); + + RenderListItem* listItem = renderListItemContainerForNode(node); + if (!listItem) + return String(); + + // If this is in a list item, we need to manually add the text for the list marker + // because a RenderListMarker does not have a Node equivalent and thus does not appear + // when iterating text. + const String& markerText = listItem->markerText(); + if (markerText.isEmpty()) + return String(); + + // Append text, plus the period that follows the text. + // FIXME: Not all list marker styles are followed by a period, but this + // sounds much better when there is a synthesized pause because of a period. + Vector<UChar> resultVector; + resultVector.append(markerText.characters(), markerText.length()); + resultVector.append('.'); + resultVector.append(' '); + + return String::adopt(resultVector); +} + String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const { if (visiblePositionRange.isNull()) @@ -373,6 +446,11 @@ String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionR for (TextIterator it(range.get()); !it.atEnd(); it.advance()) { // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) if (it.length() != 0) { + // Add a textual representation for list marker text + String listMarkerText = listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start); + if (!listMarkerText.isEmpty()) + resultVector.append(listMarkerText.characters(), listMarkerText.length()); + resultVector.append(it.characters(), it.length()); } else { // locate the node and starting offset for this replaced range diff --git a/WebCore/accessibility/AccessibilityObject.h b/WebCore/accessibility/AccessibilityObject.h index 3f6c395..c5ba1ed 100644 --- a/WebCore/accessibility/AccessibilityObject.h +++ b/WebCore/accessibility/AccessibilityObject.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2008 Nuanti Ltd. * * Redistribution and use in source and binary forms, with or without @@ -86,10 +86,13 @@ class IntPoint; class IntSize; class Node; class RenderObject; +class RenderListItem; class VisibleSelection; class String; class Widget; +typedef unsigned AXID; + enum AccessibilityRole { UnknownRole = 1, ButtonRole, @@ -156,7 +159,28 @@ enum AccessibilityRole { DefinitionListTermRole, DefinitionListDefinitionRole, AnnotationRole, - SliderThumbRole + SliderThumbRole, + + // ARIA Grouping roles + LandmarkApplicationRole, + LandmarkBannerRole, + LandmarkComplementaryRole, + LandmarkContentInfoRole, + LandmarkMainRole, + LandmarkNavigationRole, + LandmarkSearchRole, + + ApplicationLogRole, + ApplicationMarqueeRole, + ApplicationStatusRole, + ApplicationTimerRole, + + DocumentRole, + DocumentArticleRole, + DocumentNoteRole, + DocumentRegionRole, + + UserInterfaceTooltipRole }; enum AccessibilityOrientation { @@ -220,6 +244,7 @@ public: virtual bool isWebArea() const { return false; }; virtual bool isCheckboxOrRadio() const { return false; }; virtual bool isListBox() const { return roleValue() == ListBoxRole; }; + virtual bool isMediaTimeline() const { return false; } virtual bool isMenuRelated() const { return false; } virtual bool isMenu() const { return false; } virtual bool isMenuBar() const { return false; } @@ -285,11 +310,13 @@ public: virtual AccessibilityObject* parentObject() const = 0; virtual AccessibilityObject* parentObjectUnignored() const; virtual AccessibilityObject* parentObjectIfExists() const { return 0; } + static AccessibilityObject* firstAccessibleObjectFromNode(const Node*); virtual AccessibilityObject* observableObject() const { return 0; } virtual void linkedUIElements(AccessibilityChildrenVector&) const { } virtual AccessibilityObject* titleUIElement() const { return 0; } virtual bool exposesTitleUIElement() const { return true; } + virtual AccessibilityObject* correspondingControlForLabelElement() const { return 0; } virtual AccessibilityRole ariaRoleAttribute() const { return UnknownRole; } virtual bool isPresentationalChildOfAriaRole() const { return false; } @@ -304,8 +331,8 @@ public: virtual PassRefPtr<Range> ariaSelectedTextDOMRange() const { return 0; } virtual AXObjectCache* axObjectCache() const { return 0; } - unsigned axObjectID() const { return m_id; } - void setAXObjectID(unsigned axObjectID) { m_id = axObjectID; } + AXID axObjectID() const { return m_id; } + void setAXObjectID(AXID axObjectID) { m_id = axObjectID; } static AccessibilityObject* anchorElementForNode(Node*); virtual Element* anchorElement() const { return 0; } @@ -408,6 +435,7 @@ public: virtual String doAXStringForRange(const PlainTextRange&) const { return String(); } virtual IntRect doAXBoundsForRange(const PlainTextRange&) const { return IntRect(); } + String listMarkerTextForNodeAndPosition(Node*, const VisiblePosition&) const; unsigned doAXLineForIndex(unsigned); @@ -436,14 +464,15 @@ public: virtual void updateBackingStore() { } protected: - unsigned m_id; + AXID m_id; AccessibilityChildrenVector m_children; mutable bool m_haveChildren; AccessibilityRole m_role; virtual void clearChildren(); virtual bool isDetached() const { return true; } - + RenderListItem* renderListItemContainerForNode(Node* node) const; + #if PLATFORM(MAC) RetainPtr<AccessibilityObjectWrapper> m_wrapper; #elif PLATFORM(WIN) && !PLATFORM(WINCE) diff --git a/WebCore/accessibility/AccessibilityRenderObject.cpp b/WebCore/accessibility/AccessibilityRenderObject.cpp index e0f9a91..834e931 100644 --- a/WebCore/accessibility/AccessibilityRenderObject.cpp +++ b/WebCore/accessibility/AccessibilityRenderObject.cpp @@ -35,7 +35,6 @@ #include "CharacterNames.h" #include "EventNames.h" #include "FloatRect.h" -#include "FocusController.h" #include "Frame.h" #include "FrameLoader.h" #include "HTMLAreaElement.h" @@ -54,7 +53,6 @@ #include "HitTestResult.h" #include "LocalizedStrings.h" #include "NodeList.h" -#include "Page.h" #include "RenderButton.h" #include "RenderFieldset.h" #include "RenderFileUploadControl.h" @@ -404,6 +402,11 @@ bool AccessibilityRenderObject::isReadOnly() const return !frame->isContentEditable(); } + if (m_renderer->isTextField()) + return static_cast<HTMLInputElement*>(m_renderer->node())->readOnly(); + if (m_renderer->isTextArea()) + return static_cast<HTMLTextAreaElement*>(m_renderer->node())->readOnly(); + return !m_renderer->node() || !m_renderer->node()->isContentEditable(); } @@ -603,6 +606,22 @@ Element* AccessibilityRenderObject::mouseButtonListener() const return 0; } +void AccessibilityRenderObject::increment() +{ + if (roleValue() != SliderRole) + return; + + changeValueByPercent(5); +} + +void AccessibilityRenderObject::decrement() +{ + if (roleValue() != SliderRole) + return; + + changeValueByPercent(-5); +} + static Element* siblingWithAriaRole(String role, Node* node) { Node* sibling = node->parent()->firstChild(); @@ -1020,7 +1039,7 @@ String AccessibilityRenderObject::accessibilityDescription() const const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr); if (!title.isEmpty()) return title; - return static_cast<HTMLFrameElementBase*>(owner)->name(); + return static_cast<HTMLFrameElementBase*>(owner)->getAttribute(nameAttr); } if (owner->isHTMLElement()) return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr); @@ -1135,22 +1154,10 @@ AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const if (!linkedNode) return 0; - // the element we find may not be accessible, keep searching until we find a good one - AccessibilityObject* linkedAXElement = m_renderer->document()->axObjectCache()->getOrCreate(linkedNode->renderer()); - while (linkedAXElement && linkedAXElement->accessibilityIsIgnored()) { - linkedNode = linkedNode->traverseNextNode(); - - while (linkedNode && !linkedNode->renderer()) - linkedNode = linkedNode->traverseNextSibling(); - - if (!linkedNode) - return 0; - linkedAXElement = m_renderer->document()->axObjectCache()->getOrCreate(linkedNode->renderer()); - } - - return linkedAXElement; + // The element we find may not be accessible, so find the first accessible object. + return firstAccessibleObjectFromNode(linkedNode); } - + void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const { if (!m_renderer || roleValue() != RadioButtonRole) @@ -1269,15 +1276,9 @@ bool AccessibilityRenderObject::accessibilityIsIgnored() const // find out if this element is inside of a label element. // if so, it may be ignored because it's the label for a checkbox or radio button - HTMLLabelElement* labelElement = labelElementContainer(); - if (labelElement) { - HTMLElement* correspondingControl = labelElement->correspondingControl(); - if (correspondingControl && correspondingControl->renderer()) { - AccessibilityObject* controlObject = axObjectCache()->getOrCreate(correspondingControl->renderer()); - if (!controlObject->exposesTitleUIElement()) - return true; - } - } + AccessibilityObject* controlObject = correspondingControlForLabelElement(); + if (controlObject && !controlObject->exposesTitleUIElement()) + return true; AccessibilityRole ariaRole = ariaRoleAttribute(); if (ariaRole == TextAreaRole || ariaRole == StaticTextRole) { @@ -1590,8 +1591,22 @@ void AccessibilityRenderObject::setFocused(bool on) } } +void AccessibilityRenderObject::changeValueByPercent(float percentChange) +{ + float range = maxValueForRange() - minValueForRange(); + float value = valueForRange(); + + value += range * (percentChange / 100); + setValue(String::number(value)); + + axObjectCache()->postNotification(m_renderer, AXObjectCache::AXValueChanged, true); +} + void AccessibilityRenderObject::setValue(const String& string) { + if (!m_renderer) + return; + // FIXME: Do we want to do anything here for ARIA textboxes? if (m_renderer->isTextField()) { HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node()); @@ -1599,6 +1614,10 @@ void AccessibilityRenderObject::setValue(const String& string) } else if (m_renderer->isTextArea()) { HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(m_renderer->node()); textArea->setValue(string); + } else if (roleValue() == SliderRole) { + Node* element = m_renderer->node(); + if (element && element->isElementNode()) + static_cast<Element*>(element)->setAttribute(aria_valuenowAttr, string); } } @@ -1658,7 +1677,7 @@ AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(H // The HTMLImageElement's useMap() value includes the '#' symbol at the beginning, // which has to be stripped off - String useMapName = static_cast<HTMLImageElement*>(curr)->useMap().substring(1).lower(); + String useMapName = static_cast<HTMLImageElement*>(curr)->getAttribute(usemapAttr).string().substring(1).lower(); if (useMapName == mapName) return axObjectCache()->getOrCreate(obj); } @@ -2094,40 +2113,25 @@ AccessibilityObject* AccessibilityRenderObject::doAccessibilityHitTest(const Int if (obj->isListBox()) return static_cast<AccessibilityListBox*>(result)->doAccessibilityHitTest(point); - if (result->accessibilityIsIgnored()) + if (result->accessibilityIsIgnored()) { + // If this element is the label of a control, a hit test should return the control. + AccessibilityObject* controlObject = result->correspondingControlForLabelElement(); + if (controlObject && !controlObject->exposesTitleUIElement()) + return controlObject; + result = result->parentObjectUnignored(); + } return result; } AccessibilityObject* AccessibilityRenderObject::focusedUIElement() const { - // get the focused node in the page Page* page = m_renderer->document()->page(); if (!page) return 0; - - Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document(); - Node* focusedNode = focusedDocument->focusedNode(); - if (!focusedNode) - focusedNode = focusedDocument; - - RenderObject* focusedNodeRenderer = focusedNode->renderer(); - if (!focusedNodeRenderer) - return 0; - - AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer); - - if (obj->shouldFocusActiveDescendant()) { - if (AccessibilityObject* descendant = obj->activeDescendant()) - obj = descendant; - } - - // the HTML element, for example, is focusable but has an AX object that is ignored - if (obj->accessibilityIsIgnored()) - obj = obj->parentObjectUnignored(); - - return obj; + + return AXObjectCache::focusedUIElementForPage(page); } bool AccessibilityRenderObject::shouldFocusActiveDescendant() const @@ -2174,7 +2178,7 @@ AccessibilityObject* AccessibilityRenderObject::activeDescendant() const return 0; AccessibilityObject* obj = renderer()->document()->axObjectCache()->getOrCreate(target->renderer()); - if (obj->isAccessibilityRenderObject()) + if (obj && obj->isAccessibilityRenderObject()) // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification return obj; return 0; @@ -2192,9 +2196,21 @@ void AccessibilityRenderObject::handleActiveDescendantChanged() AccessibilityRenderObject* activedescendant = static_cast<AccessibilityRenderObject*>(activeDescendant()); if (activedescendant && shouldFocusActiveDescendant()) - doc->axObjectCache()->postNotification(activedescendant->renderer(), "AXFocusedUIElementChanged", true); + doc->axObjectCache()->postNotification(activedescendant->renderer(), AXObjectCache::AXFocusedUIElementChanged, true); } +AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const +{ + HTMLLabelElement* labelElement = labelElementContainer(); + if (!labelElement) + return 0; + + HTMLElement* correspondingControl = labelElement->correspondingControl(); + if (!correspondingControl) + return 0; + + return axObjectCache()->getOrCreate(correspondingControl->renderer()); +} AccessibilityObject* AccessibilityRenderObject::observableObject() const { @@ -2216,32 +2232,52 @@ static const ARIARoleMap& createARIARoleMap() }; const RoleEntry roles[] = { + { "application", LandmarkApplicationRole }, + { "article", DocumentArticleRole }, + { "banner", LandmarkBannerRole }, { "button", ButtonRole }, { "checkbox", CheckBoxRole }, + { "complementary", LandmarkComplementaryRole }, + { "contentinfo", LandmarkContentInfoRole }, { "grid", TableRole }, { "gridcell", CellRole }, { "columnheader", ColumnHeaderRole }, + { "definition", DefinitionListDefinitionRole }, + { "document", DocumentRole }, { "rowheader", RowHeaderRole }, { "group", GroupRole }, { "heading", HeadingRole }, { "img", ImageRole }, { "link", WebCoreLinkRole }, + { "list", ListRole }, + { "listitem", GroupRole }, { "listbox", ListBoxRole }, + { "log", ApplicationLogRole }, // "option" isn't here because it may map to different roles depending on the parent element's role + { "main", LandmarkMainRole }, + { "marquee", ApplicationMarqueeRole }, { "menu", MenuRole }, { "menubar", GroupRole }, // "menuitem" isn't here because it may map to different roles depending on the parent element's role { "menuitemcheckbox", MenuItemRole }, { "menuitemradio", MenuItemRole }, + { "note", DocumentNoteRole }, + { "navigation", LandmarkNavigationRole }, { "progressbar", ProgressIndicatorRole }, { "radio", RadioButtonRole }, { "radiogroup", RadioGroupRole }, + { "region", DocumentRegionRole }, { "row", RowRole }, { "range", SliderRole }, + { "search", LandmarkSearchRole }, + { "separator", SplitterRole }, { "slider", SliderRole }, { "spinbutton", ProgressIndicatorRole }, + { "status", ApplicationStatusRole }, { "textbox", TextAreaRole }, - { "toolbar", ToolbarRole } + { "timer", ApplicationTimerRole }, + { "toolbar", ToolbarRole }, + { "tooltip", UserInterfaceTooltipRole } }; ARIARoleMap& roleMap = *new ARIARoleMap; @@ -2418,6 +2454,7 @@ bool AccessibilityRenderObject::canSetFocusAttribute() const case PopUpButtonRole: case CheckBoxRole: case RadioButtonRole: + case SliderRole: return true; default: return false; @@ -2429,10 +2466,10 @@ bool AccessibilityRenderObject::canSetValueAttribute() const if (equalIgnoringCase(getAttribute(aria_readonlyAttr).string(), "true")) return false; - if (isWebArea()) + if (isWebArea() || isTextControl()) return !isReadOnly(); - return isTextControl() || isProgressIndicator() || isSlider(); + return isProgressIndicator() || isSlider(); } bool AccessibilityRenderObject::canSetTextRangeAttributes() const @@ -2639,7 +2676,9 @@ void AccessibilityRenderObject::updateBackingStore() { if (!m_renderer) return; - m_renderer->view()->layoutIfNeeded(); -} - + + // Updating layout may delete m_renderer and this object. + m_renderer->document()->updateLayoutIgnorePendingStylesheets(); +} + } // namespace WebCore diff --git a/WebCore/accessibility/AccessibilityRenderObject.h b/WebCore/accessibility/AccessibilityRenderObject.h index 2b29f4a..d82ca71 100644 --- a/WebCore/accessibility/AccessibilityRenderObject.h +++ b/WebCore/accessibility/AccessibilityRenderObject.h @@ -133,6 +133,8 @@ public: virtual void linkedUIElements(AccessibilityChildrenVector&) const; virtual bool exposesTitleUIElement() const; virtual AccessibilityObject* titleUIElement() const; + virtual AccessibilityObject* correspondingControlForLabelElement() const; + virtual AccessibilityRole ariaRoleAttribute() const; virtual bool isPresentationalChildOfAriaRole() const; virtual bool ariaRoleHasPresentationalChildren() const; @@ -188,6 +190,9 @@ public: virtual void setFocused(bool); virtual void setSelectedTextRange(const PlainTextRange&); virtual void setValue(const String&); + virtual void changeValueByPercent(float percentChange); + virtual void increment(); + virtual void decrement(); virtual void detach(); virtual void childrenChanged(); diff --git a/WebCore/accessibility/AccessibilitySlider.cpp b/WebCore/accessibility/AccessibilitySlider.cpp index e3a654b..5aca672 100644 --- a/WebCore/accessibility/AccessibilitySlider.cpp +++ b/WebCore/accessibility/AccessibilitySlider.cpp @@ -75,6 +75,7 @@ AccessibilityOrientation AccessibilitySlider::orientation() const case SliderThumbVerticalPart: case SliderVerticalPart: + case MediaVolumeSliderPart: return AccessibilityOrientationVertical; default: @@ -113,15 +114,6 @@ float AccessibilitySlider::minValueForRange() const return getAttribute(minAttr).toFloat(); } -void AccessibilitySlider::changeValue(float percentChange) -{ - float range = maxValueForRange() - minValueForRange(); - float value = valueForRange(); - - value += range * (percentChange / 100); - setValue(String::number(value)); -} - void AccessibilitySlider::setValue(const String& value) { HTMLInputElement* input = element(); @@ -135,16 +127,6 @@ void AccessibilitySlider::setValue(const String& value) input->dispatchFormControlChangeEvent(); } -void AccessibilitySlider::increment() -{ - return changeValue(5); -} - -void AccessibilitySlider::decrement() -{ - return changeValue(-5); -} - HTMLInputElement* AccessibilitySlider::element() const { return static_cast<HTMLInputElement*>(m_renderer->node()); diff --git a/WebCore/accessibility/AccessibilitySlider.h b/WebCore/accessibility/AccessibilitySlider.h index eadab08..254ebdd 100644 --- a/WebCore/accessibility/AccessibilitySlider.h +++ b/WebCore/accessibility/AccessibilitySlider.h @@ -58,12 +58,10 @@ namespace WebCore { virtual float minValueForRange() const; virtual AccessibilityOrientation orientation() const; - virtual void increment(); - virtual void decrement(); + protected: + AccessibilitySlider(RenderObject*); private: - AccessibilitySlider(RenderObject*); - void changeValue(float /*percentChange*/); HTMLInputElement* element() const; }; diff --git a/WebCore/accessibility/chromium/AXObjectCacheChromium.cpp b/WebCore/accessibility/chromium/AXObjectCacheChromium.cpp index d0f1882..a97dfe2 100644 --- a/WebCore/accessibility/chromium/AXObjectCacheChromium.cpp +++ b/WebCore/accessibility/chromium/AXObjectCacheChromium.cpp @@ -43,11 +43,15 @@ void AXObjectCache::attachWrapper(AccessibilityObject*) // In Chromium, AccessibilityObjects are wrapped lazily. } -void AXObjectCache::postPlatformNotification(AccessibilityObject*, const String&) +void AXObjectCache::postPlatformNotification(AccessibilityObject*, AXNotification) { } -void AXObjectCache::handleFocusedUIElementChanged() +void AXObjectCache::handleFocusedUIElementChanged(RenderObject*, RenderObject*) +{ +} + +void AXObjectCache::handleScrolledToAnchor(const Node*) { } diff --git a/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp b/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp index a04367f..cc515ad 100644 --- a/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp +++ b/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp @@ -37,20 +37,16 @@ void AXObjectCache::attachWrapper(AccessibilityObject* obj) g_object_unref(atkObj); } -void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, const String& message) +void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, AXNotification notification) { - if (message == "AXCheckedStateChanged") { + if (notification == AXCheckedStateChanged) { if (!coreObject->isCheckboxOrRadio()) return; g_signal_emit_by_name(coreObject->wrapper(), "state-change", "checked", coreObject->isChecked()); } } - -void AXObjectCache::handleFocusedUIElementChanged() -{ -} -void AXObjectCache::handleFocusedUIElementChangedWithRenderers(RenderObject* oldFocusedRender, RenderObject* newFocusedRender) +void AXObjectCache::handleFocusedUIElementChanged(RenderObject* oldFocusedRender, RenderObject* newFocusedRender) { RefPtr<AccessibilityObject> oldObject = getOrCreate(oldFocusedRender); if (oldObject) { @@ -64,4 +60,8 @@ void AXObjectCache::handleFocusedUIElementChangedWithRenderers(RenderObject* old } } +void AXObjectCache::handleScrolledToAnchor(const Node*) +{ +} + } // namespace WebCore diff --git a/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp b/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp index 6d3729b..811903d 100644 --- a/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp +++ b/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp @@ -60,7 +60,7 @@ using namespace WebCore; static AccessibilityObject* fallbackObject() { - static AXObjectCache* fallbackCache = new AXObjectCache(); + static AXObjectCache* fallbackCache = new AXObjectCache; static AccessibilityObject* object = 0; if (!object) { // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack @@ -556,7 +556,7 @@ static PangoLayout* getPangoLayoutForAtk(AtkText* textObject) HostWindow* hostWindow = coreObject->document()->view()->hostWindow(); if (!hostWindow) return 0; - PlatformWidget webView = hostWindow->platformWindow(); + PlatformPageClient webView = hostWindow->platformPageClient(); if (!webView) return 0; diff --git a/WebCore/accessibility/mac/AXObjectCacheMac.mm b/WebCore/accessibility/mac/AXObjectCacheMac.mm index be7968b..2f18cf3 100644 --- a/WebCore/accessibility/mac/AXObjectCacheMac.mm +++ b/WebCore/accessibility/mac/AXObjectCacheMac.mm @@ -51,19 +51,51 @@ void AXObjectCache::attachWrapper(AccessibilityObject* obj) obj->setWrapper([[AccessibilityObjectWrapper alloc] initWithAccessibilityObject:obj]); } -void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, const String& message) +void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification) { if (!obj) return; - NSAccessibilityPostNotification(obj->wrapper(), message); + // Some notifications are unique to Safari and do not have NSAccessibility equivalents. + String macNotification; + switch (notification) { + case AXCheckedStateChanged: + macNotification = "AXCheckedStateChanged"; + break; + case AXFocusedUIElementChanged: + macNotification = NSAccessibilityFocusedUIElementChangedNotification; + break; + case AXLayoutComplete: + macNotification = "AXLayoutComplete"; + break; + case AXLoadComplete: + macNotification = "AXLoadComplete"; + break; + case AXSelectedChildrenChanged: + macNotification = NSAccessibilitySelectedChildrenChangedNotification; + break; + case AXSelectedTextChanged: + macNotification = NSAccessibilitySelectedTextChangedNotification; + break; + case AXValueChanged: + macNotification = NSAccessibilityValueChangedNotification; + break; + default: + return; + } + + NSAccessibilityPostNotification(obj->wrapper(), macNotification); } -void AXObjectCache::handleFocusedUIElementChanged() +void AXObjectCache::handleFocusedUIElementChanged(RenderObject*, RenderObject*) { [[WebCoreViewFactory sharedFactory] accessibilityHandleFocusChanged]; } +void AXObjectCache::handleScrolledToAnchor(const Node*) +{ +} + } #endif // HAVE(ACCESSIBILITY) diff --git a/WebCore/accessibility/mac/AccessibilityObjectWrapper.mm b/WebCore/accessibility/mac/AccessibilityObjectWrapper.mm index d1e0599..e56e77c 100644 --- a/WebCore/accessibility/mac/AccessibilityObjectWrapper.mm +++ b/WebCore/accessibility/mac/AccessibilityObjectWrapper.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -122,6 +122,7 @@ using namespace std; #ifdef BUILDING_ON_TIGER typedef unsigned NSUInteger; #define NSAccessibilityValueDescriptionAttribute @"AXValueDescription" +#define NSAccessibilityTimelineSubrole @"AXTimeline" #endif @interface NSObject (WebKitAccessibilityArrayCategory) @@ -378,7 +379,7 @@ static void AXAttributeStringSetSpelling(NSMutableAttributedString* attrString, // add misspelling attribute for the intersection of the marker and the range int rStart = range.location + (marker.startOffset - offset); - int rLength = MIN(marker.endOffset, endOffset) - marker.startOffset; + int rLength = min(marker.endOffset, endOffset) - marker.startOffset; NSRange spellRange = NSMakeRange(rStart, rLength); AXAttributeStringSetNumber(attrString, NSAccessibilityMisspelledTextAttribute, [NSNumber numberWithBool:YES], spellRange); @@ -478,6 +479,9 @@ static NSString* nsStringForReplacedNode(Node* replacedNode) - (NSAttributedString*)doAXAttributedStringForTextMarkerRange:(WebCoreTextMarkerRange*)textMarkerRange { + if (!m_object) + return nil; + // extract the start and end VisiblePosition VisiblePosition startVisiblePosition = visiblePositionForStartOfTextMarkerRange(textMarkerRange); if (startVisiblePosition.isNull()) @@ -487,6 +491,7 @@ static NSString* nsStringForReplacedNode(Node* replacedNode) if (endVisiblePosition.isNull()) return nil; + VisiblePositionRange visiblePositionRange(startVisiblePosition, endVisiblePosition); // iterate over the range to build the AX attributed string NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init]; TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get()); @@ -499,6 +504,11 @@ static NSString* nsStringForReplacedNode(Node* replacedNode) // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) if (it.length() != 0) { + // Add the text of the list marker item if necessary. + String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition())); + if (!listMarkerText.isEmpty()) + AXAttributedStringAppendText(attrString, node, offset, listMarkerText.characters(), listMarkerText.length()); + AXAttributedStringAppendText(attrString, node, offset, it.characters(), it.length()); } else { Node* replacedNode = node->childNode(offset); @@ -536,6 +546,8 @@ static WebCoreTextMarkerRange* textMarkerRangeFromVisiblePositions(VisiblePositi return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; static NSArray* actionElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil]; static NSArray* defaultElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityShowMenuAction, nil]; @@ -563,6 +575,8 @@ static WebCoreTextMarkerRange* textMarkerRangeFromVisiblePositions(VisiblePositi return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; if (m_object->isAttachment()) return [[self attachmentView] accessibilityAttributeNames]; @@ -669,6 +683,7 @@ static WebCoreTextMarkerRange* textMarkerRangeFromVisiblePositions(VisiblePositi [tempArray addObject:NSAccessibilityMinValueAttribute]; [tempArray addObject:NSAccessibilityMaxValueAttribute]; [tempArray addObject:NSAccessibilityOrientationAttribute]; + [tempArray addObject:NSAccessibilityValueDescriptionAttribute]; rangeAttrs = [[NSArray alloc] initWithArray:tempArray]; [tempArray release]; } @@ -969,8 +984,23 @@ static const AccessibilityRoleMap& createAccessibilityRoleMap() { TableHeaderContainerRole, NSAccessibilityGroupRole }, { DefinitionListDefinitionRole, NSAccessibilityGroupRole }, { DefinitionListTermRole, NSAccessibilityGroupRole }, - { SliderThumbRole, NSAccessibilityValueIndicatorRole }, + { LandmarkApplicationRole, NSAccessibilityGroupRole }, + { LandmarkBannerRole, NSAccessibilityGroupRole }, + { LandmarkComplementaryRole, NSAccessibilityGroupRole }, + { LandmarkContentInfoRole, NSAccessibilityGroupRole }, + { LandmarkMainRole, NSAccessibilityGroupRole }, + { LandmarkNavigationRole, NSAccessibilityGroupRole }, + { LandmarkSearchRole, NSAccessibilityGroupRole }, + { ApplicationLogRole, NSAccessibilityGroupRole }, + { ApplicationMarqueeRole, NSAccessibilityGroupRole }, + { ApplicationStatusRole, NSAccessibilityGroupRole }, + { ApplicationTimerRole, NSAccessibilityGroupRole }, + { DocumentRole, NSAccessibilityGroupRole }, + { DocumentArticleRole, NSAccessibilityGroupRole }, + { DocumentNoteRole, NSAccessibilityGroupRole }, + { DocumentRegionRole, NSAccessibilityGroupRole }, + { UserInterfaceTooltipRole, NSAccessibilityGroupRole }, }; @@ -1019,6 +1049,47 @@ static NSString* roleValueToNSString(AccessibilityRole value) return NSAccessibilityDefinitionListSubrole; } + // ARIA content subroles. + switch (m_object->roleValue()) { + case LandmarkApplicationRole: + return @"AXLandmarkApplication"; + case LandmarkBannerRole: + return @"AXLandmarkBanner"; + case LandmarkComplementaryRole: + return @"AXLandmarkComplementary"; + case LandmarkContentInfoRole: + return @"AXLandmarkContentInfo"; + case LandmarkMainRole: + return @"AXLandmarkMain"; + case LandmarkNavigationRole: + return @"AXLandmarkNavigation"; + case LandmarkSearchRole: + return @"AXLandmarkSearch"; + case ApplicationLogRole: + return @"AXApplicationLog"; + case ApplicationMarqueeRole: + return @"AXApplicationMarquee"; + case ApplicationStatusRole: + return @"AXApplicationStatus"; + case ApplicationTimerRole: + return @"AXApplicationTimer"; + case DocumentRole: + return @"AXDocument"; + case DocumentArticleRole: + return @"AXDocumentArticle"; + case DocumentNoteRole: + return @"AXDocumentNote"; + case DocumentRegionRole: + return @"AXDocumentRegion"; + case UserInterfaceTooltipRole: + return @"AXUserInterfaceTooltip"; + default: + return nil; + } + + if (m_object->isMediaTimeline()) + return NSAccessibilityTimelineSubrole; + return nil; } @@ -1047,8 +1118,44 @@ static NSString* roleValueToNSString(AccessibilityRole value) if ([axRole isEqualToString:NSAccessibilityImageRole]) return NSAccessibilityRoleDescription(NSAccessibilityImageRole, [self subrole]); - if ([axRole isEqualToString:NSAccessibilityGroupRole]) - return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]); + if ([axRole isEqualToString:NSAccessibilityGroupRole]) { + switch (m_object->roleValue()) { + default: + return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]); + case LandmarkApplicationRole: + return AXARIAContentGroupText(@"ARIALandmarkApplication"); + case LandmarkBannerRole: + return AXARIAContentGroupText(@"ARIALandmarkBanner"); + case LandmarkComplementaryRole: + return AXARIAContentGroupText(@"ARIALandmarkComplementary"); + case LandmarkContentInfoRole: + return AXARIAContentGroupText(@"ARIALandmarkContentInfo"); + case LandmarkMainRole: + return AXARIAContentGroupText(@"ARIALandmarkMain"); + case LandmarkNavigationRole: + return AXARIAContentGroupText(@"ARIALandmarkNavigation"); + case LandmarkSearchRole: + return AXARIAContentGroupText(@"ARIALandmarkSearch"); + case ApplicationLogRole: + return AXARIAContentGroupText(@"ARIAApplicationLog"); + case ApplicationMarqueeRole: + return AXARIAContentGroupText(@"ARIAApplicationMarquee"); + case ApplicationStatusRole: + return AXARIAContentGroupText(@"ARIAApplicationStatus"); + case ApplicationTimerRole: + return AXARIAContentGroupText(@"ARIAApplicationTimer"); + case DocumentRole: + return AXARIAContentGroupText(@"ARIADocument"); + case DocumentArticleRole: + return AXARIAContentGroupText(@"ARIADocumentArticle"); + case DocumentNoteRole: + return AXARIAContentGroupText(@"ARIADocumentNote"); + case DocumentRegionRole: + return AXARIAContentGroupText(@"ARIADocumentRegion"); + case UserInterfaceTooltipRole: + return AXARIAContentGroupText(@"ARIAUserInterfaceTooltip"); + } + } if ([axRole isEqualToString:NSAccessibilityCheckBoxRole]) return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, [self subrole]); @@ -1111,6 +1218,9 @@ static NSString* roleValueToNSString(AccessibilityRole value) if ([axRole isEqualToString:NSAccessibilityToolbarRole]) return NSAccessibilityRoleDescription(NSAccessibilityToolbarRole, [self subrole]); + if ([axRole isEqualToString:NSAccessibilitySplitterRole]) + return NSAccessibilityRoleDescription(NSAccessibilitySplitterRole, [self subrole]); + return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil); } @@ -1123,6 +1233,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; if ([attributeName isEqualToString: NSAccessibilityRoleAttribute]) return [self role]; @@ -1244,7 +1356,7 @@ static NSString* roleValueToNSString(AccessibilityRole value) } return m_object->accessibilityDescription(); } - + if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) { if (m_object->isAttachment()) { if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute]) @@ -1465,6 +1577,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; RefPtr<AccessibilityObject> focusedObj = m_object->focusedUIElement(); @@ -1480,6 +1594,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; RefPtr<AccessibilityObject> axObject = m_object->doAccessibilityHitTest(IntPoint(point)); if (axObject) @@ -1493,6 +1609,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) return YES; @@ -1532,6 +1650,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; if (m_object->isAttachment()) return [[self attachmentView] accessibilityIsIgnored]; @@ -1544,6 +1664,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; if (m_object->isAttachment()) return nil; @@ -1581,6 +1703,7 @@ static NSString* roleValueToNSString(AccessibilityRole value) @"AXStyleTextMarkerRangeForTextMarker", @"AXLengthForTextMarkerRange", NSAccessibilityBoundsForRangeParameterizedAttribute, + NSAccessibilityStringForRangeParameterizedAttribute, nil]; } @@ -1629,6 +1752,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return; m_object->updateBackingStore(); + if (!m_object) + return; if (m_object->isAttachment()) [[self attachmentView] accessibilityPerformAction:NSAccessibilityPressAction]; @@ -1642,6 +1767,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return; m_object->updateBackingStore(); + if (!m_object) + return; if (m_object->isAttachment()) [[self attachmentView] accessibilityPerformAction:NSAccessibilityIncrementAction]; @@ -1655,6 +1782,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return; m_object->updateBackingStore(); + if (!m_object) + return; if (m_object->isAttachment()) [[self attachmentView] accessibilityPerformAction:NSAccessibilityDecrementAction]; @@ -1704,6 +1833,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return; m_object->updateBackingStore(); + if (!m_object) + return; if ([action isEqualToString:NSAccessibilityPressAction]) [self accessibilityPerformPressAction]; @@ -1724,6 +1855,8 @@ static NSString* roleValueToNSString(AccessibilityRole value) return; m_object->updateBackingStore(); + if (!m_object) + return; WebCoreTextMarkerRange* textMarkerRange = nil; NSNumber* number = nil; @@ -1848,6 +1981,8 @@ static RenderObject* rendererForView(NSView* view) return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; // common parameter type check/casting. Nil checks in handlers catch wrong type case. // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from @@ -1932,6 +2067,14 @@ static RenderObject* rendererForView(NSView* view) NSRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange(start, end)); return [NSValue valueWithRect:rect]; } + + if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute]) { + VisiblePosition start = m_object->visiblePositionForIndex(range.location); + VisiblePosition end = m_object->visiblePositionForIndex(range.location+range.length); + if (start.isNull() || end.isNull()) + return nil; + return m_object->stringForVisiblePositionRange(VisiblePositionRange(start, end)); + } if ([attribute isEqualToString: @"AXAttributedStringForTextMarkerRange"]) return [self doAXAttributedStringForTextMarkerRange:textMarkerRange]; @@ -2098,7 +2241,9 @@ static RenderObject* rendererForView(NSView* view) return NSNotFound; m_object->updateBackingStore(); - + if (!m_object) + return NSNotFound; + const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children(); if (children.isEmpty()) @@ -2120,6 +2265,8 @@ static RenderObject* rendererForView(NSView* view) return 0; m_object->updateBackingStore(); + if (!m_object) + return 0; if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children(); @@ -2138,6 +2285,8 @@ static RenderObject* rendererForView(NSView* view) return nil; m_object->updateBackingStore(); + if (!m_object) + return nil; if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { if (m_object->children().isEmpty()) { diff --git a/WebCore/accessibility/win/AXObjectCacheWin.cpp b/WebCore/accessibility/win/AXObjectCacheWin.cpp index e39d5a5..a1bdcc0 100644 --- a/WebCore/accessibility/win/AXObjectCacheWin.cpp +++ b/WebCore/accessibility/win/AXObjectCacheWin.cpp @@ -28,6 +28,11 @@ #include "AXObjectCache.h" #include "AccessibilityObject.h" +#include "Document.h" +#include "Page.h" +#include "RenderObject.h" + +using namespace std; namespace WebCore { @@ -46,12 +51,86 @@ void AXObjectCache::attachWrapper(AccessibilityObject*) // software requests them via get_accChild. } -void AXObjectCache::postPlatformNotification(AccessibilityObject*, const String&) +void AXObjectCache::handleScrolledToAnchor(const Node* anchorNode) +{ + // The anchor node may not be accessible. Post the notification for the + // first accessible object. + postPlatformNotification(AccessibilityObject::firstAccessibleObjectFromNode(anchorNode), AXScrolledToAnchor); +} + +void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification) { + if (!obj) + return; + + Document* document = obj->document(); + if (!document) + return; + + Page* page = document->page(); + if (!page || !page->chrome()->platformPageClient()) + return; + + DWORD msaaEvent; + switch (notification) { + case AXFocusedUIElementChanged: + msaaEvent = EVENT_OBJECT_FOCUS; + break; + + case AXScrolledToAnchor: + msaaEvent = EVENT_SYSTEM_SCROLLINGSTART; + break; + + default: + return; + } + + // Windows will end up calling get_accChild() on the root accessible + // object for the WebView, passing the child ID that we specify below. We + // negate the AXID so we know that the caller is passing the ID of an + // element, not the index of a child element. + + ASSERT(obj->axObjectID() >= 1); + ASSERT(obj->axObjectID() <= numeric_limits<LONG>::max()); + + NotifyWinEvent(msaaEvent, page->chrome()->platformPageClient(), OBJID_CLIENT, -static_cast<LONG>(obj->axObjectID())); } -void AXObjectCache::handleFocusedUIElementChanged() +AXID AXObjectCache::platformGenerateAXID() const { + static AXID lastUsedID = 0; + + // Generate a new ID. Windows accessibility relies on a positive AXID, + // ranging from 1 to LONG_MAX. + AXID objID = lastUsedID; + do { + ++objID; + objID %= std::numeric_limits<LONG>::max(); + } while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID)); + + ASSERT(objID >= 1 && objID <= std::numeric_limits<LONG>::max()); + + lastUsedID = objID; + + return objID; +} + +void AXObjectCache::handleFocusedUIElementChanged(RenderObject*, RenderObject* newFocusedRenderer) +{ + if (!newFocusedRenderer) + return; + + Page* page = newFocusedRenderer->document()->page(); + if (!page || !page->chrome()->platformPageClient()) + return; + + AccessibilityObject* focusedObject = focusedUIElementForPage(page); + if (!focusedObject) + return; + + ASSERT(!focusedObject->accessibilityIsIgnored()); + + postPlatformNotification(focusedObject, AXFocusedUIElementChanged); } } // namespace WebCore |