blob: 1372dd28de59bd1eb9048991dd8cc9ccad5fcb54 [file] [log] [blame]
// Copyright 2014 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 "ui/views/accessibility/ax_aura_obj_cache.h"
#include "base/memory/ptr_util.h"
#include "base/memory/singleton.h"
#include "base/strings/string_util.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window.h"
#include "ui/views/accessibility/ax_aura_obj_wrapper.h"
#include "ui/views/accessibility/ax_view_obj_wrapper.h"
#include "ui/views/accessibility/ax_widget_obj_wrapper.h"
#include "ui/views/accessibility/ax_window_obj_wrapper.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace views {
aura::client::FocusClient* GetFocusClient(aura::Window* root_window) {
if (!root_window)
return nullptr;
return aura::client::GetFocusClient(root_window);
}
// static
AXAuraObjCache* AXAuraObjCache::GetInstance() {
return base::Singleton<AXAuraObjCache>::get();
}
AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(View* view) {
return CreateInternal<AXViewObjWrapper>(view, view_to_id_map_);
}
AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(Widget* widget) {
return CreateInternal<AXWidgetObjWrapper>(widget, widget_to_id_map_);
}
AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(aura::Window* window) {
return CreateInternal<AXWindowObjWrapper>(window, window_to_id_map_);
}
int32_t AXAuraObjCache::GetID(View* view) const {
return GetIDInternal(view, view_to_id_map_);
}
int32_t AXAuraObjCache::GetID(Widget* widget) const {
return GetIDInternal(widget, widget_to_id_map_);
}
int32_t AXAuraObjCache::GetID(aura::Window* window) const {
return GetIDInternal(window, window_to_id_map_);
}
void AXAuraObjCache::Remove(View* view) {
RemoveInternal(view, view_to_id_map_);
}
void AXAuraObjCache::RemoveViewSubtree(View* view) {
Remove(view);
for (int i = 0; i < view->child_count(); ++i)
RemoveViewSubtree(view->child_at(i));
}
void AXAuraObjCache::Remove(Widget* widget) {
RemoveInternal(widget, widget_to_id_map_);
// When an entire widget is deleted, it doesn't always send a notification
// on each of its views, so we need to explore them recursively.
if (widget->GetRootView())
RemoveViewSubtree(widget->GetRootView());
}
void AXAuraObjCache::Remove(aura::Window* window, aura::Window* parent) {
int id = GetIDInternal(parent, window_to_id_map_);
AXAuraObjWrapper* parent_window_obj = Get(id);
RemoveInternal(window, window_to_id_map_);
if (parent && delegate_)
delegate_->OnChildWindowRemoved(parent_window_obj);
}
AXAuraObjWrapper* AXAuraObjCache::Get(int32_t id) {
auto it = cache_.find(id);
if (it == cache_.end())
return nullptr;
return it->second.get();
}
void AXAuraObjCache::Remove(int32_t id) {
AXAuraObjWrapper* obj = Get(id);
if (id == -1 || !obj)
return;
cache_.erase(id);
}
void AXAuraObjCache::GetTopLevelWindows(
std::vector<AXAuraObjWrapper*>* children) {
for (auto it = window_to_id_map_.begin(); it != window_to_id_map_.end();
++it) {
if (!it->first->parent())
children->push_back(GetOrCreate(it->first));
}
}
AXAuraObjWrapper* AXAuraObjCache::GetFocus() {
View* focused_view = GetFocusedView();
if (focused_view)
return GetOrCreate(focused_view);
return nullptr;
}
void AXAuraObjCache::OnFocusedViewChanged() {
View* view = GetFocusedView();
if (view)
view->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
}
void AXAuraObjCache::FireEvent(AXAuraObjWrapper* aura_obj,
ui::AXEvent event_type) {
if (delegate_)
delegate_->OnEvent(aura_obj, event_type);
}
AXAuraObjCache::AXAuraObjCache() = default;
AXAuraObjCache::~AXAuraObjCache() {
is_destroying_ = true;
cache_.clear();
}
View* AXAuraObjCache::GetFocusedView() {
if (root_windows_.empty())
return nullptr;
aura::client::FocusClient* focus_client =
GetFocusClient(*root_windows_.begin());
if (!focus_client)
return nullptr;
aura::Window* focused_window = focus_client->GetFocusedWindow();
if (!focused_window)
return nullptr;
Widget* focused_widget = Widget::GetWidgetForNativeView(focused_window);
while (!focused_widget) {
focused_window = focused_window->parent();
if (!focused_window)
break;
focused_widget = Widget::GetWidgetForNativeView(focused_window);
}
if (!focused_widget)
return nullptr;
FocusManager* focus_manager = focused_widget->GetFocusManager();
if (!focus_manager)
return nullptr;
View* focused_view = focus_manager->GetFocusedView();
if (focused_view)
return focused_view;
if (focused_window->GetProperty(
aura::client::kAccessibilityFocusFallsbackToWidgetKey)) {
// If no view is focused, falls back to root view.
return focused_widget->GetRootView();
}
return nullptr;
}
void AXAuraObjCache::OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) {
OnFocusedViewChanged();
}
void AXAuraObjCache::OnRootWindowObjCreated(aura::Window* window) {
if (root_windows_.empty() && GetFocusClient(window))
GetFocusClient(window)->AddObserver(this);
root_windows_.insert(window);
}
void AXAuraObjCache::OnRootWindowObjDestroyed(aura::Window* window) {
root_windows_.erase(window);
if (root_windows_.empty() && GetFocusClient(window))
GetFocusClient(window)->RemoveObserver(this);
}
template <typename AuraViewWrapper, typename AuraView>
AXAuraObjWrapper* AXAuraObjCache::CreateInternal(
AuraView* aura_view,
std::map<AuraView*, int32_t>& aura_view_to_id_map) {
if (!aura_view)
return nullptr;
auto it = aura_view_to_id_map.find(aura_view);
if (it != aura_view_to_id_map.end())
return Get(it->second);
AXAuraObjWrapper* wrapper = new AuraViewWrapper(aura_view);
int32_t id = wrapper->GetUniqueId().Get();
aura_view_to_id_map[aura_view] = id;
cache_[id] = base::WrapUnique(wrapper);
return wrapper;
}
template <typename AuraView>
int32_t AXAuraObjCache::GetIDInternal(
AuraView* aura_view,
const std::map<AuraView*, int32_t>& aura_view_to_id_map) const {
if (!aura_view)
return -1;
auto it = aura_view_to_id_map.find(aura_view);
if (it != aura_view_to_id_map.end())
return it->second;
return -1;
}
template <typename AuraView>
void AXAuraObjCache::RemoveInternal(
AuraView* aura_view,
std::map<AuraView*, int32_t>& aura_view_to_id_map) {
int32_t id = GetID(aura_view);
if (id == -1)
return;
aura_view_to_id_map.erase(aura_view);
Remove(id);
}
} // namespace views