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