1 // Copyright (c) 2011 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 9 #include "base/json/json_writer.h" 10 #include "base/memory/singleton.h" 11 #include "base/stl_util-inl.h" 12 #include "base/stringprintf.h" 13 #include "base/values.h" 14 #include "chrome/browser/extensions/extension_event_router.h" 15 #include "chrome/browser/extensions/extension_prefs.h" 16 #include "chrome/browser/extensions/extension_proxy_api.h" 17 #include "chrome/browser/extensions/extension_service.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/common/pref_names.h" 20 #include "content/common/notification_type.h" 21 #include "content/common/notification_service.h" 22 23 namespace { 24 25 struct PrefMappingEntry { 26 const char* extension_pref; 27 const char* browser_pref; 28 const char* permission; 29 }; 30 31 const char kNotControllable[] = "NotControllable"; 32 const char kControlledByOtherExtensions[] = "ControlledByOtherExtensions"; 33 const char kControllableByThisExtension[] = "ControllableByThisExtension"; 34 const char kControlledByThisExtension[] = "ControlledByThisExtension"; 35 36 const char kIncognito[] = "incognito"; 37 const char kIncognitoSpecific[] = "incognitoSpecific"; 38 const char kLevelOfControl[] = "levelOfControl"; 39 const char kValue[] = "value"; 40 41 const char kOnPrefChangeFormat[] = "experimental.preferences.%s.onChange"; 42 43 const char kIncognitoErrorMessage[] = 44 "You do not have permission to access incognito preferences."; 45 46 const char kPermissionErrorMessage[] = 47 "You do not have permission to access the preference '%s'. " 48 "Be sure to declare in your manifest what permissions you need."; 49 50 PrefMappingEntry kPrefMapping[] = { 51 { "blockThirdPartyCookies", 52 prefs::kBlockThirdPartyCookies, 53 Extension::kContentSettingsPermission 54 }, 55 { "enableReferrers", 56 prefs::kEnableReferrers, 57 Extension::kContentSettingsPermission 58 }, 59 { "enableHyperlinkAuditing", 60 prefs::kEnableHyperlinkAuditing, 61 Extension::kContentSettingsPermission 62 }, 63 { "proxy", 64 prefs::kProxy, 65 Extension::kProxyPermission 66 }, 67 }; 68 69 class IdentityPrefTransformer : public PrefTransformerInterface { 70 public: 71 IdentityPrefTransformer() { } 72 virtual ~IdentityPrefTransformer() { } 73 74 virtual Value* ExtensionToBrowserPref(const Value* extension_pref, 75 std::string* error) { 76 return extension_pref->DeepCopy(); 77 } 78 79 virtual Value* BrowserToExtensionPref(const Value* browser_pref) { 80 return browser_pref->DeepCopy(); 81 } 82 }; 83 84 // Returns a string constant (defined in the API) indicating the level of 85 // control this extension has over the specified preference. 86 const char* GetLevelOfControl( 87 Profile* profile, 88 const std::string& extension_id, 89 const std::string& browser_pref, 90 bool incognito) { 91 PrefService* prefs = incognito ? profile->GetOffTheRecordPrefs() 92 : profile->GetPrefs(); 93 const PrefService::Preference* pref = 94 prefs->FindPreference(browser_pref.c_str()); 95 CHECK(pref); 96 ExtensionPrefs* ep = profile->GetExtensionService()->extension_prefs(); 97 98 if (!pref->IsExtensionModifiable()) 99 return kNotControllable; 100 101 if (ep->DoesExtensionControlPref(extension_id, browser_pref, incognito)) 102 return kControlledByThisExtension; 103 104 if (ep->CanExtensionControlPref(extension_id, browser_pref, incognito)) 105 return kControllableByThisExtension; 106 107 return kControlledByOtherExtensions; 108 } 109 110 class PrefMapping { 111 public: 112 static PrefMapping* GetInstance() { 113 return Singleton<PrefMapping>::get(); 114 } 115 116 bool FindBrowserPrefForExtensionPref(const std::string& extension_pref, 117 std::string* browser_pref, 118 std::string* permission) { 119 std::map<std::string, std::pair<std::string, std::string> >::iterator it = 120 mapping_.find(extension_pref); 121 if (it != mapping_.end()) { 122 *browser_pref = it->second.first; 123 *permission = it->second.second; 124 return true; 125 } 126 return false; 127 } 128 129 bool FindEventForBrowserPref(const std::string& browser_pref, 130 std::string* event_name, 131 std::string* permission) { 132 std::map<std::string, std::pair<std::string, std::string> >::iterator it = 133 event_mapping_.find(browser_pref); 134 if (it != event_mapping_.end()) { 135 *event_name = it->second.first; 136 *permission = it->second.second; 137 return true; 138 } 139 return false; 140 } 141 142 PrefTransformerInterface* FindTransformerForBrowserPref( 143 const std::string& browser_pref) { 144 std::map<std::string, PrefTransformerInterface*>::iterator it = 145 transformers_.find(browser_pref); 146 if (it != transformers_.end()) 147 return it->second; 148 else 149 return identity_transformer_.get(); 150 } 151 152 private: 153 friend struct DefaultSingletonTraits<PrefMapping>; 154 155 PrefMapping() { 156 identity_transformer_.reset(new IdentityPrefTransformer()); 157 for (size_t i = 0; i < arraysize(kPrefMapping); ++i) { 158 mapping_[kPrefMapping[i].extension_pref] = 159 std::make_pair(kPrefMapping[i].browser_pref, 160 kPrefMapping[i].permission); 161 std::string event_name = 162 base::StringPrintf(kOnPrefChangeFormat, 163 kPrefMapping[i].extension_pref); 164 event_mapping_[kPrefMapping[i].browser_pref] = 165 std::make_pair(event_name, kPrefMapping[i].permission); 166 } 167 DCHECK_EQ(arraysize(kPrefMapping), mapping_.size()); 168 DCHECK_EQ(arraysize(kPrefMapping), event_mapping_.size()); 169 RegisterPrefTransformer(prefs::kProxy, new ProxyPrefTransformer()); 170 } 171 172 ~PrefMapping() { 173 STLDeleteContainerPairSecondPointers(transformers_.begin(), 174 transformers_.end()); 175 } 176 177 void RegisterPrefTransformer(const std::string& browser_pref, 178 PrefTransformerInterface* transformer) { 179 DCHECK_EQ(0u, transformers_.count(browser_pref)) << 180 "Trying to register pref transformer for " << browser_pref << " twice"; 181 transformers_[browser_pref] = transformer; 182 } 183 184 // Mapping from extension pref keys to browser pref keys and permissions. 185 std::map<std::string, std::pair<std::string, std::string> > mapping_; 186 187 // Mapping from browser pref keys to extension event names and permissions. 188 std::map<std::string, std::pair<std::string, std::string> > event_mapping_; 189 190 // Mapping from browser pref keys to transformers. 191 std::map<std::string, PrefTransformerInterface*> transformers_; 192 193 scoped_ptr<PrefTransformerInterface> identity_transformer_; 194 195 DISALLOW_COPY_AND_ASSIGN(PrefMapping); 196 }; 197 198 } // namespace 199 200 ExtensionPreferenceEventRouter::ExtensionPreferenceEventRouter( 201 Profile* profile) : profile_(profile) { 202 registrar_.Init(profile_->GetPrefs()); 203 incognito_registrar_.Init(profile_->GetOffTheRecordPrefs()); 204 for (size_t i = 0; i < arraysize(kPrefMapping); ++i) { 205 registrar_.Add(kPrefMapping[i].browser_pref, this); 206 incognito_registrar_.Add(kPrefMapping[i].browser_pref, this); 207 } 208 } 209 210 ExtensionPreferenceEventRouter::~ExtensionPreferenceEventRouter() { } 211 212 void ExtensionPreferenceEventRouter::Observe( 213 NotificationType type, 214 const NotificationSource& source, 215 const NotificationDetails& details) { 216 if (type == NotificationType::PREF_CHANGED) { 217 const std::string* pref_key = 218 Details<const std::string>(details).ptr(); 219 OnPrefChanged(Source<PrefService>(source).ptr(), *pref_key); 220 } else { 221 NOTREACHED(); 222 } 223 } 224 225 void ExtensionPreferenceEventRouter::OnPrefChanged( 226 PrefService* pref_service, 227 const std::string& browser_pref) { 228 bool incognito = (pref_service != profile_->GetPrefs()); 229 230 std::string event_name; 231 std::string permission; 232 bool rv = PrefMapping::GetInstance()->FindEventForBrowserPref( 233 browser_pref, &event_name, &permission); 234 DCHECK(rv); 235 236 ListValue args; 237 DictionaryValue* dict = new DictionaryValue(); 238 args.Append(dict); 239 const PrefService::Preference* pref = 240 pref_service->FindPreference(browser_pref.c_str()); 241 CHECK(pref); 242 ExtensionService* extension_service = profile_->GetExtensionService(); 243 PrefTransformerInterface* transformer = 244 PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref); 245 dict->Set(kValue, transformer->BrowserToExtensionPref(pref->GetValue())); 246 if (incognito) { 247 ExtensionPrefs* ep = extension_service->extension_prefs(); 248 dict->Set( 249 kIncognitoSpecific, 250 Value::CreateBooleanValue(ep->HasIncognitoPrefValue(browser_pref))); 251 } 252 253 ExtensionEventRouter* router = profile_->GetExtensionEventRouter(); 254 if (!router || !router->HasEventListener(event_name)) 255 return; 256 const ExtensionList* extensions = extension_service->extensions(); 257 for (ExtensionList::const_iterator it = extensions->begin(); 258 it != extensions->end(); ++it) { 259 std::string extension_id = (*it)->id(); 260 // TODO(bauerb): Only iterate over registered event listeners. 261 if (router->ExtensionHasEventListener(extension_id, event_name) && 262 (*it)->HasApiPermission(permission) && 263 (!incognito || extension_service->CanCrossIncognito(*it))) { 264 std::string level_of_control = 265 GetLevelOfControl(profile_, extension_id, browser_pref, incognito); 266 dict->Set(kLevelOfControl, Value::CreateStringValue(level_of_control)); 267 268 std::string json_args; 269 base::JSONWriter::Write(&args, false, &json_args); 270 271 DispatchEvent(extension_id, event_name, json_args); 272 } 273 } 274 } 275 276 void ExtensionPreferenceEventRouter::DispatchEvent( 277 const std::string& extension_id, 278 const std::string& event_name, 279 const std::string& json_args) { 280 profile_->GetExtensionEventRouter()->DispatchEventToExtension( 281 extension_id, event_name, json_args, NULL, GURL()); 282 } 283 284 // TODO(battre): Factor out common parts once this is stable. 285 286 GetPreferenceFunction::~GetPreferenceFunction() { } 287 288 bool GetPreferenceFunction::RunImpl() { 289 std::string pref_key; 290 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key)); 291 DictionaryValue* details = NULL; 292 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details)); 293 294 bool incognito = false; 295 if (details->HasKey(kIncognito)) 296 EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(kIncognito, &incognito)); 297 298 if (incognito && !include_incognito()) { 299 error_ = kIncognitoErrorMessage; 300 return false; 301 } 302 303 PrefService* prefs = incognito ? profile_->GetOffTheRecordPrefs() 304 : profile_->GetPrefs(); 305 std::string browser_pref; 306 std::string permission; 307 EXTENSION_FUNCTION_VALIDATE( 308 PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref( 309 pref_key, &browser_pref, &permission)); 310 if (!GetExtension()->HasApiPermission(permission)) { 311 error_ = base::StringPrintf(kPermissionErrorMessage, pref_key.c_str()); 312 return false; 313 } 314 315 const PrefService::Preference* pref = 316 prefs->FindPreference(browser_pref.c_str()); 317 CHECK(pref); 318 std::string level_of_control = 319 GetLevelOfControl(profile_, extension_id(), browser_pref, incognito); 320 321 scoped_ptr<DictionaryValue> result(new DictionaryValue); 322 PrefTransformerInterface* transformer = 323 PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref); 324 result->Set(kValue, transformer->BrowserToExtensionPref(pref->GetValue())); 325 result->Set(kLevelOfControl, Value::CreateStringValue(level_of_control)); 326 if (incognito) { 327 ExtensionPrefs* ep = profile_->GetExtensionService()->extension_prefs(); 328 result->Set( 329 kIncognitoSpecific, 330 Value::CreateBooleanValue(ep->HasIncognitoPrefValue(browser_pref))); 331 } 332 result_.reset(result.release()); 333 return true; 334 } 335 336 SetPreferenceFunction::~SetPreferenceFunction() { } 337 338 bool SetPreferenceFunction::RunImpl() { 339 std::string pref_key; 340 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key)); 341 DictionaryValue* details = NULL; 342 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details)); 343 344 Value* value = NULL; 345 EXTENSION_FUNCTION_VALIDATE(details->Get(kValue, &value)); 346 347 bool incognito = false; 348 if (details->HasKey(kIncognito)) 349 EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(kIncognito, &incognito)); 350 351 if (incognito && !include_incognito()) { 352 error_ = kIncognitoErrorMessage; 353 return false; 354 } 355 356 std::string browser_pref; 357 std::string permission; 358 EXTENSION_FUNCTION_VALIDATE( 359 PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref( 360 pref_key, &browser_pref, &permission)); 361 if (!GetExtension()->HasApiPermission(permission)) { 362 error_ = base::StringPrintf(kPermissionErrorMessage, pref_key.c_str()); 363 return false; 364 } 365 ExtensionPrefs* prefs = profile_->GetExtensionService()->extension_prefs(); 366 const PrefService::Preference* pref = 367 prefs->pref_service()->FindPreference(browser_pref.c_str()); 368 CHECK(pref); 369 EXTENSION_FUNCTION_VALIDATE(value->GetType() == pref->GetType()); 370 PrefTransformerInterface* transformer = 371 PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref); 372 std::string error; 373 Value* browserPrefValue = transformer->ExtensionToBrowserPref(value, &error); 374 if (!browserPrefValue) { 375 error_ = error; 376 return false; 377 } 378 prefs->SetExtensionControlledPref(extension_id(), 379 browser_pref, 380 incognito, 381 browserPrefValue); 382 return true; 383 } 384 385 ClearPreferenceFunction::~ClearPreferenceFunction() { } 386 387 bool ClearPreferenceFunction::RunImpl() { 388 std::string pref_key; 389 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key)); 390 DictionaryValue* details = NULL; 391 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details)); 392 393 bool incognito = false; 394 if (details->HasKey(kIncognito)) 395 EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(kIncognito, &incognito)); 396 397 // We don't check incognito permissions here, as an extension should be always 398 // allowed to clear its own settings. 399 400 std::string browser_pref; 401 std::string permission; 402 EXTENSION_FUNCTION_VALIDATE( 403 PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref( 404 pref_key, &browser_pref, &permission)); 405 if (!GetExtension()->HasApiPermission(permission)) { 406 error_ = base::StringPrintf(kPermissionErrorMessage, pref_key.c_str()); 407 return false; 408 } 409 ExtensionPrefs* prefs = profile_->GetExtensionService()->extension_prefs(); 410 prefs->RemoveExtensionControlledPref(extension_id(), browser_pref, incognito); 411 return true; 412 } 413