preference_api.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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 "chrome/browser/extensions/api/preference/preference_api.h"
6
7#include <map>
8#include <utility>
9
10#include "base/lazy_instance.h"
11#include "base/memory/singleton.h"
12#include "base/prefs/pref_service.h"
13#include "base/stl_util.h"
14#include "base/stringprintf.h"
15#include "base/values.h"
16#include "chrome/browser/extensions/api/preference/preference_api_constants.h"
17#include "chrome/browser/extensions/api/preference/preference_helpers.h"
18#include "chrome/browser/extensions/api/proxy/proxy_api.h"
19#include "chrome/browser/extensions/extension_prefs.h"
20#include "chrome/browser/extensions/extension_prefs_scope.h"
21#include "chrome/browser/extensions/extension_service.h"
22#include "chrome/browser/extensions/extension_system.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/common/chrome_notification_types.h"
25#include "chrome/common/extensions/permissions/api_permission.h"
26#include "chrome/common/pref_names.h"
27#include "content/public/browser/notification_details.h"
28#include "content/public/browser/notification_source.h"
29#include "extensions/common/error_utils.h"
30
31namespace keys = extensions::preference_api_constants;
32namespace helpers = extensions::preference_helpers;
33
34namespace extensions {
35
36namespace {
37
38struct PrefMappingEntry {
39  // Name of the preference referenced by the extension API JSON.
40  const char* extension_pref;
41
42  // Name of the preference in the PrefStores.
43  const char* browser_pref;
44
45  // Permission required to access this preference.
46  // Use APIPermission::kInvalid for |permission| to express that no
47  // permission is necessary.
48  APIPermission::ID permission;
49};
50
51const char kOnPrefChangeFormat[] = "types.ChromeSetting.%s.onChange";
52const char kConversionErrorMessage[] =
53    "Internal error: Stored value for preference '*' cannot be converted "
54    "properly.";
55
56PrefMappingEntry kPrefMapping[] = {
57  { "protectedContentEnabled",
58    prefs::kEnableDRM,
59    APIPermission::kPrivacy
60  },
61  { "alternateErrorPagesEnabled",
62    prefs::kAlternateErrorPagesEnabled,
63    APIPermission::kPrivacy
64  },
65  { "autofillEnabled",
66    autofill::prefs::kAutofillEnabled,
67    APIPermission::kPrivacy
68  },
69  { "hyperlinkAuditingEnabled",
70    prefs::kEnableHyperlinkAuditing,
71    APIPermission::kPrivacy
72  },
73  { "managedModeEnabled",
74    prefs::kInManagedMode,
75    APIPermission::kManagedModePrivate
76  },
77  { "networkPredictionEnabled",
78    prefs::kNetworkPredictionEnabled,
79    APIPermission::kPrivacy
80  },
81  { "proxy",
82    prefs::kProxy,
83    APIPermission::kProxy
84  },
85  { "referrersEnabled",
86    prefs::kEnableReferrers,
87    APIPermission::kPrivacy
88  },
89  { "safeBrowsingEnabled",
90    prefs::kSafeBrowsingEnabled,
91    APIPermission::kPrivacy
92  },
93  { "searchSuggestEnabled",
94    prefs::kSearchSuggestEnabled,
95    APIPermission::kPrivacy
96  },
97  { "spellingServiceEnabled",
98    prefs::kSpellCheckUseSpellingService,
99    APIPermission::kPrivacy
100  },
101  { "thirdPartyCookiesAllowed",
102    prefs::kBlockThirdPartyCookies,
103    APIPermission::kPrivacy
104  },
105  { "translationServiceEnabled",
106    prefs::kEnableTranslate,
107    APIPermission::kPrivacy
108  }
109};
110
111class IdentityPrefTransformer : public PrefTransformerInterface {
112 public:
113  virtual Value* ExtensionToBrowserPref(const Value* extension_pref,
114                                        std::string* error,
115                                        bool* bad_message) OVERRIDE {
116    return extension_pref->DeepCopy();
117  }
118
119  virtual Value* BrowserToExtensionPref(const Value* browser_pref) OVERRIDE {
120    return browser_pref->DeepCopy();
121  }
122};
123
124class InvertBooleanTransformer : public PrefTransformerInterface {
125 public:
126  virtual Value* ExtensionToBrowserPref(const Value* extension_pref,
127                                        std::string* error,
128                                        bool* bad_message) OVERRIDE {
129    return InvertBooleanValue(extension_pref);
130  }
131
132  virtual Value* BrowserToExtensionPref(const Value* browser_pref) OVERRIDE {
133    return InvertBooleanValue(browser_pref);
134  }
135
136 private:
137  static Value* InvertBooleanValue(const Value* value) {
138    bool bool_value = false;
139    bool result = value->GetAsBoolean(&bool_value);
140    DCHECK(result);
141    return Value::CreateBooleanValue(!bool_value);
142  }
143};
144
145class PrefMapping {
146 public:
147  static PrefMapping* GetInstance() {
148    return Singleton<PrefMapping>::get();
149  }
150
151  bool FindBrowserPrefForExtensionPref(const std::string& extension_pref,
152                                       std::string* browser_pref,
153                                       APIPermission::ID* permission) {
154    PrefMap::iterator it = mapping_.find(extension_pref);
155    if (it != mapping_.end()) {
156      *browser_pref = it->second.first;
157      *permission = it->second.second;
158      return true;
159    }
160    return false;
161  }
162
163  bool FindEventForBrowserPref(const std::string& browser_pref,
164                               std::string* event_name,
165                               APIPermission::ID* permission) {
166    PrefMap::iterator it = event_mapping_.find(browser_pref);
167    if (it != event_mapping_.end()) {
168      *event_name = it->second.first;
169      *permission = it->second.second;
170      return true;
171    }
172    return false;
173  }
174
175  PrefTransformerInterface* FindTransformerForBrowserPref(
176      const std::string& browser_pref) {
177    std::map<std::string, PrefTransformerInterface*>::iterator it =
178        transformers_.find(browser_pref);
179    if (it != transformers_.end())
180      return it->second;
181    else
182      return identity_transformer_.get();
183  }
184
185 private:
186  friend struct DefaultSingletonTraits<PrefMapping>;
187
188  PrefMapping() {
189    identity_transformer_.reset(new IdentityPrefTransformer());
190    for (size_t i = 0; i < arraysize(kPrefMapping); ++i) {
191      mapping_[kPrefMapping[i].extension_pref] =
192          std::make_pair(kPrefMapping[i].browser_pref,
193                         kPrefMapping[i].permission);
194      std::string event_name =
195          base::StringPrintf(kOnPrefChangeFormat,
196                             kPrefMapping[i].extension_pref);
197      event_mapping_[kPrefMapping[i].browser_pref] =
198          std::make_pair(event_name, kPrefMapping[i].permission);
199    }
200    DCHECK_EQ(arraysize(kPrefMapping), mapping_.size());
201    DCHECK_EQ(arraysize(kPrefMapping), event_mapping_.size());
202    RegisterPrefTransformer(prefs::kProxy, new ProxyPrefTransformer());
203    RegisterPrefTransformer(prefs::kBlockThirdPartyCookies,
204                            new InvertBooleanTransformer());
205  }
206
207  ~PrefMapping() {
208    STLDeleteContainerPairSecondPointers(transformers_.begin(),
209                                         transformers_.end());
210  }
211
212  void RegisterPrefTransformer(const std::string& browser_pref,
213                               PrefTransformerInterface* transformer) {
214    DCHECK_EQ(0u, transformers_.count(browser_pref)) <<
215        "Trying to register pref transformer for " << browser_pref << " twice";
216    transformers_[browser_pref] = transformer;
217  }
218
219  typedef std::map<std::string,
220                   std::pair<std::string, APIPermission::ID> >
221          PrefMap;
222
223  // Mapping from extension pref keys to browser pref keys and permissions.
224  PrefMap mapping_;
225
226  // Mapping from browser pref keys to extension event names and permissions.
227  PrefMap event_mapping_;
228
229  // Mapping from browser pref keys to transformers.
230  std::map<std::string, PrefTransformerInterface*> transformers_;
231
232  scoped_ptr<PrefTransformerInterface> identity_transformer_;
233
234  DISALLOW_COPY_AND_ASSIGN(PrefMapping);
235};
236
237}  // namespace
238
239PreferenceEventRouter::PreferenceEventRouter(Profile* profile)
240    : profile_(profile) {
241  registrar_.Init(profile_->GetPrefs());
242  incognito_registrar_.Init(profile_->GetOffTheRecordPrefs());
243  for (size_t i = 0; i < arraysize(kPrefMapping); ++i) {
244    registrar_.Add(kPrefMapping[i].browser_pref,
245                   base::Bind(&PreferenceEventRouter::OnPrefChanged,
246                              base::Unretained(this),
247                              registrar_.prefs()));
248    incognito_registrar_.Add(kPrefMapping[i].browser_pref,
249                             base::Bind(&PreferenceEventRouter::OnPrefChanged,
250                                        base::Unretained(this),
251                                        incognito_registrar_.prefs()));
252  }
253}
254
255PreferenceEventRouter::~PreferenceEventRouter() { }
256
257void PreferenceEventRouter::OnPrefChanged(PrefService* pref_service,
258                                          const std::string& browser_pref) {
259  bool incognito = (pref_service != profile_->GetPrefs());
260
261  std::string event_name;
262  APIPermission::ID permission = APIPermission::kInvalid;
263  bool rv = PrefMapping::GetInstance()->FindEventForBrowserPref(
264      browser_pref, &event_name, &permission);
265  DCHECK(rv);
266
267  ListValue args;
268  DictionaryValue* dict = new DictionaryValue();
269  args.Append(dict);
270  const PrefService::Preference* pref =
271      pref_service->FindPreference(browser_pref.c_str());
272  CHECK(pref);
273  ExtensionService* extension_service =
274      ExtensionSystem::Get(profile_)->extension_service();
275  PrefTransformerInterface* transformer =
276      PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
277  Value* transformed_value =
278      transformer->BrowserToExtensionPref(pref->GetValue());
279  if (!transformed_value) {
280    LOG(ERROR) << ErrorUtils::FormatErrorMessage(kConversionErrorMessage,
281                                                 pref->name());
282    return;
283  }
284
285  dict->Set(keys::kValue, transformed_value);
286  if (incognito) {
287    ExtensionPrefs* ep = extension_service->extension_prefs();
288    dict->SetBoolean(keys::kIncognitoSpecific,
289                     ep->HasIncognitoPrefValue(browser_pref));
290  }
291
292  helpers::DispatchEventToExtensions(profile_,
293                                     event_name,
294                                     &args,
295                                     permission,
296                                     incognito,
297                                     browser_pref);
298}
299
300PreferenceAPI::PreferenceAPI(Profile* profile) : profile_(profile) {
301  for (size_t i = 0; i < arraysize(kPrefMapping); ++i) {
302    std::string event_name;
303    APIPermission::ID permission = APIPermission::kInvalid;
304    bool rv = PrefMapping::GetInstance()->FindEventForBrowserPref(
305        kPrefMapping[i].browser_pref, &event_name, &permission);
306    DCHECK(rv);
307    ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
308        this, event_name);
309  }
310}
311
312PreferenceAPI::~PreferenceAPI() {
313}
314
315void PreferenceAPI::Shutdown() {
316  ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
317}
318
319static base::LazyInstance<ProfileKeyedAPIFactory<PreferenceAPI> >
320g_factory = LAZY_INSTANCE_INITIALIZER;
321
322// static
323ProfileKeyedAPIFactory<PreferenceAPI>* PreferenceAPI::GetFactoryInstance() {
324  return &g_factory.Get();
325}
326
327void PreferenceAPI::OnListenerAdded(const EventListenerInfo& details) {
328  preference_event_router_.reset(new PreferenceEventRouter(profile_));
329  ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
330}
331
332PreferenceFunction::~PreferenceFunction() { }
333
334bool PreferenceFunction::ValidateBrowserPref(
335    const std::string& extension_pref_key,
336    std::string* browser_pref_key) {
337  APIPermission::ID permission = APIPermission::kInvalid;
338  EXTENSION_FUNCTION_VALIDATE(
339      PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref(
340          extension_pref_key, browser_pref_key, &permission));
341  if (!GetExtension()->HasAPIPermission(permission)) {
342    error_ = ErrorUtils::FormatErrorMessage(
343        keys::kPermissionErrorMessage, extension_pref_key);
344    return false;
345  }
346  return true;
347}
348
349GetPreferenceFunction::~GetPreferenceFunction() { }
350
351bool GetPreferenceFunction::RunImpl() {
352  std::string pref_key;
353  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key));
354  DictionaryValue* details = NULL;
355  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details));
356
357  bool incognito = false;
358  if (details->HasKey(keys::kIncognitoKey))
359    EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(keys::kIncognitoKey,
360                                                    &incognito));
361
362  // Check incognito access.
363  if (incognito && !include_incognito()) {
364    error_ = keys::kIncognitoErrorMessage;
365    return false;
366  }
367
368  // Obtain pref.
369  std::string browser_pref;
370  if (!ValidateBrowserPref(pref_key, &browser_pref))
371    return false;
372  PrefService* prefs = incognito ? profile_->GetOffTheRecordPrefs()
373                                 : profile_->GetPrefs();
374  const PrefService::Preference* pref =
375      prefs->FindPreference(browser_pref.c_str());
376  CHECK(pref);
377
378  scoped_ptr<DictionaryValue> result(new DictionaryValue);
379
380  // Retrieve level of control.
381  std::string level_of_control =
382      helpers::GetLevelOfControl(profile_, extension_id(), browser_pref,
383                                 incognito);
384  result->SetString(keys::kLevelOfControl, level_of_control);
385
386  // Retrieve pref value.
387  PrefTransformerInterface* transformer =
388      PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
389  Value* transformed_value =
390      transformer->BrowserToExtensionPref(pref->GetValue());
391  if (!transformed_value) {
392    LOG(ERROR) <<
393        ErrorUtils::FormatErrorMessage(kConversionErrorMessage,
394                                                pref->name());
395    return false;
396  }
397  result->Set(keys::kValue, transformed_value);
398
399  // Retrieve incognito status.
400  if (incognito) {
401    ExtensionPrefs* ep =
402        ExtensionSystem::Get(profile_)->extension_service()->extension_prefs();
403    result->SetBoolean(keys::kIncognitoSpecific,
404                       ep->HasIncognitoPrefValue(browser_pref));
405  }
406
407  SetResult(result.release());
408  return true;
409}
410
411SetPreferenceFunction::~SetPreferenceFunction() { }
412
413bool SetPreferenceFunction::RunImpl() {
414  std::string pref_key;
415  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key));
416  DictionaryValue* details = NULL;
417  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details));
418
419  Value* value = NULL;
420  EXTENSION_FUNCTION_VALIDATE(details->Get(keys::kValue, &value));
421
422  ExtensionPrefsScope scope = kExtensionPrefsScopeRegular;
423  if (details->HasKey(keys::kScopeKey)) {
424    std::string scope_str;
425    EXTENSION_FUNCTION_VALIDATE(
426        details->GetString(keys::kScopeKey, &scope_str));
427
428    EXTENSION_FUNCTION_VALIDATE(helpers::StringToScope(scope_str, &scope));
429  }
430
431  // Check incognito scope.
432  bool incognito =
433      (scope == kExtensionPrefsScopeIncognitoPersistent ||
434       scope == kExtensionPrefsScopeIncognitoSessionOnly);
435  if (incognito) {
436    // Regular profiles can't access incognito unless include_incognito is true.
437    if (!profile()->IsOffTheRecord() && !include_incognito()) {
438      error_ = keys::kIncognitoErrorMessage;
439      return false;
440    }
441  } else {
442    // Incognito profiles can't access regular mode ever, they only exist in
443    // split mode.
444    if (profile()->IsOffTheRecord()) {
445      error_ = "Can't modify regular settings from an incognito context.";
446      return false;
447    }
448  }
449
450  if (scope == kExtensionPrefsScopeIncognitoSessionOnly &&
451      !profile_->HasOffTheRecordProfile()) {
452    error_ = keys::kIncognitoSessionOnlyErrorMessage;
453    return false;
454  }
455
456  // Obtain pref.
457  std::string browser_pref;
458  if (!ValidateBrowserPref(pref_key, &browser_pref))
459    return false;
460  ExtensionPrefs* prefs =
461      ExtensionSystem::Get(profile_)->extension_service()->extension_prefs();
462  const PrefService::Preference* pref =
463      prefs->pref_service()->FindPreference(browser_pref.c_str());
464  CHECK(pref);
465
466  // Validate new value.
467  EXTENSION_FUNCTION_VALIDATE(value->GetType() == pref->GetType());
468  PrefTransformerInterface* transformer =
469      PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
470  std::string error;
471  bool bad_message = false;
472  scoped_ptr<Value> browser_pref_value(
473      transformer->ExtensionToBrowserPref(value, &error, &bad_message));
474  if (!browser_pref_value) {
475    error_ = error;
476    bad_message_ = bad_message;
477    return false;
478  }
479
480  // Validate also that the stored value can be converted back by the
481  // transformer.
482  scoped_ptr<Value> extensionPrefValue(
483      transformer->BrowserToExtensionPref(browser_pref_value.get()));
484  if (!extensionPrefValue) {
485    error_ =  ErrorUtils::FormatErrorMessage(kConversionErrorMessage,
486                                                      pref->name());
487    bad_message_ = true;
488    return false;
489  }
490
491  prefs->SetExtensionControlledPref(extension_id(),
492                                    browser_pref,
493                                    scope,
494                                    browser_pref_value.release());
495  return true;
496}
497
498ClearPreferenceFunction::~ClearPreferenceFunction() { }
499
500bool ClearPreferenceFunction::RunImpl() {
501  std::string pref_key;
502  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key));
503  DictionaryValue* details = NULL;
504  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details));
505
506  ExtensionPrefsScope scope = kExtensionPrefsScopeRegular;
507  if (details->HasKey(keys::kScopeKey)) {
508    std::string scope_str;
509    EXTENSION_FUNCTION_VALIDATE(
510        details->GetString(keys::kScopeKey, &scope_str));
511
512    EXTENSION_FUNCTION_VALIDATE(helpers::StringToScope(scope_str, &scope));
513  }
514
515  // Check incognito scope.
516  bool incognito =
517      (scope == kExtensionPrefsScopeIncognitoPersistent ||
518       scope == kExtensionPrefsScopeIncognitoSessionOnly);
519  if (incognito) {
520    // We don't check incognito permissions here, as an extension should be
521    // always allowed to clear its own settings.
522  } else {
523    // Incognito profiles can't access regular mode ever, they only exist in
524    // split mode.
525    if (profile()->IsOffTheRecord()) {
526      error_ = "Can't modify regular settings from an incognito context.";
527      return false;
528    }
529  }
530
531  std::string browser_pref;
532  if (!ValidateBrowserPref(pref_key, &browser_pref))
533    return false;
534
535  ExtensionPrefs* prefs =
536      ExtensionSystem::Get(profile_)->extension_service()->extension_prefs();
537  prefs->RemoveExtensionControlledPref(extension_id(), browser_pref, scope);
538  return true;
539}
540
541}  // namespace extensions
542