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