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