1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/accessibility/ax_aura_obj_cache.h"
6
7 #include <utility>
8
9 #include "base/no_destructor.h"
10 #include "base/strings/string_util.h"
11 #include "ui/accessibility/ax_enums.mojom.h"
12 #include "ui/accessibility/ax_node.h"
13 #include "ui/aura/client/aura_constants.h"
14 #include "ui/aura/client/focus_client.h"
15 #include "ui/aura/window.h"
16 #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
17 #include "ui/views/accessibility/ax_view_obj_wrapper.h"
18 #include "ui/views/accessibility/ax_widget_obj_wrapper.h"
19 #include "ui/views/accessibility/ax_window_obj_wrapper.h"
20 #include "ui/views/accessibility/view_accessibility.h"
21 #include "ui/views/view.h"
22 #include "ui/views/widget/widget.h"
23 #include "ui/views/widget/widget_delegate.h"
24
25 namespace views {
26 namespace {
27
GetFocusClient(aura::Window * root_window)28 aura::client::FocusClient* GetFocusClient(aura::Window* root_window) {
29 if (!root_window)
30 return nullptr;
31 return aura::client::GetFocusClient(root_window);
32 }
33
34 } // namespace
35
GetOrCreate(View * view)36 AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(View* view) {
37 // Avoid problems with transient focus events. https://crbug.com/729449
38 if (!view->GetWidget())
39 return nullptr;
40 return CreateInternal<AXViewObjWrapper>(view, &view_to_id_map_);
41 }
42
GetOrCreate(Widget * widget)43 AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(Widget* widget) {
44 return CreateInternal<AXWidgetObjWrapper>(widget, &widget_to_id_map_);
45 }
46
GetOrCreate(aura::Window * window)47 AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(aura::Window* window) {
48 return CreateInternal<AXWindowObjWrapper>(window, &window_to_id_map_);
49 }
50
CreateOrReplace(std::unique_ptr<AXAuraObjWrapper> obj)51 void AXAuraObjCache::CreateOrReplace(std::unique_ptr<AXAuraObjWrapper> obj) {
52 cache_[obj->GetUniqueId()] = std::move(obj);
53 }
54
GetID(View * view) const55 int32_t AXAuraObjCache::GetID(View* view) const {
56 return GetIDInternal(view, view_to_id_map_);
57 }
58
GetID(Widget * widget) const59 int32_t AXAuraObjCache::GetID(Widget* widget) const {
60 return GetIDInternal(widget, widget_to_id_map_);
61 }
62
GetID(aura::Window * window) const63 int32_t AXAuraObjCache::GetID(aura::Window* window) const {
64 return GetIDInternal(window, window_to_id_map_);
65 }
66
Remove(View * view)67 void AXAuraObjCache::Remove(View* view) {
68 RemoveInternal(view, &view_to_id_map_);
69 }
70
RemoveViewSubtree(View * view)71 void AXAuraObjCache::RemoveViewSubtree(View* view) {
72 Remove(view);
73 for (View* child : view->children())
74 RemoveViewSubtree(child);
75 }
76
Remove(Widget * widget)77 void AXAuraObjCache::Remove(Widget* widget) {
78 RemoveInternal(widget, &widget_to_id_map_);
79
80 // When an entire widget is deleted, it doesn't always send a notification
81 // on each of its views, so we need to explore them recursively.
82 auto* view = widget->GetRootView();
83 if (view)
84 RemoveViewSubtree(view);
85 }
86
Remove(aura::Window * window,aura::Window * parent)87 void AXAuraObjCache::Remove(aura::Window* window, aura::Window* parent) {
88 int id = GetIDInternal(parent, window_to_id_map_);
89 AXAuraObjWrapper* parent_window_obj = Get(id);
90 RemoveInternal(window, &window_to_id_map_);
91 if (parent && delegate_)
92 delegate_->OnChildWindowRemoved(parent_window_obj);
93 }
94
Get(int32_t id)95 AXAuraObjWrapper* AXAuraObjCache::Get(int32_t id) {
96 auto it = cache_.find(id);
97 return it != cache_.end() ? it->second.get() : nullptr;
98 }
99
GetTopLevelWindows(std::vector<AXAuraObjWrapper * > * children)100 void AXAuraObjCache::GetTopLevelWindows(
101 std::vector<AXAuraObjWrapper*>* children) {
102 for (aura::Window* root : root_windows_)
103 children->push_back(GetOrCreate(root));
104 }
105
GetFocus()106 AXAuraObjWrapper* AXAuraObjCache::GetFocus() {
107 View* focused_view = GetFocusedView();
108 if (focused_view) {
109 const ViewAccessibility& view_accessibility =
110 focused_view->GetViewAccessibility();
111 if (view_accessibility.FocusedVirtualChild())
112 return view_accessibility.FocusedVirtualChild()->GetOrCreateWrapper(this);
113
114 return GetOrCreate(focused_view);
115 }
116 return nullptr;
117 }
118
OnFocusedViewChanged()119 void AXAuraObjCache::OnFocusedViewChanged() {
120 View* view = GetFocusedView();
121 if (view)
122 view->NotifyAccessibilityEvent(ax::mojom::Event::kFocus, true);
123 }
124
FireEvent(AXAuraObjWrapper * aura_obj,ax::mojom::Event event_type)125 void AXAuraObjCache::FireEvent(AXAuraObjWrapper* aura_obj,
126 ax::mojom::Event event_type) {
127 if (delegate_)
128 delegate_->OnEvent(aura_obj, event_type);
129 }
130
131 AXAuraObjCache::AXAuraObjCache() = default;
132
133 // Never runs because object is leaked.
~AXAuraObjCache()134 AXAuraObjCache::~AXAuraObjCache() {
135 if (!root_windows_.empty() && GetFocusClient(*root_windows_.begin()))
136 GetFocusClient(*root_windows_.begin())->RemoveObserver(this);
137 }
138
GetFocusedView()139 View* AXAuraObjCache::GetFocusedView() {
140 Widget* focused_widget = focused_widget_for_testing_;
141 aura::Window* focused_window = nullptr;
142 if (!focused_widget) {
143 if (root_windows_.empty())
144 return nullptr;
145 aura::client::FocusClient* focus_client =
146 GetFocusClient(*root_windows_.begin());
147 if (!focus_client)
148 return nullptr;
149
150 focused_window = focus_client->GetFocusedWindow();
151 if (!focused_window)
152 return nullptr;
153
154 focused_widget = Widget::GetWidgetForNativeView(focused_window);
155 while (!focused_widget) {
156 focused_window = focused_window->parent();
157 if (!focused_window)
158 break;
159
160 focused_widget = Widget::GetWidgetForNativeView(focused_window);
161 }
162 }
163
164 if (!focused_widget)
165 return nullptr;
166
167 FocusManager* focus_manager = focused_widget->GetFocusManager();
168 if (!focus_manager)
169 return nullptr;
170
171 View* focused_view = focus_manager->GetFocusedView();
172 if (focused_view)
173 return focused_view;
174
175 if (focused_window &&
176 focused_window->GetProperty(
177 aura::client::kAccessibilityFocusFallsbackToWidgetKey)) {
178 // If focused widget has non client view, falls back to first child view of
179 // its client view. We don't expect that non client view gets keyboard
180 // focus.
181 auto* non_client = focused_widget->non_client_view();
182 auto* client = non_client ? non_client->client_view() : nullptr;
183 return (client && !client->children().empty())
184 ? client->children().front()
185 : focused_widget->GetRootView();
186 }
187
188 return nullptr;
189 }
190
OnWindowFocused(aura::Window * gained_focus,aura::Window * lost_focus)191 void AXAuraObjCache::OnWindowFocused(aura::Window* gained_focus,
192 aura::Window* lost_focus) {
193 OnFocusedViewChanged();
194 }
195
OnRootWindowObjCreated(aura::Window * window)196 void AXAuraObjCache::OnRootWindowObjCreated(aura::Window* window) {
197 if (root_windows_.empty() && GetFocusClient(window))
198 GetFocusClient(window)->AddObserver(this);
199 root_windows_.insert(window);
200 }
201
OnRootWindowObjDestroyed(aura::Window * window)202 void AXAuraObjCache::OnRootWindowObjDestroyed(aura::Window* window) {
203 root_windows_.erase(window);
204 if (root_windows_.empty() && GetFocusClient(window))
205 GetFocusClient(window)->RemoveObserver(this);
206 }
207
208 template <typename AuraViewWrapper, typename AuraView>
CreateInternal(AuraView * aura_view,std::map<AuraView *,int32_t> * aura_view_to_id_map)209 AXAuraObjWrapper* AXAuraObjCache::CreateInternal(
210 AuraView* aura_view,
211 std::map<AuraView*, int32_t>* aura_view_to_id_map) {
212 if (!aura_view)
213 return nullptr;
214
215 auto it = aura_view_to_id_map->find(aura_view);
216
217 if (it != aura_view_to_id_map->end())
218 return Get(it->second);
219
220 auto wrapper = std::make_unique<AuraViewWrapper>(this, aura_view);
221 int32_t id = wrapper->GetUniqueId();
222 (*aura_view_to_id_map)[aura_view] = id;
223 cache_[id] = std::move(wrapper);
224 return cache_[id].get();
225 }
226
227 template <typename AuraView>
GetIDInternal(AuraView * aura_view,const std::map<AuraView *,int32_t> & aura_view_to_id_map) const228 int32_t AXAuraObjCache::GetIDInternal(
229 AuraView* aura_view,
230 const std::map<AuraView*, int32_t>& aura_view_to_id_map) const {
231 if (!aura_view)
232 return ui::AXNode::kInvalidAXID;
233
234 auto it = aura_view_to_id_map.find(aura_view);
235 return it != aura_view_to_id_map.end() ? it->second
236 : ui::AXNode::kInvalidAXID;
237 }
238
239 template <typename AuraView>
RemoveInternal(AuraView * aura_view,std::map<AuraView *,int32_t> * aura_view_to_id_map)240 void AXAuraObjCache::RemoveInternal(
241 AuraView* aura_view,
242 std::map<AuraView*, int32_t>* aura_view_to_id_map) {
243 int32_t id = GetID(aura_view);
244 if (id == ui::AXNode::kInvalidAXID)
245 return;
246 aura_view_to_id_map->erase(aura_view);
247 cache_.erase(id);
248 }
249
250 } // namespace views
251