[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 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/external_extension_provider_impl.h" |
| 6 | |
[email protected] | 83b59325a | 2011-10-14 15:58:07 | [diff] [blame] | 7 | #include "base/command_line.h" |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 8 | #include "base/file_path.h" |
| 9 | #include "base/logging.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 10 | #include "base/memory/linked_ptr.h" |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 11 | #include "base/metrics/field_trial.h" |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 12 | #include "base/path_service.h" |
[email protected] | e601e82 | 2011-10-05 19:25:37 | [diff] [blame] | 13 | #include "base/string_util.h" |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 14 | #include "base/values.h" |
| 15 | #include "base/version.h" |
[email protected] | e601e82 | 2011-10-05 19:25:37 | [diff] [blame] | 16 | #include "chrome/browser/browser_process.h" |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 17 | #include "chrome/browser/extensions/extension_service.h" |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 18 | #include "chrome/browser/extensions/external_extension_provider_interface.h" |
| 19 | #include "chrome/browser/extensions/external_policy_extension_loader.h" |
| 20 | #include "chrome/browser/extensions/external_pref_extension_loader.h" |
| 21 | #include "chrome/browser/profiles/profile.h" |
[email protected] | f0841cd | 2011-01-19 15:07:24 | [diff] [blame] | 22 | #include "chrome/common/chrome_paths.h" |
[email protected] | 83b59325a | 2011-10-14 15:58:07 | [diff] [blame] | 23 | #include "chrome/common/chrome_switches.h" |
| 24 | #include "chrome/common/pref_names.h" |
[email protected] | c38831a1 | 2011-10-28 12:44:49 | [diff] [blame] | 25 | #include "content/public/browser/browser_thread.h" |
[email protected] | 9d32ded07 | 2011-10-11 16:31:05 | [diff] [blame] | 26 | #include "ui/base/l10n/l10n_util.h" |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 27 | |
[email protected] | 86c6b9e3 | 2011-10-25 17:09:10 | [diff] [blame] | 28 | #if !defined(OS_CHROMEOS) |
| 29 | #include "chrome/browser/extensions/default_apps.h" |
| 30 | #endif |
| 31 | |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 32 | #if defined(OS_WIN) |
| 33 | #include "chrome/browser/extensions/external_registry_extension_loader_win.h" |
| 34 | #endif |
| 35 | |
[email protected] | 631bb74 | 2011-11-02 11:29:39 | [diff] [blame] | 36 | using content::BrowserThread; |
| 37 | |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 38 | // Constants for keeping track of extension preferences in a dictionary. |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 39 | const char ExternalExtensionProviderImpl::kExternalCrx[] = "external_crx"; |
| 40 | const char ExternalExtensionProviderImpl::kExternalVersion[] = |
| 41 | "external_version"; |
| 42 | const char ExternalExtensionProviderImpl::kExternalUpdateUrl[] = |
| 43 | "external_update_url"; |
[email protected] | 9d32ded07 | 2011-10-11 16:31:05 | [diff] [blame] | 44 | const char ExternalExtensionProviderImpl::kSupportedLocales[] = |
| 45 | "supported_locales"; |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 46 | |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 47 | ExternalExtensionProviderImpl::ExternalExtensionProviderImpl( |
| 48 | VisitorInterface* service, |
| 49 | ExternalExtensionLoader* loader, |
| 50 | Extension::Location crx_location, |
[email protected] | 1bf73cc | 2011-10-26 22:38:31 | [diff] [blame] | 51 | Extension::Location download_location, |
| 52 | int creation_flags) |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 53 | : crx_location_(crx_location), |
| 54 | download_location_(download_location), |
| 55 | service_(service), |
| 56 | prefs_(NULL), |
| 57 | ready_(false), |
[email protected] | 1bf73cc | 2011-10-26 22:38:31 | [diff] [blame] | 58 | loader_(loader), |
[email protected] | 47fc70c | 2011-12-06 07:29:51 | [diff] [blame^] | 59 | creation_flags_(creation_flags), |
| 60 | auto_acknowledge_(false) { |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 61 | loader_->Init(this); |
| 62 | } |
| 63 | |
| 64 | ExternalExtensionProviderImpl::~ExternalExtensionProviderImpl() { |
| 65 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 66 | loader_->OwnerShutdown(); |
| 67 | } |
| 68 | |
[email protected] | d190cef | 2011-11-09 02:09:24 | [diff] [blame] | 69 | void ExternalExtensionProviderImpl::VisitRegisteredExtension() { |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 70 | // The loader will call back to SetPrefs. |
| 71 | loader_->StartLoading(); |
| 72 | } |
| 73 | |
| 74 | void ExternalExtensionProviderImpl::SetPrefs(DictionaryValue* prefs) { |
| 75 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 76 | |
[email protected] | 47fc70c | 2011-12-06 07:29:51 | [diff] [blame^] | 77 | // Check if the service is still alive. It is possible that it went |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 78 | // away while |loader_| was working on the FILE thread. |
| 79 | if (!service_) return; |
| 80 | |
| 81 | prefs_.reset(prefs); |
| 82 | ready_ = true; // Queries for extensions are allowed from this point. |
| 83 | |
[email protected] | 9d32ded07 | 2011-10-11 16:31:05 | [diff] [blame] | 84 | // Set of unsupported extensions that need to be deleted from prefs_. |
| 85 | std::set<std::string> unsupported_extensions; |
| 86 | |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 87 | // Notify ExtensionService about all the extensions this provider has. |
| 88 | for (DictionaryValue::key_iterator i = prefs_->begin_keys(); |
| 89 | i != prefs_->end_keys(); ++i) { |
| 90 | const std::string& extension_id = *i; |
| 91 | DictionaryValue* extension; |
[email protected] | ab22ba4 | 2011-01-14 16:36:38 | [diff] [blame] | 92 | |
| 93 | if (!Extension::IdIsValid(extension_id)) { |
| 94 | LOG(WARNING) << "Malformed extension dictionary: key " |
| 95 | << extension_id.c_str() << " is not a valid id."; |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 96 | continue; |
[email protected] | ab22ba4 | 2011-01-14 16:36:38 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) { |
| 100 | LOG(WARNING) << "Malformed extension dictionary: key " |
| 101 | << extension_id.c_str() |
| 102 | << " has a value that is not a dictionary."; |
| 103 | continue; |
| 104 | } |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 105 | |
| 106 | FilePath::StringType external_crx; |
| 107 | std::string external_version; |
| 108 | std::string external_update_url; |
| 109 | |
| 110 | bool has_external_crx = extension->GetString(kExternalCrx, &external_crx); |
| 111 | bool has_external_version = extension->GetString(kExternalVersion, |
| 112 | &external_version); |
| 113 | bool has_external_update_url = extension->GetString(kExternalUpdateUrl, |
| 114 | &external_update_url); |
| 115 | if (has_external_crx != has_external_version) { |
| 116 | LOG(WARNING) << "Malformed extension dictionary for extension: " |
| 117 | << extension_id.c_str() << ". " << kExternalCrx |
| 118 | << " and " << kExternalVersion << " must be used together."; |
| 119 | continue; |
| 120 | } |
| 121 | |
| 122 | if (has_external_crx == has_external_update_url) { |
| 123 | LOG(WARNING) << "Malformed extension dictionary for extension: " |
| 124 | << extension_id.c_str() << ". Exactly one of the " |
| 125 | << "followng keys should be used: " << kExternalCrx |
| 126 | << ", " << kExternalUpdateUrl << "."; |
| 127 | continue; |
| 128 | } |
| 129 | |
[email protected] | 9d32ded07 | 2011-10-11 16:31:05 | [diff] [blame] | 130 | // Check that extension supports current browser locale. |
| 131 | ListValue* supported_locales = NULL; |
| 132 | if (extension->GetList(kSupportedLocales, &supported_locales)) { |
| 133 | std::vector<std::string> browser_locales; |
| 134 | l10n_util::GetParentLocales(g_browser_process->GetApplicationLocale(), |
| 135 | &browser_locales); |
| 136 | |
| 137 | size_t num_locales = supported_locales->GetSize(); |
| 138 | bool locale_supported = false; |
| 139 | for (size_t j = 0; j < num_locales; j++) { |
| 140 | std::string current_locale; |
| 141 | if (supported_locales->GetString(j, ¤t_locale) && |
| 142 | l10n_util::IsValidLocaleSyntax(current_locale)) { |
| 143 | current_locale = l10n_util::NormalizeLocale(current_locale); |
| 144 | if (std::find(browser_locales.begin(), browser_locales.end(), |
| 145 | current_locale) != browser_locales.end()) { |
| 146 | locale_supported = true; |
| 147 | break; |
| 148 | } |
| 149 | } else { |
| 150 | LOG(WARNING) << "Unrecognized locale '" << current_locale |
| 151 | << "' found as supported locale for extension: " |
| 152 | << extension_id; |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | if (!locale_supported) { |
| 157 | unsupported_extensions.insert(extension_id); |
| 158 | LOG(INFO) << "Skip installing (or uninstall) external extension: " |
| 159 | << extension_id << " because the extension doesn't support " |
| 160 | << "the browser locale."; |
| 161 | continue; |
| 162 | } |
| 163 | } |
| 164 | |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 165 | if (has_external_crx) { |
| 166 | if (crx_location_ == Extension::INVALID) { |
| 167 | LOG(WARNING) << "This provider does not support installing external " |
| 168 | << "extensions from crx files."; |
| 169 | continue; |
| 170 | } |
| 171 | if (external_crx.find(FilePath::kParentDirectory) != |
| 172 | base::StringPiece::npos) { |
| 173 | LOG(WARNING) << "Path traversal not allowed in path: " |
| 174 | << external_crx.c_str(); |
| 175 | continue; |
| 176 | } |
| 177 | |
[email protected] | f0841cd | 2011-01-19 15:07:24 | [diff] [blame] | 178 | // If the path is relative, and the provider has a base path, |
| 179 | // build the absolute path to the crx file. |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 180 | FilePath path(external_crx); |
| 181 | if (!path.IsAbsolute()) { |
[email protected] | f0841cd | 2011-01-19 15:07:24 | [diff] [blame] | 182 | FilePath base_path = loader_->GetBaseCrxFilePath(); |
| 183 | if (base_path.empty()) { |
| 184 | LOG(WARNING) << "File path " << external_crx.c_str() |
| 185 | << " is relative. An absolute path is required."; |
| 186 | continue; |
| 187 | } |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 188 | path = base_path.Append(external_crx); |
| 189 | } |
| 190 | |
| 191 | scoped_ptr<Version> version; |
| 192 | version.reset(Version::GetVersionFromString(external_version)); |
| 193 | if (!version.get()) { |
| 194 | LOG(WARNING) << "Malformed extension dictionary for extension: " |
| 195 | << extension_id.c_str() << ". Invalid version string \"" |
| 196 | << external_version << "\"."; |
| 197 | continue; |
| 198 | } |
| 199 | service_->OnExternalExtensionFileFound(extension_id, version.get(), path, |
[email protected] | 47fc70c | 2011-12-06 07:29:51 | [diff] [blame^] | 200 | crx_location_, creation_flags_, |
| 201 | auto_acknowledge_); |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 202 | } else { // if (has_external_update_url) |
| 203 | CHECK(has_external_update_url); // Checking of keys above ensures this. |
| 204 | if (download_location_ == Extension::INVALID) { |
| 205 | LOG(WARNING) << "This provider does not support installing external " |
| 206 | << "extensions from update URLs."; |
| 207 | continue; |
| 208 | } |
| 209 | GURL update_url(external_update_url); |
| 210 | if (!update_url.is_valid()) { |
| 211 | LOG(WARNING) << "Malformed extension dictionary for extension: " |
[email protected] | ab22ba4 | 2011-01-14 16:36:38 | [diff] [blame] | 212 | << extension_id.c_str() << ". Key " << kExternalUpdateUrl |
| 213 | << " has value \"" << external_update_url |
| 214 | << "\", which is not a valid URL."; |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 215 | continue; |
| 216 | } |
| 217 | service_->OnExternalExtensionUpdateUrlFound( |
| 218 | extension_id, update_url, download_location_); |
| 219 | } |
| 220 | } |
| 221 | |
[email protected] | 9d32ded07 | 2011-10-11 16:31:05 | [diff] [blame] | 222 | for (std::set<std::string>::iterator it = unsupported_extensions.begin(); |
| 223 | it != unsupported_extensions.end(); ++it) { |
| 224 | // Remove extension for the list of know external extensions. The extension |
| 225 | // will be uninstalled later because provider doesn't provide it anymore. |
| 226 | prefs_->Remove(*it, NULL); |
| 227 | } |
| 228 | |
[email protected] | 50067e5 | 2011-10-20 23:17:07 | [diff] [blame] | 229 | service_->OnExternalProviderReady(this); |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | void ExternalExtensionProviderImpl::ServiceShutdown() { |
| 233 | service_ = NULL; |
| 234 | } |
| 235 | |
[email protected] | 50067e5 | 2011-10-20 23:17:07 | [diff] [blame] | 236 | bool ExternalExtensionProviderImpl::IsReady() const { |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 237 | return ready_; |
| 238 | } |
| 239 | |
| 240 | bool ExternalExtensionProviderImpl::HasExtension( |
| 241 | const std::string& id) const { |
| 242 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 243 | CHECK(prefs_.get()); |
| 244 | CHECK(ready_); |
| 245 | return prefs_->HasKey(id); |
| 246 | } |
| 247 | |
| 248 | bool ExternalExtensionProviderImpl::GetExtensionDetails( |
| 249 | const std::string& id, Extension::Location* location, |
| 250 | scoped_ptr<Version>* version) const { |
| 251 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 252 | CHECK(prefs_.get()); |
| 253 | CHECK(ready_); |
| 254 | DictionaryValue* extension = NULL; |
| 255 | if (!prefs_->GetDictionary(id, &extension)) |
| 256 | return false; |
| 257 | |
| 258 | Extension::Location loc = Extension::INVALID; |
| 259 | if (extension->HasKey(kExternalUpdateUrl)) { |
| 260 | loc = download_location_; |
| 261 | |
| 262 | } else if (extension->HasKey(kExternalCrx)) { |
| 263 | loc = crx_location_; |
| 264 | |
| 265 | std::string external_version; |
| 266 | if (!extension->GetString(kExternalVersion, &external_version)) |
| 267 | return false; |
| 268 | |
| 269 | if (version) |
| 270 | version->reset(Version::GetVersionFromString(external_version)); |
| 271 | |
| 272 | } else { |
| 273 | NOTREACHED(); // Chrome should not allow prefs to get into this state. |
| 274 | return false; |
| 275 | } |
| 276 | |
| 277 | if (location) |
| 278 | *location = loc; |
| 279 | |
| 280 | return true; |
| 281 | } |
| 282 | |
| 283 | // static |
| 284 | void ExternalExtensionProviderImpl::CreateExternalProviders( |
| 285 | VisitorInterface* service, |
| 286 | Profile* profile, |
| 287 | ProviderCollection* provider_list) { |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 288 | |
| 289 | // On Mac OS, items in /Library/... should be written by the superuser. |
| 290 | // Check that all components of the path are writable by root only. |
[email protected] | 0edc5541 | 2011-11-07 16:47:33 | [diff] [blame] | 291 | ExternalPrefExtensionLoader::Options check_admin_permissions_on_mac; |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 292 | #if defined(OS_MACOSX) |
[email protected] | 0edc5541 | 2011-11-07 16:47:33 | [diff] [blame] | 293 | check_admin_permissions_on_mac = |
| 294 | ExternalPrefExtensionLoader::ENSURE_PATH_CONTROLLED_BY_ADMIN; |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 295 | #else |
[email protected] | 0edc5541 | 2011-11-07 16:47:33 | [diff] [blame] | 296 | check_admin_permissions_on_mac = ExternalPrefExtensionLoader::NONE; |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 297 | #endif |
| 298 | |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 299 | provider_list->push_back( |
| 300 | linked_ptr<ExternalExtensionProviderInterface>( |
| 301 | new ExternalExtensionProviderImpl( |
| 302 | service, |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 303 | new ExternalPrefExtensionLoader( |
[email protected] | 0edc5541 | 2011-11-07 16:47:33 | [diff] [blame] | 304 | chrome::DIR_EXTERNAL_EXTENSIONS, |
| 305 | check_admin_permissions_on_mac), |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 306 | Extension::EXTERNAL_PREF, |
[email protected] | 1bf73cc | 2011-10-26 22:38:31 | [diff] [blame] | 307 | Extension::EXTERNAL_PREF_DOWNLOAD, |
| 308 | Extension::NO_FLAGS))); |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 309 | |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 310 | #if defined(OS_MACOSX) |
| 311 | // Support old path to external extensions file as we migrate to the |
| 312 | // new one. See crbug/67203. |
| 313 | provider_list->push_back( |
| 314 | linked_ptr<ExternalExtensionProviderInterface>( |
| 315 | new ExternalExtensionProviderImpl( |
| 316 | service, |
| 317 | new ExternalPrefExtensionLoader( |
[email protected] | 23b0097 | 2011-10-04 17:17:26 | [diff] [blame] | 318 | chrome::DIR_DEPRECATED_EXTERNAL_EXTENSIONS, |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 319 | ExternalPrefExtensionLoader::NONE), |
| 320 | Extension::EXTERNAL_PREF, |
[email protected] | 1bf73cc | 2011-10-26 22:38:31 | [diff] [blame] | 321 | Extension::EXTERNAL_PREF_DOWNLOAD, |
| 322 | Extension::NO_FLAGS))); |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 323 | #endif |
| 324 | |
[email protected] | 0edc5541 | 2011-11-07 16:47:33 | [diff] [blame] | 325 | #if defined(OS_CHROMEOS) || defined (OS_MACOSX) |
| 326 | // Define a per-user source of external extensions. |
| 327 | // On Chrome OS, this serves as a source for OEM customization. |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 328 | provider_list->push_back( |
| 329 | linked_ptr<ExternalExtensionProviderInterface>( |
| 330 | new ExternalExtensionProviderImpl( |
| 331 | service, |
| 332 | new ExternalPrefExtensionLoader( |
[email protected] | 998e8b9 | 2011-09-22 15:07:24 | [diff] [blame] | 333 | chrome::DIR_USER_EXTERNAL_EXTENSIONS, |
| 334 | ExternalPrefExtensionLoader::NONE), |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 335 | Extension::EXTERNAL_PREF, |
[email protected] | 1bf73cc | 2011-10-26 22:38:31 | [diff] [blame] | 336 | Extension::EXTERNAL_PREF_DOWNLOAD, |
| 337 | Extension::NO_FLAGS))); |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 338 | #endif |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 339 | #if defined(OS_WIN) |
| 340 | provider_list->push_back( |
| 341 | linked_ptr<ExternalExtensionProviderInterface>( |
| 342 | new ExternalExtensionProviderImpl( |
| 343 | service, |
| 344 | new ExternalRegistryExtensionLoader, |
| 345 | Extension::EXTERNAL_REGISTRY, |
[email protected] | 1bf73cc | 2011-10-26 22:38:31 | [diff] [blame] | 346 | Extension::INVALID, |
| 347 | Extension::NO_FLAGS))); |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 348 | #endif |
| 349 | provider_list->push_back( |
| 350 | linked_ptr<ExternalExtensionProviderInterface>( |
| 351 | new ExternalExtensionProviderImpl( |
| 352 | service, |
| 353 | new ExternalPolicyExtensionLoader(profile), |
| 354 | Extension::INVALID, |
[email protected] | 1bf73cc | 2011-10-26 22:38:31 | [diff] [blame] | 355 | Extension::EXTERNAL_POLICY_DOWNLOAD, |
| 356 | Extension::NO_FLAGS))); |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 357 | |
[email protected] | 98b4aca6 | 2011-09-28 01:27:23 | [diff] [blame] | 358 | #if !defined(OS_CHROMEOS) |
[email protected] | d190cef | 2011-11-09 02:09:24 | [diff] [blame] | 359 | provider_list->push_back( |
| 360 | linked_ptr<ExternalExtensionProviderInterface>( |
| 361 | new default_apps::Provider( |
| 362 | profile, |
| 363 | service, |
| 364 | new ExternalPrefExtensionLoader( |
| 365 | chrome::DIR_DEFAULT_APPS, |
| 366 | ExternalPrefExtensionLoader::NONE), |
| 367 | Extension::EXTERNAL_PREF, |
| 368 | Extension::INVALID, |
| 369 | Extension::FROM_BOOKMARK))); |
[email protected] | 98b4aca6 | 2011-09-28 01:27:23 | [diff] [blame] | 370 | #endif |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 371 | } |