summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/app/generated_resources.grd9
-rw-r--r--chrome/browser/dom_ui/plugins_ui.cc126
-rw-r--r--chrome/browser/plugin_service.cc76
-rw-r--r--chrome/browser/plugin_service.h3
-rw-r--r--chrome/browser/plugin_updater.cc518
-rw-r--r--chrome/browser/plugin_updater.h141
-rw-r--r--chrome/browser/plugin_updater_unittest.cc112
-rw-r--r--chrome/browser/resources/plugins.html230
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
10 files changed, 971 insertions, 247 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index a59e90c..3ae294c 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3674,6 +3674,15 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_PLUGINS_ENABLE" desc="The link for enabling a plug-in.">
Enable
</message>
+ <message name="IDS_PLUGINS_DOWNLOAD" desc="The link label to download the latest version of this plugin">
+ Download Critical Security Update
+ </message>
+ <message name="IDS_PLUGINS_NAME" desc="The label for the name of this plugin">
+ Name:
+ </message>
+ <message name="IDS_PLUGINS_PRIORITY" desc="The label for the priority of this plugin in the list">
+ Priority:
+ </message>
<!-- Session Crashed Info Bar-->
<message name="IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON" desc="Title of the restore button in the session crashed view.">
diff --git a/chrome/browser/dom_ui/plugins_ui.cc b/chrome/browser/dom_ui/plugins_ui.cc
index b24a902..00e8a31 100644
--- a/chrome/browser/dom_ui/plugins_ui.cc
+++ b/chrome/browser/dom_ui/plugins_ui.cc
@@ -18,6 +18,7 @@
#include "chrome/browser/browser_window.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
+#include "chrome/browser/plugin_updater.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/chrome_paths.h"
@@ -70,6 +71,12 @@ void PluginsUIHTMLSource::StartDataRequest(const std::string& path,
l10n_util::GetString(IDS_PLUGINS_NONE_INSTALLED));
localized_strings.SetString(L"pluginDisabled",
l10n_util::GetString(IDS_PLUGINS_DISABLED_PLUGIN));
+ localized_strings.SetString(L"pluginDownload",
+ l10n_util::GetString(IDS_PLUGINS_DOWNLOAD));
+ localized_strings.SetString(L"pluginName",
+ l10n_util::GetString(IDS_PLUGINS_NAME));
+ localized_strings.SetString(L"pluginPriority",
+ l10n_util::GetString(IDS_PLUGINS_PRIORITY));
localized_strings.SetString(L"pluginVersion",
l10n_util::GetString(IDS_PLUGINS_VERSION));
localized_strings.SetString(L"pluginDescription",
@@ -136,10 +143,6 @@ class PluginsDOMHandler : public DOMMessageHandler {
void HandleShowTermsOfServiceMessage(const Value* value);
private:
- // Creates a dictionary containing all the information about the given plugin;
- // this is put into the list to "return" for the "requestPluginsData" message.
- DictionaryValue* CreatePluginDetailValue(const WebPluginInfo& plugin);
-
// Creates a dictionary containing the important parts of the information
// about the given plugin; this is put into a list and saved in prefs.
DictionaryValue* CreatePluginSummaryValue(const WebPluginInfo& plugin);
@@ -163,18 +166,8 @@ void PluginsDOMHandler::RegisterMessages() {
void PluginsDOMHandler::HandleRequestPluginsData(const Value* value) {
DictionaryValue* results = new DictionaryValue();
- // Add plugins to the results structure.
- ListValue* plugins_list = new ListValue();
-
- std::vector<WebPluginInfo> plugins;
- NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins);
-
- for (std::vector<WebPluginInfo>::const_iterator it = plugins.begin();
- it != plugins.end();
- ++it) {
- plugins_list->Append(CreatePluginDetailValue(*it));
- }
- results->Set(L"plugins", plugins_list);
+ // Grouped plugins.
+ results->Set(L"plugins", PluginUpdater::GetInstance()->GetPluginGroupsData());
dom_ui_->CallJavascriptFunction(L"returnPluginsData", *results);
}
@@ -186,26 +179,35 @@ void PluginsDOMHandler::HandleEnablePluginMessage(const Value* value) {
return;
const ListValue* list = static_cast<const ListValue*>(value);
- if (list->GetSize() != 2)
+ if (list->GetSize() != 3)
return;
- FilePath::StringType plugin_path;
std::string enable_str;
- if (!list->GetString(0, &plugin_path) ||
- !list->GetString(1, &enable_str))
+ std::string is_group_str;
+ if (!list->GetString(1, &enable_str) ||
+ !list->GetString(2, &is_group_str))
return;
- if (enable_str == "true")
- NPAPI::PluginList::Singleton()->EnablePlugin(FilePath(plugin_path));
- else
- NPAPI::PluginList::Singleton()->DisablePlugin(FilePath(plugin_path));
+ if (is_group_str == "true") {
+ std::wstring group_name;
+ if (!list->GetString(0, &group_name))
+ return;
+
+ PluginUpdater::GetInstance()->EnablePluginGroup(enable_str == "true",
+ WideToUTF16(group_name));
+ } else {
+ FilePath::StringType file_path;
+ if (!list->GetString(0, &file_path))
+ return;
- // TODO(viettrungluu): It's morally wrong to do this here (it should be done
- // by the plugins service), and we might also want to ensure that the plugins
+ PluginUpdater::GetInstance()->EnablePluginFile(enable_str == "true",
+ file_path);
+ }
+
+ // TODO(viettrungluu): We might also want to ensure that the plugins
// list is always written to prefs even when the user hasn't disabled a
- // plugin. This will require refactoring the plugin list and service.
- // <http://crbug.com/39101>
- UpdatePreferences();
+ // plugin. <http://crbug.com/39101>
+ PluginUpdater::GetInstance()->UpdatePreferences(dom_ui_->GetProfile());
}
void PluginsDOMHandler::HandleShowTermsOfServiceMessage(const Value* value) {
@@ -216,72 +218,6 @@ void PluginsDOMHandler::HandleShowTermsOfServiceMessage(const Value* value) {
browser->window()->Show();
}
-DictionaryValue* PluginsDOMHandler::CreatePluginDetailValue(
- const WebPluginInfo& plugin) {
- DictionaryValue* plugin_data = new DictionaryValue();
- plugin_data->SetString(L"path", plugin.path.value());
- plugin_data->SetString(L"name", plugin.name);
- plugin_data->SetString(L"version", plugin.version);
- plugin_data->SetString(L"description", plugin.desc);
- plugin_data->SetBoolean(L"enabled", plugin.enabled);
-
- ListValue* mime_types = new ListValue();
- for (std::vector<WebPluginMimeType>::const_iterator type_it =
- plugin.mime_types.begin();
- type_it != plugin.mime_types.end();
- ++type_it) {
- DictionaryValue* mime_type = new DictionaryValue();
- mime_type->SetString(L"mimeType", type_it->mime_type);
- mime_type->SetString(L"description", type_it->description);
-
- ListValue* file_extensions = new ListValue();
- for (std::vector<std::string>::const_iterator ext_it =
- type_it->file_extensions.begin();
- ext_it != type_it->file_extensions.end();
- ++ext_it) {
- file_extensions->Append(new StringValue(*ext_it));
- }
- mime_type->Set(L"fileExtensions", file_extensions);
-
- mime_types->Append(mime_type);
- }
- plugin_data->Set(L"mimeTypes", mime_types);
-
- return plugin_data;
-}
-
-DictionaryValue* PluginsDOMHandler::CreatePluginSummaryValue(
- const WebPluginInfo& plugin) {
- DictionaryValue* plugin_data = new DictionaryValue();
- plugin_data->SetString(L"path", plugin.path.value());
- plugin_data->SetString(L"name", plugin.name);
- plugin_data->SetString(L"version", plugin.version);
- plugin_data->SetBoolean(L"enabled", plugin.enabled);
- return plugin_data;
-}
-
-// TODO(viettrungluu): move this (and the registration of the prefs into the
-// plugins service.
-void PluginsDOMHandler::UpdatePreferences() {
- PrefService* prefs = dom_ui_->GetProfile()->GetPrefs();
-
- FilePath internal_dir;
- if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
- prefs->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir);
-
- ListValue* plugins_list = prefs->GetMutableList(prefs::kPluginsPluginsList);
- plugins_list->Clear();
-
- std::vector<WebPluginInfo> plugins;
- NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins);
-
- for (std::vector<WebPluginInfo>::const_iterator it = plugins.begin();
- it != plugins.end();
- ++it) {
- plugins_list->Append(CreatePluginSummaryValue(*it));
- }
-}
-
} // namespace
///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/plugin_service.cc b/chrome/browser/plugin_service.cc
index 165ca52..466aa8f 100644
--- a/chrome/browser/plugin_service.cc
+++ b/chrome/browser/plugin_service.cc
@@ -17,6 +17,7 @@
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/plugin_process_host.h"
+#include "chrome/browser/plugin_updater.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_process_host.h"
@@ -53,84 +54,13 @@ static void NotifyPluginsOfActivation() {
// static
bool PluginService::enable_chrome_plugins_ = true;
-bool PluginService::enable_internal_pdf_ = false;
// static
void PluginService::InitGlobalInstance(Profile* profile) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
- bool update_internal_dir = false;
- FilePath last_internal_dir =
- profile->GetPrefs()->GetFilePath(prefs::kPluginsLastInternalDirectory);
- FilePath cur_internal_dir;
- if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
- cur_internal_dir != last_internal_dir) {
- update_internal_dir = true;
- profile->GetPrefs()->SetFilePath(
- prefs::kPluginsLastInternalDirectory, cur_internal_dir);
- }
-
- bool found_internal_pdf = false;
- bool force_enable_internal_pdf = false;
- FilePath pdf_path;
- PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path);
- FilePath::StringType pdf_path_str = pdf_path.value();
- if (enable_internal_pdf_ &&
- !profile->GetPrefs()->GetBoolean(prefs::kPluginsEnabledInternalPDF)) {
- // We switched to the internal pdf plugin being on by default, and so we
- // need to force it to be enabled. We only want to do it this once though,
- // i.e. we don't want to enable it again if the user disables it afterwards.
- profile->GetPrefs()->SetBoolean(prefs::kPluginsEnabledInternalPDF, true);
- force_enable_internal_pdf = true;
- }
-
- // Disable plugins listed as disabled in prefs.
- if (ListValue* saved_plugins_list =
- profile->GetPrefs()->GetMutableList(prefs::kPluginsPluginsList)) {
- for (ListValue::const_iterator it = saved_plugins_list->begin();
- it != saved_plugins_list->end();
- ++it) {
- if (!(*it)->IsType(Value::TYPE_DICTIONARY)) {
- LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
- continue; // Oops, don't know what to do with this item.
- }
-
- DictionaryValue* plugin = static_cast<DictionaryValue*>(*it);
- FilePath::StringType path;
- if (!plugin->GetString(L"path", &path))
- continue;
-
- bool enabled = true;
- plugin->GetBoolean(L"enabled", &enabled);
-
- if (FilePath::CompareIgnoreCase(path, pdf_path_str) == 0) {
- found_internal_pdf = true;
- if (!enabled && force_enable_internal_pdf) {
- enabled = true;
- plugin->SetBoolean(L"enabled", true);
- }
- }
-
- FilePath plugin_path(path);
- if (update_internal_dir &&
- FilePath::CompareIgnoreCase(plugin_path.DirName().value(),
- last_internal_dir.value()) == 0) {
- // If the internal plugin directory has changed and if the plugin looks
- // internal, update its path in the prefs.
- plugin_path = cur_internal_dir.Append(plugin_path.BaseName());
- plugin->SetString(L"path", plugin_path.value());
- }
-
- if (!enabled)
- NPAPI::PluginList::Singleton()->DisablePlugin(plugin_path);
- }
- }
-
- if (!enable_internal_pdf_ && !found_internal_pdf) {
- // The internal PDF plugin is disabled by default, and the user hasn't
- // overridden the default.
- NPAPI::PluginList::Singleton()->DisablePlugin(pdf_path);
- }
+ // We first group the plugins and then figure out which groups to disable.
+ PluginUpdater::GetInstance()->DisablePluginGroupsFromPrefs(profile);
// Have Chrome plugins write their data to the profile directory.
GetInstance()->SetChromePluginDataDir(profile->GetPath());
diff --git a/chrome/browser/plugin_service.h b/chrome/browser/plugin_service.h
index b20e5ae..c1863df 100644
--- a/chrome/browser/plugin_service.h
+++ b/chrome/browser/plugin_service.h
@@ -155,9 +155,6 @@ class PluginService
// Set to true if chrome plugins are enabled. Defaults to true.
static bool enable_chrome_plugins_;
- // Set to true iff the internal pdf plugin is enabled by default.
- static bool enable_internal_pdf_;
-
DISALLOW_COPY_AND_ASSIGN(PluginService);
};
diff --git a/chrome/browser/plugin_updater.cc b/chrome/browser/plugin_updater.cc
new file mode 100644
index 0000000..5483561
--- /dev/null
+++ b/chrome/browser/plugin_updater.cc
@@ -0,0 +1,518 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/plugin_updater.h"
+
+#include <string>
+#include <vector>
+
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/pref_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/pref_names.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+
+/*private*/
+PluginGroup::PluginGroup(const string16& group_name,
+ const string16& name_matcher,
+ const std::string& version_range_low,
+ const std::string& version_range_high,
+ const std::string& min_version,
+ const std::string& update_url) {
+ group_name_ = group_name;
+ name_matcher_ = name_matcher;
+ version_range_low_str_ = version_range_low;
+ if (!version_range_low.empty()) {
+ version_range_low_.reset(
+ Version::GetVersionFromString(version_range_low));
+ }
+ version_range_high_str_ = version_range_high;
+ if (!version_range_high.empty()) {
+ version_range_high_.reset(
+ Version::GetVersionFromString(version_range_high));
+ }
+ min_version_str_ = min_version;
+ if (!min_version.empty()) {
+ min_version_.reset(Version::GetVersionFromString(min_version));
+ }
+ update_url_ = update_url;
+ enabled_ = false;
+ max_version_.reset(Version::GetVersionFromString("0"));
+}
+
+PluginGroup* PluginGroup::FromPluginGroupDefinition(
+ const PluginGroupDefinition& definition) {
+ return new PluginGroup(ASCIIToUTF16(definition.name),
+ ASCIIToUTF16(definition.name_matcher),
+ definition.version_matcher_low,
+ definition.version_matcher_high,
+ definition.min_version,
+ definition.update_url);
+}
+
+PluginGroup* PluginGroup::FromWebPluginInfo(const WebPluginInfo& wpi) {
+ // Create a matcher from the name of this plugin.
+ return new PluginGroup(WideToUTF16Hack(wpi.name), WideToUTF16Hack(wpi.name),
+ "", "", "", "");
+}
+
+PluginGroup* PluginGroup::Copy() {
+ return new PluginGroup(group_name_, name_matcher_, version_range_low_str_,
+ version_range_high_str_, min_version_str_,
+ update_url_);
+}
+
+const string16 PluginGroup::GetGroupName() const {
+ return group_name_;
+}
+
+bool PluginGroup::Match(const WebPluginInfo& plugin) const {
+ if (name_matcher_.empty()) {
+ return false;
+ }
+
+ // Look for the name matcher anywhere in the plugin name.
+ if (WideToUTF16Hack(plugin.name).find(name_matcher_) == string16::npos) {
+ return false;
+ }
+
+ if (version_range_low_.get() == NULL ||
+ version_range_high_.get() == NULL) {
+ return true;
+ }
+
+ // There's a version range, we must be in it.
+ scoped_ptr<Version> plugin_version(
+ Version::GetVersionFromString(plugin.version));
+ if (plugin_version.get() == NULL) {
+ // No version could be extracted, assume we don't match the range.
+ return false;
+ }
+
+ // We match if we are in the range: [low, high)
+ return (version_range_low_->CompareTo(*plugin_version) <= 0 &&
+ version_range_high_->CompareTo(*plugin_version) > 0);
+}
+
+void PluginGroup::AddPlugin(const WebPluginInfo& plugin, int position) {
+ web_plugin_infos_.push_back(plugin);
+ // The position of this plugin relative to the global list of plugins.
+ web_plugin_positions_.push_back(position);
+ description_ = WideToUTF16Hack(plugin.desc);
+
+ // A group is enabled if any of the files are enabled.
+ if (plugin.enabled) {
+ enabled_ = true;
+ }
+
+ // update max_version_. Remove spaces and ')' from the version string,
+ // Replace any instances of 'r', ',' or '(' with a dot.
+ std::wstring version = plugin.version;
+ RemoveChars(version, L") ", &version);
+ std::replace(version.begin(), version.end(), 'r', '.');
+ std::replace(version.begin(), version.end(), ',', '.');
+ std::replace(version.begin(), version.end(), '(', '.');
+
+ scoped_ptr<Version> plugin_version(
+ Version::GetVersionFromString(version));
+ if (plugin_version.get() != NULL) {
+ if (plugin_version->CompareTo(*(max_version_)) > 0) {
+ max_version_.reset(plugin_version.release());
+ }
+ }
+}
+
+DictionaryValue* PluginGroup::GetSummary() const {
+ DictionaryValue* result = new DictionaryValue();
+ result->SetStringFromUTF16(L"name", group_name_);
+ result->SetBoolean(L"enabled", enabled_);
+ return result;
+}
+
+DictionaryValue* PluginGroup::GetData() const {
+ DictionaryValue* result = new DictionaryValue();
+ result->SetStringFromUTF16(L"name", group_name_);
+ result->SetStringFromUTF16(L"description", description_);
+ result->SetString(L"version", max_version_->GetString());
+ result->SetString(L"update_url", update_url_);
+ result->SetBoolean(L"critical", IsVulnerable());
+ result->SetBoolean(L"enabled", enabled_);
+
+ ListValue* plugin_files = new ListValue();
+ for (size_t i = 0; i < web_plugin_infos_.size(); ++i) {
+ const WebPluginInfo& web_plugin = web_plugin_infos_[i];
+ int priority = web_plugin_positions_[i];
+ DictionaryValue* plugin_file = new DictionaryValue();
+ plugin_file->SetString(L"name", web_plugin.name);
+ plugin_file->SetString(L"description", web_plugin.desc);
+ plugin_file->SetString(L"path", web_plugin.path.value());
+ plugin_file->SetString(L"version", web_plugin.version);
+ plugin_file->SetBoolean(L"enabled", web_plugin.enabled);
+ plugin_file->SetInteger(L"priority", priority);
+
+ ListValue* mime_types = new ListValue();
+ for (std::vector<WebPluginMimeType>::const_iterator type_it =
+ web_plugin.mime_types.begin();
+ type_it != web_plugin.mime_types.end();
+ ++type_it) {
+ DictionaryValue* mime_type = new DictionaryValue();
+ mime_type->SetString(L"mimeType", type_it->mime_type);
+ mime_type->SetString(L"description", type_it->description);
+
+ ListValue* file_extensions = new ListValue();
+ for (std::vector<std::string>::const_iterator ext_it =
+ type_it->file_extensions.begin();
+ ext_it != type_it->file_extensions.end();
+ ++ext_it) {
+ file_extensions->Append(new StringValue(*ext_it));
+ }
+ mime_type->Set(L"fileExtensions", file_extensions);
+
+ mime_types->Append(mime_type);
+ }
+ plugin_file->Set(L"mimeTypes", mime_types);
+
+ plugin_files->Append(plugin_file);
+ }
+ result->Set(L"plugin_files", plugin_files);
+
+ return result;
+}
+
+// Returns true if the latest version of this plugin group is vulnerable.
+bool PluginGroup::IsVulnerable() const {
+ if (min_version_.get() == NULL || max_version_->GetString() == "0") {
+ return false;
+ }
+ return max_version_->CompareTo(*min_version_) < 0;
+}
+
+void PluginGroup::Enable(bool enable) {
+ for (std::vector<WebPluginInfo>::const_iterator it =
+ web_plugin_infos_.begin();
+ it != web_plugin_infos_.end(); ++it) {
+ if (enable) {
+ NPAPI::PluginList::Singleton()->EnablePlugin(
+ FilePath(it->path));
+ } else {
+ NPAPI::PluginList::Singleton()->DisablePlugin(
+ FilePath(it->path));
+ }
+ }
+}
+
+#if defined(OS_MACOSX)
+// Plugin Groups for Mac.
+// Plugins are listed here as soon as vulnerabilities and solutions
+// (new versions) are published.
+// TODO(panayiotis): Track Java as soon as it's supported on Chrome Mac.
+// TODO(panayiotis): Get the Real Player version on Mac, somehow.
+static const PluginGroupDefinition kGroupDefinitions[] = {
+ { "Quicktime", "QuickTime Plug-in", "", "", "7.6.6",
+ "http://www.apple.com/quicktime/download/" },
+ { "Flash", "Shockwave Flash", "", "", "10.0.45",
+ "http://get.adobe.com/flashplayer/" },
+ { "Silverlight 3", "Silverlight", "0", "4", "3.0.50106.0",
+ "http://go.microsoft.com/fwlink/?LinkID=185927" },
+ { "Silverlight 4", "Silverlight", "4", "5", "",
+ "http://go.microsoft.com/fwlink/?LinkID=185927" },
+ { "Flip4Mac", "Flip4Mac", "", "", "2.2.1",
+ "http://www.telestream.net/flip4mac-wmv/overview.htm" },
+ { "Shockwave", "Shockwave for Director", "", "", "11.5.7.609",
+ "http://www.adobe.com/shockwave/download/" }
+};
+
+#elif defined(OS_WIN)
+// TODO(panayiotis): We should group "RealJukebox NS Plugin" with the rest of
+// the RealPlayer files.
+static const PluginGroupDefinition kGroupDefinitions[] = {
+ { "Quicktime", "QuickTime Plug-in", "", "", "7.6.6",
+ "http://www.apple.com/quicktime/download/" },
+ { "Java 6", "Java", "", "6", "6.0.200",
+ "http://www.java.com/" },
+ { "Adobe Reader 9", "Adobe Acrobat", "9", "10", "9.3.2",
+ "http://get.adobe.com/reader/" },
+ { "Adobe Reader 8", "Adobe Acrobat", "0", "9", "8.2.2",
+ "http://get.adobe.com/reader/" },
+ { "Flash", "Shockwave Flash", "", "", "10.0.45",
+ "http://get.adobe.com/flashplayer/" },
+ { "Silverlight 3", "Silverlight", "0", "4", "3.0.50106.0",
+ "http://go.microsoft.com/fwlink/?LinkID=185927" },
+ { "Silverlight 4", "Silverlight", "4", "5", "",
+ "http://go.microsoft.com/fwlink/?LinkID=185927" },
+ { "Shockwave", "Shockwave for Director", "", "", "11.5.7.609",
+ "http://www.adobe.com/shockwave/download/" },
+ { "DivX Player", "DivX Web Player", "", "", "1.4.3.4",
+ "http://download.divx.com/divx/autoupdate/player/DivXWebPlayerInstaller.exe" },
+ // These are here for grouping, no vulnerabilies known.
+ { "Windows Media Player", "Windows Media Player", "", "", "", "" },
+ { "Microsoft Office", "Microsoft Office", "", "", "", "" },
+ // TODO(panayiotis): The vulnerable versions are
+ // (v >= 6.0.12.1040 && v <= 6.0.12.1663)
+ // || v == 6.0.12.1698 || v == 6.0.12.1741
+ { "RealPlayer", "RealPlayer", "", "", "",
+ "http://www.adobe.com/shockwave/download/" },
+};
+
+#else
+static const PluginGroupDefinition kGroupDefinitions[] = {};
+#endif
+
+/*static*/
+const PluginGroupDefinition* PluginUpdater::GetPluginGroupDefinitions() {
+ return kGroupDefinitions;
+}
+
+/*static*/
+const size_t PluginUpdater::GetPluginGroupDefinitionsSize() {
+ // TODO(viettrungluu): |arraysize()| doesn't work with zero-size arrays.
+ return ARRAYSIZE_UNSAFE(kGroupDefinitions);
+}
+
+// static
+PluginUpdater* PluginUpdater::GetInstance() {
+ return Singleton<PluginUpdater>::get();
+}
+
+PluginUpdater::PluginUpdater() {
+ const PluginGroupDefinition* definitions = GetPluginGroupDefinitions();
+ for (size_t i = 0; i < GetPluginGroupDefinitionsSize(); ++i) {
+ PluginGroup* definition_group = PluginGroup::FromPluginGroupDefinition(
+ definitions[i]);
+ plugin_group_definitions_.push_back(linked_ptr<PluginGroup>(
+ definition_group));
+ }
+}
+
+PluginUpdater::~PluginUpdater() {
+}
+
+// Convert to a List of Groups
+void PluginUpdater::GetPluginGroups(
+ std::vector<linked_ptr<PluginGroup> >* plugin_groups) {
+ // Read all plugins and convert them to plugin groups
+ std::vector<WebPluginInfo> web_plugins;
+ NPAPI::PluginList::Singleton()->GetPlugins(false, &web_plugins);
+
+ // We first search for an existing group that matches our name,
+ // and only create a new group if we can't find any.
+ for (size_t i = 0; i < web_plugins.size(); ++i) {
+ const WebPluginInfo& web_plugin = web_plugins[i];
+ bool found = false;
+ for (std::vector<linked_ptr<PluginGroup> >::iterator existing_it =
+ plugin_groups->begin();
+ existing_it != plugin_groups->end();
+ ++existing_it) {
+ if ((*existing_it)->Match(web_plugin)) {
+ (*existing_it)->AddPlugin(web_plugin, i);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // See if this plugin matches any of the hardcoded groups.
+ for (std::vector<linked_ptr<PluginGroup> >::iterator defs_it =
+ plugin_group_definitions_.begin();
+ defs_it != plugin_group_definitions_.end();
+ ++defs_it) {
+ if ((*defs_it)->Match(web_plugin)) {
+ // Make a copy, otherwise we'd be modifying plugin_group_defs_ every
+ // time this method is called.
+ PluginGroup* copy = (*defs_it)->Copy();
+ copy->AddPlugin(web_plugin, i);
+ plugin_groups->push_back(linked_ptr<PluginGroup>(copy));
+ found = true;
+ break;
+ }
+ }
+ }
+
+ // Not found in our hardcoded list, create a new one.
+ if (!found) {
+ PluginGroup* plugin_group = PluginGroup::FromWebPluginInfo(web_plugin);
+ plugin_group->AddPlugin(web_plugin, i);
+ plugin_groups->push_back(linked_ptr<PluginGroup>(plugin_group));
+ }
+ }
+}
+
+ListValue* PluginUpdater::GetPluginGroupsData() {
+ std::vector<linked_ptr<PluginGroup> > plugin_groups;
+ GetPluginGroups(&plugin_groups);
+
+ // Construct DictionaryValues to return to the UI
+ ListValue* plugin_groups_data = new ListValue();
+ for (std::vector<linked_ptr<PluginGroup> >::iterator it =
+ plugin_groups.begin();
+ it != plugin_groups.end();
+ ++it) {
+ plugin_groups_data->Append((*it)->GetData());
+ }
+ return plugin_groups_data;
+}
+
+ListValue* PluginUpdater::GetPluginGroupsSummary() {
+ std::vector<linked_ptr<PluginGroup> > plugin_groups;
+ GetPluginGroups(&plugin_groups);
+
+ // Construct DictionaryValues to return to the UI
+ ListValue* plugin_groups_data = new ListValue();
+ for (std::vector<linked_ptr<PluginGroup> >::iterator it =
+ plugin_groups.begin();
+ it != plugin_groups.end();
+ ++it) {
+ plugin_groups_data->Append((*it)->GetSummary());
+ }
+ return plugin_groups_data;
+}
+
+void PluginUpdater::EnablePluginGroup(bool enable,
+ const string16& group_name) {
+ std::vector<linked_ptr<PluginGroup> > plugin_groups;
+ GetPluginGroups(&plugin_groups);
+
+ for (std::vector<linked_ptr<PluginGroup> >::iterator it =
+ plugin_groups.begin();
+ it != plugin_groups.end();
+ ++it) {
+ if ((*it)->GetGroupName() == group_name) {
+ (*it)->Enable(enable);
+ }
+ }
+}
+
+void PluginUpdater::EnablePluginFile(bool enable,
+ const FilePath::StringType& file_path) {
+ if (enable)
+ NPAPI::PluginList::Singleton()->EnablePlugin(FilePath(file_path));
+ else
+ NPAPI::PluginList::Singleton()->DisablePlugin(FilePath(file_path));
+}
+
+// static
+bool PluginUpdater::enable_internal_pdf_ = false;
+
+void PluginUpdater::DisablePluginGroupsFromPrefs(Profile* profile) {
+ bool update_internal_dir = false;
+ FilePath last_internal_dir =
+ profile->GetPrefs()->GetFilePath(prefs::kPluginsLastInternalDirectory);
+ FilePath cur_internal_dir;
+ if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
+ cur_internal_dir != last_internal_dir) {
+ update_internal_dir = true;
+ profile->GetPrefs()->SetFilePath(
+ prefs::kPluginsLastInternalDirectory, cur_internal_dir);
+ }
+
+ bool found_internal_pdf = false;
+ bool force_enable_internal_pdf = false;
+ FilePath pdf_path;
+ PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path);
+ FilePath::StringType pdf_path_str = pdf_path.value();
+ if (enable_internal_pdf_ &&
+ !profile->GetPrefs()->GetBoolean(prefs::kPluginsEnabledInternalPDF)) {
+ // We switched to the internal pdf plugin being on by default, and so we
+ // need to force it to be enabled. We only want to do it this once though,
+ // i.e. we don't want to enable it again if the user disables it afterwards.
+ profile->GetPrefs()->SetBoolean(prefs::kPluginsEnabledInternalPDF, true);
+ force_enable_internal_pdf = true;
+ }
+
+ if (ListValue* saved_plugins_list =
+ profile->GetPrefs()->GetMutableList(prefs::kPluginsPluginsList)) {
+ for (ListValue::const_iterator it = saved_plugins_list->begin();
+ it != saved_plugins_list->end();
+ ++it) {
+ if (!(*it)->IsType(Value::TYPE_DICTIONARY)) {
+ LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
+ continue; // Oops, don't know what to do with this item.
+ }
+
+ DictionaryValue* plugin = static_cast<DictionaryValue*>(*it);
+ string16 group_name;
+ bool enabled = true;
+ plugin->GetBoolean(L"enabled", &enabled);
+
+ FilePath::StringType path;
+ // The plugin list constains all the plugin files in addition to the
+ // plugin groups.
+ if (!enabled && plugin->GetString(L"path", &path)) {
+ // Files have a path attribute, groups don't.
+ if (FilePath::CompareIgnoreCase(path, pdf_path_str) == 0) {
+ found_internal_pdf = true;
+ if (!enabled && force_enable_internal_pdf) {
+ enabled = true;
+ plugin->SetBoolean(L"enabled", true);
+ }
+ }
+ FilePath plugin_path(path);
+ if (update_internal_dir &&
+ FilePath::CompareIgnoreCase(plugin_path.DirName().value(),
+ last_internal_dir.value()) == 0) {
+ // If the internal plugin directory has changed and if the plugin
+ // looks internal, update its path in the prefs.
+ plugin_path = cur_internal_dir.Append(plugin_path.BaseName());
+ plugin->SetString(L"path", plugin_path.value()); }
+
+ if (!enabled)
+ NPAPI::PluginList::Singleton()->DisablePlugin(plugin_path);
+ } else if (!enabled && plugin->GetStringAsUTF16(L"name", &group_name)) {
+ // Otherwise this is a list of groups.
+ EnablePluginGroup(false, group_name);
+ }
+ }
+ }
+
+ if (!enable_internal_pdf_ && !found_internal_pdf) {
+ // The internal PDF plugin is disabled by default, and the user hasn't
+ // overridden the default.
+ NPAPI::PluginList::Singleton()->DisablePlugin(pdf_path);
+ }
+}
+
+DictionaryValue* PluginUpdater::CreatePluginFileSummary(
+ const WebPluginInfo& plugin) {
+ DictionaryValue* data = new DictionaryValue();
+ data->SetString(L"path", plugin.path.value());
+ data->SetString(L"name", plugin.name);
+ data->SetString(L"version", plugin.version);
+ data->SetBoolean(L"enabled", plugin.enabled);
+ return data;
+}
+
+void PluginUpdater::UpdatePreferences(Profile* profile) {
+ ListValue* plugins_list = profile->GetPrefs()->GetMutableList(
+ prefs::kPluginsPluginsList);
+ plugins_list->Clear();
+
+ FilePath internal_dir;
+ if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
+ profile->GetPrefs()->SetFilePath(prefs::kPluginsLastInternalDirectory,
+ internal_dir);
+
+ // Add the plugin files.
+ std::vector<WebPluginInfo> plugins;
+ NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins);
+ for (std::vector<WebPluginInfo>::const_iterator it = plugins.begin();
+ it != plugins.end();
+ ++it) {
+ plugins_list->Append(CreatePluginFileSummary(*it));
+ }
+
+ // Add the groups as well.
+ ListValue* plugin_groups = GetPluginGroupsSummary();
+ for (ListValue::const_iterator it = plugin_groups->begin();
+ it != plugin_groups->end();
+ ++it) {
+ plugins_list->Append(*it);
+ }
+}
diff --git a/chrome/browser/plugin_updater.h b/chrome/browser/plugin_updater.h
new file mode 100644
index 0000000..a18d222
--- /dev/null
+++ b/chrome/browser/plugin_updater.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PLUGIN_UPDATER_H_
+#define CHROME_BROWSER_PLUGIN_UPDATER_H_
+
+#include <vector>
+#include "base/basictypes.h"
+#include "base/linked_ptr.h"
+#include "base/scoped_ptr.h"
+#include "base/singleton.h"
+#include "base/string16.h"
+#include "base/values.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+
+class Version;
+class Profile;
+
+// Hard-coded definitions of plugin groups.
+typedef struct {
+ const char* const name; // Name of this group.
+ const char* const name_matcher; // Substring matcher for the plugin name.
+ const char* const version_matcher_low; // Matchers for the plugin version.
+ const char* const version_matcher_high;
+ const char* const min_version; // Minimum secure version.
+ const char* const update_url; // Location of latest secure version.
+} PluginGroupDefinition;
+
+
+// A PluginGroup contains at least one WebPluginInfo.
+// In addition, it knows if the plugin is critically vulnerable.
+class PluginGroup {
+ public:
+ // Creates a PluginGroup from a PluginGroupDefinition.
+ static PluginGroup* FromPluginGroupDefinition(
+ const PluginGroupDefinition& definition);
+
+ // Creates a PluginGroup from a WebPluginInfo -- when no hard-coded
+ // definition is found.
+ static PluginGroup* FromWebPluginInfo(const WebPluginInfo& wpi);
+
+ // Creates a copy of this plugin group.
+ PluginGroup* Copy();
+
+ // Returns true if the given plugin matches this group.
+ bool Match(const WebPluginInfo& plugin) const;
+
+ // Adds the given plugin to this group. Provide the position of the
+ // plugin as given by PluginList so we can display its priority.
+ void AddPlugin(const WebPluginInfo& plugin, int position);
+
+ // Enables/disables this group. This enables/disables all plugins in the
+ // group.
+ void Enable(bool enable);
+
+ // Returns this group's name
+ const string16 GetGroupName() const;
+
+ // Returns a DictionaryValue with data to display in the UI.
+ DictionaryValue* GetData() const;
+
+ // Returns a DictionaryValue with data to save in the preferences.
+ DictionaryValue* GetSummary() const;
+
+ // Returns true if the latest plugin in this group has known
+ // security problems.
+ bool IsVulnerable() const;
+
+ private:
+ PluginGroup(const string16& group_name,
+ const string16& name_matcher,
+ const std::string& version_range_low,
+ const std::string& version_range_high,
+ const std::string& min_version,
+ const std::string& update_url);
+
+ string16 group_name_;
+ string16 name_matcher_;
+ std::string version_range_low_str_;
+ std::string version_range_high_str_;
+ scoped_ptr<Version> version_range_low_;
+ scoped_ptr<Version> version_range_high_;
+ string16 description_;
+ std::string update_url_;
+ bool enabled_;
+ std::string min_version_str_;
+ scoped_ptr<Version> min_version_;
+ scoped_ptr<Version> max_version_;
+ std::vector<WebPluginInfo> web_plugin_infos_;
+ std::vector<int> web_plugin_positions_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginGroup);
+};
+
+class PluginUpdater {
+ public:
+ // Returns the PluginUpdater singleton.
+ static PluginUpdater* GetInstance();
+
+ static const PluginGroupDefinition* GetPluginGroupDefinitions();
+ static const size_t GetPluginGroupDefinitionsSize();
+
+ // Get a list of all the Plugin groups.
+ ListValue* GetPluginGroupsData();
+
+ // Get a list of all the Plugin groups.
+ ListValue* GetPluginGroupsSummary();
+
+ // Enable or disable a plugin group.
+ void EnablePluginGroup(bool enable, const string16& group_name);
+
+ // Enable or disable a specific plugin file.
+ void EnablePluginFile(bool enable, const FilePath::StringType& file_path);
+
+ // Disable all plugin groups as defined by the user's preference file.
+ void DisablePluginGroupsFromPrefs(Profile* profile);
+
+ // Write the enable/disable status to the user's preference file.
+ void UpdatePreferences(Profile* profile);
+
+ private:
+ friend struct DefaultSingletonTraits<PluginUpdater>;
+
+ PluginUpdater();
+ ~PluginUpdater();
+
+ void GetPluginGroups(std::vector<linked_ptr<PluginGroup> >* plugin_groups);
+
+ DictionaryValue* CreatePluginFileSummary(const WebPluginInfo& plugin);
+
+ std::vector<linked_ptr<PluginGroup> > plugin_group_definitions_;
+
+ // Set to true iff the internal pdf plugin is enabled by default.
+ static bool enable_internal_pdf_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginUpdater);
+};
+
+#endif // CHROME_BROWSER_PLUGIN_UPDATER_H_
diff --git a/chrome/browser/plugin_updater_unittest.cc b/chrome/browser/plugin_updater_unittest.cc
new file mode 100644
index 0000000..a7a2dfe
--- /dev/null
+++ b/chrome/browser/plugin_updater_unittest.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/plugin_updater.h"
+
+#include <string>
+#include <vector>
+
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+
+static const PluginGroupDefinition kPluginDef = {
+ "MyPlugin", "MyPlugin", "", "", "3.0.44", "http://latest/" };
+static const PluginGroupDefinition kPluginDef3 = {
+ "MyPlugin 3", "MyPlugin", "0", "4", "3.0.44", "http://latest" };
+static const PluginGroupDefinition kPluginDef4 = {
+ "MyPlugin 4", "MyPlugin", "4", "5", "4.0.44", "http://latest" };
+static const PluginGroupDefinition kPluginDefNotVulnerable = {
+ "MyPlugin", "MyPlugin", "", "", "", "http://latest" };
+
+// name, path, version, desc, mime_types, enabled.
+static WebPluginInfo kPlugin2043 = {
+ L"MyPlugin", FilePath(), L"2.0.43", L"",
+ std::vector<WebPluginMimeType>(), true };
+static WebPluginInfo kPlugin3043 = {
+ L"MyPlugin", FilePath(), L"3.0.43", L"",
+ std::vector<WebPluginMimeType>(), true };
+static WebPluginInfo kPlugin3044 = {
+ L"MyPlugin", FilePath(), L"3.0.44", L"",
+ std::vector<WebPluginMimeType>(), true };
+static WebPluginInfo kPlugin3045 = {
+ L"MyPlugin", FilePath(), L"3.0.45", L"",
+ std::vector<WebPluginMimeType>(), true };
+static WebPluginInfo kPlugin4043 = {
+ L"MyPlugin", FilePath(), L"4.0.43", L"",
+ std::vector<WebPluginMimeType>(), true };
+
+class PluginUpdaterTest : public testing::Test {
+};
+
+TEST(PluginUpdaterTest, PluginGroupMatch) {
+ scoped_ptr<PluginGroup> group(PluginGroup::FromPluginGroupDefinition(
+ kPluginDef3));
+ EXPECT_TRUE(group->Match(kPlugin3045));
+ group->AddPlugin(kPlugin3045, 0);
+ EXPECT_FALSE(group->IsVulnerable());
+}
+
+TEST(PluginUpdaterTest, PluginGroupMatchMultipleFiles) {
+ scoped_ptr<PluginGroup> group(PluginGroup::FromPluginGroupDefinition(
+ kPluginDef3));
+ EXPECT_TRUE(group->Match(kPlugin3043));
+ group->AddPlugin(kPlugin3043, 0);
+ EXPECT_TRUE(group->IsVulnerable());
+
+ EXPECT_TRUE(group->Match(kPlugin3045));
+ group->AddPlugin(kPlugin3045, 1);
+ EXPECT_FALSE(group->IsVulnerable());
+}
+
+TEST(PluginUpdaterTest, PluginGroupMatchCorrectVersion) {
+ scoped_ptr<PluginGroup> group(PluginGroup::FromPluginGroupDefinition(
+ kPluginDef3));
+ EXPECT_TRUE(group->Match(kPlugin2043));
+ EXPECT_TRUE(group->Match(kPlugin3043));
+ EXPECT_FALSE(group->Match(kPlugin4043));
+
+ group.reset(PluginGroup::FromPluginGroupDefinition(kPluginDef4));
+ EXPECT_FALSE(group->Match(kPlugin2043));
+ EXPECT_FALSE(group->Match(kPlugin3043));
+ EXPECT_TRUE(group->Match(kPlugin4043));
+}
+
+TEST(PluginUpdaterTest, PluginGroupDefinition) {
+ const PluginGroupDefinition* definitions =
+ PluginUpdater::GetPluginGroupDefinitions();
+ for (size_t i = 0; i < PluginUpdater::GetPluginGroupDefinitionsSize(); ++i) {
+ scoped_ptr<PluginGroup> def_group(
+ PluginGroup::FromPluginGroupDefinition(definitions[i]));
+ ASSERT_TRUE(def_group.get() != NULL);
+ EXPECT_FALSE(def_group->Match(kPlugin2043));
+ }
+}
+
+TEST(PluginUpdaterTest, VersionExtraction) {
+ // Some real-world plugin versions (spaces, commata, parentheses, 'r', oh my)
+ const char* versions[][2] = {
+ { "7.6.6 (1671)", "7.6.6.1671" }, // Quicktime
+ { "2, 0, 0, 254", "2.0.0.254" }, // DivX
+ { "3, 0, 0, 0", "3.0.0.0" }, // Picasa
+ { "1, 0, 0, 1", "1.0.0.1" }, // Earth
+ { "10,0,45,2", "10.0.45.2" }, // Flash
+ { "11.5.7r609", "11.5.7.609"} // Shockwave
+ };
+
+ for (size_t i = 0; i < arraysize(versions); i++) {
+ const WebPluginInfo plugin = {
+ L"Blah Plugin", FilePath(), ASCIIToWide(versions[i][0]), L"",
+ std::vector<WebPluginMimeType>(), true };
+ scoped_ptr<PluginGroup> group(PluginGroup::FromWebPluginInfo(plugin));
+ EXPECT_TRUE(group->Match(plugin));
+ group->AddPlugin(plugin, 0);
+ scoped_ptr<DictionaryValue> data(group->GetData());
+ std::string version;
+ data->GetString(L"version", &version);
+ EXPECT_EQ(versions[i][1], version);
+ }
+}
diff --git a/chrome/browser/resources/plugins.html b/chrome/browser/resources/plugins.html
index ba0a4a2..d1a8d46 100644
--- a/chrome/browser/resources/plugins.html
+++ b/chrome/browser/resources/plugins.html
@@ -162,7 +162,7 @@ html[dir=rtl] .tmi-mode {
}
.plugin-disabled > td {
- background: url('gray.png') 0px 0px;
+ background-color: #f0f0f0;
color: #a0a0a0;
padding-bottom: 4px;
padding-top: 5px;
@@ -173,10 +173,26 @@ html[dir=rtl] .tmi-mode {
padding-top: 5px;
}
+.plugin-file-disabled {
+ background-color: #f0f0f0;
+ color: #a0a0a0;
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
+
+.plugin-file-enabled {
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
+
.plugin {
border-bottom: 1px solid #cdcdcd;
}
+.critical {
+ color: red;
+}
+
/* Indent the text related to each plug-in. */
.plugin-text {
padding-left: 5px;
@@ -203,6 +219,10 @@ html[dir=rtl] .plugin-text {
-webkit-border-vertical-spacing: 0;
}
+.plugin-details {
+ padding-left: 1em;
+}
+
/* Separate the inital line, Description, Location, and MIME Types lines. */
.plugin-details > div {
padding-top: 0.1em
@@ -269,32 +289,41 @@ button {
*/
var pluginDataFormat = {
'plugins': [
- {
- 'path': '/blahblah/blahblah/MyCrappyPlugin.plugin',
- 'name': 'MyCrappyPlugin',
- 'version': '1.2.3',
- 'description': 'My crappy plugin',
- 'mimeTypes': [
- { 'description': 'Foo Media',
- 'fileExtensions': [ 'foo' ],
- 'mimeType': 'application/x-my-foo' },
- { 'description': 'Bar Stuff',
- 'fileExtensions': [ 'bar','baz' ],
- 'mimeType': 'application/my-bar' }
- ],
- 'enabled': true
- },
- {
- 'path': '/foobar/baz/YourGreatPlugin.plugin',
- 'name': 'YourGreatPlugin',
- 'version': '4.5',
- 'description': 'Your great plugin',
- 'mimeTypes': [
- { 'description': 'Baz Stuff',
- 'fileExtensions': [ 'baz' ],
- 'mimeType': 'application/x-your-baz' }
- ],
- 'enabled': false
+ { 'name': 'Group Name',
+ 'description': 'description',
+ 'version': 'version',
+ 'update_url': 'http://update/',
+ 'critical': true,
+ 'enabled': true,
+ 'plugin_files': [
+ {
+ 'path': '/blahblah/blahblah/MyCrappyPlugin.plugin',
+ 'name': 'MyCrappyPlugin',
+ 'version': '1.2.3',
+ 'description': 'My crappy plugin',
+ 'mimeTypes': [
+ { 'description': 'Foo Media',
+ 'fileExtensions': [ 'foo' ],
+ 'mimeType': 'application/x-my-foo' },
+ { 'description': 'Bar Stuff',
+ 'fileExtensions': [ 'bar','baz' ],
+ 'mimeType': 'application/my-bar' }
+ ],
+ 'enabled': true
+ },
+ {
+ 'path': '/foobar/baz/YourGreatPlugin.plugin',
+ 'name': 'YourGreatPlugin',
+ 'version': '4.5',
+ 'description': 'Your great plugin',
+ 'mimeTypes': [
+ { 'description': 'Baz Stuff',
+ 'fileExtensions': [ 'baz' ],
+ 'mimeType': 'application/x-your-baz' }
+ ],
+ 'enabled': false
+ }
+ ]
}
]
};
@@ -375,9 +404,10 @@ function returnPluginsData(pluginsData){
/**
* Handles a 'enable' or 'disable' button getting clicked.
*/
-function handleEnablePlugin(node, enable) {
+function handleEnablePlugin(node, enable, is_group) {
// Tell the C++ PluginsDOMHandler to enable/disable the plugin.
- chrome.send('enablePlugin', [node.pluginPath, String(enable)]);
+ chrome.send('enablePlugin', [String(node.path), String(enable),
+ String(is_group)]);
requestPluginsData();
}
@@ -403,7 +433,7 @@ function toggleTmiMode() {
* Determines whether a plugin's version should be displayed.
*/
function shouldDisplayPluginVersion(plugin) {
- return !!plugin.version;
+ return !!plugin.version && plugin.version != "0";
}
/**
@@ -480,70 +510,118 @@ document.addEventListener('DOMContentLoaded', requestPluginsData);
<div class="plugin-text">
<div>
<span class="plugin-name" dir="ltr"
- jscontent="name">PLUGIN NAME</span>
+ jscontent="name">NAME</span>
+ <span jsdisplay="plugin_files.length > 1"
+ jscontent="'(' + plugin_files.length +' files)'">(x)</span>
<span jsdisplay="shouldDisplayPluginVersion($this)">
- <span i18n-content="pluginVersion">VERSION</span>
- <span dir="ltr" jscontent="version">x.x.x.x</span>
+ <span jsvalues=".className:critical? 'critical': ''"
+ dir="ltr" jscontent="version">x.x.x.x</span>
</span>
+ <a jsdisplay="critical" jsvalues=".href:update_url"
+ i18n-content="pluginDownload">DOWNLOAD UPDATE</a>
<span jsdisplay="!enabled"
i18n-content="pluginDisabled">(DISABLED)</span>
+ <div jsdisplay="shouldDisplayPluginDescription($this)">
+ <span dir="ltr" jsvalues=".innerHTML:description">
+ </div>
</div>
- <div class="plugin-details">
- <div><table><tr jsdisplay="shouldDisplayPluginDescription($this)">
- <td class="plugin-details-label"
- i18n-content="pluginDescription">DESCRIPTION:</td>
- <td><span dir="ltr" jsvalues=".innerHTML:description"></td>
- </tr></table></div>
- <div><table><tr>
- <td class="plugin-details-label"
- i18n-content="pluginPath">PATH:</td>
- <td><span dir="ltr" jscontent="path"></span></td>
- </tr></table></div>
+ <div jsselect="plugin_files" class="plugin-details">
<div class="showInTmiMode">
- <table><tr jsdisplay="mimeTypes.length > 0">
- <td class="plugin-details-label"
- i18n-content="pluginMimeTypes">MIME_TYPES:</td>
- <td><table width="100%" class="mime-types">
- <tr class="header">
- <td i18n-content="pluginMimeTypesMimeType"
- >MIME type</td>
- <td i18n-content="pluginMimeTypesDescription"
- >DESCRIPTION</td>
- <td i18n-content="pluginMimeTypesFileExtensions"
- >FILE_EXTENSIONS</td>
- </tr>
- <tr jsselect="mimeTypes">
- <td><span dir="ltr"
- jscontent="mimeType"></span></td>
- <td><span dir="ltr"
- jsvalues=".innerHTML:description"></span></td>
- <td><table jsdisplay="fileExtensions.length > 0"
- class="hlisting">
- <tr><td jsselect="fileExtensions">
- <span dir="ltr" jscontent="'.' + $this">
- </td></tr>
- </table></td>
- </tr>
- </table></td>
- </tr></table>
+ <div jsvalues=
+ ".className:enabled ? 'plugin-file-enabled' : 'plugin-file-disabled'">
+ <div><table><tr>
+ <td class="plugin-details-label"
+ i18n-content="pluginName">NAME:</td>
+ <td><span dir="ltr" jscontent="name">NAME</span></td>
+ </tr></table></div>
+ <div><table><tr jsdisplay="shouldDisplayPluginDescription($this)">
+ <td class="plugin-details-label"
+ i18n-content="pluginDescription">DESCRIPTION:</td>
+ <td><span dir="ltr" jsvalues=".innerHTML:description"></td>
+ </tr></table></div>
+ <div><table><tr>
+ <td class="plugin-details-label"
+ i18n-content="pluginVersion">VERSION:</td>
+ <td><span dir="ltr" jscontent="version">x.x.x.x</span></td>
+ </tr></table></div>
+ <div><table><tr>
+ <td class="plugin-details-label"
+ i18n-content="pluginPriority">PRIORITY:</td>
+ <td><span dir="ltr" jscontent="priority">x</span></td>
+ </tr></table></div>
+ <div><table><tr>
+ <td class="plugin-details-label"
+ i18n-content="pluginPath">PATH:</td>
+ <td><span dir="ltr" jscontent="path"></span></td>
+ </tr></table></div>
+ <div><table><tr>
+ <td class="plugin-details-label">&nbsp;</td>
+ <td>
+ <span jsdisplay="!enabled"
+ i18n-content="pluginDisabled">(DISABLED)</span>
+ <span>
+ <a
+ jsvalues=".path:path"
+ jsdisplay="enabled"
+ onclick="handleEnablePlugin(this, false, false)"
+ href="javascript:void(0);"
+ i18n-content="disable"
+ >DISABLE</a>
+ <a
+ jsvalues=".path:path"
+ jsdisplay="!enabled"
+ onclick="handleEnablePlugin(this, true, false)"
+ href="javascript:void(0);"
+ i18n-content="enable"
+ >ENABLE</a>
+ </span>
+ </td>
+ </tr></table></div>
+ <table><tr jsdisplay="mimeTypes.length > 0">
+ <td class="plugin-details-label"
+ i18n-content="pluginMimeTypes">MIME_TYPES:</td>
+ <td><table width="100%" class="mime-types">
+ <tr class="header">
+ <td i18n-content="pluginMimeTypesMimeType"
+ >MIME type</td>
+ <td i18n-content="pluginMimeTypesDescription"
+ >DESCRIPTION</td>
+ <td i18n-content="pluginMimeTypesFileExtensions"
+ >FILE_EXTENSIONS</td>
+ </tr>
+ <tr jsselect="mimeTypes">
+ <td><span dir="ltr"
+ jscontent="mimeType"></span></td>
+ <td><span dir="ltr"
+ jsvalues=".innerHTML:description"></span></td>
+ <td><table jsdisplay="fileExtensions.length > 0"
+ class="hlisting">
+ <tr><td jsselect="fileExtensions">
+ <span dir="ltr" jscontent="'.' + $this">
+ </td></tr>
+ </table></td>
+ </tr>
+ </table></td>
+ </tr></table>
+ </div>
</div>
</div>
</div>
-
<div class="plugin-actions">
<span>
<a
- jsvalues=".pluginPath:path"
+ jsvalues=".path:name"
jsdisplay="enabled"
- onclick="handleEnablePlugin(this, false)"
- href="javascript:void();"
+ onclick="handleEnablePlugin(this, false, true)"
+ href="javascript:void(0);"
i18n-content="disable"
>DISABLE</a>
<a
- jsvalues=".pluginPath:path"
+ jsvalues=".path:name"
jsdisplay="!enabled"
- onclick="handleEnablePlugin(this, true)"
- href="javascript:void();"
+ onclick="handleEnablePlugin(this, true, true)"
+ href="javascript:void(0);"
i18n-content="enable"
>ENABLE</a>
</span>
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index b8cdca3..db25b34 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1817,6 +1817,8 @@
'browser/plugin_process_host_mac.cc',
'browser/plugin_service.cc',
'browser/plugin_service.h',
+ 'browser/plugin_updater.cc',
+ 'browser/plugin_updater.h',
'browser/possible_url_model.cc',
'browser/possible_url_model.h',
'browser/power_save_blocker.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 09a0f18..02c7a4a 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -858,6 +858,7 @@
'browser/password_manager/password_store_default_unittest.cc',
'browser/password_manager/password_store_mac_unittest.cc',
'browser/password_manager/password_store_win_unittest.cc',
+ 'browser/plugin_updater_unittest.cc',
'browser/pref_member_unittest.cc',
'browser/pref_service_unittest.cc',
'browser/printing/print_dialog_cloud_unittest.cc',