| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/plugins/plugin_finder.h" | 5 #include "chrome/browser/plugins/plugin_finder.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
| 9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
| 10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
| 11 #include "base/sys_string_conversions.h" | 11 #include "base/sys_string_conversions.h" |
| 12 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
| 13 #include "base/values.h" | 13 #include "base/values.h" |
| 14 #include "chrome/browser/browser_process.h" | 14 #include "chrome/browser/browser_process.h" |
| 15 #include "chrome/browser/plugins/plugin_installer.h" | 15 #include "chrome/browser/plugins/plugin_installer.h" |
| 16 #include "chrome/browser/prefs/pref_service.h" | 16 #include "chrome/browser/prefs/pref_service.h" |
| 17 #include "chrome/common/pref_names.h" | 17 #include "chrome/common/pref_names.h" |
| 18 #include "content/public/browser/browser_thread.h" | 18 #include "content/public/browser/browser_thread.h" |
| 19 #include "content/public/browser/plugin_service.h" | 19 #include "content/public/browser/plugin_service.h" |
| 20 #include "googleurl/src/gurl.h" | 20 #include "googleurl/src/gurl.h" |
| 21 #include "grit/browser_resources.h" | 21 #include "grit/browser_resources.h" |
| 22 #include "ui/base/layout.h" | 22 #include "ui/base/layout.h" |
| 23 #include "ui/base/resource/resource_bundle.h" | 23 #include "ui/base/resource/resource_bundle.h" |
| 24 | 24 |
| 25 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 26 #include "chrome/browser/plugins/plugin_installer.h" |
| 27 #endif |
| 28 |
| 25 using base::DictionaryValue; | 29 using base::DictionaryValue; |
| 26 using content::PluginService; | 30 using content::PluginService; |
| 27 | 31 |
| 28 namespace { | 32 namespace { |
| 29 | 33 |
| 30 // Gets the base name of the file path as the identifier. | 34 // Gets the base name of the file path as the identifier. |
| 31 static std::string GetIdentifier(const webkit::WebPluginInfo& plugin) { | 35 static std::string GetIdentifier(const webkit::WebPluginInfo& plugin) { |
| 32 #if defined(OS_POSIX) | 36 #if defined(OS_POSIX) |
| 33 return plugin.path.BaseName().value(); | 37 return plugin.path.BaseName().value(); |
| 34 #elif defined(OS_WIN) | 38 #elif defined(OS_WIN) |
| 35 return base::SysWideToUTF8(plugin.path.BaseName().value()); | 39 return base::SysWideToUTF8(plugin.path.BaseName().value()); |
| 36 #endif | 40 #endif |
| 37 } | 41 } |
| 38 | 42 |
| 39 // Gets the plug-in group name as the plug-in name if it is not empty or | 43 // Gets the plug-in group name as the plug-in name if it is not empty or |
| 40 // the filename without extension if the name is empty. | 44 // the filename without extension if the name is empty. |
| 41 static string16 GetGroupName(const webkit::WebPluginInfo& plugin) { | 45 static string16 GetGroupName(const webkit::WebPluginInfo& plugin) { |
| 42 if (!plugin.name.empty()) | 46 if (!plugin.name.empty()) |
| 43 return plugin.name; | 47 return plugin.name; |
| 44 | 48 |
| 45 FilePath::StringType path = plugin.path.BaseName().RemoveExtension().value(); | 49 FilePath::StringType path = plugin.path.BaseName().RemoveExtension().value(); |
| 46 #if defined(OS_POSIX) | 50 #if defined(OS_POSIX) |
| 47 return UTF8ToUTF16(path); | 51 return UTF8ToUTF16(path); |
| 48 #elif defined(OS_WIN) | 52 #elif defined(OS_WIN) |
| 49 return WideToUTF16(path); | 53 return WideToUTF16(path); |
| 50 #endif | 54 #endif |
| 51 } | 55 } |
| 52 | 56 |
| 53 // A callback barrier used to enforce the execution of a callback function | |
| 54 // only when two different asynchronous callbacks are done execution. | |
| 55 // The first asynchronous callback gets a PluginFinder instance and the | |
| 56 // second asynchronous callback gets a list of plugins. | |
| 57 class PluginFinderCallbackBarrier | |
| 58 : public base::RefCountedThreadSafe<PluginFinderCallbackBarrier> { | |
| 59 public: | |
| 60 typedef base::Callback<void(const PluginFinder::PluginVector&)> | |
| 61 PluginsCallback; | |
| 62 | |
| 63 explicit PluginFinderCallbackBarrier( | |
| 64 const PluginFinder::CombinedCallback& callback) | |
| 65 : callback_(callback), | |
| 66 finder_(NULL) { | |
| 67 DCHECK(!callback_.is_null()); | |
| 68 } | |
| 69 | |
| 70 base::Callback<void(PluginFinder*)> CreatePluginFinderCallback() { | |
| 71 return base::Bind(&PluginFinderCallbackBarrier::GotPluginFinder, this); | |
| 72 } | |
| 73 | |
| 74 PluginsCallback CreatePluginsCallback() { | |
| 75 return base::Bind(&PluginFinderCallbackBarrier::GotPlugins, this); | |
| 76 } | |
| 77 | |
| 78 private: | |
| 79 friend class base::RefCountedThreadSafe<PluginFinderCallbackBarrier>; | |
| 80 | |
| 81 ~PluginFinderCallbackBarrier() { | |
| 82 DCHECK(callback_.is_null()); | |
| 83 } | |
| 84 | |
| 85 void GotPlugins(const PluginFinder::PluginVector& plugins) { | |
| 86 plugins_.reset(new PluginFinder::PluginVector(plugins)); | |
| 87 MaybeRunCallback(); | |
| 88 } | |
| 89 | |
| 90 void GotPluginFinder(PluginFinder* finder) { | |
| 91 finder_ = finder; | |
| 92 MaybeRunCallback(); | |
| 93 } | |
| 94 | |
| 95 // Executes the callback only when both asynchronous methods have finished | |
| 96 // their executions. This is identified by having non-null values in both | |
| 97 // |finder_| and |plugins_|. | |
| 98 void MaybeRunCallback() { | |
| 99 if (!finder_ || !plugins_.get()) | |
| 100 return; | |
| 101 | |
| 102 callback_.Run(*plugins_, finder_); | |
| 103 callback_.Reset(); | |
| 104 } | |
| 105 | |
| 106 PluginFinder::CombinedCallback callback_; | |
| 107 PluginFinder* finder_; | |
| 108 scoped_ptr<PluginFinder::PluginVector> plugins_; | |
| 109 }; | |
| 110 | |
| 111 } // namespace | 57 } // namespace |
| 112 | 58 |
| 113 // static | 59 // TODO(ibraaaa): DELETE. http://crbug.com/124396 |
| 114 void PluginFinder::GetPluginsAndPluginFinder( | |
| 115 const PluginFinder::CombinedCallback& cb) { | |
| 116 scoped_refptr<PluginFinderCallbackBarrier> barrier = | |
| 117 new PluginFinderCallbackBarrier(cb); | |
| 118 | |
| 119 PluginFinder::Get(barrier->CreatePluginFinderCallback()); | |
| 120 PluginService::GetInstance()->GetPlugins(barrier->CreatePluginsCallback()); | |
| 121 } | |
| 122 | |
| 123 // static | 60 // static |
| 124 void PluginFinder::Get(const base::Callback<void(PluginFinder*)>& cb) { | 61 void PluginFinder::Get(const base::Callback<void(PluginFinder*)>& cb) { |
| 125 // At a later point we might want to do intialization here that needs to be | 62 // At a later point we might want to do intialization here that needs to be |
| 126 // done asynchronously, like loading the plug-in list from disk or from a URL. | 63 // done asynchronously, like loading the plug-in list from disk or from a URL. |
| 127 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(cb, GetInstance())); | 64 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(cb, GetInstance())); |
| 128 } | 65 } |
| 129 | 66 |
| 130 // static | 67 // static |
| 131 PluginFinder* PluginFinder::GetInstance() { | 68 PluginFinder* PluginFinder::GetInstance() { |
| 132 // PluginFinder::GetInstance() is the only method that's allowed to call | 69 // PluginFinder::GetInstance() is the only method that's allowed to call |
| 133 // Singleton<PluginFinder>::get(). | 70 // Singleton<PluginFinder>::get(). |
| 134 return Singleton<PluginFinder>::get(); | 71 return Singleton<PluginFinder, PluginFinder::CustomSingletonTraits>::get(); |
| 135 } | 72 } |
| 136 | 73 |
| 137 PluginFinder::PluginFinder() : plugin_list_(LoadPluginList()) { | 74 PluginFinder::PluginFinder() { |
| 75 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 76 } |
| 77 |
| 78 // TODO(ibraaaa): initialize |plugin_list_| from Local State. |
| 79 // http://crbug.com/124396 |
| 80 void PluginFinder::Init() { |
| 81 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 82 plugin_list_.reset(LoadPluginList()); |
| 138 if (!plugin_list_.get()) | 83 if (!plugin_list_.get()) |
| 139 plugin_list_.reset(new DictionaryValue()); | 84 plugin_list_.reset(new DictionaryValue()); |
| 85 |
| 86 for (DictionaryValue::Iterator plugin_it(*plugin_list_); |
| 87 plugin_it.HasNext(); plugin_it.Advance()) { |
| 88 DictionaryValue* plugin = NULL; |
| 89 const std::string& identifier = plugin_it.key(); |
| 90 if (plugin_list_->GetDictionaryWithoutPathExpansion(identifier, &plugin)) { |
| 91 PluginMetadata* metadata = CreatePluginMetadata(identifier, plugin); |
| 92 identifier_plugin_[identifier] = metadata; |
| 93 |
| 94 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 95 installers_[identifier] = new PluginInstaller(metadata); |
| 96 #endif |
| 97 } |
| 98 } |
| 140 } | 99 } |
| 141 | 100 |
| 142 // static | 101 // static |
| 143 DictionaryValue* PluginFinder::LoadPluginList() { | 102 DictionaryValue* PluginFinder::LoadPluginList() { |
| 144 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) | 103 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) |
| 145 base::StringPiece json_resource( | 104 base::StringPiece json_resource( |
| 146 ResourceBundle::GetSharedInstance().GetRawDataResource( | 105 ResourceBundle::GetSharedInstance().GetRawDataResource( |
| 147 IDR_PLUGIN_DB_JSON, ui::SCALE_FACTOR_NONE)); | 106 IDR_PLUGIN_DB_JSON, ui::SCALE_FACTOR_NONE)); |
| 148 std::string error_str; | 107 std::string error_str; |
| 149 scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError( | 108 scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError( |
| 150 json_resource, | 109 json_resource, |
| 151 base::JSON_PARSE_RFC, | 110 base::JSON_PARSE_RFC, |
| 152 NULL, | 111 NULL, |
| 153 &error_str)); | 112 &error_str)); |
| 154 if (!value.get()) { | 113 if (!value.get()) { |
| 155 DLOG(ERROR) << error_str; | 114 DLOG(ERROR) << error_str; |
| 156 return NULL; | 115 return NULL; |
| 157 } | 116 } |
| 158 if (value->GetType() != base::Value::TYPE_DICTIONARY) | 117 if (value->GetType() != base::Value::TYPE_DICTIONARY) |
| 159 return NULL; | 118 return NULL; |
| 160 return static_cast<base::DictionaryValue*>(value.release()); | 119 return static_cast<base::DictionaryValue*>(value.release()); |
| 161 #else | 120 #else |
| 162 return new DictionaryValue(); | 121 return new DictionaryValue(); |
| 163 #endif | 122 #endif |
| 164 } | 123 } |
| 165 | 124 |
| 166 PluginFinder::~PluginFinder() { | 125 PluginFinder::~PluginFinder() { |
| 126 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 167 STLDeleteValues(&installers_); | 127 STLDeleteValues(&installers_); |
| 128 #endif |
| 129 STLDeleteValues(&identifier_plugin_); |
| 168 } | 130 } |
| 169 | 131 |
| 132 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 170 PluginInstaller* PluginFinder::FindPlugin(const std::string& mime_type, | 133 PluginInstaller* PluginFinder::FindPlugin(const std::string& mime_type, |
| 171 const std::string& language) { | 134 const std::string& language) { |
| 172 if (g_browser_process->local_state()->GetBoolean(prefs::kDisablePluginFinder)) | 135 if (g_browser_process->local_state()->GetBoolean(prefs::kDisablePluginFinder)) |
| 173 return NULL; | 136 return NULL; |
| 174 for (DictionaryValue::Iterator plugin_it(*plugin_list_); | 137 for (DictionaryValue::Iterator plugin_it(*plugin_list_); |
| 175 plugin_it.HasNext(); plugin_it.Advance()) { | 138 plugin_it.HasNext(); plugin_it.Advance()) { |
| 176 const DictionaryValue* plugin = NULL; | 139 const DictionaryValue* plugin = NULL; |
| 177 if (!plugin_it.value().GetAsDictionary(&plugin)) { | 140 if (!plugin_it.value().GetAsDictionary(&plugin)) { |
| 178 NOTREACHED(); | 141 NOTREACHED(); |
| 179 continue; | 142 continue; |
| 180 } | 143 } |
| 181 std::string language_str; | 144 std::string language_str; |
| 182 bool success = plugin->GetString("lang", &language_str); | 145 bool success = plugin->GetString("lang", &language_str); |
| 183 if (language_str != language) | 146 if (language_str != language) |
| 184 continue; | 147 continue; |
| 185 const ListValue* mime_types = NULL; | 148 const ListValue* mime_types = NULL; |
| 186 plugin->GetList("mime_types", &mime_types); | 149 plugin->GetList("mime_types", &mime_types); |
| 187 DCHECK(success); | 150 DCHECK(success); |
| 188 for (ListValue::const_iterator mime_type_it = mime_types->begin(); | 151 for (ListValue::const_iterator mime_type_it = mime_types->begin(); |
| 189 mime_type_it != mime_types->end(); ++mime_type_it) { | 152 mime_type_it != mime_types->end(); ++mime_type_it) { |
| 190 std::string mime_type_str; | 153 std::string mime_type_str; |
| 191 success = (*mime_type_it)->GetAsString(&mime_type_str); | 154 success = (*mime_type_it)->GetAsString(&mime_type_str); |
| 192 DCHECK(success); | 155 DCHECK(success); |
| 193 if (mime_type_str == mime_type) { | 156 if (mime_type_str == mime_type) { |
| 194 std::string identifier = plugin_it.key(); | 157 std::string identifier = plugin_it.key(); |
| 195 std::map<std::string, PluginInstaller*>::const_iterator installer = | 158 { |
| 196 installers_.find(identifier); | 159 base::AutoLock lock(mutex_); |
| 197 if (installer != installers_.end()) | 160 std::map<std::string, PluginInstaller*>::const_iterator installer = |
| 161 installers_.find(identifier); |
| 162 DCHECK(installer != installers_.end()); |
| 198 return installer->second; | 163 return installer->second; |
| 199 return CreateInstaller(identifier, plugin); | 164 } |
| 200 } | 165 } |
| 201 } | 166 } |
| 202 } | 167 } |
| 203 return NULL; | 168 return NULL; |
| 204 } | 169 } |
| 205 | 170 |
| 206 PluginInstaller* PluginFinder::FindPluginWithIdentifier( | 171 PluginInstaller* PluginFinder::FindPluginWithIdentifier( |
| 207 const std::string& identifier) { | 172 const std::string& identifier) { |
| 173 base::AutoLock lock(mutex_); |
| 208 std::map<std::string, PluginInstaller*>::const_iterator it = | 174 std::map<std::string, PluginInstaller*>::const_iterator it = |
| 209 installers_.find(identifier); | 175 installers_.find(identifier); |
| 210 if (it != installers_.end()) | 176 if (it != installers_.end()) |
| 211 return it->second; | 177 return it->second; |
| 212 DictionaryValue* plugin = NULL; | 178 |
| 213 if (plugin_list_->GetDictionaryWithoutPathExpansion(identifier, &plugin)) | 179 return NULL; |
| 214 return CreateInstaller(identifier, plugin); | 180 } |
| 181 #endif |
| 182 |
| 183 PluginMetadata* PluginFinder::FindPluginMetadataWithIdentifier( |
| 184 const std::string& identifier) { |
| 185 base::AutoLock lock(mutex_); |
| 186 std::map<std::string, PluginMetadata*>::const_iterator it = |
| 187 identifier_plugin_.find(identifier); |
| 188 if (it != identifier_plugin_.end()) |
| 189 return it->second; |
| 190 |
| 215 return NULL; | 191 return NULL; |
| 216 } | 192 } |
| 217 | 193 |
| 218 PluginInstaller* PluginFinder::CreateInstaller( | 194 PluginMetadata* PluginFinder::CreatePluginMetadata( |
| 219 const std::string& identifier, | 195 const std::string& identifier, |
| 220 const DictionaryValue* plugin_dict) { | 196 const DictionaryValue* plugin_dict) { |
| 221 DCHECK(!installers_[identifier]); | 197 DCHECK(!identifier_plugin_[identifier]); |
| 222 std::string url; | 198 std::string url; |
| 223 bool success = plugin_dict->GetString("url", &url); | 199 bool success = plugin_dict->GetString("url", &url); |
| 224 std::string help_url; | 200 std::string help_url; |
| 225 plugin_dict->GetString("help_url", &help_url); | 201 plugin_dict->GetString("help_url", &help_url); |
| 226 string16 name; | 202 string16 name; |
| 227 success = plugin_dict->GetString("name", &name); | 203 success = plugin_dict->GetString("name", &name); |
| 228 DCHECK(success); | 204 DCHECK(success); |
| 229 bool display_url = false; | 205 bool display_url = false; |
| 230 plugin_dict->GetBoolean("displayurl", &display_url); | 206 plugin_dict->GetBoolean("displayurl", &display_url); |
| 231 string16 group_name_matcher; | 207 string16 group_name_matcher; |
| 232 success = plugin_dict->GetString("group_name_matcher", &group_name_matcher); | 208 success = plugin_dict->GetString("group_name_matcher", &group_name_matcher); |
| 233 DCHECK(success); | 209 DCHECK(success); |
| 234 | 210 |
| 235 PluginInstaller* installer = new PluginInstaller(identifier, | 211 PluginMetadata* plugin = new PluginMetadata(identifier, |
| 236 name, | 212 name, |
| 237 display_url, | 213 display_url, |
| 238 GURL(url), | 214 GURL(url), |
| 239 GURL(help_url), | 215 GURL(help_url), |
| 240 group_name_matcher); | 216 group_name_matcher); |
| 241 const ListValue* versions = NULL; | 217 const ListValue* versions = NULL; |
| 242 if (plugin_dict->GetList("versions", &versions)) { | 218 if (plugin_dict->GetList("versions", &versions)) { |
| 243 for (ListValue::const_iterator it = versions->begin(); | 219 for (ListValue::const_iterator it = versions->begin(); |
| 244 it != versions->end(); ++it) { | 220 it != versions->end(); ++it) { |
| 245 DictionaryValue* version_dict = NULL; | 221 DictionaryValue* version_dict = NULL; |
| 246 if (!(*it)->GetAsDictionary(&version_dict)) { | 222 if (!(*it)->GetAsDictionary(&version_dict)) { |
| 247 NOTREACHED(); | 223 NOTREACHED(); |
| 248 continue; | 224 continue; |
| 249 } | 225 } |
| 250 std::string version; | 226 std::string version; |
| 251 success = version_dict->GetString("version", &version); | 227 success = version_dict->GetString("version", &version); |
| 252 DCHECK(success); | 228 DCHECK(success); |
| 253 std::string status_str; | 229 std::string status_str; |
| 254 success = version_dict->GetString("status", &status_str); | 230 success = version_dict->GetString("status", &status_str); |
| 255 DCHECK(success); | 231 DCHECK(success); |
| 256 PluginInstaller::SecurityStatus status = | 232 PluginMetadata::SecurityStatus status = |
| 257 PluginInstaller::SECURITY_STATUS_UP_TO_DATE; | 233 PluginMetadata::SECURITY_STATUS_UP_TO_DATE; |
| 258 success = PluginInstaller::ParseSecurityStatus(status_str, &status); | 234 success = PluginMetadata::ParseSecurityStatus(status_str, &status); |
| 259 DCHECK(success); | 235 DCHECK(success); |
| 260 installer->AddVersion(Version(version), status); | 236 plugin->AddVersion(Version(version), status); |
| 261 } | 237 } |
| 262 } | 238 } |
| 263 | 239 |
| 264 installers_[identifier] = installer; | 240 return plugin; |
| 265 return installer; | |
| 266 } | 241 } |
| 267 | 242 |
| 268 PluginInstaller* PluginFinder::GetPluginInstaller( | 243 PluginMetadata* PluginFinder::GetPluginMetadata( |
| 269 const webkit::WebPluginInfo& plugin) { | 244 const webkit::WebPluginInfo& plugin) { |
| 270 if (name_installers_.find(plugin.name) != name_installers_.end()) | 245 base::AutoLock lock(mutex_); |
| 271 return name_installers_[plugin.name]; | 246 if (name_plugin_.find(plugin.name) != name_plugin_.end()) |
| 247 return name_plugin_[plugin.name]; |
| 272 | 248 |
| 273 for (DictionaryValue::Iterator plugin_it(*plugin_list_); | 249 // Use the group name matcher to find the plug-in metadata we want. |
| 274 plugin_it.HasNext(); plugin_it.Advance()) { | 250 for (std::map<std::string, PluginMetadata*>::const_iterator it = |
| 275 // This method triggers the lazy initialization for all PluginInstallers. | 251 identifier_plugin_.begin(); it != identifier_plugin_.end(); ++it) { |
| 276 FindPluginWithIdentifier(plugin_it.key()); | |
| 277 } | |
| 278 | |
| 279 // Use the group name matcher to find the plug-in installer we want. | |
| 280 for (std::map<std::string, PluginInstaller*>::const_iterator it = | |
| 281 installers_.begin(); it != installers_.end(); ++it) { | |
| 282 if (!it->second->MatchesPlugin(plugin)) | 252 if (!it->second->MatchesPlugin(plugin)) |
| 283 continue; | 253 continue; |
| 284 | 254 |
| 285 name_installers_[plugin.name] = it->second; | 255 name_plugin_[plugin.name] = it->second; |
| 286 return it->second; | 256 return it->second; |
| 287 } | 257 } |
| 288 | 258 |
| 289 // The plug-in installer was not found, create a dummy one holding | 259 // The plug-in metadata was not found, create a dummy one holding |
| 290 // the name, identifier and group name only. | 260 // the name, identifier and group name only. |
| 291 std::string identifier = GetIdentifier(plugin); | 261 std::string identifier = GetIdentifier(plugin); |
| 292 PluginInstaller* installer = new PluginInstaller(identifier, | 262 PluginMetadata* metadata = new PluginMetadata(identifier, |
| 293 GetGroupName(plugin), | 263 GetGroupName(plugin), |
| 294 false, GURL(), GURL(), | 264 false, GURL(), GURL(), |
| 295 GetGroupName(plugin)); | 265 GetGroupName(plugin)); |
| 296 installers_[identifier] = installer; | 266 |
| 297 name_installers_[plugin.name] = installer; | 267 name_plugin_[plugin.name] = metadata; |
| 298 return installer; | 268 identifier_plugin_[identifier] = metadata; |
| 269 return metadata; |
| 299 } | 270 } |
| 271 |
| 272 struct PluginFinder::CustomSingletonTraits |
| 273 : DefaultSingletonTraits<PluginFinder> { |
| 274 static PluginFinder* New() { |
| 275 PluginFinder* finder = new PluginFinder(); |
| 276 finder->Init(); |
| 277 return finder; |
| 278 } |
| 279 }; |
| OLD | NEW |