1 // Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_state_impl.h"
6 
7 #include <stddef.h>
8 
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/debug/crash_logging.h"
12 #include "base/metrics/histogram_functions.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/task/thread_pool.h"
15 #include "build/build_config.h"
16 #include "content/browser/renderer_host/render_widget_host_impl.h"
17 #include "content/browser/web_contents/web_contents_impl.h"
18 #include "content/public/browser/browser_task_traits.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/common/content_switches.h"
21 #include "ui/accessibility/platform/ax_platform_node.h"
22 #include "ui/gfx/color_utils.h"
23 #include "ui/native_theme/native_theme.h"
24 
25 namespace content {
26 
27 // IMPORTANT!
28 // These values are written to logs.  Do not renumber or delete
29 // existing items; add new entries to the end of the list.
30 enum ModeFlagHistogramValue {
31   UMA_AX_MODE_NATIVE_APIS = 0,
32   UMA_AX_MODE_WEB_CONTENTS = 1,
33   UMA_AX_MODE_INLINE_TEXT_BOXES = 2,
34   UMA_AX_MODE_SCREEN_READER = 3,
35   UMA_AX_MODE_HTML = 4,
36 
37   // This must always be the last enum. It's okay for its value to
38   // increase, but none of the other enum values may change.
39   UMA_AX_MODE_MAX
40 };
41 
42 // Record a histograms for an accessibility mode when it's enabled.
RecordNewAccessibilityModeFlags(ModeFlagHistogramValue mode_flag)43 void RecordNewAccessibilityModeFlags(ModeFlagHistogramValue mode_flag) {
44   UMA_HISTOGRAM_ENUMERATION("Accessibility.ModeFlag", mode_flag,
45                             UMA_AX_MODE_MAX);
46 }
47 
48 // Update the accessibility histogram 45 seconds after initialization.
49 static const int ACCESSIBILITY_HISTOGRAM_DELAY_SECS = 45;
50 
51 // static
GetInstance()52 BrowserAccessibilityState* BrowserAccessibilityState::GetInstance() {
53   return BrowserAccessibilityStateImpl::GetInstance();
54 }
55 
56 // static
GetInstance()57 BrowserAccessibilityStateImpl* BrowserAccessibilityStateImpl::GetInstance() {
58   return base::Singleton<
59       BrowserAccessibilityStateImpl,
60       base::LeakySingletonTraits<BrowserAccessibilityStateImpl>>::get();
61 }
62 
BrowserAccessibilityStateImpl()63 BrowserAccessibilityStateImpl::BrowserAccessibilityStateImpl()
64     : BrowserAccessibilityState(), disable_hot_tracking_(false) {
65   ResetAccessibilityModeValue();
66 
67   // We need to AddRef() the leaky singleton so that Bind doesn't
68   // delete it prematurely.
69   AddRef();
70 
71   // Hook ourselves up to observe ax mode changes.
72   ui::AXPlatformNode::AddAXModeObserver(this);
73 
74   // Let each platform do its own initialization.
75   PlatformInitialize();
76 
77   // Schedule calls to update histograms after a delay.
78   //
79   // The delay is necessary because assistive technology sometimes isn't
80   // detected until after the user interacts in some way, so a reasonable delay
81   // gives us better numbers.
82 
83   // Some things can be done on another thread safely.
84   base::ThreadPool::PostDelayedTask(
85       FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
86       base::BindOnce(
87           &BrowserAccessibilityStateImpl::UpdateHistogramsOnOtherThread, this),
88       base::TimeDelta::FromSeconds(ACCESSIBILITY_HISTOGRAM_DELAY_SECS));
89 
90   // Other things must be done on the UI thread (e.g. to access PrefService).
91   GetUIThreadTaskRunner({})->PostDelayedTask(
92       FROM_HERE,
93       base::BindOnce(&BrowserAccessibilityStateImpl::UpdateHistogramsOnUIThread,
94                      this),
95       base::TimeDelta::FromSeconds(ACCESSIBILITY_HISTOGRAM_DELAY_SECS));
96 }
97 
~BrowserAccessibilityStateImpl()98 BrowserAccessibilityStateImpl::~BrowserAccessibilityStateImpl() {
99   // Remove ourselves from the AXMode global observer list.
100   ui::AXPlatformNode::RemoveAXModeObserver(this);
101 }
102 
OnScreenReaderDetected()103 void BrowserAccessibilityStateImpl::OnScreenReaderDetected() {
104   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
105           switches::kDisableRendererAccessibility)) {
106     return;
107   }
108   EnableAccessibility();
109 }
110 
EnableAccessibility()111 void BrowserAccessibilityStateImpl::EnableAccessibility() {
112   AddAccessibilityModeFlags(ui::kAXModeComplete);
113 }
114 
DisableAccessibility()115 void BrowserAccessibilityStateImpl::DisableAccessibility() {
116   ResetAccessibilityMode();
117 }
118 
IsRendererAccessibilityEnabled()119 bool BrowserAccessibilityStateImpl::IsRendererAccessibilityEnabled() {
120   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
121       switches::kDisableRendererAccessibility);
122 }
123 
ResetAccessibilityModeValue()124 void BrowserAccessibilityStateImpl::ResetAccessibilityModeValue() {
125   accessibility_mode_ = ui::AXMode();
126   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
127           switches::kForceRendererAccessibility)) {
128     AddAccessibilityModeFlags(ui::kAXModeComplete);
129   }
130 }
131 
ResetAccessibilityMode()132 void BrowserAccessibilityStateImpl::ResetAccessibilityMode() {
133   ResetAccessibilityModeValue();
134 
135   std::vector<WebContentsImpl*> web_contents_vector =
136       WebContentsImpl::GetAllWebContents();
137   for (size_t i = 0; i < web_contents_vector.size(); ++i)
138     web_contents_vector[i]->SetAccessibilityMode(accessibility_mode_);
139 }
140 
IsAccessibleBrowser()141 bool BrowserAccessibilityStateImpl::IsAccessibleBrowser() {
142   return accessibility_mode_ == ui::kAXModeComplete;
143 }
144 
AddUIThreadHistogramCallback(base::OnceClosure callback)145 void BrowserAccessibilityStateImpl::AddUIThreadHistogramCallback(
146     base::OnceClosure callback) {
147   ui_thread_histogram_callbacks_.push_back(std::move(callback));
148 }
149 
AddOtherThreadHistogramCallback(base::OnceClosure callback)150 void BrowserAccessibilityStateImpl::AddOtherThreadHistogramCallback(
151     base::OnceClosure callback) {
152   other_thread_histogram_callbacks_.push_back(std::move(callback));
153 }
154 
UpdateHistogramsForTesting()155 void BrowserAccessibilityStateImpl::UpdateHistogramsForTesting() {
156   UpdateHistogramsOnUIThread();
157   UpdateHistogramsOnOtherThread();
158 }
159 
SetCaretBrowsingState(bool enabled)160 void BrowserAccessibilityStateImpl::SetCaretBrowsingState(bool enabled) {
161   caret_browsing_enabled_ = enabled;
162 }
163 
IsCaretBrowsingEnabled() const164 bool BrowserAccessibilityStateImpl::IsCaretBrowsingEnabled() const {
165   return caret_browsing_enabled_;
166 }
167 
UpdateHistogramsOnUIThread()168 void BrowserAccessibilityStateImpl::UpdateHistogramsOnUIThread() {
169   UpdatePlatformSpecificHistogramsOnUIThread();
170 
171   for (auto& callback : ui_thread_histogram_callbacks_)
172     std::move(callback).Run();
173   ui_thread_histogram_callbacks_.clear();
174 
175   UMA_HISTOGRAM_BOOLEAN("Accessibility.ManuallyEnabled",
176                         base::CommandLine::ForCurrentProcess()->HasSwitch(
177                             switches::kForceRendererAccessibility));
178 #if defined(OS_WIN)
179   UMA_HISTOGRAM_ENUMERATION(
180       "Accessibility.WinHighContrastTheme",
181       ui::NativeTheme::GetInstanceForNativeUi()
182           ->GetPlatformHighContrastColorScheme(),
183       ui::NativeTheme::PlatformHighContrastColorScheme::kMaxValue);
184 #endif
185 }
186 
UpdateHistogramsOnOtherThread()187 void BrowserAccessibilityStateImpl::UpdateHistogramsOnOtherThread() {
188   UpdatePlatformSpecificHistogramsOnOtherThread();
189 
190   for (auto& callback : other_thread_histogram_callbacks_)
191     std::move(callback).Run();
192   other_thread_histogram_callbacks_.clear();
193 }
194 
OnAXModeAdded(ui::AXMode mode)195 void BrowserAccessibilityStateImpl::OnAXModeAdded(ui::AXMode mode) {
196   AddAccessibilityModeFlags(mode);
197 }
198 
GetAccessibilityMode()199 ui::AXMode BrowserAccessibilityStateImpl::GetAccessibilityMode() {
200   return accessibility_mode_;
201 }
202 
203 #if !defined(OS_ANDROID) && !defined(OS_WIN) && !defined(OS_MAC)
PlatformInitialize()204 void BrowserAccessibilityStateImpl::PlatformInitialize() {}
205 
206 void BrowserAccessibilityStateImpl::
UpdatePlatformSpecificHistogramsOnUIThread()207     UpdatePlatformSpecificHistogramsOnUIThread() {}
208 void BrowserAccessibilityStateImpl::
UpdatePlatformSpecificHistogramsOnOtherThread()209     UpdatePlatformSpecificHistogramsOnOtherThread() {}
UpdateUniqueUserHistograms()210 void BrowserAccessibilityStateImpl::UpdateUniqueUserHistograms() {}
211 #endif
212 
AddAccessibilityModeFlags(ui::AXMode mode)213 void BrowserAccessibilityStateImpl::AddAccessibilityModeFlags(ui::AXMode mode) {
214   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
215           switches::kDisableRendererAccessibility)) {
216     return;
217   }
218 
219   ui::AXMode previous_mode = accessibility_mode_;
220   accessibility_mode_ |= mode;
221   if (accessibility_mode_ == previous_mode)
222     return;
223 
224   // Proxy the AXMode to AXPlatformNode to enable accessibility.
225   ui::AXPlatformNode::NotifyAddAXModeFlags(accessibility_mode_);
226 
227   // Retrieve only newly added modes for the purposes of logging.
228   int new_mode_flags = mode.mode() & (~previous_mode.mode());
229   if (new_mode_flags & ui::AXMode::kNativeAPIs)
230     RecordNewAccessibilityModeFlags(UMA_AX_MODE_NATIVE_APIS);
231   if (new_mode_flags & ui::AXMode::kWebContents)
232     RecordNewAccessibilityModeFlags(UMA_AX_MODE_WEB_CONTENTS);
233   if (new_mode_flags & ui::AXMode::kInlineTextBoxes)
234     RecordNewAccessibilityModeFlags(UMA_AX_MODE_INLINE_TEXT_BOXES);
235   if (new_mode_flags & ui::AXMode::kScreenReader)
236     RecordNewAccessibilityModeFlags(UMA_AX_MODE_SCREEN_READER);
237   if (new_mode_flags & ui::AXMode::kHTML)
238     RecordNewAccessibilityModeFlags(UMA_AX_MODE_HTML);
239 
240   std::vector<WebContentsImpl*> web_contents_vector =
241       WebContentsImpl::GetAllWebContents();
242   for (size_t i = 0; i < web_contents_vector.size(); ++i)
243     web_contents_vector[i]->AddAccessibilityMode(accessibility_mode_);
244 
245   // Add a crash key with the ax_mode, to enable searching for top crashes that
246   // occur when accessibility is turned on. This adds it for the browser
247   // process, and elsewhere the same key is added to renderer processes.
248   static auto* ax_mode_crash_key = base::debug::AllocateCrashKeyString(
249       "ax_mode", base::debug::CrashKeySize::Size64);
250   if (ax_mode_crash_key) {
251     base::debug::SetCrashKeyString(ax_mode_crash_key,
252                                    accessibility_mode_.ToString());
253   }
254 }
255 
RemoveAccessibilityModeFlags(ui::AXMode mode)256 void BrowserAccessibilityStateImpl::RemoveAccessibilityModeFlags(
257     ui::AXMode mode) {
258   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
259           switches::kForceRendererAccessibility) &&
260       mode == ui::kAXModeComplete) {
261     return;
262   }
263 
264   int raw_flags =
265       accessibility_mode_.mode() ^ (mode.mode() & accessibility_mode_.mode());
266   accessibility_mode_ = raw_flags;
267 
268   std::vector<WebContentsImpl*> web_contents_vector =
269       WebContentsImpl::GetAllWebContents();
270   for (size_t i = 0; i < web_contents_vector.size(); ++i)
271     web_contents_vector[i]->SetAccessibilityMode(accessibility_mode_);
272 }
273 
274 }  // namespace content
275