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.
5 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
9 #include "ash/magnifier/magnification_controller.h"
10 #include "ash/magnifier/partial_magnification_controller.h"
11 #include "ash/session/session_state_delegate.h"
12 #include "ash/shell.h"
13 #include "ash/shell_delegate.h"
14 #include "ash/system/tray/system_tray_notifier.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/singleton.h"
17 #include "base/prefs/pref_member.h"
18 #include "base/prefs/pref_service.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
21 #include "chrome/browser/chromeos/profiles/profile_helper.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/profiles/profile_manager.h"
24 #include "chrome/common/pref_names.h"
25 #include "content/public/browser/focused_node_details.h"
26 #include "content/public/browser/notification_details.h"
27 #include "content/public/browser/notification_observer.h"
28 #include "content/public/browser/notification_registrar.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/notification_source.h"
35 static MagnificationManager
* g_magnification_manager
= NULL
;
38 class MagnificationManagerImpl
: public MagnificationManager
,
39 public content::NotificationObserver
,
40 public ash::SessionStateObserver
{
42 MagnificationManagerImpl()
44 magnifier_enabled_pref_handler_(
45 prefs::kAccessibilityScreenMagnifierEnabled
),
46 magnifier_type_pref_handler_(prefs::kAccessibilityScreenMagnifierType
),
47 magnifier_scale_pref_handler_(
48 prefs::kAccessibilityScreenMagnifierScale
),
49 type_(ui::kDefaultMagnifierType
),
51 keep_focus_centered_(false),
52 observing_focus_change_in_page_(false) {
54 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
,
55 content::NotificationService::AllSources());
57 chrome::NOTIFICATION_SESSION_STARTED
,
58 content::NotificationService::AllSources());
60 chrome::NOTIFICATION_PROFILE_DESTROYED
,
61 content::NotificationService::AllSources());
64 ~MagnificationManagerImpl() override
{
65 CHECK(this == g_magnification_manager
);
68 // MagnificationManager implimentation:
69 bool IsMagnifierEnabled() const override
{ return enabled_
; }
71 ui::MagnifierType
GetMagnifierType() const override
{ return type_
; }
73 void SetMagnifierEnabled(bool enabled
) override
{
77 PrefService
* prefs
= profile_
->GetPrefs();
78 prefs
->SetBoolean(prefs::kAccessibilityScreenMagnifierEnabled
, enabled
);
79 prefs
->CommitPendingWrite();
82 void SetMagnifierType(ui::MagnifierType type
) override
{
86 PrefService
* prefs
= profile_
->GetPrefs();
87 prefs
->SetInteger(prefs::kAccessibilityScreenMagnifierType
, type
);
88 prefs
->CommitPendingWrite();
91 void SaveScreenMagnifierScale(double scale
) override
{
95 profile_
->GetPrefs()->SetDouble(prefs::kAccessibilityScreenMagnifierScale
,
99 double GetSavedScreenMagnifierScale() const override
{
101 return std::numeric_limits
<double>::min();
103 return profile_
->GetPrefs()->GetDouble(
104 prefs::kAccessibilityScreenMagnifierScale
);
107 void SetProfileForTest(Profile
* profile
) override
{ SetProfile(profile
); }
109 // SessionStateObserver overrides:
110 void ActiveUserChanged(const std::string
& user_id
) override
{
111 SetProfile(ProfileManager::GetActiveUserProfile());
115 void SetProfile(Profile
* profile
) {
116 pref_change_registrar_
.reset();
119 // TODO(yoshiki): Move following code to PrefHandler.
120 pref_change_registrar_
.reset(new PrefChangeRegistrar
);
121 pref_change_registrar_
->Init(profile
->GetPrefs());
122 pref_change_registrar_
->Add(
123 prefs::kAccessibilityScreenMagnifierEnabled
,
124 base::Bind(&MagnificationManagerImpl::UpdateMagnifierFromPrefs
,
125 base::Unretained(this)));
126 pref_change_registrar_
->Add(
127 prefs::kAccessibilityScreenMagnifierType
,
128 base::Bind(&MagnificationManagerImpl::UpdateMagnifierFromPrefs
,
129 base::Unretained(this)));
130 pref_change_registrar_
->Add(
131 prefs::kAccessibilityScreenMagnifierCenterFocus
,
132 base::Bind(&MagnificationManagerImpl::UpdateMagnifierFromPrefs
,
133 base::Unretained(this)));
136 magnifier_enabled_pref_handler_
.HandleProfileChanged(profile_
, profile
);
137 magnifier_type_pref_handler_
.HandleProfileChanged(profile_
, profile
);
138 magnifier_scale_pref_handler_
.HandleProfileChanged(profile_
, profile
);
141 UpdateMagnifierFromPrefs();
144 virtual void SetMagnifierEnabledInternal(bool enabled
) {
145 // This method may be invoked even when the other magnifier settings (e.g.
146 // type or scale) are changed, so we need to call magnification controller
147 // even if |enabled| is unchanged. Only if |enabled| is false and the
148 // magnifier is already disabled, we are sure that we don't need to reflect
149 // the new settings right now because the magnifier keeps disabled.
150 if (!enabled
&& !enabled_
)
155 if (type_
== ui::MAGNIFIER_FULL
) {
156 ash::Shell::GetInstance()->magnification_controller()->SetEnabled(
158 MonitorFocusInPageChange();
160 ash::Shell::GetInstance()->partial_magnification_controller()->SetEnabled(
165 virtual void SetMagnifierTypeInternal(ui::MagnifierType type
) {
169 type_
= ui::MAGNIFIER_FULL
; // (leave out for full magnifier)
172 virtual void SetMagniferKeepFocusCenteredInternal(bool keep_focus_centered
) {
173 if (keep_focus_centered_
== keep_focus_centered
)
176 keep_focus_centered_
= keep_focus_centered
;
178 if (type_
== ui::MAGNIFIER_FULL
) {
179 ash::Shell::GetInstance()
180 ->magnification_controller()
181 ->SetKeepFocusCentered(keep_focus_centered_
);
185 void UpdateMagnifierFromPrefs() {
189 const bool enabled
= profile_
->GetPrefs()->GetBoolean(
190 prefs::kAccessibilityScreenMagnifierEnabled
);
191 const int type_integer
= profile_
->GetPrefs()->GetInteger(
192 prefs::kAccessibilityScreenMagnifierType
);
193 const bool keep_focus_centered
= profile_
->GetPrefs()->GetBoolean(
194 prefs::kAccessibilityScreenMagnifierCenterFocus
);
196 ui::MagnifierType type
= ui::kDefaultMagnifierType
;
197 if (type_integer
> 0 && type_integer
<= ui::kMaxMagnifierType
) {
198 type
= static_cast<ui::MagnifierType
>(type_integer
);
199 } else if (type_integer
== 0) {
200 // Type 0 is used to disable the screen magnifier through policy. As the
201 // magnifier type is irrelevant in this case, it is OK to just fall back
208 SetMagnifierEnabledInternal(enabled
);
209 SetMagnifierTypeInternal(type
);
210 SetMagniferKeepFocusCenteredInternal(keep_focus_centered
);
212 SetMagniferKeepFocusCenteredInternal(keep_focus_centered
);
213 SetMagnifierTypeInternal(type
);
214 SetMagnifierEnabledInternal(enabled
);
217 AccessibilityStatusEventDetails
details(
218 ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFIER
,
221 ui::A11Y_NOTIFICATION_NONE
);
223 if (AccessibilityManager::Get()) {
224 AccessibilityManager::Get()->NotifyAccessibilityStatusChanged(details
);
225 if (ash::Shell::GetInstance()) {
226 ash::Shell::GetInstance()->SetCursorCompositingEnabled(
227 AccessibilityManager::Get()->ShouldEnableCursorCompositing());
232 void MonitorFocusInPageChange() {
233 if (enabled_
&& !observing_focus_change_in_page_
) {
234 registrar_
.Add(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE
,
235 content::NotificationService::AllSources());
236 observing_focus_change_in_page_
= true;
237 } else if (!enabled_
&& observing_focus_change_in_page_
) {
238 registrar_
.Remove(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE
,
239 content::NotificationService::AllSources());
240 observing_focus_change_in_page_
= false;
244 // content::NotificationObserver implementation:
245 void Observe(int type
,
246 const content::NotificationSource
& source
,
247 const content::NotificationDetails
& details
) override
{
249 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
: {
250 // Update |profile_| when entering the login screen.
251 Profile
* profile
= ProfileManager::GetActiveUserProfile();
252 if (ProfileHelper::IsSigninProfile(profile
))
256 case chrome::NOTIFICATION_SESSION_STARTED
:
257 // Update |profile_| when entering a session.
258 SetProfile(ProfileManager::GetActiveUserProfile());
260 // Add a session state observer to be able to monitor session changes.
261 if (!session_state_observer_
.get() && ash::Shell::HasInstance())
262 session_state_observer_
.reset(
263 new ash::ScopedSessionStateObserver(this));
265 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
266 // Update |profile_| when exiting a session or shutting down.
267 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
268 if (profile_
== profile
)
272 case content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE
: {
273 content::FocusedNodeDetails
* node_details
=
274 content::Details
<content::FocusedNodeDetails
>(details
).ptr();
275 ash::Shell::GetInstance()
276 ->magnification_controller()
277 ->HandleFocusedNodeChanged(node_details
->is_editable_node
,
278 node_details
->node_bounds_in_screen
);
286 AccessibilityManager::PrefHandler magnifier_enabled_pref_handler_
;
287 AccessibilityManager::PrefHandler magnifier_type_pref_handler_
;
288 AccessibilityManager::PrefHandler magnifier_scale_pref_handler_
;
290 ui::MagnifierType type_
;
292 bool keep_focus_centered_
;
293 bool observing_focus_change_in_page_
;
295 content::NotificationRegistrar registrar_
;
296 scoped_ptr
<PrefChangeRegistrar
> pref_change_registrar_
;
297 scoped_ptr
<ash::ScopedSessionStateObserver
> session_state_observer_
;
299 DISALLOW_COPY_AND_ASSIGN(MagnificationManagerImpl
);
303 void MagnificationManager::Initialize() {
304 CHECK(g_magnification_manager
== NULL
);
305 g_magnification_manager
= new MagnificationManagerImpl();
309 void MagnificationManager::Shutdown() {
310 CHECK(g_magnification_manager
);
311 delete g_magnification_manager
;
312 g_magnification_manager
= NULL
;
316 MagnificationManager
* MagnificationManager::Get() {
317 return g_magnification_manager
;
320 } // namespace chromeos