| 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_prefs.h" | |
| 6 | |
| 7 #include <iterator> | |
| 8 | |
| 9 #include "base/command_line.h" | |
| 10 #include "base/prefs/pref_notifier.h" | |
| 11 #include "base/prefs/pref_service.h" | |
| 12 #include "base/strings/string_number_conversions.h" | |
| 13 #include "base/strings/string_util.h" | |
| 14 #include "base/value_conversions.h" | |
| 15 #include "chrome/browser/extensions/api/content_settings/content_settings_store.
h" | |
| 16 #include "chrome/browser/extensions/api/preference/preference_api.h" | |
| 17 #include "chrome/browser/extensions/extension_pref_store.h" | |
| 18 #include "chrome/browser/extensions/extension_prefs_factory.h" | |
| 19 #include "components/user_prefs/pref_registry_syncable.h" | |
| 20 #include "extensions/browser/admin_policy.h" | |
| 21 #include "extensions/browser/app_sorting.h" | |
| 22 #include "extensions/browser/event_router.h" | |
| 23 #include "extensions/browser/pref_names.h" | |
| 24 #include "extensions/common/feature_switch.h" | |
| 25 #include "extensions/common/manifest.h" | |
| 26 #include "extensions/common/permissions/permission_set.h" | |
| 27 #include "extensions/common/permissions/permissions_info.h" | |
| 28 #include "extensions/common/url_pattern.h" | |
| 29 #include "extensions/common/user_script.h" | |
| 30 #include "grit/generated_resources.h" | |
| 31 #include "ui/base/l10n/l10n_util.h" | |
| 32 | |
| 33 using base::Value; | |
| 34 using base::DictionaryValue; | |
| 35 using base::ListValue; | |
| 36 | |
| 37 namespace extensions { | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 // Additional preferences keys, which are not needed by external clients. | |
| 42 | |
| 43 // True if this extension is running. Note this preference stops getting updated | |
| 44 // during Chrome shutdown (and won't be updated on a browser crash) and so can | |
| 45 // be used at startup to determine whether the extension was running when Chrome | |
| 46 // was last terminated. | |
| 47 const char kPrefRunning[] = "running"; | |
| 48 | |
| 49 // Whether this extension had windows when it was last running. | |
| 50 const char kIsActive[] = "is_active"; | |
| 51 | |
| 52 // Where an extension was installed from. (see Manifest::Location) | |
| 53 const char kPrefLocation[] = "location"; | |
| 54 | |
| 55 // Enabled, disabled, killed, etc. (see Extension::State) | |
| 56 const char kPrefState[] = "state"; | |
| 57 | |
| 58 // The path to the current version's manifest file. | |
| 59 const char kPrefPath[] = "path"; | |
| 60 | |
| 61 // The dictionary containing the extension's manifest. | |
| 62 const char kPrefManifest[] = "manifest"; | |
| 63 | |
| 64 // The version number. | |
| 65 const char kPrefVersion[] = "manifest.version"; | |
| 66 | |
| 67 // Indicates whether an extension is blacklisted. | |
| 68 const char kPrefBlacklist[] = "blacklist"; | |
| 69 | |
| 70 // The count of how many times we prompted the user to acknowledge an | |
| 71 // extension. | |
| 72 const char kPrefAcknowledgePromptCount[] = "ack_prompt_count"; | |
| 73 | |
| 74 // Indicates whether the user has acknowledged various types of extensions. | |
| 75 const char kPrefExternalAcknowledged[] = "ack_external"; | |
| 76 const char kPrefBlacklistAcknowledged[] = "ack_blacklist"; | |
| 77 const char kPrefWipeoutAcknowledged[] = "ack_wiped"; | |
| 78 | |
| 79 // Indicates whether the external extension was installed during the first | |
| 80 // run of this profile. | |
| 81 const char kPrefExternalInstallFirstRun[] = "external_first_run"; | |
| 82 | |
| 83 // Indicates whether to show an install warning when the user enables. | |
| 84 const char kExtensionDidEscalatePermissions[] = "install_warning_on_enable"; | |
| 85 | |
| 86 // DO NOT USE, use kPrefDisableReasons instead. | |
| 87 // Indicates whether the extension was updated while it was disabled. | |
| 88 const char kDeprecatedPrefDisableReason[] = "disable_reason"; | |
| 89 | |
| 90 // A bitmask of all the reasons an extension is disabled. | |
| 91 const char kPrefDisableReasons[] = "disable_reasons"; | |
| 92 | |
| 93 // The key for a serialized Time value indicating the start of the day (from the | |
| 94 // server's perspective) an extension last included a "ping" parameter during | |
| 95 // its update check. | |
| 96 const char kLastPingDay[] = "lastpingday"; | |
| 97 | |
| 98 // Similar to kLastPingDay, but for "active" instead of "rollcall" pings. | |
| 99 const char kLastActivePingDay[] = "last_active_pingday"; | |
| 100 | |
| 101 // A bit we use to keep track of whether we need to do an "active" ping. | |
| 102 const char kActiveBit[] = "active_bit"; | |
| 103 | |
| 104 // Path for settings specific to blacklist update. | |
| 105 const char kExtensionsBlacklistUpdate[] = "extensions.blacklistupdate"; | |
| 106 | |
| 107 // Path for the delayed install info dictionary preference. The actual string | |
| 108 // value is a legacy artifact for when delayed installs only pertained to | |
| 109 // updates that were waiting for idle. | |
| 110 const char kDelayedInstallInfo[] = "idle_install_info"; | |
| 111 | |
| 112 // Reason why the extension's install was delayed. | |
| 113 const char kDelayedInstallReason[] = "delay_install_reason"; | |
| 114 | |
| 115 // Path for the suggested page ordinal of a delayed extension install. | |
| 116 const char kPrefSuggestedPageOrdinal[] = "suggested_page_ordinal"; | |
| 117 | |
| 118 // A preference that, if true, will allow this extension to run in incognito | |
| 119 // mode. | |
| 120 const char kPrefIncognitoEnabled[] = "incognito"; | |
| 121 | |
| 122 // A preference to control whether an extension is allowed to inject script in | |
| 123 // pages with file URLs. | |
| 124 const char kPrefAllowFileAccess[] = "newAllowFileAccess"; | |
| 125 // TODO(jstritar): As part of fixing http://crbug.com/91577, we revoked all | |
| 126 // extension file access by renaming the pref. We should eventually clean up | |
| 127 // the old flag and possibly go back to that name. | |
| 128 // const char kPrefAllowFileAccessOld[] = "allowFileAccess"; | |
| 129 | |
| 130 // A preference specifying if the user dragged the app on the NTP. | |
| 131 const char kPrefUserDraggedApp[] = "user_dragged_app_ntp"; | |
| 132 | |
| 133 // Preferences that hold which permissions the user has granted the extension. | |
| 134 // We explicitly keep track of these so that extensions can contain unknown | |
| 135 // permissions, for backwards compatibility reasons, and we can still prompt | |
| 136 // the user to accept them once recognized. We store the active permission | |
| 137 // permissions because they may differ from those defined in the manifest. | |
| 138 const char kPrefActivePermissions[] = "active_permissions"; | |
| 139 const char kPrefGrantedPermissions[] = "granted_permissions"; | |
| 140 | |
| 141 // The preference names for PermissionSet values. | |
| 142 const char kPrefAPIs[] = "api"; | |
| 143 const char kPrefManifestPermissions[] = "manifest_permissions"; | |
| 144 const char kPrefExplicitHosts[] = "explicit_host"; | |
| 145 const char kPrefScriptableHosts[] = "scriptable_host"; | |
| 146 | |
| 147 // The preference names for the old granted permissions scheme. | |
| 148 const char kPrefOldGrantedFullAccess[] = "granted_permissions.full"; | |
| 149 const char kPrefOldGrantedHosts[] = "granted_permissions.host"; | |
| 150 const char kPrefOldGrantedAPIs[] = "granted_permissions.api"; | |
| 151 | |
| 152 // A preference that indicates when an extension was installed. | |
| 153 const char kPrefInstallTime[] = "install_time"; | |
| 154 | |
| 155 // A preference which saves the creation flags for extensions. | |
| 156 const char kPrefCreationFlags[] = "creation_flags"; | |
| 157 | |
| 158 // A preference that indicates whether the extension was installed from the | |
| 159 // Chrome Web Store. | |
| 160 const char kPrefFromWebStore[] = "from_webstore"; | |
| 161 | |
| 162 // A preference that indicates whether the extension was installed from a | |
| 163 // mock App created from a bookmark. | |
| 164 const char kPrefFromBookmark[] = "from_bookmark"; | |
| 165 | |
| 166 // A preference that indicates whether the extension was installed as | |
| 167 // default apps. | |
| 168 const char kPrefWasInstalledByDefault[] = "was_installed_by_default"; | |
| 169 | |
| 170 // Key for Geometry Cache preference. | |
| 171 const char kPrefGeometryCache[] = "geometry_cache"; | |
| 172 | |
| 173 // A preference that indicates when an extension is last launched. | |
| 174 const char kPrefLastLaunchTime[] = "last_launch_time"; | |
| 175 | |
| 176 // A list of installed ids and a signature. | |
| 177 const char kInstallSignature[] = "extensions.install_signature"; | |
| 178 | |
| 179 // Provider of write access to a dictionary storing extension prefs. | |
| 180 class ScopedExtensionPrefUpdate : public DictionaryPrefUpdate { | |
| 181 public: | |
| 182 ScopedExtensionPrefUpdate(PrefService* service, | |
| 183 const std::string& extension_id) : | |
| 184 DictionaryPrefUpdate(service, pref_names::kExtensions), | |
| 185 extension_id_(extension_id) {} | |
| 186 | |
| 187 virtual ~ScopedExtensionPrefUpdate() { | |
| 188 } | |
| 189 | |
| 190 // DictionaryPrefUpdate overrides: | |
| 191 virtual base::DictionaryValue* Get() OVERRIDE { | |
| 192 base::DictionaryValue* dict = DictionaryPrefUpdate::Get(); | |
| 193 base::DictionaryValue* extension = NULL; | |
| 194 if (!dict->GetDictionary(extension_id_, &extension)) { | |
| 195 // Extension pref does not exist, create it. | |
| 196 extension = new base::DictionaryValue(); | |
| 197 dict->SetWithoutPathExpansion(extension_id_, extension); | |
| 198 } | |
| 199 return extension; | |
| 200 } | |
| 201 | |
| 202 private: | |
| 203 const std::string extension_id_; | |
| 204 | |
| 205 DISALLOW_COPY_AND_ASSIGN(ScopedExtensionPrefUpdate); | |
| 206 }; | |
| 207 | |
| 208 std::string JoinPrefs(const std::string& parent, const char* child) { | |
| 209 return parent + "." + child; | |
| 210 } | |
| 211 | |
| 212 // Checks if kPrefBlacklist is set to true in the base::DictionaryValue. | |
| 213 // Return false if the value is false or kPrefBlacklist does not exist. | |
| 214 // This is used to decide if an extension is blacklisted. | |
| 215 bool IsBlacklistBitSet(const base::DictionaryValue* ext) { | |
| 216 bool bool_value; | |
| 217 return ext->GetBoolean(kPrefBlacklist, &bool_value) && bool_value; | |
| 218 } | |
| 219 | |
| 220 } // namespace | |
| 221 | |
| 222 // | |
| 223 // TimeProvider | |
| 224 // | |
| 225 | |
| 226 ExtensionPrefs::TimeProvider::TimeProvider() { | |
| 227 } | |
| 228 | |
| 229 ExtensionPrefs::TimeProvider::~TimeProvider() { | |
| 230 } | |
| 231 | |
| 232 base::Time ExtensionPrefs::TimeProvider::GetCurrentTime() const { | |
| 233 return base::Time::Now(); | |
| 234 } | |
| 235 | |
| 236 // | |
| 237 // ScopedUpdate | |
| 238 // | |
| 239 template <typename T, base::Value::Type type_enum_value> | |
| 240 ExtensionPrefs::ScopedUpdate<T, type_enum_value>::ScopedUpdate( | |
| 241 ExtensionPrefs* prefs, | |
| 242 const std::string& extension_id, | |
| 243 const std::string& key) | |
| 244 : update_(prefs->pref_service(), pref_names::kExtensions), | |
| 245 extension_id_(extension_id), | |
| 246 key_(key) { | |
| 247 DCHECK(Extension::IdIsValid(extension_id_)); | |
| 248 } | |
| 249 | |
| 250 template <typename T, base::Value::Type type_enum_value> | |
| 251 ExtensionPrefs::ScopedUpdate<T, type_enum_value>::~ScopedUpdate() { | |
| 252 } | |
| 253 | |
| 254 template <typename T, base::Value::Type type_enum_value> | |
| 255 T* ExtensionPrefs::ScopedUpdate<T, type_enum_value>::Get() { | |
| 256 base::DictionaryValue* dict = update_.Get(); | |
| 257 base::DictionaryValue* extension = NULL; | |
| 258 base::Value* key_value = NULL; | |
| 259 if (!dict->GetDictionary(extension_id_, &extension) || | |
| 260 !extension->Get(key_, &key_value)) { | |
| 261 return NULL; | |
| 262 } | |
| 263 return key_value->GetType() == type_enum_value ? | |
| 264 static_cast<T*>(key_value) : | |
| 265 NULL; | |
| 266 } | |
| 267 | |
| 268 template <typename T, base::Value::Type type_enum_value> | |
| 269 T* ExtensionPrefs::ScopedUpdate<T, type_enum_value>::Create() { | |
| 270 base::DictionaryValue* dict = update_.Get(); | |
| 271 base::DictionaryValue* extension = NULL; | |
| 272 base::Value* key_value = NULL; | |
| 273 T* value_as_t = NULL; | |
| 274 if (!dict->GetDictionary(extension_id_, &extension)) { | |
| 275 extension = new base::DictionaryValue; | |
| 276 dict->SetWithoutPathExpansion(extension_id_, extension); | |
| 277 } | |
| 278 if (!extension->Get(key_, &key_value)) { | |
| 279 value_as_t = new T; | |
| 280 extension->SetWithoutPathExpansion(key_, value_as_t); | |
| 281 } else { | |
| 282 CHECK(key_value->GetType() == type_enum_value); | |
| 283 value_as_t = static_cast<T*>(key_value); | |
| 284 } | |
| 285 return value_as_t; | |
| 286 } | |
| 287 | |
| 288 // Explicit instantiations for Dictionary and List value types. | |
| 289 template class ExtensionPrefs::ScopedUpdate<base::DictionaryValue, | |
| 290 base::Value::TYPE_DICTIONARY>; | |
| 291 template class ExtensionPrefs::ScopedUpdate<base::ListValue, | |
| 292 base::Value::TYPE_LIST>; | |
| 293 | |
| 294 // | |
| 295 // ExtensionPrefs | |
| 296 // | |
| 297 | |
| 298 // static | |
| 299 ExtensionPrefs* ExtensionPrefs::Create( | |
| 300 PrefService* prefs, | |
| 301 const base::FilePath& root_dir, | |
| 302 ExtensionPrefValueMap* extension_pref_value_map, | |
| 303 scoped_ptr<AppSorting> app_sorting, | |
| 304 bool extensions_disabled) { | |
| 305 return ExtensionPrefs::Create(prefs, | |
| 306 root_dir, | |
| 307 extension_pref_value_map, | |
| 308 app_sorting.Pass(), | |
| 309 extensions_disabled, | |
| 310 make_scoped_ptr(new TimeProvider())); | |
| 311 } | |
| 312 | |
| 313 // static | |
| 314 ExtensionPrefs* ExtensionPrefs::Create( | |
| 315 PrefService* pref_service, | |
| 316 const base::FilePath& root_dir, | |
| 317 ExtensionPrefValueMap* extension_pref_value_map, | |
| 318 scoped_ptr<AppSorting> app_sorting, | |
| 319 bool extensions_disabled, | |
| 320 scoped_ptr<TimeProvider> time_provider) { | |
| 321 return new ExtensionPrefs(pref_service, | |
| 322 root_dir, | |
| 323 extension_pref_value_map, | |
| 324 app_sorting.Pass(), | |
| 325 time_provider.Pass(), | |
| 326 extensions_disabled); | |
| 327 } | |
| 328 | |
| 329 ExtensionPrefs::~ExtensionPrefs() { | |
| 330 } | |
| 331 | |
| 332 // static | |
| 333 ExtensionPrefs* ExtensionPrefs::Get(content::BrowserContext* context) { | |
| 334 return ExtensionPrefsFactory::GetInstance()->GetForBrowserContext(context); | |
| 335 } | |
| 336 | |
| 337 static base::FilePath::StringType MakePathRelative(const base::FilePath& parent, | |
| 338 const base::FilePath& child) { | |
| 339 if (!parent.IsParent(child)) | |
| 340 return child.value(); | |
| 341 | |
| 342 base::FilePath::StringType retval = child.value().substr( | |
| 343 parent.value().length()); | |
| 344 if (base::FilePath::IsSeparator(retval[0])) | |
| 345 return retval.substr(1); | |
| 346 else | |
| 347 return retval; | |
| 348 } | |
| 349 | |
| 350 void ExtensionPrefs::MakePathsRelative() { | |
| 351 const base::DictionaryValue* dict = | |
| 352 prefs_->GetDictionary(pref_names::kExtensions); | |
| 353 if (!dict || dict->empty()) | |
| 354 return; | |
| 355 | |
| 356 // Collect all extensions ids with absolute paths in |absolute_keys|. | |
| 357 std::set<std::string> absolute_keys; | |
| 358 for (base::DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) { | |
| 359 const base::DictionaryValue* extension_dict = NULL; | |
| 360 if (!i.value().GetAsDictionary(&extension_dict)) | |
| 361 continue; | |
| 362 int location_value; | |
| 363 if (extension_dict->GetInteger(kPrefLocation, &location_value) && | |
| 364 Manifest::IsUnpackedLocation( | |
| 365 static_cast<Manifest::Location>(location_value))) { | |
| 366 // Unpacked extensions can have absolute paths. | |
| 367 continue; | |
| 368 } | |
| 369 base::FilePath::StringType path_string; | |
| 370 if (!extension_dict->GetString(kPrefPath, &path_string)) | |
| 371 continue; | |
| 372 base::FilePath path(path_string); | |
| 373 if (path.IsAbsolute()) | |
| 374 absolute_keys.insert(i.key()); | |
| 375 } | |
| 376 if (absolute_keys.empty()) | |
| 377 return; | |
| 378 | |
| 379 // Fix these paths. | |
| 380 DictionaryPrefUpdate update(prefs_, pref_names::kExtensions); | |
| 381 base::DictionaryValue* update_dict = update.Get(); | |
| 382 for (std::set<std::string>::iterator i = absolute_keys.begin(); | |
| 383 i != absolute_keys.end(); ++i) { | |
| 384 base::DictionaryValue* extension_dict = NULL; | |
| 385 if (!update_dict->GetDictionaryWithoutPathExpansion(*i, &extension_dict)) { | |
| 386 NOTREACHED() << "Control should never reach here for extension " << *i; | |
| 387 continue; | |
| 388 } | |
| 389 base::FilePath::StringType path_string; | |
| 390 extension_dict->GetString(kPrefPath, &path_string); | |
| 391 base::FilePath path(path_string); | |
| 392 extension_dict->SetString(kPrefPath, | |
| 393 MakePathRelative(install_directory_, path)); | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 const base::DictionaryValue* ExtensionPrefs::GetExtensionPref( | |
| 398 const std::string& extension_id) const { | |
| 399 const base::DictionaryValue* extensions = | |
| 400 prefs_->GetDictionary(pref_names::kExtensions); | |
| 401 const base::DictionaryValue* extension_dict = NULL; | |
| 402 if (!extensions || | |
| 403 !extensions->GetDictionary(extension_id, &extension_dict)) { | |
| 404 return NULL; | |
| 405 } | |
| 406 return extension_dict; | |
| 407 } | |
| 408 | |
| 409 void ExtensionPrefs::UpdateExtensionPref(const std::string& extension_id, | |
| 410 const std::string& key, | |
| 411 base::Value* data_value) { | |
| 412 if (!Extension::IdIsValid(extension_id)) { | |
| 413 NOTREACHED() << "Invalid extension_id " << extension_id; | |
| 414 return; | |
| 415 } | |
| 416 ScopedExtensionPrefUpdate update(prefs_, extension_id); | |
| 417 if (data_value) | |
| 418 update->Set(key, data_value); | |
| 419 else | |
| 420 update->Remove(key, NULL); | |
| 421 } | |
| 422 | |
| 423 void ExtensionPrefs::DeleteExtensionPrefs(const std::string& extension_id) { | |
| 424 extension_pref_value_map_->UnregisterExtension(extension_id); | |
| 425 content_settings_store_->UnregisterExtension(extension_id); | |
| 426 DictionaryPrefUpdate update(prefs_, pref_names::kExtensions); | |
| 427 base::DictionaryValue* dict = update.Get(); | |
| 428 dict->Remove(extension_id, NULL); | |
| 429 } | |
| 430 | |
| 431 bool ExtensionPrefs::ReadPrefAsBoolean(const std::string& extension_id, | |
| 432 const std::string& pref_key, | |
| 433 bool* out_value) const { | |
| 434 const base::DictionaryValue* ext = GetExtensionPref(extension_id); | |
| 435 if (!ext || !ext->GetBoolean(pref_key, out_value)) | |
| 436 return false; | |
| 437 | |
| 438 return true; | |
| 439 } | |
| 440 | |
| 441 bool ExtensionPrefs::ReadPrefAsInteger(const std::string& extension_id, | |
| 442 const std::string& pref_key, | |
| 443 int* out_value) const { | |
| 444 const base::DictionaryValue* ext = GetExtensionPref(extension_id); | |
| 445 if (!ext || !ext->GetInteger(pref_key, out_value)) | |
| 446 return false; | |
| 447 | |
| 448 return true; | |
| 449 } | |
| 450 | |
| 451 bool ExtensionPrefs::ReadPrefAsString(const std::string& extension_id, | |
| 452 const std::string& pref_key, | |
| 453 std::string* out_value) const { | |
| 454 const base::DictionaryValue* ext = GetExtensionPref(extension_id); | |
| 455 if (!ext || !ext->GetString(pref_key, out_value)) | |
| 456 return false; | |
| 457 | |
| 458 return true; | |
| 459 } | |
| 460 | |
| 461 bool ExtensionPrefs::ReadPrefAsList(const std::string& extension_id, | |
| 462 const std::string& pref_key, | |
| 463 const base::ListValue** out_value) const { | |
| 464 const base::DictionaryValue* ext = GetExtensionPref(extension_id); | |
| 465 const base::ListValue* out = NULL; | |
| 466 if (!ext || !ext->GetList(pref_key, &out)) | |
| 467 return false; | |
| 468 if (out_value) | |
| 469 *out_value = out; | |
| 470 | |
| 471 return true; | |
| 472 } | |
| 473 | |
| 474 bool ExtensionPrefs::ReadPrefAsDictionary( | |
| 475 const std::string& extension_id, | |
| 476 const std::string& pref_key, | |
| 477 const base::DictionaryValue** out_value) const { | |
| 478 const base::DictionaryValue* ext = GetExtensionPref(extension_id); | |
| 479 const base::DictionaryValue* out = NULL; | |
| 480 if (!ext || !ext->GetDictionary(pref_key, &out)) | |
| 481 return false; | |
| 482 if (out_value) | |
| 483 *out_value = out; | |
| 484 | |
| 485 return true; | |
| 486 } | |
| 487 | |
| 488 bool ExtensionPrefs::HasPrefForExtension( | |
| 489 const std::string& extension_id) const { | |
| 490 return GetExtensionPref(extension_id) != NULL; | |
| 491 } | |
| 492 | |
| 493 bool ExtensionPrefs::ReadPrefAsURLPatternSet(const std::string& extension_id, | |
| 494 const std::string& pref_key, | |
| 495 URLPatternSet* result, | |
| 496 int valid_schemes) { | |
| 497 const base::ListValue* value = NULL; | |
| 498 if (!ReadPrefAsList(extension_id, pref_key, &value)) | |
| 499 return false; | |
| 500 | |
| 501 bool allow_file_access = AllowFileAccess(extension_id); | |
| 502 return result->Populate(*value, valid_schemes, allow_file_access, NULL); | |
| 503 } | |
| 504 | |
| 505 void ExtensionPrefs::SetExtensionPrefURLPatternSet( | |
| 506 const std::string& extension_id, | |
| 507 const std::string& pref_key, | |
| 508 const URLPatternSet& new_value) { | |
| 509 UpdateExtensionPref(extension_id, pref_key, new_value.ToValue().release()); | |
| 510 } | |
| 511 | |
| 512 bool ExtensionPrefs::ReadPrefAsBooleanAndReturn( | |
| 513 const std::string& extension_id, | |
| 514 const std::string& pref_key) const { | |
| 515 bool out_value = false; | |
| 516 return ReadPrefAsBoolean(extension_id, pref_key, &out_value) && out_value; | |
| 517 } | |
| 518 | |
| 519 PermissionSet* ExtensionPrefs::ReadPrefAsPermissionSet( | |
| 520 const std::string& extension_id, | |
| 521 const std::string& pref_key) { | |
| 522 if (!GetExtensionPref(extension_id)) | |
| 523 return NULL; | |
| 524 | |
| 525 // Retrieve the API permissions. Please refer SetExtensionPrefPermissionSet() | |
| 526 // for api_values format. | |
| 527 APIPermissionSet apis; | |
| 528 const base::ListValue* api_values = NULL; | |
| 529 std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); | |
| 530 if (ReadPrefAsList(extension_id, api_pref, &api_values)) { | |
| 531 APIPermissionSet::ParseFromJSON(api_values, | |
| 532 APIPermissionSet::kAllowInternalPermissions, | |
| 533 &apis, NULL, NULL); | |
| 534 } | |
| 535 | |
| 536 // Retrieve the Manifest Keys permissions. Please refer to | |
| 537 // |SetExtensionPrefPermissionSet| for manifest_permissions_values format. | |
| 538 ManifestPermissionSet manifest_permissions; | |
| 539 const base::ListValue* manifest_permissions_values = NULL; | |
| 540 std::string manifest_permission_pref = | |
| 541 JoinPrefs(pref_key, kPrefManifestPermissions); | |
| 542 if (ReadPrefAsList(extension_id, manifest_permission_pref, | |
| 543 &manifest_permissions_values)) { | |
| 544 ManifestPermissionSet::ParseFromJSON( | |
| 545 manifest_permissions_values, &manifest_permissions, NULL, NULL); | |
| 546 } | |
| 547 | |
| 548 // Retrieve the explicit host permissions. | |
| 549 URLPatternSet explicit_hosts; | |
| 550 ReadPrefAsURLPatternSet( | |
| 551 extension_id, JoinPrefs(pref_key, kPrefExplicitHosts), | |
| 552 &explicit_hosts, Extension::kValidHostPermissionSchemes); | |
| 553 | |
| 554 // Retrieve the scriptable host permissions. | |
| 555 URLPatternSet scriptable_hosts; | |
| 556 ReadPrefAsURLPatternSet( | |
| 557 extension_id, JoinPrefs(pref_key, kPrefScriptableHosts), | |
| 558 &scriptable_hosts, UserScript::ValidUserScriptSchemes()); | |
| 559 | |
| 560 return new PermissionSet( | |
| 561 apis, manifest_permissions, explicit_hosts, scriptable_hosts); | |
| 562 } | |
| 563 | |
| 564 // Set the API or Manifest permissions. | |
| 565 // The format of api_values is: | |
| 566 // [ "permission_name1", // permissions do not support detail. | |
| 567 // "permission_name2", | |
| 568 // {"permission_name3": value }, | |
| 569 // // permission supports detail, permission detail will be stored in value. | |
| 570 // ... | |
| 571 // ] | |
| 572 template<typename T> | |
| 573 static base::ListValue* CreatePermissionList(const T& permissions) { | |
| 574 base::ListValue* values = new base::ListValue(); | |
| 575 for (typename T::const_iterator i = permissions.begin(); | |
| 576 i != permissions.end(); ++i) { | |
| 577 scoped_ptr<base::Value> detail(i->ToValue()); | |
| 578 if (detail) { | |
| 579 base::DictionaryValue* tmp = new base::DictionaryValue(); | |
| 580 tmp->Set(i->name(), detail.release()); | |
| 581 values->Append(tmp); | |
| 582 } else { | |
| 583 values->Append(new base::StringValue(i->name())); | |
| 584 } | |
| 585 } | |
| 586 return values; | |
| 587 } | |
| 588 | |
| 589 void ExtensionPrefs::SetExtensionPrefPermissionSet( | |
| 590 const std::string& extension_id, | |
| 591 const std::string& pref_key, | |
| 592 const PermissionSet* new_value) { | |
| 593 std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); | |
| 594 base::ListValue* api_values = CreatePermissionList(new_value->apis()); | |
| 595 UpdateExtensionPref(extension_id, api_pref, api_values); | |
| 596 | |
| 597 std::string manifest_permissions_pref = | |
| 598 JoinPrefs(pref_key, kPrefManifestPermissions); | |
| 599 base::ListValue* manifest_permissions_values = CreatePermissionList( | |
| 600 new_value->manifest_permissions()); | |
| 601 UpdateExtensionPref(extension_id, | |
| 602 manifest_permissions_pref, | |
| 603 manifest_permissions_values); | |
| 604 | |
| 605 // Set the explicit host permissions. | |
| 606 if (!new_value->explicit_hosts().is_empty()) { | |
| 607 SetExtensionPrefURLPatternSet(extension_id, | |
| 608 JoinPrefs(pref_key, kPrefExplicitHosts), | |
| 609 new_value->explicit_hosts()); | |
| 610 } | |
| 611 | |
| 612 // Set the scriptable host permissions. | |
| 613 if (!new_value->scriptable_hosts().is_empty()) { | |
| 614 SetExtensionPrefURLPatternSet(extension_id, | |
| 615 JoinPrefs(pref_key, kPrefScriptableHosts), | |
| 616 new_value->scriptable_hosts()); | |
| 617 } | |
| 618 } | |
| 619 | |
| 620 int ExtensionPrefs::IncrementAcknowledgePromptCount( | |
| 621 const std::string& extension_id) { | |
| 622 int count = 0; | |
| 623 ReadPrefAsInteger(extension_id, kPrefAcknowledgePromptCount, &count); | |
| 624 ++count; | |
| 625 UpdateExtensionPref(extension_id, kPrefAcknowledgePromptCount, | |
| 626 new base::FundamentalValue(count)); | |
| 627 return count; | |
| 628 } | |
| 629 | |
| 630 bool ExtensionPrefs::IsExternalExtensionAcknowledged( | |
| 631 const std::string& extension_id) { | |
| 632 return ReadPrefAsBooleanAndReturn(extension_id, kPrefExternalAcknowledged); | |
| 633 } | |
| 634 | |
| 635 void ExtensionPrefs::AcknowledgeExternalExtension( | |
| 636 const std::string& extension_id) { | |
| 637 DCHECK(Extension::IdIsValid(extension_id)); | |
| 638 UpdateExtensionPref(extension_id, kPrefExternalAcknowledged, | |
| 639 new base::FundamentalValue(true)); | |
| 640 UpdateExtensionPref(extension_id, kPrefAcknowledgePromptCount, NULL); | |
| 641 } | |
| 642 | |
| 643 bool ExtensionPrefs::IsBlacklistedExtensionAcknowledged( | |
| 644 const std::string& extension_id) { | |
| 645 return ReadPrefAsBooleanAndReturn(extension_id, kPrefBlacklistAcknowledged); | |
| 646 } | |
| 647 | |
| 648 void ExtensionPrefs::AcknowledgeBlacklistedExtension( | |
| 649 const std::string& extension_id) { | |
| 650 DCHECK(Extension::IdIsValid(extension_id)); | |
| 651 UpdateExtensionPref(extension_id, kPrefBlacklistAcknowledged, | |
| 652 new base::FundamentalValue(true)); | |
| 653 UpdateExtensionPref(extension_id, kPrefAcknowledgePromptCount, NULL); | |
| 654 } | |
| 655 | |
| 656 bool ExtensionPrefs::IsExternalInstallFirstRun( | |
| 657 const std::string& extension_id) { | |
| 658 return ReadPrefAsBooleanAndReturn(extension_id, kPrefExternalInstallFirstRun); | |
| 659 } | |
| 660 | |
| 661 void ExtensionPrefs::SetExternalInstallFirstRun( | |
| 662 const std::string& extension_id) { | |
| 663 DCHECK(Extension::IdIsValid(extension_id)); | |
| 664 UpdateExtensionPref(extension_id, kPrefExternalInstallFirstRun, | |
| 665 new base::FundamentalValue(true)); | |
| 666 } | |
| 667 | |
| 668 bool ExtensionPrefs::HasWipeoutBeenAcknowledged( | |
| 669 const std::string& extension_id) { | |
| 670 return ReadPrefAsBooleanAndReturn(extension_id, kPrefWipeoutAcknowledged); | |
| 671 } | |
| 672 | |
| 673 void ExtensionPrefs::SetWipeoutAcknowledged( | |
| 674 const std::string& extension_id, | |
| 675 bool value) { | |
| 676 UpdateExtensionPref(extension_id, kPrefWipeoutAcknowledged, | |
| 677 value ? base::Value::CreateBooleanValue(value) : NULL); | |
| 678 } | |
| 679 | |
| 680 bool ExtensionPrefs::SetAlertSystemFirstRun() { | |
| 681 if (prefs_->GetBoolean(pref_names::kAlertsInitialized)) { | |
| 682 return true; | |
| 683 } | |
| 684 prefs_->SetBoolean(pref_names::kAlertsInitialized, true); | |
| 685 return false; | |
| 686 } | |
| 687 | |
| 688 bool ExtensionPrefs::ExtensionsBlacklistedByDefault() const { | |
| 689 return admin_policy::BlacklistedByDefault( | |
| 690 prefs_->GetList(pref_names::kInstallDenyList)); | |
| 691 } | |
| 692 | |
| 693 bool ExtensionPrefs::DidExtensionEscalatePermissions( | |
| 694 const std::string& extension_id) { | |
| 695 return ReadPrefAsBooleanAndReturn(extension_id, | |
| 696 kExtensionDidEscalatePermissions); | |
| 697 } | |
| 698 | |
| 699 void ExtensionPrefs::SetDidExtensionEscalatePermissions( | |
| 700 const Extension* extension, bool did_escalate) { | |
| 701 UpdateExtensionPref(extension->id(), kExtensionDidEscalatePermissions, | |
| 702 new base::FundamentalValue(did_escalate)); | |
| 703 } | |
| 704 | |
| 705 int ExtensionPrefs::GetDisableReasons(const std::string& extension_id) const { | |
| 706 int value = -1; | |
| 707 if (ReadPrefAsInteger(extension_id, kPrefDisableReasons, &value) && | |
| 708 value >= 0) { | |
| 709 return value; | |
| 710 } | |
| 711 return Extension::DISABLE_NONE; | |
| 712 } | |
| 713 | |
| 714 void ExtensionPrefs::AddDisableReason(const std::string& extension_id, | |
| 715 Extension::DisableReason disable_reason) { | |
| 716 int new_value = GetDisableReasons(extension_id) | | |
| 717 static_cast<int>(disable_reason); | |
| 718 UpdateExtensionPref(extension_id, kPrefDisableReasons, | |
| 719 new base::FundamentalValue(new_value)); | |
| 720 } | |
| 721 | |
| 722 void ExtensionPrefs::RemoveDisableReason( | |
| 723 const std::string& extension_id, | |
| 724 Extension::DisableReason disable_reason) { | |
| 725 int new_value = GetDisableReasons(extension_id) & | |
| 726 ~static_cast<int>(disable_reason); | |
| 727 if (new_value == Extension::DISABLE_NONE) { | |
| 728 UpdateExtensionPref(extension_id, kPrefDisableReasons, NULL); | |
| 729 } else { | |
| 730 UpdateExtensionPref(extension_id, kPrefDisableReasons, | |
| 731 new base::FundamentalValue(new_value)); | |
| 732 } | |
| 733 } | |
| 734 | |
| 735 void ExtensionPrefs::ClearDisableReasons(const std::string& extension_id) { | |
| 736 UpdateExtensionPref(extension_id, kPrefDisableReasons, NULL); | |
| 737 } | |
| 738 | |
| 739 std::set<std::string> ExtensionPrefs::GetBlacklistedExtensions() { | |
| 740 std::set<std::string> ids; | |
| 741 | |
| 742 const base::DictionaryValue* extensions = | |
| 743 prefs_->GetDictionary(pref_names::kExtensions); | |
| 744 if (!extensions) | |
| 745 return ids; | |
| 746 | |
| 747 for (base::DictionaryValue::Iterator it(*extensions); | |
| 748 !it.IsAtEnd(); it.Advance()) { | |
| 749 if (!it.value().IsType(base::Value::TYPE_DICTIONARY)) { | |
| 750 NOTREACHED() << "Invalid pref for extension " << it.key(); | |
| 751 continue; | |
| 752 } | |
| 753 if (IsBlacklistBitSet( | |
| 754 static_cast<const base::DictionaryValue*>(&it.value()))) { | |
| 755 ids.insert(it.key()); | |
| 756 } | |
| 757 } | |
| 758 | |
| 759 return ids; | |
| 760 } | |
| 761 | |
| 762 void ExtensionPrefs::SetExtensionBlacklisted(const std::string& extension_id, | |
| 763 bool is_blacklisted) { | |
| 764 bool currently_blacklisted = IsExtensionBlacklisted(extension_id); | |
| 765 if (is_blacklisted == currently_blacklisted) { | |
| 766 NOTREACHED() << extension_id << " is " << | |
| 767 (currently_blacklisted ? "already" : "not") << | |
| 768 " blacklisted"; | |
| 769 return; | |
| 770 } | |
| 771 | |
| 772 // Always make sure the "acknowledged" bit is cleared since the blacklist bit | |
| 773 // is changing. | |
| 774 UpdateExtensionPref(extension_id, kPrefBlacklistAcknowledged, NULL); | |
| 775 | |
| 776 if (is_blacklisted) { | |
| 777 UpdateExtensionPref(extension_id, | |
| 778 kPrefBlacklist, | |
| 779 new base::FundamentalValue(true)); | |
| 780 } else { | |
| 781 UpdateExtensionPref(extension_id, kPrefBlacklist, NULL); | |
| 782 const base::DictionaryValue* dict = GetExtensionPref(extension_id); | |
| 783 if (dict && dict->empty()) | |
| 784 DeleteExtensionPrefs(extension_id); | |
| 785 } | |
| 786 } | |
| 787 | |
| 788 bool ExtensionPrefs::IsExtensionBlacklisted(const std::string& id) const { | |
| 789 const base::DictionaryValue* ext_prefs = GetExtensionPref(id); | |
| 790 return ext_prefs && IsBlacklistBitSet(ext_prefs); | |
| 791 } | |
| 792 | |
| 793 namespace { | |
| 794 | |
| 795 // Serializes |time| as a string value mapped to |key| in |dictionary|. | |
| 796 void SaveTime(base::DictionaryValue* dictionary, | |
| 797 const char* key, | |
| 798 const base::Time& time) { | |
| 799 if (!dictionary) | |
| 800 return; | |
| 801 std::string string_value = base::Int64ToString(time.ToInternalValue()); | |
| 802 dictionary->SetString(key, string_value); | |
| 803 } | |
| 804 | |
| 805 // The opposite of SaveTime. If |key| is not found, this returns an empty Time | |
| 806 // (is_null() will return true). | |
| 807 base::Time ReadTime(const base::DictionaryValue* dictionary, const char* key) { | |
| 808 if (!dictionary) | |
| 809 return base::Time(); | |
| 810 std::string string_value; | |
| 811 int64 value; | |
| 812 if (dictionary->GetString(key, &string_value)) { | |
| 813 if (base::StringToInt64(string_value, &value)) { | |
| 814 return base::Time::FromInternalValue(value); | |
| 815 } | |
| 816 } | |
| 817 return base::Time(); | |
| 818 } | |
| 819 | |
| 820 } // namespace | |
| 821 | |
| 822 base::Time ExtensionPrefs::LastPingDay(const std::string& extension_id) const { | |
| 823 DCHECK(Extension::IdIsValid(extension_id)); | |
| 824 return ReadTime(GetExtensionPref(extension_id), kLastPingDay); | |
| 825 } | |
| 826 | |
| 827 void ExtensionPrefs::SetLastPingDay(const std::string& extension_id, | |
| 828 const base::Time& time) { | |
| 829 DCHECK(Extension::IdIsValid(extension_id)); | |
| 830 ScopedExtensionPrefUpdate update(prefs_, extension_id); | |
| 831 SaveTime(update.Get(), kLastPingDay, time); | |
| 832 } | |
| 833 | |
| 834 base::Time ExtensionPrefs::BlacklistLastPingDay() const { | |
| 835 return ReadTime(prefs_->GetDictionary(kExtensionsBlacklistUpdate), | |
| 836 kLastPingDay); | |
| 837 } | |
| 838 | |
| 839 void ExtensionPrefs::SetBlacklistLastPingDay(const base::Time& time) { | |
| 840 DictionaryPrefUpdate update(prefs_, kExtensionsBlacklistUpdate); | |
| 841 SaveTime(update.Get(), kLastPingDay, time); | |
| 842 } | |
| 843 | |
| 844 base::Time ExtensionPrefs::LastActivePingDay(const std::string& extension_id) { | |
| 845 DCHECK(Extension::IdIsValid(extension_id)); | |
| 846 return ReadTime(GetExtensionPref(extension_id), kLastActivePingDay); | |
| 847 } | |
| 848 | |
| 849 void ExtensionPrefs::SetLastActivePingDay(const std::string& extension_id, | |
| 850 const base::Time& time) { | |
| 851 DCHECK(Extension::IdIsValid(extension_id)); | |
| 852 ScopedExtensionPrefUpdate update(prefs_, extension_id); | |
| 853 SaveTime(update.Get(), kLastActivePingDay, time); | |
| 854 } | |
| 855 | |
| 856 bool ExtensionPrefs::GetActiveBit(const std::string& extension_id) { | |
| 857 const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); | |
| 858 bool result = false; | |
| 859 if (dictionary && dictionary->GetBoolean(kActiveBit, &result)) | |
| 860 return result; | |
| 861 return false; | |
| 862 } | |
| 863 | |
| 864 void ExtensionPrefs::SetActiveBit(const std::string& extension_id, | |
| 865 bool active) { | |
| 866 UpdateExtensionPref(extension_id, kActiveBit, | |
| 867 new base::FundamentalValue(active)); | |
| 868 } | |
| 869 | |
| 870 void ExtensionPrefs::MigratePermissions(const ExtensionIdList& extension_ids) { | |
| 871 PermissionsInfo* info = PermissionsInfo::GetInstance(); | |
| 872 for (ExtensionIdList::const_iterator ext_id = | |
| 873 extension_ids.begin(); ext_id != extension_ids.end(); ++ext_id) { | |
| 874 | |
| 875 // An extension's granted permissions need to be migrated if the | |
| 876 // full_access bit is present. This bit was always present in the previous | |
| 877 // scheme and is never present now. | |
| 878 bool full_access; | |
| 879 const base::DictionaryValue* ext = GetExtensionPref(*ext_id); | |
| 880 if (!ext || !ext->GetBoolean(kPrefOldGrantedFullAccess, &full_access)) | |
| 881 continue; | |
| 882 | |
| 883 // Remove the full access bit (empty list will get trimmed). | |
| 884 UpdateExtensionPref( | |
| 885 *ext_id, kPrefOldGrantedFullAccess, new base::ListValue()); | |
| 886 | |
| 887 // Add the plugin permission if the full access bit was set. | |
| 888 if (full_access) { | |
| 889 const base::ListValue* apis = NULL; | |
| 890 base::ListValue* new_apis = NULL; | |
| 891 | |
| 892 std::string granted_apis = | |
| 893 JoinPrefs(kPrefGrantedPermissions, kPrefAPIs); | |
| 894 if (ext->GetList(kPrefOldGrantedAPIs, &apis)) | |
| 895 new_apis = apis->DeepCopy(); | |
| 896 else | |
| 897 new_apis = new base::ListValue(); | |
| 898 | |
| 899 std::string plugin_name = info->GetByID( | |
| 900 APIPermission::kPlugin)->name(); | |
| 901 new_apis->Append(new base::StringValue(plugin_name)); | |
| 902 UpdateExtensionPref(*ext_id, granted_apis, new_apis); | |
| 903 } | |
| 904 | |
| 905 // The granted permissions originally only held the effective hosts, | |
| 906 // which are a combination of host and user script host permissions. | |
| 907 // We now maintain these lists separately. For migration purposes, it | |
| 908 // does not matter how we treat the old effective hosts as long as the | |
| 909 // new effective hosts will be the same, so we move them to explicit | |
| 910 // host permissions. | |
| 911 const base::ListValue* hosts = NULL; | |
| 912 std::string explicit_hosts = | |
| 913 JoinPrefs(kPrefGrantedPermissions, kPrefExplicitHosts); | |
| 914 if (ext->GetList(kPrefOldGrantedHosts, &hosts)) { | |
| 915 UpdateExtensionPref( | |
| 916 *ext_id, explicit_hosts, hosts->DeepCopy()); | |
| 917 | |
| 918 // We can get rid of the old one by setting it to an empty list. | |
| 919 UpdateExtensionPref(*ext_id, kPrefOldGrantedHosts, new base::ListValue()); | |
| 920 } | |
| 921 } | |
| 922 } | |
| 923 | |
| 924 void ExtensionPrefs::MigrateDisableReasons( | |
| 925 const ExtensionIdList& extension_ids) { | |
| 926 for (ExtensionIdList::const_iterator ext_id = | |
| 927 extension_ids.begin(); ext_id != extension_ids.end(); ++ext_id) { | |
| 928 int value = -1; | |
| 929 if (ReadPrefAsInteger(*ext_id, kDeprecatedPrefDisableReason, &value)) { | |
| 930 int new_value = Extension::DISABLE_NONE; | |
| 931 switch (value) { | |
| 932 case Extension::DEPRECATED_DISABLE_USER_ACTION: | |
| 933 new_value = Extension::DISABLE_USER_ACTION; | |
| 934 break; | |
| 935 case Extension::DEPRECATED_DISABLE_PERMISSIONS_INCREASE: | |
| 936 new_value = Extension::DISABLE_PERMISSIONS_INCREASE; | |
| 937 break; | |
| 938 case Extension::DEPRECATED_DISABLE_RELOAD: | |
| 939 new_value = Extension::DISABLE_RELOAD; | |
| 940 break; | |
| 941 } | |
| 942 | |
| 943 UpdateExtensionPref(*ext_id, kPrefDisableReasons, | |
| 944 new base::FundamentalValue(new_value)); | |
| 945 // Remove the old disable reason. | |
| 946 UpdateExtensionPref(*ext_id, kDeprecatedPrefDisableReason, NULL); | |
| 947 } | |
| 948 } | |
| 949 } | |
| 950 | |
| 951 PermissionSet* ExtensionPrefs::GetGrantedPermissions( | |
| 952 const std::string& extension_id) { | |
| 953 CHECK(Extension::IdIsValid(extension_id)); | |
| 954 return ReadPrefAsPermissionSet(extension_id, kPrefGrantedPermissions); | |
| 955 } | |
| 956 | |
| 957 void ExtensionPrefs::AddGrantedPermissions( | |
| 958 const std::string& extension_id, | |
| 959 const PermissionSet* permissions) { | |
| 960 CHECK(Extension::IdIsValid(extension_id)); | |
| 961 | |
| 962 scoped_refptr<PermissionSet> granted_permissions( | |
| 963 GetGrantedPermissions(extension_id)); | |
| 964 | |
| 965 // The new granted permissions are the union of the already granted | |
| 966 // permissions and the newly granted permissions. | |
| 967 scoped_refptr<PermissionSet> new_perms( | |
| 968 PermissionSet::CreateUnion( | |
| 969 permissions, granted_permissions.get())); | |
| 970 | |
| 971 SetExtensionPrefPermissionSet( | |
| 972 extension_id, kPrefGrantedPermissions, new_perms.get()); | |
| 973 } | |
| 974 | |
| 975 void ExtensionPrefs::RemoveGrantedPermissions( | |
| 976 const std::string& extension_id, | |
| 977 const PermissionSet* permissions) { | |
| 978 CHECK(Extension::IdIsValid(extension_id)); | |
| 979 | |
| 980 scoped_refptr<PermissionSet> granted_permissions( | |
| 981 GetGrantedPermissions(extension_id)); | |
| 982 | |
| 983 // The new granted permissions are the difference of the already granted | |
| 984 // permissions and the newly ungranted permissions. | |
| 985 scoped_refptr<PermissionSet> new_perms( | |
| 986 PermissionSet::CreateDifference( | |
| 987 granted_permissions.get(), permissions)); | |
| 988 | |
| 989 SetExtensionPrefPermissionSet( | |
| 990 extension_id, kPrefGrantedPermissions, new_perms.get()); | |
| 991 } | |
| 992 | |
| 993 PermissionSet* ExtensionPrefs::GetActivePermissions( | |
| 994 const std::string& extension_id) { | |
| 995 CHECK(Extension::IdIsValid(extension_id)); | |
| 996 return ReadPrefAsPermissionSet(extension_id, kPrefActivePermissions); | |
| 997 } | |
| 998 | |
| 999 void ExtensionPrefs::SetActivePermissions( | |
| 1000 const std::string& extension_id, | |
| 1001 const PermissionSet* permissions) { | |
| 1002 SetExtensionPrefPermissionSet( | |
| 1003 extension_id, kPrefActivePermissions, permissions); | |
| 1004 } | |
| 1005 | |
| 1006 void ExtensionPrefs::SetExtensionRunning(const std::string& extension_id, | |
| 1007 bool is_running) { | |
| 1008 base::Value* value = new base::FundamentalValue(is_running); | |
| 1009 UpdateExtensionPref(extension_id, kPrefRunning, value); | |
| 1010 } | |
| 1011 | |
| 1012 bool ExtensionPrefs::IsExtensionRunning(const std::string& extension_id) { | |
| 1013 const base::DictionaryValue* extension = GetExtensionPref(extension_id); | |
| 1014 if (!extension) | |
| 1015 return false; | |
| 1016 bool running = false; | |
| 1017 extension->GetBoolean(kPrefRunning, &running); | |
| 1018 return running; | |
| 1019 } | |
| 1020 | |
| 1021 void ExtensionPrefs::SetIsActive(const std::string& extension_id, | |
| 1022 bool is_active) { | |
| 1023 base::Value* value = new base::FundamentalValue(is_active); | |
| 1024 UpdateExtensionPref(extension_id, kIsActive, value); | |
| 1025 } | |
| 1026 | |
| 1027 bool ExtensionPrefs::IsActive(const std::string& extension_id) { | |
| 1028 const base::DictionaryValue* extension = GetExtensionPref(extension_id); | |
| 1029 if (!extension) | |
| 1030 return false; | |
| 1031 bool is_active = false; | |
| 1032 extension->GetBoolean(kIsActive, &is_active); | |
| 1033 return is_active; | |
| 1034 } | |
| 1035 | |
| 1036 bool ExtensionPrefs::IsIncognitoEnabled(const std::string& extension_id) const { | |
| 1037 return ReadPrefAsBooleanAndReturn(extension_id, kPrefIncognitoEnabled); | |
| 1038 } | |
| 1039 | |
| 1040 void ExtensionPrefs::SetIsIncognitoEnabled(const std::string& extension_id, | |
| 1041 bool enabled) { | |
| 1042 UpdateExtensionPref(extension_id, kPrefIncognitoEnabled, | |
| 1043 new base::FundamentalValue(enabled)); | |
| 1044 } | |
| 1045 | |
| 1046 bool ExtensionPrefs::AllowFileAccess(const std::string& extension_id) const { | |
| 1047 return ReadPrefAsBooleanAndReturn(extension_id, kPrefAllowFileAccess); | |
| 1048 } | |
| 1049 | |
| 1050 void ExtensionPrefs::SetAllowFileAccess(const std::string& extension_id, | |
| 1051 bool allow) { | |
| 1052 UpdateExtensionPref(extension_id, kPrefAllowFileAccess, | |
| 1053 new base::FundamentalValue(allow)); | |
| 1054 } | |
| 1055 | |
| 1056 bool ExtensionPrefs::HasAllowFileAccessSetting( | |
| 1057 const std::string& extension_id) const { | |
| 1058 const base::DictionaryValue* ext = GetExtensionPref(extension_id); | |
| 1059 return ext && ext->HasKey(kPrefAllowFileAccess); | |
| 1060 } | |
| 1061 | |
| 1062 bool ExtensionPrefs::DoesExtensionHaveState( | |
| 1063 const std::string& id, Extension::State check_state) const { | |
| 1064 const base::DictionaryValue* extension = GetExtensionPref(id); | |
| 1065 int state = -1; | |
| 1066 if (!extension || !extension->GetInteger(kPrefState, &state)) | |
| 1067 return false; | |
| 1068 | |
| 1069 if (state < 0 || state >= Extension::NUM_STATES) { | |
| 1070 LOG(ERROR) << "Bad pref 'state' for extension '" << id << "'"; | |
| 1071 return false; | |
| 1072 } | |
| 1073 | |
| 1074 return state == check_state; | |
| 1075 } | |
| 1076 | |
| 1077 bool ExtensionPrefs::IsExternalExtensionUninstalled( | |
| 1078 const std::string& id) const { | |
| 1079 return DoesExtensionHaveState(id, Extension::EXTERNAL_EXTENSION_UNINSTALLED); | |
| 1080 } | |
| 1081 | |
| 1082 bool ExtensionPrefs::IsExtensionDisabled( | |
| 1083 const std::string& id) const { | |
| 1084 return DoesExtensionHaveState(id, Extension::DISABLED); | |
| 1085 } | |
| 1086 | |
| 1087 ExtensionIdList ExtensionPrefs::GetToolbarOrder() { | |
| 1088 ExtensionIdList id_list_out; | |
| 1089 GetUserExtensionPrefIntoContainer(pref_names::kToolbar, &id_list_out); | |
| 1090 return id_list_out; | |
| 1091 } | |
| 1092 | |
| 1093 void ExtensionPrefs::SetToolbarOrder(const ExtensionIdList& extension_ids) { | |
| 1094 SetExtensionPrefFromContainer(pref_names::kToolbar, extension_ids); | |
| 1095 } | |
| 1096 | |
| 1097 bool ExtensionPrefs::GetKnownDisabled(ExtensionIdSet* id_set_out) { | |
| 1098 return GetUserExtensionPrefIntoContainer(pref_names::kKnownDisabled, | |
| 1099 id_set_out); | |
| 1100 } | |
| 1101 | |
| 1102 void ExtensionPrefs::SetKnownDisabled(const ExtensionIdSet& extension_ids) { | |
| 1103 SetExtensionPrefFromContainer(pref_names::kKnownDisabled, extension_ids); | |
| 1104 } | |
| 1105 | |
| 1106 void ExtensionPrefs::OnExtensionInstalled( | |
| 1107 const Extension* extension, | |
| 1108 Extension::State initial_state, | |
| 1109 bool blacklisted_for_malware, | |
| 1110 const syncer::StringOrdinal& page_ordinal) { | |
| 1111 ScopedExtensionPrefUpdate update(prefs_, extension->id()); | |
| 1112 base::DictionaryValue* extension_dict = update.Get(); | |
| 1113 const base::Time install_time = time_provider_->GetCurrentTime(); | |
| 1114 PopulateExtensionInfoPrefs(extension, install_time, initial_state, | |
| 1115 blacklisted_for_malware, extension_dict); | |
| 1116 FinishExtensionInfoPrefs(extension->id(), install_time, | |
| 1117 extension->RequiresSortOrdinal(), | |
| 1118 page_ordinal, extension_dict); | |
| 1119 } | |
| 1120 | |
| 1121 void ExtensionPrefs::OnExtensionUninstalled(const std::string& extension_id, | |
| 1122 const Manifest::Location& location, | |
| 1123 bool external_uninstall) { | |
| 1124 app_sorting_->ClearOrdinals(extension_id); | |
| 1125 | |
| 1126 // For external extensions, we save a preference reminding ourself not to try | |
| 1127 // and install the extension anymore (except when |external_uninstall| is | |
| 1128 // true, which signifies that the registry key was deleted or the pref file | |
| 1129 // no longer lists the extension). | |
| 1130 if (!external_uninstall && Manifest::IsExternalLocation(location)) { | |
| 1131 UpdateExtensionPref(extension_id, kPrefState, | |
| 1132 new base::FundamentalValue( | |
| 1133 Extension::EXTERNAL_EXTENSION_UNINSTALLED)); | |
| 1134 extension_pref_value_map_->SetExtensionState(extension_id, false); | |
| 1135 content_settings_store_->SetExtensionState(extension_id, false); | |
| 1136 } else { | |
| 1137 DeleteExtensionPrefs(extension_id); | |
| 1138 } | |
| 1139 } | |
| 1140 | |
| 1141 void ExtensionPrefs::SetExtensionState(const std::string& extension_id, | |
| 1142 Extension::State state) { | |
| 1143 UpdateExtensionPref(extension_id, kPrefState, | |
| 1144 new base::FundamentalValue(state)); | |
| 1145 bool enabled = (state == Extension::ENABLED); | |
| 1146 extension_pref_value_map_->SetExtensionState(extension_id, enabled); | |
| 1147 content_settings_store_->SetExtensionState(extension_id, enabled); | |
| 1148 } | |
| 1149 | |
| 1150 std::string ExtensionPrefs::GetVersionString(const std::string& extension_id) { | |
| 1151 const base::DictionaryValue* extension = GetExtensionPref(extension_id); | |
| 1152 if (!extension) | |
| 1153 return std::string(); | |
| 1154 | |
| 1155 std::string version; | |
| 1156 extension->GetString(kPrefVersion, &version); | |
| 1157 | |
| 1158 return version; | |
| 1159 } | |
| 1160 | |
| 1161 void ExtensionPrefs::UpdateManifest(const Extension* extension) { | |
| 1162 if (!Manifest::IsUnpackedLocation(extension->location())) { | |
| 1163 const base::DictionaryValue* extension_dict = | |
| 1164 GetExtensionPref(extension->id()); | |
| 1165 if (!extension_dict) | |
| 1166 return; | |
| 1167 const base::DictionaryValue* old_manifest = NULL; | |
| 1168 bool update_required = | |
| 1169 !extension_dict->GetDictionary(kPrefManifest, &old_manifest) || | |
| 1170 !extension->manifest()->value()->Equals(old_manifest); | |
| 1171 if (update_required) { | |
| 1172 UpdateExtensionPref(extension->id(), kPrefManifest, | |
| 1173 extension->manifest()->value()->DeepCopy()); | |
| 1174 } | |
| 1175 } | |
| 1176 } | |
| 1177 | |
| 1178 base::FilePath ExtensionPrefs::GetExtensionPath( | |
| 1179 const std::string& extension_id) { | |
| 1180 const base::DictionaryValue* dict = GetExtensionPref(extension_id); | |
| 1181 if (!dict) | |
| 1182 return base::FilePath(); | |
| 1183 | |
| 1184 std::string path; | |
| 1185 if (!dict->GetString(kPrefPath, &path)) | |
| 1186 return base::FilePath(); | |
| 1187 | |
| 1188 return install_directory_.Append(base::FilePath::FromUTF8Unsafe(path)); | |
| 1189 } | |
| 1190 | |
| 1191 scoped_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledInfoHelper( | |
| 1192 const std::string& extension_id, | |
| 1193 const base::DictionaryValue* extension) const { | |
| 1194 int location_value; | |
| 1195 if (!extension->GetInteger(kPrefLocation, &location_value)) | |
| 1196 return scoped_ptr<ExtensionInfo>(); | |
| 1197 | |
| 1198 base::FilePath::StringType path; | |
| 1199 if (!extension->GetString(kPrefPath, &path)) | |
| 1200 return scoped_ptr<ExtensionInfo>(); | |
| 1201 | |
| 1202 // Make path absolute. Unpacked extensions will already have absolute paths, | |
| 1203 // otherwise make it so. | |
| 1204 Manifest::Location location = static_cast<Manifest::Location>(location_value); | |
| 1205 if (!Manifest::IsUnpackedLocation(location)) { | |
| 1206 DCHECK(location == Manifest::COMPONENT || | |
| 1207 !base::FilePath(path).IsAbsolute()); | |
| 1208 path = install_directory_.Append(path).value(); | |
| 1209 } | |
| 1210 | |
| 1211 // Only the following extension types have data saved in the preferences. | |
| 1212 if (location != Manifest::INTERNAL && | |
| 1213 !Manifest::IsUnpackedLocation(location) && | |
| 1214 !Manifest::IsExternalLocation(location)) { | |
| 1215 NOTREACHED(); | |
| 1216 return scoped_ptr<ExtensionInfo>(); | |
| 1217 } | |
| 1218 | |
| 1219 const base::DictionaryValue* manifest = NULL; | |
| 1220 if (!Manifest::IsUnpackedLocation(location) && | |
| 1221 !extension->GetDictionary(kPrefManifest, &manifest)) { | |
| 1222 LOG(WARNING) << "Missing manifest for extension " << extension_id; | |
| 1223 // Just a warning for now. | |
| 1224 } | |
| 1225 | |
| 1226 return scoped_ptr<ExtensionInfo>(new ExtensionInfo( | |
| 1227 manifest, extension_id, base::FilePath(path), location)); | |
| 1228 } | |
| 1229 | |
| 1230 scoped_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledExtensionInfo( | |
| 1231 const std::string& extension_id) const { | |
| 1232 const base::DictionaryValue* ext = NULL; | |
| 1233 const base::DictionaryValue* extensions = | |
| 1234 prefs_->GetDictionary(pref_names::kExtensions); | |
| 1235 if (!extensions || | |
| 1236 !extensions->GetDictionaryWithoutPathExpansion(extension_id, &ext)) | |
| 1237 return scoped_ptr<ExtensionInfo>(); | |
| 1238 int state_value; | |
| 1239 if (!ext->GetInteger(kPrefState, &state_value) || | |
| 1240 state_value == Extension::ENABLED_COMPONENT) { | |
| 1241 // Old preferences files may not have kPrefState for component extensions. | |
| 1242 return scoped_ptr<ExtensionInfo>(); | |
| 1243 } | |
| 1244 | |
| 1245 if (state_value == Extension::EXTERNAL_EXTENSION_UNINSTALLED) { | |
| 1246 LOG(WARNING) << "External extension with id " << extension_id | |
| 1247 << " has been uninstalled by the user"; | |
| 1248 return scoped_ptr<ExtensionInfo>(); | |
| 1249 } | |
| 1250 | |
| 1251 return GetInstalledInfoHelper(extension_id, ext); | |
| 1252 } | |
| 1253 | |
| 1254 scoped_ptr<ExtensionPrefs::ExtensionsInfo> | |
| 1255 ExtensionPrefs::GetInstalledExtensionsInfo() const { | |
| 1256 scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo); | |
| 1257 | |
| 1258 const base::DictionaryValue* extensions = | |
| 1259 prefs_->GetDictionary(pref_names::kExtensions); | |
| 1260 for (base::DictionaryValue::Iterator extension_id(*extensions); | |
| 1261 !extension_id.IsAtEnd(); extension_id.Advance()) { | |
| 1262 if (!Extension::IdIsValid(extension_id.key())) | |
| 1263 continue; | |
| 1264 | |
| 1265 scoped_ptr<ExtensionInfo> info = | |
| 1266 GetInstalledExtensionInfo(extension_id.key()); | |
| 1267 if (info) | |
| 1268 extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release())); | |
| 1269 } | |
| 1270 | |
| 1271 return extensions_info.Pass(); | |
| 1272 } | |
| 1273 | |
| 1274 scoped_ptr<ExtensionPrefs::ExtensionsInfo> | |
| 1275 ExtensionPrefs::GetUninstalledExtensionsInfo() const { | |
| 1276 scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo); | |
| 1277 | |
| 1278 const base::DictionaryValue* extensions = | |
| 1279 prefs_->GetDictionary(pref_names::kExtensions); | |
| 1280 for (base::DictionaryValue::Iterator extension_id(*extensions); | |
| 1281 !extension_id.IsAtEnd(); extension_id.Advance()) { | |
| 1282 const base::DictionaryValue* ext = NULL; | |
| 1283 if (!Extension::IdIsValid(extension_id.key()) || | |
| 1284 !IsExternalExtensionUninstalled(extension_id.key()) || | |
| 1285 !extension_id.value().GetAsDictionary(&ext)) | |
| 1286 continue; | |
| 1287 | |
| 1288 scoped_ptr<ExtensionInfo> info = | |
| 1289 GetInstalledInfoHelper(extension_id.key(), ext); | |
| 1290 if (info) | |
| 1291 extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release())); | |
| 1292 } | |
| 1293 | |
| 1294 return extensions_info.Pass(); | |
| 1295 } | |
| 1296 | |
| 1297 void ExtensionPrefs::SetDelayedInstallInfo( | |
| 1298 const Extension* extension, | |
| 1299 Extension::State initial_state, | |
| 1300 bool blacklisted_for_malware, | |
| 1301 DelayReason delay_reason, | |
| 1302 const syncer::StringOrdinal& page_ordinal) { | |
| 1303 base::DictionaryValue* extension_dict = new base::DictionaryValue(); | |
| 1304 PopulateExtensionInfoPrefs(extension, time_provider_->GetCurrentTime(), | |
| 1305 initial_state, blacklisted_for_malware, | |
| 1306 extension_dict); | |
| 1307 | |
| 1308 // Add transient data that is needed by FinishDelayedInstallInfo(), but | |
| 1309 // should not be in the final extension prefs. All entries here should have | |
| 1310 // a corresponding Remove() call in FinishDelayedInstallInfo(). | |
| 1311 if (extension->RequiresSortOrdinal()) { | |
| 1312 extension_dict->SetString( | |
| 1313 kPrefSuggestedPageOrdinal, | |
| 1314 page_ordinal.IsValid() ? page_ordinal.ToInternalValue() | |
| 1315 : std::string()); | |
| 1316 } | |
| 1317 extension_dict->SetInteger(kDelayedInstallReason, | |
| 1318 static_cast<int>(delay_reason)); | |
| 1319 | |
| 1320 UpdateExtensionPref(extension->id(), kDelayedInstallInfo, extension_dict); | |
| 1321 } | |
| 1322 | |
| 1323 bool ExtensionPrefs::RemoveDelayedInstallInfo( | |
| 1324 const std::string& extension_id) { | |
| 1325 if (!GetExtensionPref(extension_id)) | |
| 1326 return false; | |
| 1327 ScopedExtensionPrefUpdate update(prefs_, extension_id); | |
| 1328 bool result = update->Remove(kDelayedInstallInfo, NULL); | |
| 1329 return result; | |
| 1330 } | |
| 1331 | |
| 1332 bool ExtensionPrefs::FinishDelayedInstallInfo( | |
| 1333 const std::string& extension_id) { | |
| 1334 CHECK(Extension::IdIsValid(extension_id)); | |
| 1335 ScopedExtensionPrefUpdate update(prefs_, extension_id); | |
| 1336 base::DictionaryValue* extension_dict = update.Get(); | |
| 1337 base::DictionaryValue* pending_install_dict = NULL; | |
| 1338 if (!extension_dict->GetDictionary(kDelayedInstallInfo, | |
| 1339 &pending_install_dict)) { | |
| 1340 return false; | |
| 1341 } | |
| 1342 | |
| 1343 // Retrieve and clear transient values populated by SetDelayedInstallInfo(). | |
| 1344 // Also do any other data cleanup that makes sense. | |
| 1345 std::string serialized_ordinal; | |
| 1346 syncer::StringOrdinal suggested_page_ordinal; | |
| 1347 bool needs_sort_ordinal = false; | |
| 1348 if (pending_install_dict->GetString(kPrefSuggestedPageOrdinal, | |
| 1349 &serialized_ordinal)) { | |
| 1350 suggested_page_ordinal = syncer::StringOrdinal(serialized_ordinal); | |
| 1351 needs_sort_ordinal = true; | |
| 1352 pending_install_dict->Remove(kPrefSuggestedPageOrdinal, NULL); | |
| 1353 } | |
| 1354 pending_install_dict->Remove(kDelayedInstallReason, NULL); | |
| 1355 | |
| 1356 const base::Time install_time = time_provider_->GetCurrentTime(); | |
| 1357 pending_install_dict->Set( | |
| 1358 kPrefInstallTime, | |
| 1359 new base::StringValue( | |
| 1360 base::Int64ToString(install_time.ToInternalValue()))); | |
| 1361 | |
| 1362 // Commit the delayed install data. | |
| 1363 for (base::DictionaryValue::Iterator it(*pending_install_dict); !it.IsAtEnd(); | |
| 1364 it.Advance()) { | |
| 1365 extension_dict->Set(it.key(), it.value().DeepCopy()); | |
| 1366 } | |
| 1367 FinishExtensionInfoPrefs(extension_id, install_time, needs_sort_ordinal, | |
| 1368 suggested_page_ordinal, extension_dict); | |
| 1369 return true; | |
| 1370 } | |
| 1371 | |
| 1372 scoped_ptr<ExtensionInfo> ExtensionPrefs::GetDelayedInstallInfo( | |
| 1373 const std::string& extension_id) const { | |
| 1374 const base::DictionaryValue* extension_prefs = | |
| 1375 GetExtensionPref(extension_id); | |
| 1376 if (!extension_prefs) | |
| 1377 return scoped_ptr<ExtensionInfo>(); | |
| 1378 | |
| 1379 const base::DictionaryValue* ext = NULL; | |
| 1380 if (!extension_prefs->GetDictionary(kDelayedInstallInfo, &ext)) | |
| 1381 return scoped_ptr<ExtensionInfo>(); | |
| 1382 | |
| 1383 return GetInstalledInfoHelper(extension_id, ext); | |
| 1384 } | |
| 1385 | |
| 1386 ExtensionPrefs::DelayReason ExtensionPrefs::GetDelayedInstallReason( | |
| 1387 const std::string& extension_id) const { | |
| 1388 const base::DictionaryValue* extension_prefs = | |
| 1389 GetExtensionPref(extension_id); | |
| 1390 if (!extension_prefs) | |
| 1391 return DELAY_REASON_NONE; | |
| 1392 | |
| 1393 const base::DictionaryValue* ext = NULL; | |
| 1394 if (!extension_prefs->GetDictionary(kDelayedInstallInfo, &ext)) | |
| 1395 return DELAY_REASON_NONE; | |
| 1396 | |
| 1397 int delay_reason; | |
| 1398 if (!ext->GetInteger(kDelayedInstallReason, &delay_reason)) | |
| 1399 return DELAY_REASON_NONE; | |
| 1400 | |
| 1401 return static_cast<DelayReason>(delay_reason); | |
| 1402 } | |
| 1403 | |
| 1404 scoped_ptr<ExtensionPrefs::ExtensionsInfo> ExtensionPrefs:: | |
| 1405 GetAllDelayedInstallInfo() const { | |
| 1406 scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo); | |
| 1407 | |
| 1408 const base::DictionaryValue* extensions = | |
| 1409 prefs_->GetDictionary(pref_names::kExtensions); | |
| 1410 for (base::DictionaryValue::Iterator extension_id(*extensions); | |
| 1411 !extension_id.IsAtEnd(); extension_id.Advance()) { | |
| 1412 if (!Extension::IdIsValid(extension_id.key())) | |
| 1413 continue; | |
| 1414 | |
| 1415 scoped_ptr<ExtensionInfo> info = GetDelayedInstallInfo(extension_id.key()); | |
| 1416 if (info) | |
| 1417 extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release())); | |
| 1418 } | |
| 1419 | |
| 1420 return extensions_info.Pass(); | |
| 1421 } | |
| 1422 | |
| 1423 bool ExtensionPrefs::WasAppDraggedByUser(const std::string& extension_id) { | |
| 1424 return ReadPrefAsBooleanAndReturn(extension_id, kPrefUserDraggedApp); | |
| 1425 } | |
| 1426 | |
| 1427 void ExtensionPrefs::SetAppDraggedByUser(const std::string& extension_id) { | |
| 1428 UpdateExtensionPref(extension_id, kPrefUserDraggedApp, | |
| 1429 new base::FundamentalValue(true)); | |
| 1430 } | |
| 1431 | |
| 1432 bool ExtensionPrefs::IsFromWebStore( | |
| 1433 const std::string& extension_id) const { | |
| 1434 const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); | |
| 1435 bool result = false; | |
| 1436 if (dictionary && dictionary->GetBoolean(kPrefFromWebStore, &result)) | |
| 1437 return result; | |
| 1438 return false; | |
| 1439 } | |
| 1440 | |
| 1441 bool ExtensionPrefs::IsFromBookmark( | |
| 1442 const std::string& extension_id) const { | |
| 1443 const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); | |
| 1444 bool result = false; | |
| 1445 if (dictionary && dictionary->GetBoolean(kPrefFromBookmark, &result)) | |
| 1446 return result; | |
| 1447 return false; | |
| 1448 } | |
| 1449 | |
| 1450 int ExtensionPrefs::GetCreationFlags(const std::string& extension_id) const { | |
| 1451 int creation_flags = Extension::NO_FLAGS; | |
| 1452 if (!ReadPrefAsInteger(extension_id, kPrefCreationFlags, &creation_flags)) { | |
| 1453 // Since kPrefCreationFlags was added later, it will be missing for | |
| 1454 // previously installed extensions. | |
| 1455 if (IsFromBookmark(extension_id)) | |
| 1456 creation_flags |= Extension::FROM_BOOKMARK; | |
| 1457 if (IsFromWebStore(extension_id)) | |
| 1458 creation_flags |= Extension::FROM_WEBSTORE; | |
| 1459 if (WasInstalledByDefault(extension_id)) | |
| 1460 creation_flags |= Extension::WAS_INSTALLED_BY_DEFAULT; | |
| 1461 } | |
| 1462 return creation_flags; | |
| 1463 } | |
| 1464 | |
| 1465 int ExtensionPrefs::GetDelayedInstallCreationFlags( | |
| 1466 const std::string& extension_id) const { | |
| 1467 int creation_flags = Extension::NO_FLAGS; | |
| 1468 const base::DictionaryValue* delayed_info = NULL; | |
| 1469 if (ReadPrefAsDictionary(extension_id, kDelayedInstallInfo, &delayed_info)) { | |
| 1470 delayed_info->GetInteger(kPrefCreationFlags, &creation_flags); | |
| 1471 } | |
| 1472 return creation_flags; | |
| 1473 } | |
| 1474 | |
| 1475 bool ExtensionPrefs::WasInstalledByDefault( | |
| 1476 const std::string& extension_id) const { | |
| 1477 const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); | |
| 1478 bool result = false; | |
| 1479 if (dictionary && | |
| 1480 dictionary->GetBoolean(kPrefWasInstalledByDefault, &result)) | |
| 1481 return result; | |
| 1482 return false; | |
| 1483 } | |
| 1484 | |
| 1485 base::Time ExtensionPrefs::GetInstallTime( | |
| 1486 const std::string& extension_id) const { | |
| 1487 const base::DictionaryValue* extension = GetExtensionPref(extension_id); | |
| 1488 if (!extension) { | |
| 1489 NOTREACHED(); | |
| 1490 return base::Time(); | |
| 1491 } | |
| 1492 std::string install_time_str; | |
| 1493 if (!extension->GetString(kPrefInstallTime, &install_time_str)) | |
| 1494 return base::Time(); | |
| 1495 int64 install_time_i64 = 0; | |
| 1496 if (!base::StringToInt64(install_time_str, &install_time_i64)) | |
| 1497 return base::Time(); | |
| 1498 return base::Time::FromInternalValue(install_time_i64); | |
| 1499 } | |
| 1500 | |
| 1501 base::Time ExtensionPrefs::GetLastLaunchTime( | |
| 1502 const std::string& extension_id) const { | |
| 1503 const base::DictionaryValue* extension = GetExtensionPref(extension_id); | |
| 1504 if (!extension) | |
| 1505 return base::Time(); | |
| 1506 | |
| 1507 std::string launch_time_str; | |
| 1508 if (!extension->GetString(kPrefLastLaunchTime, &launch_time_str)) | |
| 1509 return base::Time(); | |
| 1510 int64 launch_time_i64 = 0; | |
| 1511 if (!base::StringToInt64(launch_time_str, &launch_time_i64)) | |
| 1512 return base::Time(); | |
| 1513 return base::Time::FromInternalValue(launch_time_i64); | |
| 1514 } | |
| 1515 | |
| 1516 void ExtensionPrefs::SetLastLaunchTime(const std::string& extension_id, | |
| 1517 const base::Time& time) { | |
| 1518 DCHECK(Extension::IdIsValid(extension_id)); | |
| 1519 ScopedExtensionPrefUpdate update(prefs_, extension_id); | |
| 1520 SaveTime(update.Get(), kPrefLastLaunchTime, time); | |
| 1521 } | |
| 1522 | |
| 1523 void ExtensionPrefs::GetExtensions(ExtensionIdList* out) { | |
| 1524 CHECK(out); | |
| 1525 | |
| 1526 scoped_ptr<ExtensionsInfo> extensions_info(GetInstalledExtensionsInfo()); | |
| 1527 | |
| 1528 for (size_t i = 0; i < extensions_info->size(); ++i) { | |
| 1529 ExtensionInfo* info = extensions_info->at(i).get(); | |
| 1530 out->push_back(info->extension_id); | |
| 1531 } | |
| 1532 } | |
| 1533 | |
| 1534 // static | |
| 1535 ExtensionIdList ExtensionPrefs::GetExtensionsFrom( | |
| 1536 const PrefService* pref_service) { | |
| 1537 ExtensionIdList result; | |
| 1538 | |
| 1539 const base::DictionaryValue* extension_prefs = NULL; | |
| 1540 const base::Value* extension_prefs_value = | |
| 1541 pref_service->GetUserPrefValue(pref_names::kExtensions); | |
| 1542 if (!extension_prefs_value || | |
| 1543 !extension_prefs_value->GetAsDictionary(&extension_prefs)) { | |
| 1544 return result; // Empty set | |
| 1545 } | |
| 1546 | |
| 1547 for (base::DictionaryValue::Iterator it(*extension_prefs); !it.IsAtEnd(); | |
| 1548 it.Advance()) { | |
| 1549 const base::DictionaryValue* ext = NULL; | |
| 1550 if (!it.value().GetAsDictionary(&ext)) { | |
| 1551 NOTREACHED() << "Invalid pref for extension " << it.key(); | |
| 1552 continue; | |
| 1553 } | |
| 1554 if (!IsBlacklistBitSet(ext)) | |
| 1555 result.push_back(it.key()); | |
| 1556 } | |
| 1557 return result; | |
| 1558 } | |
| 1559 | |
| 1560 void ExtensionPrefs::FixMissingPrefs(const ExtensionIdList& extension_ids) { | |
| 1561 // Fix old entries that did not get an installation time entry when they | |
| 1562 // were installed or don't have a preferences field. | |
| 1563 for (ExtensionIdList::const_iterator ext_id = extension_ids.begin(); | |
| 1564 ext_id != extension_ids.end(); ++ext_id) { | |
| 1565 if (GetInstallTime(*ext_id) == base::Time()) { | |
| 1566 VLOG(1) << "Could not parse installation time of extension " | |
| 1567 << *ext_id << ". It was probably installed before setting " | |
| 1568 << kPrefInstallTime << " was introduced. Updating " | |
| 1569 << kPrefInstallTime << " to the current time."; | |
| 1570 const base::Time install_time = time_provider_->GetCurrentTime(); | |
| 1571 UpdateExtensionPref(*ext_id, | |
| 1572 kPrefInstallTime, | |
| 1573 new base::StringValue(base::Int64ToString( | |
| 1574 install_time.ToInternalValue()))); | |
| 1575 } | |
| 1576 } | |
| 1577 } | |
| 1578 | |
| 1579 void ExtensionPrefs::InitPrefStore() { | |
| 1580 if (extensions_disabled_) { | |
| 1581 extension_pref_value_map_->NotifyInitializationCompleted(); | |
| 1582 return; | |
| 1583 } | |
| 1584 | |
| 1585 // When this is called, the PrefService is initialized and provides access | |
| 1586 // to the user preferences stored in a JSON file. | |
| 1587 ExtensionIdList extension_ids; | |
| 1588 GetExtensions(&extension_ids); | |
| 1589 // Create empty preferences dictionary for each extension (these dictionaries | |
| 1590 // are pruned when persisting the preferences to disk). | |
| 1591 for (ExtensionIdList::iterator ext_id = extension_ids.begin(); | |
| 1592 ext_id != extension_ids.end(); ++ext_id) { | |
| 1593 ScopedExtensionPrefUpdate update(prefs_, *ext_id); | |
| 1594 // This creates an empty dictionary if none is stored. | |
| 1595 update.Get(); | |
| 1596 } | |
| 1597 | |
| 1598 FixMissingPrefs(extension_ids); | |
| 1599 MigratePermissions(extension_ids); | |
| 1600 MigrateDisableReasons(extension_ids); | |
| 1601 app_sorting_->Initialize(extension_ids); | |
| 1602 | |
| 1603 PreferenceAPI::InitExtensionControlledPrefs(this, extension_pref_value_map_); | |
| 1604 | |
| 1605 extension_pref_value_map_->NotifyInitializationCompleted(); | |
| 1606 } | |
| 1607 | |
| 1608 bool ExtensionPrefs::HasIncognitoPrefValue(const std::string& pref_key) { | |
| 1609 bool has_incognito_pref_value = false; | |
| 1610 extension_pref_value_map_->GetEffectivePrefValue(pref_key, | |
| 1611 true, | |
| 1612 &has_incognito_pref_value); | |
| 1613 return has_incognito_pref_value; | |
| 1614 } | |
| 1615 | |
| 1616 URLPatternSet ExtensionPrefs::GetAllowedInstallSites() { | |
| 1617 URLPatternSet result; | |
| 1618 const base::ListValue* list = | |
| 1619 prefs_->GetList(pref_names::kAllowedInstallSites); | |
| 1620 CHECK(list); | |
| 1621 | |
| 1622 for (size_t i = 0; i < list->GetSize(); ++i) { | |
| 1623 std::string entry_string; | |
| 1624 URLPattern entry(URLPattern::SCHEME_ALL); | |
| 1625 if (!list->GetString(i, &entry_string) || | |
| 1626 entry.Parse(entry_string) != URLPattern::PARSE_SUCCESS) { | |
| 1627 LOG(ERROR) << "Invalid value for preference: " | |
| 1628 << pref_names::kAllowedInstallSites << "." << i; | |
| 1629 continue; | |
| 1630 } | |
| 1631 result.AddPattern(entry); | |
| 1632 } | |
| 1633 | |
| 1634 return result; | |
| 1635 } | |
| 1636 | |
| 1637 const base::DictionaryValue* ExtensionPrefs::GetGeometryCache( | |
| 1638 const std::string& extension_id) const { | |
| 1639 const base::DictionaryValue* extension_prefs = GetExtensionPref(extension_id); | |
| 1640 if (!extension_prefs) | |
| 1641 return NULL; | |
| 1642 | |
| 1643 const base::DictionaryValue* ext = NULL; | |
| 1644 if (!extension_prefs->GetDictionary(kPrefGeometryCache, &ext)) | |
| 1645 return NULL; | |
| 1646 | |
| 1647 return ext; | |
| 1648 } | |
| 1649 | |
| 1650 void ExtensionPrefs::SetGeometryCache( | |
| 1651 const std::string& extension_id, | |
| 1652 scoped_ptr<base::DictionaryValue> cache) { | |
| 1653 UpdateExtensionPref(extension_id, kPrefGeometryCache, cache.release()); | |
| 1654 } | |
| 1655 | |
| 1656 const base::DictionaryValue* ExtensionPrefs::GetInstallSignature() { | |
| 1657 return prefs_->GetDictionary(kInstallSignature); | |
| 1658 } | |
| 1659 | |
| 1660 void ExtensionPrefs::SetInstallSignature( | |
| 1661 const base::DictionaryValue* signature) { | |
| 1662 if (signature) { | |
| 1663 prefs_->Set(kInstallSignature, *signature); | |
| 1664 DVLOG(1) << "SetInstallSignature - saving"; | |
| 1665 } else { | |
| 1666 DVLOG(1) << "SetInstallSignature - clearing"; | |
| 1667 prefs_->ClearPref(kInstallSignature); | |
| 1668 } | |
| 1669 } | |
| 1670 | |
| 1671 | |
| 1672 ExtensionPrefs::ExtensionPrefs( | |
| 1673 PrefService* prefs, | |
| 1674 const base::FilePath& root_dir, | |
| 1675 ExtensionPrefValueMap* extension_pref_value_map, | |
| 1676 scoped_ptr<AppSorting> app_sorting, | |
| 1677 scoped_ptr<TimeProvider> time_provider, | |
| 1678 bool extensions_disabled) | |
| 1679 : prefs_(prefs), | |
| 1680 install_directory_(root_dir), | |
| 1681 extension_pref_value_map_(extension_pref_value_map), | |
| 1682 app_sorting_(app_sorting.Pass()), | |
| 1683 content_settings_store_(new ContentSettingsStore()), | |
| 1684 time_provider_(time_provider.Pass()), | |
| 1685 extensions_disabled_(extensions_disabled) { | |
| 1686 app_sorting_->SetExtensionScopedPrefs(this), | |
| 1687 MakePathsRelative(); | |
| 1688 InitPrefStore(); | |
| 1689 } | |
| 1690 | |
| 1691 void ExtensionPrefs::SetNeedsStorageGarbageCollection(bool value) { | |
| 1692 prefs_->SetBoolean(pref_names::kStorageGarbageCollect, value); | |
| 1693 } | |
| 1694 | |
| 1695 bool ExtensionPrefs::NeedsStorageGarbageCollection() { | |
| 1696 return prefs_->GetBoolean(pref_names::kStorageGarbageCollect); | |
| 1697 } | |
| 1698 | |
| 1699 // static | |
| 1700 void ExtensionPrefs::RegisterProfilePrefs( | |
| 1701 user_prefs::PrefRegistrySyncable* registry) { | |
| 1702 registry->RegisterDictionaryPref( | |
| 1703 pref_names::kExtensions, | |
| 1704 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1705 registry->RegisterListPref(pref_names::kToolbar, | |
| 1706 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); | |
| 1707 registry->RegisterIntegerPref( | |
| 1708 pref_names::kToolbarSize, | |
| 1709 -1, // default value | |
| 1710 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1711 registry->RegisterDictionaryPref( | |
| 1712 kExtensionsBlacklistUpdate, | |
| 1713 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1714 registry->RegisterListPref(pref_names::kInstallAllowList, | |
| 1715 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1716 registry->RegisterListPref(pref_names::kInstallDenyList, | |
| 1717 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1718 registry->RegisterDictionaryPref( | |
| 1719 pref_names::kInstallForceList, | |
| 1720 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1721 registry->RegisterListPref(pref_names::kAllowedTypes, | |
| 1722 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1723 registry->RegisterBooleanPref( | |
| 1724 pref_names::kStorageGarbageCollect, | |
| 1725 false, // default value | |
| 1726 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1727 registry->RegisterInt64Pref( | |
| 1728 pref_names::kLastUpdateCheck, | |
| 1729 0, // default value | |
| 1730 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1731 registry->RegisterInt64Pref( | |
| 1732 pref_names::kNextUpdateCheck, | |
| 1733 0, // default value | |
| 1734 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1735 registry->RegisterListPref(pref_names::kAllowedInstallSites, | |
| 1736 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1737 registry->RegisterStringPref( | |
| 1738 pref_names::kLastChromeVersion, | |
| 1739 std::string(), // default value | |
| 1740 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1741 registry->RegisterListPref(pref_names::kKnownDisabled, | |
| 1742 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1743 #if defined(TOOLKIT_VIEWS) | |
| 1744 registry->RegisterIntegerPref( | |
| 1745 pref_names::kBrowserActionContainerWidth, | |
| 1746 0, | |
| 1747 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1748 #endif | |
| 1749 registry->RegisterDictionaryPref( | |
| 1750 kInstallSignature, | |
| 1751 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1752 | |
| 1753 registry->RegisterListPref(pref_names::kNativeMessagingBlacklist, | |
| 1754 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1755 registry->RegisterListPref(pref_names::kNativeMessagingWhitelist, | |
| 1756 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
| 1757 } | |
| 1758 | |
| 1759 template <class ExtensionIdContainer> | |
| 1760 bool ExtensionPrefs::GetUserExtensionPrefIntoContainer( | |
| 1761 const char* pref, | |
| 1762 ExtensionIdContainer* id_container_out) { | |
| 1763 DCHECK(id_container_out->empty()); | |
| 1764 | |
| 1765 const base::Value* user_pref_value = prefs_->GetUserPrefValue(pref); | |
| 1766 const base::ListValue* user_pref_as_list; | |
| 1767 if (!user_pref_value || !user_pref_value->GetAsList(&user_pref_as_list)) | |
| 1768 return false; | |
| 1769 | |
| 1770 std::insert_iterator<ExtensionIdContainer> insert_iterator( | |
| 1771 *id_container_out, id_container_out->end()); | |
| 1772 std::string extension_id; | |
| 1773 for (base::ListValue::const_iterator value_it = user_pref_as_list->begin(); | |
| 1774 value_it != user_pref_as_list->end(); ++value_it) { | |
| 1775 if (!(*value_it)->GetAsString(&extension_id)) { | |
| 1776 NOTREACHED(); | |
| 1777 continue; | |
| 1778 } | |
| 1779 insert_iterator = extension_id; | |
| 1780 } | |
| 1781 return true; | |
| 1782 } | |
| 1783 | |
| 1784 template <class ExtensionIdContainer> | |
| 1785 void ExtensionPrefs::SetExtensionPrefFromContainer( | |
| 1786 const char* pref, | |
| 1787 const ExtensionIdContainer& strings) { | |
| 1788 ListPrefUpdate update(prefs_, pref); | |
| 1789 base::ListValue* list_of_values = update.Get(); | |
| 1790 list_of_values->Clear(); | |
| 1791 for (typename ExtensionIdContainer::const_iterator iter = strings.begin(); | |
| 1792 iter != strings.end(); ++iter) { | |
| 1793 list_of_values->Append(new base::StringValue(*iter)); | |
| 1794 } | |
| 1795 } | |
| 1796 | |
| 1797 void ExtensionPrefs::PopulateExtensionInfoPrefs( | |
| 1798 const Extension* extension, | |
| 1799 const base::Time install_time, | |
| 1800 Extension::State initial_state, | |
| 1801 bool blacklisted_for_malware, | |
| 1802 base::DictionaryValue* extension_dict) { | |
| 1803 // Leave the state blank for component extensions so that old chrome versions | |
| 1804 // loading new profiles do not fail in GetInstalledExtensionInfo. Older | |
| 1805 // Chrome versions would only check for an omitted state. | |
| 1806 if (initial_state != Extension::ENABLED_COMPONENT) | |
| 1807 extension_dict->Set(kPrefState, new base::FundamentalValue(initial_state)); | |
| 1808 | |
| 1809 extension_dict->Set(kPrefLocation, | |
| 1810 new base::FundamentalValue(extension->location())); | |
| 1811 extension_dict->Set(kPrefCreationFlags, | |
| 1812 new base::FundamentalValue(extension->creation_flags())); | |
| 1813 extension_dict->Set(kPrefFromWebStore, | |
| 1814 new base::FundamentalValue(extension->from_webstore())); | |
| 1815 extension_dict->Set(kPrefFromBookmark, | |
| 1816 new base::FundamentalValue(extension->from_bookmark())); | |
| 1817 extension_dict->Set( | |
| 1818 kPrefWasInstalledByDefault, | |
| 1819 new base::FundamentalValue(extension->was_installed_by_default())); | |
| 1820 extension_dict->Set(kPrefInstallTime, | |
| 1821 new base::StringValue( | |
| 1822 base::Int64ToString(install_time.ToInternalValue()))); | |
| 1823 if (blacklisted_for_malware) | |
| 1824 extension_dict->Set(kPrefBlacklist, new base::FundamentalValue(true)); | |
| 1825 | |
| 1826 base::FilePath::StringType path = MakePathRelative(install_directory_, | |
| 1827 extension->path()); | |
| 1828 extension_dict->Set(kPrefPath, new base::StringValue(path)); | |
| 1829 // We store prefs about LOAD extensions, but don't cache their manifest | |
| 1830 // since it may change on disk. | |
| 1831 if (!Manifest::IsUnpackedLocation(extension->location())) { | |
| 1832 extension_dict->Set(kPrefManifest, | |
| 1833 extension->manifest()->value()->DeepCopy()); | |
| 1834 } | |
| 1835 } | |
| 1836 | |
| 1837 void ExtensionPrefs::FinishExtensionInfoPrefs( | |
| 1838 const std::string& extension_id, | |
| 1839 const base::Time install_time, | |
| 1840 bool needs_sort_ordinal, | |
| 1841 const syncer::StringOrdinal& suggested_page_ordinal, | |
| 1842 base::DictionaryValue* extension_dict) { | |
| 1843 // Reinitializes various preferences with empty dictionaries. | |
| 1844 if (!extension_dict->HasKey(pref_names::kPrefPreferences)) { | |
| 1845 extension_dict->Set(pref_names::kPrefPreferences, | |
| 1846 new base::DictionaryValue); | |
| 1847 } | |
| 1848 | |
| 1849 if (!extension_dict->HasKey(pref_names::kPrefIncognitoPreferences)) { | |
| 1850 extension_dict->Set(pref_names::kPrefIncognitoPreferences, | |
| 1851 new base::DictionaryValue); | |
| 1852 } | |
| 1853 | |
| 1854 if (!extension_dict->HasKey(pref_names::kPrefRegularOnlyPreferences)) { | |
| 1855 extension_dict->Set(pref_names::kPrefRegularOnlyPreferences, | |
| 1856 new base::DictionaryValue); | |
| 1857 } | |
| 1858 | |
| 1859 if (!extension_dict->HasKey(pref_names::kPrefContentSettings)) | |
| 1860 extension_dict->Set(pref_names::kPrefContentSettings, new base::ListValue); | |
| 1861 | |
| 1862 if (!extension_dict->HasKey(pref_names::kPrefIncognitoContentSettings)) { | |
| 1863 extension_dict->Set(pref_names::kPrefIncognitoContentSettings, | |
| 1864 new base::ListValue); | |
| 1865 } | |
| 1866 | |
| 1867 // If this point has been reached, any pending installs should be considered | |
| 1868 // out of date. | |
| 1869 extension_dict->Remove(kDelayedInstallInfo, NULL); | |
| 1870 | |
| 1871 // Clear state that may be registered from a previous install. | |
| 1872 extension_dict->Remove(EventRouter::kRegisteredEvents, NULL); | |
| 1873 | |
| 1874 // FYI, all code below here races on sudden shutdown because |extension_dict|, | |
| 1875 // |app_sorting_|, |extension_pref_value_map_|, and |content_settings_store_| | |
| 1876 // are updated non-transactionally. This is probably not fixable without | |
| 1877 // nested transactional updates to pref dictionaries. | |
| 1878 if (needs_sort_ordinal) | |
| 1879 app_sorting_->EnsureValidOrdinals(extension_id, suggested_page_ordinal); | |
| 1880 | |
| 1881 bool is_enabled = false; | |
| 1882 int initial_state; | |
| 1883 if (extension_dict->GetInteger(kPrefState, &initial_state)) { | |
| 1884 is_enabled = initial_state == Extension::ENABLED; | |
| 1885 } | |
| 1886 | |
| 1887 extension_pref_value_map_->RegisterExtension(extension_id, install_time, | |
| 1888 is_enabled); | |
| 1889 content_settings_store_->RegisterExtension(extension_id, install_time, | |
| 1890 is_enabled); | |
| 1891 } | |
| 1892 | |
| 1893 } // namespace extensions | |
| OLD | NEW |