blob: 69c88916bd014dd5ce705e2b7fa58151919d6cb6 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/accessibility/browser_accessibility_manager_win.h"
#include "base/command_line.h"
#include "base/win/scoped_comptr.h"
#include "base/win/windows_version.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/browser/accessibility/browser_accessibility_win.h"
#include "content/browser/renderer_host/legacy_render_widget_host_win.h"
#include "content/common/accessibility_messages.h"
namespace content {
// static
BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory) {
return new BrowserAccessibilityManagerWin(
content::LegacyRenderWidgetHostHWND::Create(GetDesktopWindow()).get(),
NULL, initial_tree, delegate, factory);
}
BrowserAccessibilityManagerWin*
BrowserAccessibilityManager::ToBrowserAccessibilityManagerWin() {
return static_cast<BrowserAccessibilityManagerWin*>(this);
}
BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin(
LegacyRenderWidgetHostHWND* accessible_hwnd,
IAccessible* parent_iaccessible,
const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory)
: BrowserAccessibilityManager(initial_tree, delegate, factory),
parent_hwnd_(accessible_hwnd->GetParent()),
parent_iaccessible_(parent_iaccessible),
tracked_scroll_object_(NULL),
accessible_hwnd_(accessible_hwnd) {
accessible_hwnd_->set_browser_accessibility_manager(this);
}
BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
if (tracked_scroll_object_) {
tracked_scroll_object_->Release();
tracked_scroll_object_ = NULL;
}
if (accessible_hwnd_)
accessible_hwnd_->OnManagerDeleted();
}
// static
ui::AXTreeUpdate BrowserAccessibilityManagerWin::GetEmptyDocument() {
ui::AXNodeData empty_document;
empty_document.id = 0;
empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
empty_document.state =
(1 << ui::AX_STATE_ENABLED) |
(1 << ui::AX_STATE_READ_ONLY) |
(1 << ui::AX_STATE_BUSY);
ui::AXTreeUpdate update;
update.nodes.push_back(empty_document);
return update;
}
void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event,
LONG child_id) {
// Don't fire events if this view isn't hooked up to its parent.
if (!parent_iaccessible())
return;
// If on Win 7 and complete accessibility is enabled, use the fake child HWND
// to use as the root of the accessibility tree. See comments above
// LegacyRenderWidgetHostHWND for details.
if (BrowserAccessibilityStateImpl::GetInstance()->IsAccessibleBrowser()) {
DCHECK(accessible_hwnd_);
parent_hwnd_ = accessible_hwnd_->hwnd();
parent_iaccessible_ = accessible_hwnd_->window_accessible();
}
::NotifyWinEvent(event, parent_hwnd(), OBJID_CLIENT, child_id);
}
void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) {
BrowserAccessibilityManager::OnNodeCreated(node);
BrowserAccessibility* obj = GetFromAXNode(node);
LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win();
unique_id_to_ax_id_map_[unique_id_win] = obj->GetId();
}
void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) {
BrowserAccessibilityManager::OnNodeWillBeDeleted(node);
BrowserAccessibility* obj = GetFromAXNode(node);
if (!obj)
return;
unique_id_to_ax_id_map_.erase(
obj->ToBrowserAccessibilityWin()->unique_id_win());
if (obj == tracked_scroll_object_) {
tracked_scroll_object_->Release();
tracked_scroll_object_ = NULL;
}
}
void BrowserAccessibilityManagerWin::OnWindowFocused() {
// Fire a focus event on the root first and then the focused node.
if (focus_ != tree_->GetRoot())
NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot());
BrowserAccessibilityManager::OnWindowFocused();
}
void BrowserAccessibilityManagerWin::OnWindowBlurred() {
// Fire a blur event on the focused node first and then the root.
BrowserAccessibilityManager::OnWindowBlurred();
if (focus_ != tree_->GetRoot())
NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetRoot());
}
void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
ui::AXEvent event_type,
BrowserAccessibility* node) {
if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX)
return;
LONG event_id = EVENT_MIN;
switch (event_type) {
case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED;
break;
case ui::AX_EVENT_ALERT:
event_id = EVENT_SYSTEM_ALERT;
break;
case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
break;
case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
break;
case ui::AX_EVENT_BLUR:
// Equivalent to focus on the root.
event_id = EVENT_OBJECT_FOCUS;
node = GetRoot();
break;
case ui::AX_EVENT_CHECKED_STATE_CHANGED:
event_id = EVENT_OBJECT_STATECHANGE;
break;
case ui::AX_EVENT_CHILDREN_CHANGED:
event_id = EVENT_OBJECT_REORDER;
break;
case ui::AX_EVENT_FOCUS:
event_id = EVENT_OBJECT_FOCUS;
break;
case ui::AX_EVENT_INVALID_STATUS_CHANGED:
event_id = EVENT_OBJECT_STATECHANGE;
break;
case ui::AX_EVENT_LIVE_REGION_CHANGED:
if (node->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY))
return;
event_id = EVENT_OBJECT_LIVEREGIONCHANGED;
break;
case ui::AX_EVENT_LOAD_COMPLETE:
event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
break;
case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
event_id = EVENT_OBJECT_FOCUS;
break;
case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
event_id = EVENT_OBJECT_VALUECHANGE;
break;
case ui::AX_EVENT_HIDE:
event_id = EVENT_OBJECT_HIDE;
break;
case ui::AX_EVENT_SHOW:
event_id = EVENT_OBJECT_SHOW;
break;
case ui::AX_EVENT_SCROLL_POSITION_CHANGED:
event_id = EVENT_SYSTEM_SCROLLINGEND;
break;
case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
event_id = EVENT_SYSTEM_SCROLLINGSTART;
break;
case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
event_id = EVENT_OBJECT_SELECTIONWITHIN;
break;
case ui::AX_EVENT_SELECTED_TEXT_CHANGED:
event_id = IA2_EVENT_TEXT_CARET_MOVED;
break;
case ui::AX_EVENT_TEXT_CHANGED:
event_id = EVENT_OBJECT_NAMECHANGE;
break;
case ui::AX_EVENT_TEXT_INSERTED:
event_id = IA2_EVENT_TEXT_INSERTED;
break;
case ui::AX_EVENT_TEXT_REMOVED:
event_id = IA2_EVENT_TEXT_REMOVED;
break;
case ui::AX_EVENT_VALUE_CHANGED:
event_id = EVENT_OBJECT_VALUECHANGE;
break;
default:
// Not all WebKit accessibility events result in a Windows
// accessibility notification.
break;
}
if (event_id != EVENT_MIN) {
// Pass the node's unique id in the |child_id| argument to NotifyWinEvent;
// the AT client will then call get_accChild on the HWND's accessibility
// object and pass it that same id, which we can use to retrieve the
// IAccessible for this node.
LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win();
MaybeCallNotifyWinEvent(event_id, child_id);
}
// If this is a layout complete notification (sent when a container scrolls)
// and there is a descendant tracked object, send a notification on it.
// TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE &&
tracked_scroll_object_ &&
tracked_scroll_object_->IsDescendantOf(node)) {
MaybeCallNotifyWinEvent(
IA2_EVENT_VISIBLE_DATA_CHANGED,
tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win());
tracked_scroll_object_->Release();
tracked_scroll_object_ = NULL;
}
}
void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode* new_root) {
if (delegate_ && delegate_->AccessibilityViewHasFocus())
NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot());
}
void BrowserAccessibilityManagerWin::TrackScrollingObject(
BrowserAccessibilityWin* node) {
if (tracked_scroll_object_)
tracked_scroll_object_->Release();
tracked_scroll_object_ = node;
tracked_scroll_object_->AddRef();
}
BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin(
LONG unique_id_win) {
base::hash_map<LONG, int32>::iterator iter =
unique_id_to_ax_id_map_.find(unique_id_win);
if (iter != unique_id_to_ax_id_map_.end()) {
BrowserAccessibility* result = GetFromID(iter->second);
if (result)
return result->ToBrowserAccessibilityWin();
}
return NULL;
}
void BrowserAccessibilityManagerWin::OnAccessibleHwndDeleted() {
// If the AccessibleHWND is deleted, |parent_hwnd_| and
// |parent_iaccessible_| are no longer valid either, since they were
// derived from AccessibleHWND. We don't have to restore them to
// previous values, though, because this should only happen
// during the destruct sequence for this window.
accessible_hwnd_ = NULL;
parent_hwnd_ = NULL;
parent_iaccessible_ = NULL;
}
} // namespace content