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 while (focused_view && focused_view->GetViewAccessibility().IsIgnored())
109 focused_view = focused_view->parent();
110
111 if (!focused_view)
112 return nullptr;
113
114 if (focused_view->GetViewAccessibility().FocusedVirtualChild()) {
115 return focused_view->GetViewAccessibility()
116 .FocusedVirtualChild()
117 ->GetOrCreateWrapper(this);
118 }
119
120 return GetOrCreate(focused_view);
121 }
122
OnFocusedViewChanged()123 void AXAuraObjCache::OnFocusedViewChanged() {
124 View* view = GetFocusedView();
125 if (view)
126 view->NotifyAccessibilityEvent(ax::mojom::Event::kFocus, true);
127 }
128
FireEvent(AXAuraObjWrapper * aura_obj,ax::mojom::Event event_type)129 void AXAuraObjCache::FireEvent(AXAuraObjWrapper* aura_obj,
130 ax::mojom::Event event_type) {
131 if (delegate_)
132 delegate_->OnEvent(aura_obj, event_type);
133 }
134
135 AXAuraObjCache::AXAuraObjCache() = default;
136
137 // Never runs because object is leaked.
~AXAuraObjCache()138 AXAuraObjCache::~AXAuraObjCache() {
139 if (!root_windows_.empty() && GetFocusClient(*root_windows_.begin()))
140 GetFocusClient(*root_windows_.begin())->RemoveObserver(this);
141 }
142
GetFocusedView()143 View* AXAuraObjCache::GetFocusedView() {
144 Widget* focused_widget = focused_widget_for_testing_;
145 aura::Window* focused_window = nullptr;
146 if (!focused_widget) {
147 if (root_windows_.empty())
148 return nullptr;
149 aura::client::FocusClient* focus_client =
150 GetFocusClient(*root_windows_.begin());
151 if (!focus_client)
152 return nullptr;
153
154 focused_window = focus_client->GetFocusedWindow();
155 if (!focused_window)
156 return nullptr;
157
158 focused_widget = Widget::GetWidgetForNativeView(focused_window);
159 while (!focused_widget) {
160 focused_window = focused_window->parent();
161 if (!focused_window)
162 break;
163
164 focused_widget = Widget::GetWidgetForNativeView(focused_window);
165 }
166 }
167
168 if (!focused_widget)
169 return nullptr;
170
171 FocusManager* focus_manager = focused_widget->GetFocusManager();
172 if (!focus_manager)
173 return nullptr;
174
175 View* focused_view = focus_manager->GetFocusedView();
176 if (focused_view)
177 return focused_view;
178
179 if (focused_window &&
180 focused_window->GetProperty(
181 aura::client::kAccessibilityFocusFallsbackToWidgetKey)) {
182 // If focused widget has non client view, falls back to first child view of
183 // its client view. We don't expect that non client view gets keyboard
184 // focus.
185 auto* non_client = focused_widget->non_client_view();
186 auto* client = non_client ? non_client->client_view() : nullptr;
187 return (client && !client->children().empty())
188 ? client->children().front()
189 : focused_widget->GetRootView();
190 }
191
192 return nullptr;
193 }
194
OnWindowFocused(aura::Window * gained_focus,aura::Window * lost_focus)195 void AXAuraObjCache::OnWindowFocused(aura::Window* gained_focus,
196 aura::Window* lost_focus) {
197 OnFocusedViewChanged();
198 }
199
OnRootWindowObjCreated(aura::Window * window)200 void AXAuraObjCache::OnRootWindowObjCreated(aura::Window* window) {
201 if (root_windows_.empty() && GetFocusClient(window))
202 GetFocusClient(window)->AddObserver(this);
203 root_windows_.insert(window);
204 }
205
OnRootWindowObjDestroyed(aura::Window * window)206 void AXAuraObjCache::OnRootWindowObjDestroyed(aura::Window* window) {
207 root_windows_.erase(window);
208 if (root_windows_.empty() && GetFocusClient(window))
209 GetFocusClient(window)->RemoveObserver(this);
210 }
211
212 template <typename AuraViewWrapper, typename AuraView>
CreateInternal(AuraView * aura_view,std::map<AuraView *,int32_t> * aura_view_to_id_map)213 AXAuraObjWrapper* AXAuraObjCache::CreateInternal(
214 AuraView* aura_view,
215 std::map<AuraView*, int32_t>* aura_view_to_id_map) {
216 if (!aura_view)
217 return nullptr;
218
219 auto it = aura_view_to_id_map->find(aura_view);
220
221 if (it != aura_view_to_id_map->end())
222 return Get(it->second);
223
224 auto wrapper = std::make_unique<AuraViewWrapper>(this, aura_view);
225 int32_t id = wrapper->GetUniqueId();
226 (*aura_view_to_id_map)[aura_view] = id;
227 cache_[id] = std::move(wrapper);
228 return cache_[id].get();
229 }
230
231 template <typename AuraView>
GetIDInternal(AuraView * aura_view,const std::map<AuraView *,int32_t> & aura_view_to_id_map) const232 int32_t AXAuraObjCache::GetIDInternal(
233 AuraView* aura_view,
234 const std::map<AuraView*, int32_t>& aura_view_to_id_map) const {
235 if (!aura_view)
236 return ui::AXNode::kInvalidAXID;
237
238 auto it = aura_view_to_id_map.find(aura_view);
239 return it != aura_view_to_id_map.end() ? it->second
240 : ui::AXNode::kInvalidAXID;
241 }
242
243 template <typename AuraView>
RemoveInternal(AuraView * aura_view,std::map<AuraView *,int32_t> * aura_view_to_id_map)244 void AXAuraObjCache::RemoveInternal(
245 AuraView* aura_view,
246 std::map<AuraView*, int32_t>* aura_view_to_id_map) {
247 int32_t id = GetID(aura_view);
248 if (id == ui::AXNode::kInvalidAXID)
249 return;
250 aura_view_to_id_map->erase(aura_view);
251 cache_.erase(id);
252 }
253
254 } // namespace views
255