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 "base/command_line.h" 8 #include "base/metrics/histogram.h" 9 #include "base/timer/timer.h" 10 #include "content/browser/accessibility/accessibility_mode_helper.h" 11 #include "content/browser/renderer_host/render_widget_host_impl.h" 12 #include "content/public/browser/browser_thread.h" 13 #include "content/public/browser/render_process_host.h" 14 #include "content/public/browser/render_widget_host_iterator.h" 15 #include "content/public/common/content_switches.h" 16 #include "ui/gfx/sys_color_change_listener.h" 17 18 #if defined(OS_WIN) 19 #include "base/win/windows_version.h" 20 #endif 21 22 namespace content { 23 24 // Update the accessibility histogram 45 seconds after initialization. 25 static const int kAccessibilityHistogramDelaySecs = 45; 26 27 // static 28 BrowserAccessibilityState* BrowserAccessibilityState::GetInstance() { 29 return BrowserAccessibilityStateImpl::GetInstance(); 30 } 31 32 // static 33 BrowserAccessibilityStateImpl* BrowserAccessibilityStateImpl::GetInstance() { 34 return Singleton<BrowserAccessibilityStateImpl, 35 LeakySingletonTraits<BrowserAccessibilityStateImpl> >::get(); 36 } 37 38 BrowserAccessibilityStateImpl::BrowserAccessibilityStateImpl() 39 : BrowserAccessibilityState(), 40 accessibility_mode_(AccessibilityModeOff) { 41 ResetAccessibilityModeValue(); 42 #if defined(OS_WIN) 43 // On Windows, UpdateHistograms calls some system functions with unknown 44 // runtime, so call it on the file thread to ensure there's no jank. 45 // Everything in that method must be safe to call on another thread. 46 BrowserThread::ID update_histogram_thread = BrowserThread::FILE; 47 #else 48 // On all other platforms, UpdateHistograms should be called on the main 49 // thread. 50 BrowserThread::ID update_histogram_thread = BrowserThread::UI; 51 #endif 52 53 // We need to AddRef() the leaky singleton so that Bind doesn't 54 // delete it prematurely. 55 AddRef(); 56 BrowserThread::PostDelayedTask( 57 update_histogram_thread, FROM_HERE, 58 base::Bind(&BrowserAccessibilityStateImpl::UpdateHistograms, this), 59 base::TimeDelta::FromSeconds(kAccessibilityHistogramDelaySecs)); 60 } 61 62 BrowserAccessibilityStateImpl::~BrowserAccessibilityStateImpl() { 63 } 64 65 void BrowserAccessibilityStateImpl::OnScreenReaderDetected() { 66 if (CommandLine::ForCurrentProcess()->HasSwitch( 67 switches::kDisableRendererAccessibility)) { 68 return; 69 } 70 EnableAccessibility(); 71 } 72 73 void BrowserAccessibilityStateImpl::EnableAccessibility() { 74 AddAccessibilityMode(AccessibilityModeComplete); 75 } 76 77 void BrowserAccessibilityStateImpl::DisableAccessibility() { 78 ResetAccessibilityMode(); 79 } 80 81 void BrowserAccessibilityStateImpl::ResetAccessibilityModeValue() { 82 accessibility_mode_ = AccessibilityModeOff; 83 #if defined(OS_WIN) 84 // On Windows 8, always enable accessibility for editable text controls 85 // so we can show the virtual keyboard when one is enabled. 86 if (base::win::GetVersion() >= base::win::VERSION_WIN8 && 87 !CommandLine::ForCurrentProcess()->HasSwitch( 88 switches::kDisableRendererAccessibility)) { 89 accessibility_mode_ = AccessibilityModeEditableTextOnly; 90 } 91 #endif // defined(OS_WIN) 92 93 if (CommandLine::ForCurrentProcess()->HasSwitch( 94 switches::kForceRendererAccessibility)) { 95 accessibility_mode_ = AccessibilityModeComplete; 96 } 97 } 98 99 void BrowserAccessibilityStateImpl::ResetAccessibilityMode() { 100 ResetAccessibilityModeValue(); 101 102 // Iterate over all RenderWidgetHosts, even swapped out ones in case 103 // they become active again. 104 scoped_ptr<RenderWidgetHostIterator> widgets( 105 RenderWidgetHostImpl::GetAllRenderWidgetHosts()); 106 while (RenderWidgetHost* widget = widgets->GetNextHost()) { 107 // Ignore processes that don't have a connection, such as crashed tabs. 108 if (!widget->GetProcess()->HasConnection()) 109 continue; 110 if (!widget->IsRenderView()) 111 continue; 112 113 RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget); 114 rwhi->ResetAccessibilityMode(); 115 } 116 } 117 118 bool BrowserAccessibilityStateImpl::IsAccessibleBrowser() { 119 return ((accessibility_mode_ & AccessibilityModeComplete) == 120 AccessibilityModeComplete); 121 } 122 123 void BrowserAccessibilityStateImpl::AddHistogramCallback( 124 base::Closure callback) { 125 histogram_callbacks_.push_back(callback); 126 } 127 128 void BrowserAccessibilityStateImpl::UpdateHistogramsForTesting() { 129 UpdateHistograms(); 130 } 131 132 void BrowserAccessibilityStateImpl::UpdateHistograms() { 133 UpdatePlatformSpecificHistograms(); 134 135 for (size_t i = 0; i < histogram_callbacks_.size(); ++i) 136 histogram_callbacks_[i].Run(); 137 138 UMA_HISTOGRAM_BOOLEAN("Accessibility.State", IsAccessibleBrowser()); 139 UMA_HISTOGRAM_BOOLEAN("Accessibility.InvertedColors", 140 gfx::IsInvertedColorScheme()); 141 UMA_HISTOGRAM_BOOLEAN("Accessibility.ManuallyEnabled", 142 CommandLine::ForCurrentProcess()->HasSwitch( 143 switches::kForceRendererAccessibility)); 144 } 145 146 #if !defined(OS_WIN) 147 void BrowserAccessibilityStateImpl::UpdatePlatformSpecificHistograms() { 148 } 149 #endif 150 151 void BrowserAccessibilityStateImpl::AddAccessibilityMode( 152 AccessibilityMode mode) { 153 if (CommandLine::ForCurrentProcess()->HasSwitch( 154 switches::kDisableRendererAccessibility)) { 155 return; 156 } 157 158 accessibility_mode_ = 159 content::AddAccessibilityModeTo(accessibility_mode_, mode); 160 161 AddOrRemoveFromRenderWidgets(mode, true); 162 } 163 164 void BrowserAccessibilityStateImpl::RemoveAccessibilityMode( 165 AccessibilityMode mode) { 166 if (CommandLine::ForCurrentProcess()->HasSwitch( 167 switches::kForceRendererAccessibility) && 168 mode == AccessibilityModeComplete) { 169 return; 170 } 171 172 accessibility_mode_ = 173 content::RemoveAccessibilityModeFrom(accessibility_mode_, mode); 174 175 AddOrRemoveFromRenderWidgets(mode, false); 176 } 177 178 void BrowserAccessibilityStateImpl::AddOrRemoveFromRenderWidgets( 179 AccessibilityMode mode, 180 bool add) { 181 // Iterate over all RenderWidgetHosts, even swapped out ones in case 182 // they become active again. 183 scoped_ptr<RenderWidgetHostIterator> widgets( 184 RenderWidgetHostImpl::GetAllRenderWidgetHosts()); 185 while (RenderWidgetHost* widget = widgets->GetNextHost()) { 186 // Ignore processes that don't have a connection, such as crashed tabs. 187 if (!widget->GetProcess()->HasConnection()) 188 continue; 189 if (!widget->IsRenderView()) 190 continue; 191 192 RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget); 193 if (add) 194 rwhi->AddAccessibilityMode(mode); 195 else 196 rwhi->RemoveAccessibilityMode(mode); 197 } 198 } 199 200 } // namespace content 201