1// Copyright (c) 2013 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 "chrome/browser/ui/gesture_prefs_observer_factory_aura.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/compiler_specific.h"
12#include "base/prefs/pref_change_registrar.h"
13#include "base/prefs/pref_service.h"
14#include "chrome/browser/chrome_notification_types.h"
15#include "chrome/browser/profiles/incognito_helpers.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/common/pref_names.h"
18#include "components/keyed_service/content/browser_context_dependency_manager.h"
19#include "components/pref_registry/pref_registry_syncable.h"
20#include "content/public/browser/notification_observer.h"
21#include "content/public/browser/notification_service.h"
22#include "content/public/browser/overscroll_configuration.h"
23#include "content/public/common/renderer_preferences.h"
24#include "ui/events/gestures/gesture_configuration.h"
25
26using ui::GestureConfiguration;
27
28namespace {
29
30struct OverscrollPref {
31  const char* pref_name;
32  content::OverscrollConfig config;
33};
34
35const std::vector<OverscrollPref>& GetOverscrollPrefs() {
36  CR_DEFINE_STATIC_LOCAL(std::vector<OverscrollPref>, overscroll_prefs, ());
37  if (overscroll_prefs.empty()) {
38    using namespace content;
39    const OverscrollPref kOverscrollPrefs[] = {
40      { prefs::kOverscrollHorizontalThresholdComplete,
41        OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE },
42      { prefs::kOverscrollVerticalThresholdComplete,
43        OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE },
44      { prefs::kOverscrollMinimumThresholdStart,
45        OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN },
46      { prefs::kOverscrollMinimumThresholdStartTouchpad,
47        OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD },
48      { prefs::kOverscrollVerticalThresholdStart,
49        OVERSCROLL_CONFIG_VERT_THRESHOLD_START },
50      { prefs::kOverscrollHorizontalResistThreshold,
51        OVERSCROLL_CONFIG_HORIZ_RESIST_AFTER },
52      { prefs::kOverscrollVerticalResistThreshold,
53        OVERSCROLL_CONFIG_VERT_RESIST_AFTER },
54    };
55    overscroll_prefs.assign(kOverscrollPrefs,
56                            kOverscrollPrefs + arraysize(kOverscrollPrefs));
57  }
58  return overscroll_prefs;
59}
60
61// This class manages gesture configuration preferences.
62class GesturePrefsObserver : public KeyedService {
63 public:
64  explicit GesturePrefsObserver(PrefService* prefs);
65  virtual ~GesturePrefsObserver();
66
67  // KeyedService implementation.
68  virtual void Shutdown() OVERRIDE;
69
70 private:
71  // Notification callback invoked when browser-side preferences
72  // are updated and need to be pushed into ui::GesturePreferences.
73  void Update();
74
75  // Notification callback invoked when the fling deacceleration
76  // gesture preferences are changed from chrome://gesture.
77  // Broadcasts the changes all renderers where they are used.
78  void Notify();
79
80  // Notification helper to push overscroll preferences into
81  // content.
82  void UpdateOverscrollPrefs();
83
84  PrefChangeRegistrar registrar_;
85  PrefService* prefs_;
86
87  DISALLOW_COPY_AND_ASSIGN(GesturePrefsObserver);
88};
89
90// The list of prefs we want to observe.
91// Note that this collection of settings should correspond to the settings used
92// in ui/events/gestures/gesture_configuration.h
93const char* kPrefsToObserve[] = {
94  prefs::kFlingMaxCancelToDownTimeInMs,
95  prefs::kFlingMaxTapGapTimeInMs,
96  prefs::kTabScrubActivationDelayInMS,
97  prefs::kMaxSeparationForGestureTouchesInPixels,
98  prefs::kSemiLongPressTimeInSeconds,
99};
100
101const char* kPrefsToRemove[] = {
102    "gesture.fling_acceleration_curve_coefficient_0",
103    "gesture.fling_acceleration_curve_coefficient_1",
104    "gesture.fling_acceleration_curve_coefficient_2",
105    "gesture.fling_acceleration_curve_coefficient_3",
106    "flingcurve.touchpad_alpha",
107    "flingcurve.touchpad_beta",
108    "flingcurve.touchpad_gamma",
109    "flingcurve.touchscreen_alpha",
110    "flingcurve.touchscreen_beta",
111    "flingcurve.touchscreen_gamma",
112};
113
114GesturePrefsObserver::GesturePrefsObserver(PrefService* prefs)
115    : prefs_(prefs) {
116  for (size_t i = 0; i < arraysize(kPrefsToRemove); ++i) {
117    if (prefs->FindPreference(kPrefsToRemove[i]))
118      prefs->ClearPref(kPrefsToRemove[i]);
119  }
120
121  registrar_.Init(prefs);
122  registrar_.RemoveAll();
123  base::Closure callback = base::Bind(&GesturePrefsObserver::Update,
124                                      base::Unretained(this));
125
126  base::Closure notify_callback = base::Bind(&GesturePrefsObserver::Notify,
127                                             base::Unretained(this));
128
129  for (size_t i = 0; i < arraysize(kPrefsToObserve); ++i)
130    registrar_.Add(kPrefsToObserve[i], callback);
131
132  const std::vector<OverscrollPref>& overscroll_prefs = GetOverscrollPrefs();
133  for (size_t i = 0; i < overscroll_prefs.size(); ++i)
134    registrar_.Add(overscroll_prefs[i].pref_name, callback);
135
136  Update();
137}
138
139GesturePrefsObserver::~GesturePrefsObserver() {}
140
141void GesturePrefsObserver::Shutdown() {
142  registrar_.RemoveAll();
143}
144
145void GesturePrefsObserver::Update() {
146  GestureConfiguration::set_fling_max_cancel_to_down_time_in_ms(
147      prefs_->GetInteger(prefs::kFlingMaxCancelToDownTimeInMs));
148  GestureConfiguration::set_fling_max_tap_gap_time_in_ms(
149      prefs_->GetInteger(prefs::kFlingMaxTapGapTimeInMs));
150  GestureConfiguration::set_tab_scrub_activation_delay_in_ms(
151      prefs_->GetInteger(prefs::kTabScrubActivationDelayInMS));
152  GestureConfiguration::set_semi_long_press_time_in_seconds(
153      prefs_->GetDouble(prefs::kSemiLongPressTimeInSeconds));
154  GestureConfiguration::set_max_separation_for_gesture_touches_in_pixels(
155      prefs_->GetDouble(prefs::kMaxSeparationForGestureTouchesInPixels));
156
157  UpdateOverscrollPrefs();
158}
159
160void GesturePrefsObserver::UpdateOverscrollPrefs() {
161  const std::vector<OverscrollPref>& overscroll_prefs = GetOverscrollPrefs();
162  for (size_t i = 0; i < overscroll_prefs.size(); ++i) {
163    content::SetOverscrollConfig(overscroll_prefs[i].config,
164        static_cast<float>(prefs_->GetDouble(overscroll_prefs[i].pref_name)));
165  }
166}
167
168void GesturePrefsObserver::Notify() {
169  // Must do a notify to distribute the changes to all renderers.
170  content::NotificationService* service =
171      content::NotificationService::current();
172  service->Notify(chrome::NOTIFICATION_BROWSER_FLING_CURVE_PARAMETERS_CHANGED,
173                  content::Source<GesturePrefsObserver>(this),
174                  content::NotificationService::NoDetails());
175}
176
177}  // namespace
178
179// static
180GesturePrefsObserverFactoryAura*
181GesturePrefsObserverFactoryAura::GetInstance() {
182  return Singleton<GesturePrefsObserverFactoryAura>::get();
183}
184
185GesturePrefsObserverFactoryAura::GesturePrefsObserverFactoryAura()
186    : BrowserContextKeyedServiceFactory(
187        "GesturePrefsObserverAura",
188        BrowserContextDependencyManager::GetInstance()) {}
189
190GesturePrefsObserverFactoryAura::~GesturePrefsObserverFactoryAura() {}
191
192KeyedService* GesturePrefsObserverFactoryAura::BuildServiceInstanceFor(
193    content::BrowserContext* profile) const {
194  return new GesturePrefsObserver(static_cast<Profile*>(profile)->GetPrefs());
195}
196
197void GesturePrefsObserverFactoryAura::RegisterOverscrollPrefs(
198    user_prefs::PrefRegistrySyncable* registry) {
199  const std::vector<OverscrollPref>& overscroll_prefs = GetOverscrollPrefs();
200
201  for (size_t i = 0; i < overscroll_prefs.size(); ++i) {
202    registry->RegisterDoublePref(
203        overscroll_prefs[i].pref_name,
204        content::GetOverscrollConfig(overscroll_prefs[i].config),
205        user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
206  }
207}
208
209void GesturePrefsObserverFactoryAura::RegisterProfilePrefs(
210    user_prefs::PrefRegistrySyncable* registry) {
211  registry->RegisterIntegerPref(
212      prefs::kFlingMaxCancelToDownTimeInMs,
213      GestureConfiguration::fling_max_cancel_to_down_time_in_ms(),
214      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
215  registry->RegisterIntegerPref(
216      prefs::kFlingMaxTapGapTimeInMs,
217      GestureConfiguration::fling_max_tap_gap_time_in_ms(),
218      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
219  registry->RegisterIntegerPref(
220      prefs::kTabScrubActivationDelayInMS,
221      GestureConfiguration::tab_scrub_activation_delay_in_ms(),
222      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
223  registry->RegisterDoublePref(
224      prefs::kSemiLongPressTimeInSeconds,
225      GestureConfiguration::semi_long_press_time_in_seconds(),
226      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
227  registry->RegisterDoublePref(
228      prefs::kMaxSeparationForGestureTouchesInPixels,
229      GestureConfiguration::max_separation_for_gesture_touches_in_pixels(),
230      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
231  RegisterOverscrollPrefs(registry);
232}
233
234bool
235GesturePrefsObserverFactoryAura::ServiceIsCreatedWithBrowserContext() const {
236  // Create the observer as soon as the profile is created.
237  return true;
238}
239
240content::BrowserContext*
241GesturePrefsObserverFactoryAura::GetBrowserContextToUse(
242    content::BrowserContext* context) const {
243  // Use same gesture preferences on incognito windows.
244  return chrome::GetBrowserContextRedirectedInIncognito(context);
245}
246
247bool GesturePrefsObserverFactoryAura::ServiceIsNULLWhileTesting() const {
248  // Some tests replace the PrefService of the TestingProfile after the
249  // GesturePrefsObserver has been created, which makes Shutdown()
250  // remove the registrar from a non-existent PrefService.
251  return true;
252}
253