| 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 "webkit/plugins/npapi/plugin_list.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/command_line.h" | |
| 10 #include "base/lazy_instance.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/strings/string_split.h" | |
| 13 #include "base/strings/string_util.h" | |
| 14 #include "base/strings/sys_string_conversions.h" | |
| 15 #include "base/strings/utf_string_conversions.h" | |
| 16 #include "net/base/mime_util.h" | |
| 17 #include "url/gurl.h" | |
| 18 #include "webkit/plugins/npapi/plugin_utils.h" | |
| 19 #include "webkit/plugins/plugin_switches.h" | |
| 20 | |
| 21 #if defined(OS_WIN) | |
| 22 #include "webkit/plugins/npapi/plugin_constants_win.h" | |
| 23 #endif | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 using webkit::npapi::PluginList; | |
| 28 | |
| 29 const char kApplicationOctetStream[] = "application/octet-stream"; | |
| 30 | |
| 31 base::LazyInstance<PluginList> g_singleton = LAZY_INSTANCE_INITIALIZER; | |
| 32 | |
| 33 bool AllowMimeTypeMismatch(const std::string& orig_mime_type, | |
| 34 const std::string& actual_mime_type) { | |
| 35 if (orig_mime_type == actual_mime_type) { | |
| 36 NOTREACHED(); | |
| 37 return true; | |
| 38 } | |
| 39 | |
| 40 // We do not permit URL-sniff based plug-in MIME type overrides aside from | |
| 41 // the case where the "type" was initially missing or generic | |
| 42 // (application/octet-stream). | |
| 43 // We collected stats to determine this approach isn't a major compat issue, | |
| 44 // and we defend against content confusion attacks in various cases, such | |
| 45 // as when the user doesn't have the Flash plug-in enabled. | |
| 46 bool allow = orig_mime_type.empty() || | |
| 47 orig_mime_type == kApplicationOctetStream; | |
| 48 LOG_IF(INFO, !allow) << "Ignoring plugin with unexpected MIME type " | |
| 49 << actual_mime_type << " (expected " << orig_mime_type | |
| 50 << ")"; | |
| 51 return allow; | |
| 52 } | |
| 53 | |
| 54 } // namespace | |
| 55 | |
| 56 namespace webkit { | |
| 57 namespace npapi { | |
| 58 | |
| 59 // static | |
| 60 PluginList* PluginList::Singleton() { | |
| 61 return g_singleton.Pointer(); | |
| 62 } | |
| 63 | |
| 64 // static | |
| 65 bool PluginList::DebugPluginLoading() { | |
| 66 return CommandLine::ForCurrentProcess()->HasSwitch( | |
| 67 switches::kDebugPluginLoading); | |
| 68 } | |
| 69 | |
| 70 void PluginList::DisablePluginsDiscovery() { | |
| 71 plugins_discovery_disabled_ = true; | |
| 72 } | |
| 73 | |
| 74 void PluginList::RefreshPlugins() { | |
| 75 base::AutoLock lock(lock_); | |
| 76 loading_state_ = LOADING_STATE_NEEDS_REFRESH; | |
| 77 } | |
| 78 | |
| 79 void PluginList::AddExtraPluginPath(const base::FilePath& plugin_path) { | |
| 80 if (!NPAPIPluginsSupported()) { | |
| 81 // TODO(jam): remove and just have CHECK once we're sure this doesn't get | |
| 82 // triggered. | |
| 83 DLOG(INFO) << "NPAPI plugins not supported"; | |
| 84 return; | |
| 85 } | |
| 86 | |
| 87 // Chrome OS only loads plugins from /opt/google/chrome/plugins. | |
| 88 #if !defined(OS_CHROMEOS) | |
| 89 base::AutoLock lock(lock_); | |
| 90 extra_plugin_paths_.push_back(plugin_path); | |
| 91 #endif | |
| 92 } | |
| 93 | |
| 94 void PluginList::RemoveExtraPluginPath(const base::FilePath& plugin_path) { | |
| 95 base::AutoLock lock(lock_); | |
| 96 std::vector<base::FilePath>::iterator it = | |
| 97 std::find(extra_plugin_paths_.begin(), extra_plugin_paths_.end(), | |
| 98 plugin_path); | |
| 99 if (it != extra_plugin_paths_.end()) | |
| 100 extra_plugin_paths_.erase(it); | |
| 101 } | |
| 102 | |
| 103 void PluginList::AddExtraPluginDir(const base::FilePath& plugin_dir) { | |
| 104 // Chrome OS only loads plugins from /opt/google/chrome/plugins. | |
| 105 #if !defined(OS_CHROMEOS) | |
| 106 base::AutoLock lock(lock_); | |
| 107 extra_plugin_dirs_.push_back(plugin_dir); | |
| 108 #endif | |
| 109 } | |
| 110 | |
| 111 void PluginList::RegisterInternalPlugin(const webkit::WebPluginInfo& info, | |
| 112 bool add_at_beginning) { | |
| 113 if (!NPAPIPluginsSupported() && | |
| 114 info.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) { | |
| 115 DLOG(INFO) << "Don't register NPAPI plugins when they're not supported"; | |
| 116 return; | |
| 117 } | |
| 118 | |
| 119 base::AutoLock lock(lock_); | |
| 120 | |
| 121 internal_plugins_.push_back(info); | |
| 122 if (add_at_beginning) { | |
| 123 // Newer registrations go earlier in the list so they can override the MIME | |
| 124 // types of older registrations. | |
| 125 extra_plugin_paths_.insert(extra_plugin_paths_.begin(), info.path); | |
| 126 } else { | |
| 127 extra_plugin_paths_.push_back(info.path); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 void PluginList::UnregisterInternalPlugin(const base::FilePath& path) { | |
| 132 base::AutoLock lock(lock_); | |
| 133 for (size_t i = 0; i < internal_plugins_.size(); i++) { | |
| 134 if (internal_plugins_[i].path == path) { | |
| 135 internal_plugins_.erase(internal_plugins_.begin() + i); | |
| 136 return; | |
| 137 } | |
| 138 } | |
| 139 NOTREACHED(); | |
| 140 } | |
| 141 | |
| 142 void PluginList::GetInternalPlugins( | |
| 143 std::vector<webkit::WebPluginInfo>* internal_plugins) { | |
| 144 base::AutoLock lock(lock_); | |
| 145 | |
| 146 for (std::vector<webkit::WebPluginInfo>::iterator it = | |
| 147 internal_plugins_.begin(); | |
| 148 it != internal_plugins_.end(); | |
| 149 ++it) { | |
| 150 internal_plugins->push_back(*it); | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 bool PluginList::ReadPluginInfo(const base::FilePath& filename, | |
| 155 webkit::WebPluginInfo* info) { | |
| 156 { | |
| 157 base::AutoLock lock(lock_); | |
| 158 for (size_t i = 0; i < internal_plugins_.size(); ++i) { | |
| 159 if (filename == internal_plugins_[i].path) { | |
| 160 *info = internal_plugins_[i]; | |
| 161 return true; | |
| 162 } | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 return PluginList::ReadWebPluginInfo(filename, info); | |
| 167 } | |
| 168 | |
| 169 // static | |
| 170 bool PluginList::ParseMimeTypes( | |
| 171 const std::string& mime_types_str, | |
| 172 const std::string& file_extensions_str, | |
| 173 const base::string16& mime_type_descriptions_str, | |
| 174 std::vector<webkit::WebPluginMimeType>* parsed_mime_types) { | |
| 175 std::vector<std::string> mime_types, file_extensions; | |
| 176 std::vector<base::string16> descriptions; | |
| 177 base::SplitString(mime_types_str, '|', &mime_types); | |
| 178 base::SplitString(file_extensions_str, '|', &file_extensions); | |
| 179 base::SplitString(mime_type_descriptions_str, '|', &descriptions); | |
| 180 | |
| 181 parsed_mime_types->clear(); | |
| 182 | |
| 183 if (mime_types.empty()) | |
| 184 return false; | |
| 185 | |
| 186 for (size_t i = 0; i < mime_types.size(); ++i) { | |
| 187 WebPluginMimeType mime_type; | |
| 188 mime_type.mime_type = StringToLowerASCII(mime_types[i]); | |
| 189 if (file_extensions.size() > i) | |
| 190 base::SplitString(file_extensions[i], ',', &mime_type.file_extensions); | |
| 191 | |
| 192 if (descriptions.size() > i) { | |
| 193 mime_type.description = descriptions[i]; | |
| 194 | |
| 195 // On Windows, the description likely has a list of file extensions | |
| 196 // embedded in it (e.g. "SurfWriter file (*.swr)"). Remove an extension | |
| 197 // list from the description if it is present. | |
| 198 size_t ext = mime_type.description.find(ASCIIToUTF16("(*")); | |
| 199 if (ext != base::string16::npos) { | |
| 200 if (ext > 1 && mime_type.description[ext - 1] == ' ') | |
| 201 ext--; | |
| 202 | |
| 203 mime_type.description.erase(ext); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 parsed_mime_types->push_back(mime_type); | |
| 208 } | |
| 209 | |
| 210 return true; | |
| 211 } | |
| 212 | |
| 213 PluginList::PluginList() | |
| 214 : loading_state_(LOADING_STATE_NEEDS_REFRESH), | |
| 215 plugins_discovery_disabled_(false) { | |
| 216 } | |
| 217 | |
| 218 void PluginList::LoadPluginsIntoPluginListInternal( | |
| 219 std::vector<webkit::WebPluginInfo>* plugins) { | |
| 220 base::Closure will_load_callback; | |
| 221 { | |
| 222 base::AutoLock lock(lock_); | |
| 223 will_load_callback = will_load_plugins_callback_; | |
| 224 } | |
| 225 if (!will_load_callback.is_null()) | |
| 226 will_load_callback.Run(); | |
| 227 | |
| 228 std::vector<base::FilePath> plugin_paths; | |
| 229 GetPluginPathsToLoad(&plugin_paths); | |
| 230 | |
| 231 for (std::vector<base::FilePath>::const_iterator it = plugin_paths.begin(); | |
| 232 it != plugin_paths.end(); | |
| 233 ++it) { | |
| 234 WebPluginInfo plugin_info; | |
| 235 LoadPluginIntoPluginList(*it, plugins, &plugin_info); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 void PluginList::LoadPlugins() { | |
| 240 { | |
| 241 base::AutoLock lock(lock_); | |
| 242 if (loading_state_ == LOADING_STATE_UP_TO_DATE) | |
| 243 return; | |
| 244 | |
| 245 loading_state_ = LOADING_STATE_REFRESHING; | |
| 246 } | |
| 247 | |
| 248 std::vector<webkit::WebPluginInfo> new_plugins; | |
| 249 // Do the actual loading of the plugins. | |
| 250 LoadPluginsIntoPluginListInternal(&new_plugins); | |
| 251 | |
| 252 base::AutoLock lock(lock_); | |
| 253 plugins_list_.swap(new_plugins); | |
| 254 | |
| 255 // If we haven't been invalidated in the mean time, mark the plug-in list as | |
| 256 // up-to-date. | |
| 257 if (loading_state_ != LOADING_STATE_NEEDS_REFRESH) | |
| 258 loading_state_ = LOADING_STATE_UP_TO_DATE; | |
| 259 } | |
| 260 | |
| 261 bool PluginList::LoadPluginIntoPluginList( | |
| 262 const base::FilePath& path, | |
| 263 std::vector<webkit::WebPluginInfo>* plugins, | |
| 264 WebPluginInfo* plugin_info) { | |
| 265 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 266 << "Loading plugin " << path.value(); | |
| 267 if (!ReadPluginInfo(path, plugin_info)) | |
| 268 return false; | |
| 269 | |
| 270 if (!ShouldLoadPluginUsingPluginList(*plugin_info, plugins)) | |
| 271 return false; | |
| 272 | |
| 273 #if defined(OS_WIN) && !defined(NDEBUG) | |
| 274 if (path.BaseName().value() != L"npspy.dll") // Make an exception for NPSPY | |
| 275 #endif | |
| 276 { | |
| 277 for (size_t i = 0; i < plugin_info->mime_types.size(); ++i) { | |
| 278 // TODO: don't load global handlers for now. | |
| 279 // WebKit hands to the Plugin before it tries | |
| 280 // to handle mimeTypes on its own. | |
| 281 const std::string &mime_type = plugin_info->mime_types[i].mime_type; | |
| 282 if (mime_type == "*") | |
| 283 return false; | |
| 284 } | |
| 285 } | |
| 286 plugins->push_back(*plugin_info); | |
| 287 return true; | |
| 288 } | |
| 289 | |
| 290 void PluginList::GetPluginPathsToLoad(std::vector<base::FilePath>* plugin_paths)
{ | |
| 291 // Don't want to hold the lock while loading new plugins, so we don't block | |
| 292 // other methods if they're called on other threads. | |
| 293 std::vector<base::FilePath> extra_plugin_paths; | |
| 294 std::vector<base::FilePath> extra_plugin_dirs; | |
| 295 { | |
| 296 base::AutoLock lock(lock_); | |
| 297 extra_plugin_paths = extra_plugin_paths_; | |
| 298 extra_plugin_dirs = extra_plugin_dirs_; | |
| 299 } | |
| 300 | |
| 301 for (size_t i = 0; i < extra_plugin_paths.size(); ++i) { | |
| 302 const base::FilePath& path = extra_plugin_paths[i]; | |
| 303 if (std::find(plugin_paths->begin(), plugin_paths->end(), path) != | |
| 304 plugin_paths->end()) { | |
| 305 continue; | |
| 306 } | |
| 307 plugin_paths->push_back(path); | |
| 308 } | |
| 309 | |
| 310 if (NPAPIPluginsSupported()) { | |
| 311 // A bit confusingly, this function is used to load Pepper plugins as well. | |
| 312 // Those are all internal plugins so we have to use extra_plugin_paths. | |
| 313 for (size_t i = 0; i < extra_plugin_dirs.size(); ++i) | |
| 314 GetPluginsInDir(extra_plugin_dirs[i], plugin_paths); | |
| 315 | |
| 316 std::vector<base::FilePath> directories_to_scan; | |
| 317 GetPluginDirectories(&directories_to_scan); | |
| 318 for (size_t i = 0; i < directories_to_scan.size(); ++i) | |
| 319 GetPluginsInDir(directories_to_scan[i], plugin_paths); | |
| 320 | |
| 321 #if defined(OS_WIN) | |
| 322 GetPluginPathsFromRegistry(plugin_paths); | |
| 323 #endif | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 void PluginList::SetPlugins(const std::vector<webkit::WebPluginInfo>& plugins) { | |
| 328 base::AutoLock lock(lock_); | |
| 329 | |
| 330 DCHECK_NE(LOADING_STATE_REFRESHING, loading_state_); | |
| 331 loading_state_ = LOADING_STATE_UP_TO_DATE; | |
| 332 | |
| 333 plugins_list_.clear(); | |
| 334 plugins_list_.insert(plugins_list_.end(), plugins.begin(), plugins.end()); | |
| 335 } | |
| 336 | |
| 337 void PluginList::set_will_load_plugins_callback(const base::Closure& callback) { | |
| 338 base::AutoLock lock(lock_); | |
| 339 will_load_plugins_callback_ = callback; | |
| 340 } | |
| 341 | |
| 342 void PluginList::GetPlugins(std::vector<WebPluginInfo>* plugins) { | |
| 343 LoadPlugins(); | |
| 344 base::AutoLock lock(lock_); | |
| 345 plugins->insert(plugins->end(), plugins_list_.begin(), plugins_list_.end()); | |
| 346 } | |
| 347 | |
| 348 bool PluginList::GetPluginsNoRefresh( | |
| 349 std::vector<webkit::WebPluginInfo>* plugins) { | |
| 350 base::AutoLock lock(lock_); | |
| 351 plugins->insert(plugins->end(), plugins_list_.begin(), plugins_list_.end()); | |
| 352 | |
| 353 return loading_state_ == LOADING_STATE_UP_TO_DATE; | |
| 354 } | |
| 355 | |
| 356 void PluginList::GetPluginInfoArray( | |
| 357 const GURL& url, | |
| 358 const std::string& mime_type, | |
| 359 bool allow_wildcard, | |
| 360 bool* use_stale, | |
| 361 std::vector<webkit::WebPluginInfo>* info, | |
| 362 std::vector<std::string>* actual_mime_types) { | |
| 363 DCHECK(mime_type == StringToLowerASCII(mime_type)); | |
| 364 DCHECK(info); | |
| 365 | |
| 366 if (!use_stale) | |
| 367 LoadPlugins(); | |
| 368 base::AutoLock lock(lock_); | |
| 369 if (use_stale) | |
| 370 *use_stale = (loading_state_ != LOADING_STATE_UP_TO_DATE); | |
| 371 info->clear(); | |
| 372 if (actual_mime_types) | |
| 373 actual_mime_types->clear(); | |
| 374 | |
| 375 std::set<base::FilePath> visited_plugins; | |
| 376 | |
| 377 // Add in plugins by mime type. | |
| 378 for (size_t i = 0; i < plugins_list_.size(); ++i) { | |
| 379 if (SupportsType(plugins_list_[i], mime_type, allow_wildcard)) { | |
| 380 base::FilePath path = plugins_list_[i].path; | |
| 381 if (visited_plugins.insert(path).second) { | |
| 382 info->push_back(plugins_list_[i]); | |
| 383 if (actual_mime_types) | |
| 384 actual_mime_types->push_back(mime_type); | |
| 385 } | |
| 386 } | |
| 387 } | |
| 388 | |
| 389 // Add in plugins by url. | |
| 390 std::string path = url.path(); | |
| 391 std::string::size_type last_dot = path.rfind('.'); | |
| 392 if (last_dot != std::string::npos) { | |
| 393 std::string extension = StringToLowerASCII(std::string(path, last_dot+1)); | |
| 394 std::string actual_mime_type; | |
| 395 for (size_t i = 0; i < plugins_list_.size(); ++i) { | |
| 396 if (SupportsExtension(plugins_list_[i], extension, &actual_mime_type)) { | |
| 397 base::FilePath path = plugins_list_[i].path; | |
| 398 if (visited_plugins.insert(path).second && | |
| 399 AllowMimeTypeMismatch(mime_type, actual_mime_type)) { | |
| 400 info->push_back(plugins_list_[i]); | |
| 401 if (actual_mime_types) | |
| 402 actual_mime_types->push_back(actual_mime_type); | |
| 403 } | |
| 404 } | |
| 405 } | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 bool PluginList::SupportsType(const webkit::WebPluginInfo& plugin, | |
| 410 const std::string& mime_type, | |
| 411 bool allow_wildcard) { | |
| 412 // Webkit will ask for a plugin to handle empty mime types. | |
| 413 if (mime_type.empty()) | |
| 414 return false; | |
| 415 | |
| 416 for (size_t i = 0; i < plugin.mime_types.size(); ++i) { | |
| 417 const webkit::WebPluginMimeType& mime_info = plugin.mime_types[i]; | |
| 418 if (net::MatchesMimeType(mime_info.mime_type, mime_type)) { | |
| 419 if (!allow_wildcard && mime_info.mime_type == "*") | |
| 420 continue; | |
| 421 return true; | |
| 422 } | |
| 423 } | |
| 424 return false; | |
| 425 } | |
| 426 | |
| 427 bool PluginList::SupportsExtension(const webkit::WebPluginInfo& plugin, | |
| 428 const std::string& extension, | |
| 429 std::string* actual_mime_type) { | |
| 430 for (size_t i = 0; i < plugin.mime_types.size(); ++i) { | |
| 431 const webkit::WebPluginMimeType& mime_type = plugin.mime_types[i]; | |
| 432 for (size_t j = 0; j < mime_type.file_extensions.size(); ++j) { | |
| 433 if (mime_type.file_extensions[j] == extension) { | |
| 434 if (actual_mime_type) | |
| 435 *actual_mime_type = mime_type.mime_type; | |
| 436 return true; | |
| 437 } | |
| 438 } | |
| 439 } | |
| 440 return false; | |
| 441 } | |
| 442 | |
| 443 PluginList::~PluginList() { | |
| 444 } | |
| 445 | |
| 446 | |
| 447 } // namespace npapi | |
| 448 } // namespace webkit | |
| OLD | NEW |