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