summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/app/chrome_dll_resource.h1
-rw-r--r--chrome/app/chromium_strings.grd10
-rw-r--r--chrome/app/generated_resources.grd71
-rw-r--r--chrome/app/google_chrome_strings.grd10
-rw-r--r--chrome/app/theme/theme_resources.grd7
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--chrome/browser/browser.cc13
-rw-r--r--chrome/browser/browser.h1
-rw-r--r--chrome/browser/browser_about_handler.cc18
-rw-r--r--chrome/browser/browser_resources.grd3
-rw-r--r--chrome/browser/dom_ui/conflicts_ui.cc213
-rw-r--r--chrome/browser/dom_ui/conflicts_ui.h28
-rw-r--r--chrome/browser/dom_ui/dom_ui_factory.cc13
-rw-r--r--chrome/browser/enumerate_modules_model_unittest_win.cc182
-rw-r--r--chrome/browser/enumerate_modules_model_win.cc626
-rw-r--r--chrome/browser/enumerate_modules_model_win.h259
-rw-r--r--chrome/browser/resources/about_conflicts.html292
-rw-r--r--chrome/browser/views/toolbar_view.cc86
-rw-r--r--chrome/browser/views/toolbar_view.h17
-rw-r--r--chrome/browser/wrench_menu_model.cc16
-rw-r--r--chrome/chrome_browser.gypi4
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/chrome_switches.cc4
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/notification_type.h11
-rw-r--r--chrome/common/url_constants.cc3
-rw-r--r--chrome/common/url_constants.h3
27 files changed, 1868 insertions, 32 deletions
diff --git a/chrome/app/chrome_dll_resource.h b/chrome/app/chrome_dll_resource.h
index 42bbdf4..d144d36 100644
--- a/chrome/app/chrome_dll_resource.h
+++ b/chrome/app/chrome_dll_resource.h
@@ -209,6 +209,7 @@
#define IDC_AUTOFILL_DEFAULT 40023
#define IDC_DEV_TOOLS_INSPECT 40025
#define IDC_UPGRADE_DIALOG 40026
+#define IDC_VIEW_INCOMPATIBILITIES 40027
// Spell-check
// Insert any additional suggestions before _LAST; these have to be consecutive.
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index d25125f..8a89544 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -454,6 +454,16 @@ be available for now. -->
<message name="IDS_TRY_TOAST_WHY" desc="Text of the url link that explains why this dialog is being shown">
Why am I seeing this?
</message>
+ <!-- about:conflicts strings -->
+ <message name="IDS_CONFLICTS_CHECK_PAGE_TITLE_LONG" desc="The long title on the compatibility page">
+ Modules loaded into Chromium
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_WARNING_CONFIRMED" desc="Warning label on the compatibility page">
+ This module is known to conflict with Chromium.
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_WARNING_SUSPECTED" desc="Warning label on the compatibility page">
+ A module with the same name has been known to conflict with Chromium.
+ </message>
<if expr="os == 'darwin'">
<message name="IDS_SHORT_PRODUCT_NAME" desc="The application's short name, used for the Mac's application menu, activity monitor, etc. This should be less than 16 characters. Example: Chrome, not Google Chrome.">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index d107443..2964ae5 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1016,6 +1016,9 @@ each locale. -->
<message name="IDS_UPDATE_NOW" desc="The text label of the Update Chrome Now menu item">
Update &amp;<ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>
</message>
+ <message name="IDS_VIEW_INCOMPATIBILITIES" desc="The text label of the View incompatibilities menu item">
+ View incompatibilities
+ </message>
<message name="IDS_EXIT" desc="The text label of the Exit menu item">
E&amp;xit
</message>
@@ -1054,6 +1057,9 @@ each locale. -->
<message name="IDS_UPDATE_NOW" desc="In Title Case: The text label of the Update Chrome Now menu item">
Update &amp;<ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>
</message>
+ <message name="IDS_VIEW_INCOMPATIBILITIES" desc="The text label of the View incompatibilities menu item">
+ View Incompatibilities
+ </message>
<message name="IDS_EXIT" desc="In Title Case: The text label of the Exit menu item">
E&amp;xit
</message>
@@ -4076,6 +4082,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_CLOUD_PRINT_PROXY_DESCRIPTION" desc="Description of the Cloud Print Proxy lab">
Enables a background service that connects the <ph name="CLOUD_PRINT_NAME">Google Cloud Print</ph> service to any printers installed on this computer. Once this lab is enabled, you can turn <ph name="CLOUD_PRINT_NAME">Google Cloud Print</ph> on by logging in with your Google account in the Options/Preferences in the Under the Hood section.
</message>
+ <message name="IDS_FLAGS_CONFLICTS_CHECK_NAME" desc="Title of the run conflicts check flag">
+ Check for known conflicts with 3rd party modules.
+ </message>
+ <message name="IDS_FLAGS_CONFLICTS_CHECK_DESCRIPTION" desc="Description of the conflicts check flag">
+ Enables a background check that warns you when a software incompatibility is detected (ie. 3rd party modules that crash the browser).
+ </message>
<message name="IDS_FLAGS_ACCELERATED_COMPOSITING_NAME" desc="Name of the 'GPU Accelerated Compositing' lab.">
GPU Accelerated Compositing
</message>
@@ -4142,6 +4154,65 @@ Keep your key file in a safe place. You will need it to create new versions of y
Omnibox input may be logged
</message>
+ <!-- Conflicts page -->
+ <message name="IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_ONE" desc="What gets appended after the page title if no conflicts were found.">
+ Modules (<ph name="TOTAL_COUNT">$1<ex>50</ex></ph>) - No conflicts detected
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_TWO" desc="What gets appended after the page title (number of conflicts found).">
+ Modules (<ph name="TOTAL_COUNT">$1<ex>50</ex></ph>) - Known conflicts: <ph name="BAD_COUNT">$2<ex>3</ex></ph>, suspected: <ph name="SUSPICIOUS_COUNT">$3<ex>2</ex></ph>
+ </message>
+ <message name="IDS_CONFLICTS_EXPLANATION_TEXT" desc="The text blurb explaining what the compatibility page is.">
+ This page lists all modules loaded into the main process and modules registered to load at a later point.
+ </message>
+ <message name="IDS_CONFLICTS_HELP_CENTER_LINK" desc="The text for the Help Center link.">
+ Learn more
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_WARNING_PREFIX" desc="Warning label prefix on the compatibility page">
+ Warning:
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_VERSION_STRING" desc="The version string to show on the compatibility page">
+ Version <ph name="VERSION">$1<ex>1.2.3.4</ex></ph>.
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_INVESTIGATING" desc="A label on the compatibility page saying we are investigating.">
+ We are currently investigating this issue.
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_POSSIBLE_ACTIONS" desc="A label on the compatibility page describing possible resolutions.">
+ If you are experiencing frequent problems, you could try the following to resolve the issue with this module:
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UNINSTALL" desc="An uninstall label. 'It' refers to 'a module'.">
+ Uninstalling
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UPDATE" desc="An update label. 'It' refers to 'a module'.">
+ Updating
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_DISABLE" desc="A disable label. 'It' refers to 'a module'.">
+ Disabling
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_SEPERATOR" desc="The separator separating the words 'Uninstalling it', 'Updating it' and 'Disabling it'">
+ /
+ </message>
+ <message name="IDS_CONFLICTS_NO_MODULES_LOADED" desc="Error message for when no modules were found in the process">
+ Unable to detect any modules loaded.
+ </message>
+ <message name="IDS_CONFLICTS_HEADER_SOFTWARE" desc="The 'Software:' header for each module item in the list">
+ Software:
+ </message>
+ <message name="IDS_CONFLICTS_HEADER_SIGNED_BY" desc="The 'Signed by:' header for each module item in the list">
+ Signed by:
+ </message>
+ <message name="IDS_CONFLICTS_HEADER_LOCATION" desc="The 'Location:' header for each module item in the list">
+ Location:
+ </message>
+ <message name="IDS_CONFLICTS_HEADER_WARNING" desc="The 'Warning:' header for each module item in the list">
+ Warning:
+ </message>
+ <message name="IDS_CONFLICTS_HEADER_HELP_TIP" desc="The 'Help tip:' header for each module item in the list">
+ Help tip:
+ </message>
+ <message name="IDS_HELP_CENTER_VIEW_CONFLICTS" desc="The url of the Help center article for the View Conflicts page">
+ http://www.google.com/support/chrome/bin/answer.py?answer=666386&amp;hl=[GRITLANGCODE]&amp;n=<ph name="NAME">$1<ex>Hash</ex></ph>&amp;l=<ph name="LOCATION">$2<ex>Hash</ex></ph>&amp;d=<ph name="DESC">$3<ex>Hash</ex></ph>&amp;s=<ph name="SIGNER">$4<ex>Hash</ex></ph>
+ </message>
+
<!-- Click-to-load -->
<message name="IDS_PLUGIN_LOAD" desc="The link for loading a blocked plug-in, displayed in the click-to-play UI.">
Click to run this plug-in
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 20ac348..116953a 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -505,6 +505,16 @@ Chrome supports. -->
<message name="IDS_TRY_TOAST_WHY" desc="Text of the url link that explains why this dialog is being shown">
Why am I seeing this?
</message>
+ <!-- about:conflicts strings -->
+ <message name="IDS_CONFLICTS_CHECK_PAGE_TITLE_LONG" desc="The long title on the compatibility page">
+ Modules loaded into Google Chrome
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_WARNING_CONFIRMED" desc="Warning label on the compatibility page">
+ This module is known to conflict with Google Chrome.
+ </message>
+ <message name="IDS_CONFLICTS_CHECK_WARNING_SUSPECTED" desc="Warning label on the compatibility page">
+ A module with the same name has been known to conflict with Google Chrome.
+ </message>
<if expr="os == 'darwin'">
<message name="IDS_SHORT_PRODUCT_NAME" desc="The application's short name, used for the Mac's application menu, activity monitor, etc. This should be less than 16 characters. Example: Chrome, not Google Chrome.">
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 79e63b9..e8a3963 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -76,6 +76,9 @@
<include name="IDR_CLOSE_BAR_MASK" file="close_bar_mask.png" type="BINDATA" />
<include name="IDR_CLOSE_BAR_P" file="close_bar_p.png" type="BINDATA" />
<include name="IDR_CLOSE_BUTTON_MASK" file="close_button_mask.png" type="BINDATA" />
+ <if expr="os.find('win') != -1">
+ <include name="IDR_CONFLICTS" file="conflicts.png" type="BINDATA" />
+ </if>
<include name="IDR_CONSTRAINED_BOTTOM_CENTER_V" file="constrained_bottom_center_v.png" type="BINDATA" />
<include name="IDR_CONSTRAINED_BOTTOM_LEFT_CORNER_V" file="constrained_bottom_left_corner_v.png" type="BINDATA" />
<include name="IDR_CONSTRAINED_BOTTOM_RIGHT_CORNER_V" file="constrained_bottom_right_corner_v.png" type="BINDATA" />
@@ -169,6 +172,10 @@
<include name="IDR_HOME" file="home.png" type="BINDATA" />
<include name="IDR_HOME_H" file="home_h.png" type="BINDATA" />
<include name="IDR_HOME_P" file="home_p.png" type="BINDATA" />
+ <if expr="os.find('win') != -1">
+ <include name="IDR_INCOMPATIBILITY_DOT_ACTIVE" file="incompatibility_dot_active.png" type="BINDATA" />
+ <include name="IDR_INCOMPATIBILITY_DOT_INACTIVE" file="incompatibility_dot_inactive.png" type="BINDATA" />
+ </if>
<include name="IDR_INFOBAR_ALT_NAV_URL" file="infobar_didyoumean.png" type="BINDATA" />
<include name="IDR_INFOBAR_AUTOFILL" file="infobar_autofill.png" type="BINDATA" />
<include name="IDR_INFOBAR_MULTIPLE_DOWNLOADS" file="infobar_multiple_downloads.png" type="BINDATA" />
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6173d60..f1cebb1 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -91,6 +91,13 @@ const Experiment kExperiments[] = {
switches::kEnableBackgroundMode
},
{
+ "conflicting-modules-check", // Do not change; see above.
+ IDS_FLAGS_CONFLICTS_CHECK_NAME,
+ IDS_FLAGS_CONFLICTS_CHECK_DESCRIPTION,
+ kOsWin,
+ switches::kConflictingModulesCheck
+ },
+ {
"cloud-print-proxy", // Do not change; see above.
IDS_FLAGS_CLOUD_PRINT_PROXY_NAME,
IDS_FLAGS_CLOUD_PRINT_PROXY_DESCRIPTION,
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 446bca6..52d3e5e 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -1730,6 +1730,11 @@ void Browser::ShowExtensionsTab() {
ShowSingletonTab(GURL(chrome::kChromeUIExtensionsURL));
}
+void Browser::ShowAboutConflictsTab() {
+ UserMetrics::RecordAction(UserMetricsAction("AboutConflicts"), profile_);
+ ShowSingletonTab(GURL(chrome::kChromeUIConflictsURL));
+}
+
void Browser::ShowBrokenPageTab(TabContents* contents) {
UserMetrics::RecordAction(UserMetricsAction("ReportBug"), profile_);
string16 page_title = contents->GetTitle();
@@ -2206,6 +2211,7 @@ void Browser::ExecuteCommandWithDisposition(
case IDC_IMPORT_SETTINGS: OpenImportSettingsDialog(); break;
case IDC_ABOUT: OpenAboutChromeDialog(); break;
case IDC_UPGRADE_DIALOG: OpenUpdateChromeDialog(); break;
+ case IDC_VIEW_INCOMPATIBILITIES: ShowAboutConflictsTab(); break;
case IDC_HELP_PAGE: OpenHelpTab(); break;
#if defined(OS_CHROMEOS)
case IDC_SYSTEM_OPTIONS: OpenSystemOptionsDialog(); break;
@@ -3075,7 +3081,6 @@ void Browser::ShowRepostFormWarningDialog(TabContents *tab_contents) {
}
void Browser::ShowContentSettingsWindow(ContentSettingsType content_type) {
-
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableTabbedOptions)) {
ShowOptionsTab(
@@ -3488,9 +3493,11 @@ void Browser::InitCommandState() {
// Show various bits of UI
command_updater_.UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, normal_window);
- // The upgrade entry should always be enabled. Whether it is visible is a
- // separate matter determined on menu show.
+ // The upgrade entry and the view incompatibility entry should always be
+ // enabled. Whether they are visible is a separate matter determined on menu
+ // show.
command_updater_.UpdateCommandEnabled(IDC_UPGRADE_DIALOG, true);
+ command_updater_.UpdateCommandEnabled(IDC_VIEW_INCOMPATIBILITIES, true);
// Initialize other commands whose state changes based on fullscreen mode.
UpdateCommandsForFullscreenMode(false);
diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h
index 7d89334..885c568 100644
--- a/chrome/browser/browser.h
+++ b/chrome/browser/browser.h
@@ -510,6 +510,7 @@ class Browser : public TabHandlerDelegate,
void ShowHistoryTab();
void ShowDownloadsTab();
void ShowExtensionsTab();
+ void ShowAboutConflictsTab();
void ShowBrokenPageTab(TabContents* contents);
void ShowOptionsTab(const std::string& sub_page);
void OpenClearBrowsingDataDialog();
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc
index bb42ea8..83b4bff 100644
--- a/chrome/browser/browser_about_handler.cc
+++ b/chrome/browser/browser_about_handler.cc
@@ -64,6 +64,7 @@
#endif
#if defined(OS_WIN)
+#include "chrome/browser/enumerate_modules_model_win.h"
#include "chrome/browser/views/about_ipc_dialog.h"
#elif defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/cros/cros_library.h"
@@ -101,6 +102,9 @@ const char kAppCacheInternalsPath[] = "appcache-internals";
const char kBlobInternalsPath[] = "blob-internals";
const char kCreditsPath[] = "credits";
const char kCachePath[] = "view-http-cache";
+#if defined(OS_WIN)
+const char kConflictsPath[] = "conflicts";
+#endif
const char kDnsPath[] = "dns";
const char kFlagsPath[] = "flags";
const char kGpuPath[] = "gpu";
@@ -134,6 +138,9 @@ const char *kAllAboutPaths[] = {
kBlobInternalsPath,
kCachePath,
kCreditsPath,
+#if defined(OS_WIN)
+ kConflictsPath,
+#endif
kDnsPath,
kFlagsPath,
kGpuPath,
@@ -263,6 +270,9 @@ std::string AboutAbout() {
if (kAllAboutPaths[i] == kAppCacheInternalsPath ||
kAllAboutPaths[i] == kBlobInternalsPath ||
kAllAboutPaths[i] == kCachePath ||
+#if defined(OS_WIN)
+ kAllAboutPaths[i] == kConflictsPath ||
+#endif
kAllAboutPaths[i] == kFlagsPath ||
kAllAboutPaths[i] == kNetInternalsPath ||
kAllAboutPaths[i] == kPluginsPath) {
@@ -1085,6 +1095,14 @@ bool WillHandleBrowserAboutURL(GURL* url, Profile* profile) {
return true;
}
+#if defined(OS_WIN)
+ // Rewrite about:conflicts/* URLs to chrome://conflicts/*
+ if (StartsWithAboutSpecifier(*url, chrome::kAboutConflicts)) {
+ *url = GURL(chrome::kChromeUIConflictsURL);
+ return true;
+ }
+#endif
+
// Rewrite about:flags and about:vaporware to chrome://flags/.
if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutFlagsURL) ||
LowerCaseEqualsASCII(url->spec(), chrome::kAboutVaporwareURL)) {
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 2f7d831..0f639f2 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -11,6 +11,9 @@ without changes to the corresponding grd file. et -->
</outputs>
<release seq="1">
<includes>
+ <if expr="os.find('win') != -1">
+ <include name="IDR_ABOUT_CONFLICTS_HTML" file="resources\about_conflicts.html" flattenhtml="true" type="BINDATA" />
+ </if>
<if expr="os == 'linux2' or os.find('bsd') != -1">
<include name="IDR_ABOUT_MEMORY_HTML" file="resources\about_memory_linux.html" flattenhtml="true" type="BINDATA" />
</if>
diff --git a/chrome/browser/dom_ui/conflicts_ui.cc b/chrome/browser/dom_ui/conflicts_ui.cc
new file mode 100644
index 0000000..a93257a
--- /dev/null
+++ b/chrome/browser/dom_ui/conflicts_ui.cc
@@ -0,0 +1,213 @@
+// 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/dom_ui/conflicts_ui.h"
+
+#if defined(OS_WIN)
+
+#include <string>
+
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
+#include "chrome/browser/enumerate_modules_model_win.h"
+#include "chrome/common/jstemplate_builder.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/url_constants.h"
+#include "grit/browser_resources.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ConflictsUIHTMLSource
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class ConflictsUIHTMLSource : public ChromeURLDataManager::DataSource {
+ public:
+ ConflictsUIHTMLSource()
+ : DataSource(chrome::kChromeUIConflictsHost, MessageLoop::current()) {}
+
+ // Called when the network layer has requested a resource underneath
+ // the path we registered.
+ virtual void StartDataRequest(const std::string& path,
+ bool is_off_the_record,
+ int request_id);
+
+ virtual std::string GetMimeType(const std::string&) const {
+ return "text/html";
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConflictsUIHTMLSource);
+};
+
+void ConflictsUIHTMLSource::StartDataRequest(const std::string& path,
+ bool is_off_the_record,
+ int request_id) {
+ // Strings used in the JsTemplate file.
+ DictionaryValue localized_strings;
+ localized_strings.SetString("modulesLongTitle",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_PAGE_TITLE_LONG));
+ localized_strings.SetString("modulesBlurb",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_EXPLANATION_TEXT));
+ localized_strings.SetString("moduleSuspectedBad",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_WARNING_SUSPECTED));
+ localized_strings.SetString("moduleConfirmedBad",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_WARNING_CONFIRMED));
+ localized_strings.SetString("helpCenterLink",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_HELP_CENTER_LINK));
+ localized_strings.SetString("investigatingText",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_INVESTIGATING));
+ localized_strings.SetString("modulesNoneLoaded",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_NO_MODULES_LOADED));
+ localized_strings.SetString("headerSoftware",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_SOFTWARE));
+ localized_strings.SetString("headerSignedBy",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_SIGNED_BY));
+ localized_strings.SetString("headerLocation",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_LOCATION));
+ localized_strings.SetString("headerWarning",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_WARNING));
+ localized_strings.SetString("headerHelpTip",
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_HELP_TIP));
+
+ ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings);
+
+ static const base::StringPiece flags_html(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_ABOUT_CONFLICTS_HTML));
+ std::string full_html(flags_html.data(), flags_html.size());
+ jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html);
+ jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html);
+ jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html);
+ jstemplate_builder::AppendJsTemplateSourceHtml(&full_html);
+
+ scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
+ html_bytes->data.resize(full_html.size());
+ std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
+
+ SendResponse(request_id, html_bytes);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ConflictsDOMHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for Javascript messages for the about:flags page.
+class ConflictsDOMHandler : public DOMMessageHandler,
+ public NotificationObserver {
+ public:
+ ConflictsDOMHandler() {}
+ virtual ~ConflictsDOMHandler() {}
+
+ // DOMMessageHandler implementation.
+ virtual void RegisterMessages();
+
+ // Callback for the "requestModuleList" message.
+ void HandleRequestModuleList(const ListValue* args);
+
+ private:
+ void SendModuleList();
+
+ void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConflictsDOMHandler);
+};
+
+void ConflictsDOMHandler::RegisterMessages() {
+ dom_ui_->RegisterMessageCallback("requestModuleList",
+ NewCallback(this, &ConflictsDOMHandler::HandleRequestModuleList));
+}
+
+void ConflictsDOMHandler::HandleRequestModuleList(const ListValue* args) {
+ // This request is handled asynchronously. See Observe for when we reply back.
+ registrar_.Add(this, NotificationType::MODULE_LIST_ENUMERATED,
+ NotificationService::AllSources());
+ EnumerateModulesModel::GetSingleton()->ScanNow();
+}
+
+void ConflictsDOMHandler::SendModuleList() {
+ EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetSingleton();
+ ListValue* list = loaded_modules->GetModuleList();
+ DictionaryValue results;
+ results.Set("moduleList", list);
+
+ // Add the section title and the total count for bad modules found.
+ int confirmed_bad = loaded_modules->confirmed_bad_modules_detected();
+ int suspected_bad = loaded_modules->suspected_bad_modules_detected();
+ string16 table_title;
+ if (!confirmed_bad && !suspected_bad) {
+ table_title += l10n_util::GetStringFUTF16(
+ IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_ONE,
+ base::IntToString16(list->GetSize()));
+ } else {
+ table_title += l10n_util::GetStringFUTF16(
+ IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_TWO,
+ base::IntToString16(list->GetSize()),
+ base::IntToString16(confirmed_bad),
+ base::IntToString16(suspected_bad));
+ }
+ results.SetString("modulesTableTitle", table_title);
+
+ dom_ui_->CallJavascriptFunction(L"returnModuleList", results);
+}
+
+void ConflictsDOMHandler::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::MODULE_LIST_ENUMERATED:
+ SendModuleList();
+ registrar_.RemoveAll();
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// ConflictsUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+ConflictsUI::ConflictsUI(TabContents* contents) : DOMUI(contents) {
+ AddMessageHandler((new ConflictsDOMHandler())->Attach(this));
+
+ ConflictsUIHTMLSource* html_source = new ConflictsUIHTMLSource();
+
+ // Set up the about:conflicts source.
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(Singleton<ChromeURLDataManager>::get(),
+ &ChromeURLDataManager::AddDataSource,
+ make_scoped_refptr(html_source)));
+}
+
+// static
+RefCountedMemory* ConflictsUI::GetFaviconResourceBytes() {
+ return ResourceBundle::GetSharedInstance().
+ LoadDataResourceBytes(IDR_CONFLICTS);
+}
+
+#endif
diff --git a/chrome/browser/dom_ui/conflicts_ui.h b/chrome/browser/dom_ui/conflicts_ui.h
new file mode 100644
index 0000000..d683a9c
--- /dev/null
+++ b/chrome/browser/dom_ui/conflicts_ui.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DOM_UI_CONFLICTS_UI_H_
+#define CHROME_BROWSER_DOM_UI_CONFLICTS_UI_H_
+#pragma once
+
+#include "chrome/browser/dom_ui/dom_ui.h"
+
+#if defined(OS_WIN)
+
+class RefCountedMemory;
+
+// The DOM UI handler for about:conflicts.
+class ConflictsUI : public DOMUI {
+ public:
+ explicit ConflictsUI(TabContents* contents);
+
+ static RefCountedMemory* GetFaviconResourceBytes();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConflictsUI);
+};
+
+#endif
+
+#endif // CHROME_BROWSER_DOM_UI_CONFLICTS_UI_H_
diff --git a/chrome/browser/dom_ui/dom_ui_factory.cc b/chrome/browser/dom_ui/dom_ui_factory.cc
index 7401ac1..fbdb42b 100644
--- a/chrome/browser/dom_ui/dom_ui_factory.cc
+++ b/chrome/browser/dom_ui/dom_ui_factory.cc
@@ -51,6 +51,10 @@
#include "chrome/browser/dom_ui/mediaplayer_ui.h"
#endif
+#if defined(OS_WIN)
+#include "chrome/browser/dom_ui/conflicts_ui.h"
+#endif
+
const DOMUITypeID DOMUIFactory::kNoDOMUI = NULL;
// A function for creating a new DOMUI. The caller owns the return value, which
@@ -131,6 +135,10 @@ static DOMUIFactoryFunction GetDOMUIFactoryFunction(Profile* profile,
return &NewDOMUI<BugReportUI>;
if (url.host() == chrome::kChromeUIDevToolsHost)
return &NewDOMUI<DevToolsUI>;
+#if defined(OS_WIN)
+ if (url.host() == chrome::kChromeUIConflictsHost)
+ return &NewDOMUI<ConflictsUI>;
+#endif
if (url.host() == chrome::kChromeUIDownloadsHost)
return &NewDOMUI<DownloadsUI>;
if (url.host() == chrome::kChromeUITextfieldsHost)
@@ -271,6 +279,11 @@ RefCountedMemory* DOMUIFactory::GetFaviconResourceBytes(Profile* profile,
if (!HasDOMUIScheme(page_url))
return NULL;
+#if defined(OS_WIN)
+ if (page_url.host() == chrome::kChromeUIConflictsHost)
+ return ConflictsUI::GetFaviconResourceBytes();
+#endif
+
if (page_url.host() == chrome::kChromeUIDownloadsHost)
return DownloadsUI::GetFaviconResourceBytes();
diff --git a/chrome/browser/enumerate_modules_model_unittest_win.cc b/chrome/browser/enumerate_modules_model_unittest_win.cc
new file mode 100644
index 0000000..374ec42
--- /dev/null
+++ b/chrome/browser/enumerate_modules_model_unittest_win.cc
@@ -0,0 +1,182 @@
+// 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 "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/enumerate_modules_model_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+typedef public testing::Test EnumerateModulesTest;
+
+// Set up some constants to use as default when creating the structs.
+static const ModuleEnumerator::ModuleType kType =
+ ModuleEnumerator::LOADED_MODULE;
+
+static const ModuleEnumerator::ModuleStatus kStatus =
+ ModuleEnumerator::NOT_MATCHED;
+
+static const ModuleEnumerator::RecommendedAction kAction =
+ ModuleEnumerator::NONE;
+
+// This is a list of test cases to normalize.
+static const struct NormalizationEntryList {
+ ModuleEnumerator::Module test_case;
+ ModuleEnumerator::Module expected;
+} kNormalizationTestCases[] = {
+ {
+ // Only path normalization needed.
+ {kType, kStatus, L"c:\\foo\\bar.dll", L"", L"Prod", L"Desc", L"1.0",
+ L"Sig", kAction},
+ {kType, kStatus, L"c:\\foo\\", L"bar.dll", L"Prod", L"Desc", L"1.0",
+ L"Sig", kAction},
+ }, {
+ // Lower case normalization.
+ {kType, kStatus, L"C:\\Foo\\Bar.dll", L"", L"", L"", L"1.0",
+ L"", kAction},
+ {kType, kStatus, L"c:\\foo\\", L"bar.dll", L"", L"", L"1.0",
+ L"", kAction},
+ }, {
+ // Version can include strings after the version number. Strip that away.
+ {kType, kStatus, L"c:\\foo.dll", L"", L"", L"", L"1.0 asdf",
+ L"", kAction},
+ {kType, kStatus, L"c:\\", L"foo.dll", L"", L"", L"1.0",
+ L"", kAction},
+ }, {
+ // Corner case: No path (not sure this will ever happen).
+ {kType, kStatus, L"bar.dll", L"", L"", L"", L"", L"", kAction},
+ {kType, kStatus, L"", L"bar.dll", L"", L"", L"", L"", kAction},
+ }, {
+ // Error case: Missing filename (not sure this will ever happen).
+ {kType, kStatus, L"", L"", L"", L"", L"1.0", L"", kAction},
+ {kType, kStatus, L"", L"", L"", L"", L"1.0", L"", kAction},
+ },
+};
+
+TEST_F(EnumerateModulesTest, NormalizeEntry) {
+ for (size_t i = 0; i < arraysize(kNormalizationTestCases); ++i) {
+ ModuleEnumerator::Module test = kNormalizationTestCases[i].test_case;
+ EXPECT_FALSE(test.normalized);
+ ModuleEnumerator::NormalizeModule(&test);
+ ModuleEnumerator::Module expected = kNormalizationTestCases[i].expected;
+
+ SCOPED_TRACE("Test case no: " + base::IntToString(i));
+ EXPECT_EQ(expected.type, test.type);
+ EXPECT_EQ(expected.status, test.status);
+ EXPECT_STREQ(expected.location.c_str(), test.location.c_str());
+ EXPECT_STREQ(expected.name.c_str(), test.name.c_str());
+ EXPECT_STREQ(expected.product_name.c_str(), test.product_name.c_str());
+ EXPECT_STREQ(expected.description.c_str(), test.description.c_str());
+ EXPECT_STREQ(expected.version.c_str(), test.version.c_str());
+ EXPECT_STREQ(expected.digital_signer.c_str(), test.digital_signer.c_str());
+ EXPECT_EQ(expected.recommended_action, test.recommended_action);
+ EXPECT_TRUE(test.normalized);
+ }
+}
+
+const ModuleEnumerator::Module kStandardModule =
+ { kType, kStatus, L"c:\\foo\\bar.dll", L"", L"Prod", L"Desc", L"1.0", L"Sig",
+ ModuleEnumerator::NONE };
+
+// Name, location, description and signature are compared by hashing.
+static const char kMatchName[] = "88e8c9e0"; // "bar.dll".
+static const char kNoMatchName[] = "barfoo.dll";
+static const char kMatchLocation[] = "e6ca7b1c"; // "c:\\foo\\".
+static const char kNoMatchLocation[] = "c:\\foobar\\";
+static const char kMatchDesc[] = "5c4419a6"; // "Desc".
+static const char kNoMatchDesc[] = "NoDesc";
+static const char kVersionHigh[] = "2.0";
+static const char kVersionLow[] = "0.5";
+static const char kMatchSignature[] = "7bfd87e1"; // "Sig".
+static const char kNoMatchSignature[] = "giS";
+static const char kEmpty[] = "";
+
+const struct MatchingEntryList {
+ ModuleEnumerator::ModuleStatus expected_result;
+ ModuleEnumerator::Module test_case;
+ ModuleEnumerator::BlacklistEntry blacklist;
+} kMatchineEntryList[] = {
+ // Each BlacklistEntry is:
+ // Filename, location, desc_or_signer, version from, version to, help_tip.
+
+ { // Matches: Name (location doesn't match) => Not enough for a match.
+ ModuleEnumerator::NOT_MATCHED,
+ kStandardModule,
+ { kMatchName, kNoMatchLocation, kEmpty, kEmpty, kEmpty,
+ ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name (location not given) => Suspected match.
+ ModuleEnumerator::SUSPECTED_BAD,
+ kStandardModule,
+ { kMatchName, kEmpty, kEmpty, kEmpty, kEmpty,
+ ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, not version (location not given) => Not a match.
+ ModuleEnumerator::NOT_MATCHED,
+ kStandardModule,
+ { kMatchName, kEmpty, kEmpty, kVersionHigh, kVersionHigh,
+ ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, location => Suspected match.
+ ModuleEnumerator::SUSPECTED_BAD,
+ kStandardModule,
+ { kMatchName, kMatchLocation, kEmpty, kEmpty, kEmpty,
+ ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, location (not version) => Not a match.
+ ModuleEnumerator::NOT_MATCHED,
+ kStandardModule,
+ { kMatchName, kMatchLocation, kEmpty, kVersionHigh, kVersionLow,
+ ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, location, signature => Confirmed match.
+ ModuleEnumerator::CONFIRMED_BAD,
+ kStandardModule,
+ { kMatchName, kMatchLocation, kMatchSignature, kEmpty, kEmpty,
+ ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, location, signature (not version) => No match.
+ ModuleEnumerator::NOT_MATCHED,
+ kStandardModule,
+ { kMatchName, kMatchLocation, kMatchSignature,
+ kVersionLow, kVersionLow, ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, location, description => Confirmed match.
+ ModuleEnumerator::CONFIRMED_BAD,
+ kStandardModule,
+ { kMatchName, kMatchLocation, kMatchDesc, kEmpty, kEmpty,
+ ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, location, description (not version) => No match.
+ ModuleEnumerator::NOT_MATCHED,
+ kStandardModule,
+ { kMatchName, kMatchLocation, kMatchDesc,
+ kVersionHigh, kVersionHigh, ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, location, signature, version => Confirmed match.
+ ModuleEnumerator::CONFIRMED_BAD,
+ kStandardModule,
+ { kMatchName, kMatchLocation, kMatchSignature,
+ kVersionLow, kVersionHigh, ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, location, signature, version (lower) => Confirmed.
+ ModuleEnumerator::CONFIRMED_BAD,
+ kStandardModule,
+ { kMatchName, kMatchLocation, kMatchSignature,
+ kVersionLow, kEmpty, ModuleEnumerator::SEE_LINK }
+ }, { // Matches: Name, location, signature, version (upper) => Confirmed.
+ ModuleEnumerator::CONFIRMED_BAD,
+ kStandardModule,
+ { kMatchName, kMatchLocation, kMatchSignature,
+ kEmpty, kVersionHigh, ModuleEnumerator::SEE_LINK }
+ }, { // All empty fields doesn't produce a match.
+ ModuleEnumerator::NOT_MATCHED,
+ {kType, kStatus, L"", L"", L"", L"", L""},
+ { "a.dll", "", "", "", "", ModuleEnumerator::SEE_LINK }
+ },
+};
+
+TEST_F(EnumerateModulesTest, MatchFunction) {
+ for (size_t i = 0; i < arraysize(kMatchineEntryList); ++i) {
+ ModuleEnumerator::Module test = kMatchineEntryList[i].test_case;
+ ModuleEnumerator::NormalizeModule(&test);
+ ModuleEnumerator::BlacklistEntry blacklist =
+ kMatchineEntryList[i].blacklist;
+
+ SCOPED_TRACE("Test case no " + base::IntToString(i) +
+ ": '" + UTF16ToASCII(test.name) + "'");
+ EXPECT_EQ(kMatchineEntryList[i].expected_result,
+ ModuleEnumerator::Match(test, blacklist));
+ }
+}
diff --git a/chrome/browser/enumerate_modules_model_win.cc b/chrome/browser/enumerate_modules_model_win.cc
new file mode 100644
index 0000000..0fbd575
--- /dev/null
+++ b/chrome/browser/enumerate_modules_model_win.cc
@@ -0,0 +1,626 @@
+// 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/enumerate_modules_model_win.h"
+
+#include <Tlhelp32.h>
+#include <wintrust.h>
+
+#include "app/l10n_util.h"
+#include "app/win_util.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/file_path.h"
+#include "base/file_version_info_win.h"
+#include "base/scoped_handle.h"
+#include "base/sha2.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "chrome/browser/net/service_providers_win.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_service.h"
+#include "grit/generated_resources.h"
+
+// The period of time (in milliseconds) to wait until checking to see if any
+// incompatible modules exist.
+static const int kModuleCheckDelayMs = 60 * 1000;
+
+// A sort method that sorts by ModuleType ordinal (loaded module at the top),
+// then by full name (including path).
+static bool ModuleSort(const ModuleEnumerator::Module& a,
+ const ModuleEnumerator::Module& b) {
+ if (a.type != b.type)
+ return a.type < b.type;
+ if (a.location == b.location)
+ return a.name < b.name;
+
+ return a.location < b.location;
+}
+
+namespace {
+
+// Used to protect the LoadedModuleVector which is accessed
+// from both the UI thread and the FILE thread.
+Lock* lock = NULL;
+
+}
+
+// The browser process module blacklist. This lists all modules that are known
+// to cause compatibility issues within the browser process. When adding to this
+// list, make sure that all paths are lower-case, in long pathname form, end
+// with a slash and use environments variables (or just look at one of the
+// comments below and keep it consistent with that). When adding an entry with
+// an environment variable not currently used in the list below, make sure to
+// update the list in PreparePathMappings. Filename, Description/Signer, and
+// Location must be entered as hashes (see GenerateHash). Filename is mandatory.
+// Entries without any Description, Signer info, or Location will never be
+// marked as confirmed bad (only as suspicious).
+const ModuleEnumerator::BlacklistEntry ModuleEnumerator::kModuleBlacklist[] = {
+ // Test DLLs, to demonstrate the feature. Will be removed soon.
+ { // apphelp.dll, "%systemroot%\\system32\\"
+ "f5fda581", "23d01d5b", "", "", "", NONE
+ }, { // rsaenh.dll, "%systemroot%\\system32\\", "Microsoft Windows"
+ "6af212cb", "23d01d5b", "7b47bf79", "", "",
+ static_cast<RecommendedAction>(UPDATE | DISABLE | SEE_LINK)
+ },
+
+ // NOTE: Please keep this list sorted by dll name, then location.
+
+ // foldersizecolumn.dll.
+ {"5ec91bd7", "", "", "", "", NONE},
+
+ // idmmbc.dll, "%programfiles%\\internet download manager\\", "Tonec Inc.".
+ // See: http://crbug.com/26892/.
+ {"b8dce5c3", "94541bf5", "d33ad640", "", "", NONE},
+
+ // imon.dll. See: http://crbug.com/21715.
+ {"8f42f22e", "", "", "", "", NONE},
+
+ // is3lsp.dll. See: http://crbug.com/26892.
+ {"7ffbdce9", "", "", "", "", NONE},
+
+ // nvlsp.dll. See: http://crbug.com/22083.
+ {"37f907e2", "", "", "", "", NONE},
+
+ // nvshell.dll. See: http://crbug.com/3269.
+ {"9290318f", "", "", "", "", NONE},
+
+ // securenet.dll. See: http://crbug.com/5165.
+ {"9b266e1c", "", "", "", "", NONE},
+
+ // sgprxy.dll.
+ {"005965ea", "", "", "", "", NONE},
+
+ // vaproxyd.dll. See: http://crbug.com/42445.
+ {"0a1c7f81", "", "", "", "", NONE},
+
+ // vlsp.dll. See: http://crbug.com/22826.
+ {"2e4eb93d", "", "", "", "", NONE},
+};
+
+// Generates an 8 digit hash from the input given.
+static void GenerateHash(const std::string& input, std::string* output) {
+ if (input.empty()) {
+ *output = "";
+ return;
+ }
+
+ uint8 hash[4];
+ base::SHA256HashString(input, hash, sizeof(hash));
+ *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash)));
+}
+
+// -----------------------------------------------------------------------------
+
+// static
+void ModuleEnumerator::NormalizeModule(Module* module) {
+ string16 path = module->location;
+ if (!win_util::ConvertToLongPath(path, &module->location))
+ module->location = path;
+
+ module->location = l10n_util::ToLower(module->location);
+
+ // Location contains the filename, so the last slash is where the path
+ // ends.
+ size_t last_slash = module->location.find_last_of(L"\\");
+ if (last_slash != string16::npos) {
+ module->name = module->location.substr(last_slash + 1);
+ module->location = module->location.substr(0, last_slash + 1);
+ } else {
+ module->name = module->location;
+ module->location.clear();
+ }
+
+ // Some version strings have things like (win7_rtm.090713-1255) appended
+ // to them. Remove that.
+ size_t first_space = module->version.find_first_of(L" ");
+ if (first_space != string16::npos)
+ module->version = module->version.substr(0, first_space);
+
+ module->normalized = true;
+}
+
+// static
+ModuleEnumerator::ModuleStatus ModuleEnumerator::Match(
+ const ModuleEnumerator::Module& module,
+ const ModuleEnumerator::BlacklistEntry& blacklisted) {
+ // All modules must be normalized before matching against blacklist.
+ DCHECK(module.normalized);
+ // Filename is mandatory and version should not contain spaces.
+ DCHECK(strlen(blacklisted.filename) > 0);
+ DCHECK(!strstr(blacklisted.version_from, " "));
+ DCHECK(!strstr(blacklisted.version_to, " "));
+
+ std::string filename_hash, location_hash;
+ GenerateHash(WideToUTF8(module.name), &filename_hash);
+ GenerateHash(WideToUTF8(module.location), &location_hash);
+
+ // Filenames are mandatory. Location is mandatory if given.
+ if (filename_hash == blacklisted.filename &&
+ (std::string(blacklisted.location).empty() ||
+ location_hash == blacklisted.location)) {
+ // We have a name match against the blacklist (and possibly location match
+ // also), so check version.
+ scoped_ptr<Version> module_version(
+ Version::GetVersionFromString(module.version));
+ scoped_ptr<Version> version_min(
+ Version::GetVersionFromString(blacklisted.version_from));
+ scoped_ptr<Version> version_max(
+ Version::GetVersionFromString(blacklisted.version_to));
+ bool version_ok = !version_min.get() && !version_max.get();
+ if (!version_ok) {
+ bool too_low = version_min.get() &&
+ (!module_version.get() ||
+ module_version->CompareTo(*version_min.get()) < 0);
+ bool too_high = version_max.get() &&
+ (!module_version.get() ||
+ module_version->CompareTo(*version_max.get()) > 0);
+ version_ok = !too_low && !too_high;
+ }
+
+ if (version_ok) {
+ // At this point, the names match and there is no version specified
+ // or the versions also match.
+
+ std::string desc_or_signer(blacklisted.desc_or_signer);
+ std::string signer_hash, description_hash;
+ GenerateHash(WideToUTF8(module.digital_signer), &signer_hash);
+ GenerateHash(WideToUTF8(module.description), &description_hash);
+
+ // If signatures match, we have a winner.
+ if (!desc_or_signer.empty() && signer_hash == desc_or_signer)
+ return CONFIRMED_BAD;
+
+ // If description matches and location, then we also have a match.
+ if (!desc_or_signer.empty() && description_hash == desc_or_signer &&
+ !location_hash.empty() && location_hash == blacklisted.location) {
+ return CONFIRMED_BAD;
+ }
+
+ // We are not sure, but it is likely bad.
+ return SUSPECTED_BAD;
+ }
+ }
+
+ return NOT_MATCHED;
+}
+
+ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer)
+: observer_(observer) {
+ CHECK(BrowserThread::GetCurrentThreadIdentifier(&callback_thread_id_));
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE));
+}
+
+ModuleEnumerator::~ModuleEnumerator() {
+}
+
+void ModuleEnumerator::ScanNow(ModulesVector* list) {
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ enumerated_modules_ = list;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &ModuleEnumerator::ScanOnFileThread));
+}
+
+void ModuleEnumerator::ScanOnFileThread() {
+ enumerated_modules_->clear();
+
+ // Make sure the path mapping vector is setup so we can collapse paths.
+ PreparePathMappings();
+
+ // Get all modules in the current process.
+ ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
+ ::GetCurrentProcessId()));
+ if (!snap.Get())
+ return;
+
+ // Walk the module list.
+ MODULEENTRY32 module = { sizeof(module) };
+ if (!::Module32First(snap.Get(), &module))
+ return;
+
+ do {
+ // It would be weird to present chrome.exe as a loaded module.
+ if (_wcsicmp(chrome::kBrowserProcessExecutableName, module.szModule) == 0)
+ continue;
+
+ Module entry;
+ entry.type = LOADED_MODULE;
+ entry.status = NOT_MATCHED;
+ entry.normalized = false;
+ entry.location = module.szExePath;
+ entry.digital_signer =
+ GetSubjectNameFromDigitalSignature(FilePath(entry.location));
+ entry.recommended_action = NONE;
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(FilePath(entry.location)));
+ if (version_info.get()) {
+ FileVersionInfoWin* version_info_win =
+ static_cast<FileVersionInfoWin*>(version_info.get());
+
+ VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info();
+ if (fixed_file_info) {
+ entry.description = version_info_win->file_description();
+ entry.version = version_info_win->file_version();
+ entry.product_name = version_info_win->product_name();
+ }
+ }
+
+ NormalizeModule(&entry);
+ CollapsePath(&entry);
+ enumerated_modules_->push_back(entry);
+ } while (::Module32Next(snap.Get(), &module));
+
+ // Add to this list the Winsock LSP DLLs.
+ WinsockLayeredServiceProviderList layered_providers;
+ GetWinsockLayeredServiceProviders(&layered_providers);
+ for (size_t i = 0; i < layered_providers.size(); ++i) {
+ Module entry;
+ entry.type = WINSOCK_MODULE_REGISTRATION;
+ entry.status = NOT_MATCHED;
+ entry.normalized = false;
+ entry.location = layered_providers[i].path;
+ entry.description = layered_providers[i].name;
+ entry.recommended_action = NONE;
+
+ wchar_t expanded[MAX_PATH];
+ DWORD size = ExpandEnvironmentStrings(
+ entry.location.c_str(), expanded, MAX_PATH);
+ if (size != 0 && size <= MAX_PATH) {
+ entry.digital_signer =
+ GetSubjectNameFromDigitalSignature(FilePath(expanded));
+ }
+ entry.version = base::IntToString16(layered_providers[i].version);
+
+ // Paths have already been collapsed.
+ NormalizeModule(&entry);
+ enumerated_modules_->push_back(entry);
+ }
+
+ MatchAgainstBlacklist();
+
+ std::sort(enumerated_modules_->begin(),
+ enumerated_modules_->end(), ModuleSort);
+
+ // Send a reply back on the UI thread.
+ BrowserThread::PostTask(
+ callback_thread_id_, FROM_HERE,
+ NewRunnableMethod(this, &ModuleEnumerator::ReportBack));
+}
+
+void ModuleEnumerator::PreparePathMappings() {
+ path_mapping_.clear();
+
+ scoped_ptr<base::Environment> environment(base::Environment::Create());
+ std::vector<string16> env_vars;
+ env_vars.push_back(L"LOCALAPPDATA");
+ env_vars.push_back(L"ProgramFiles");
+ env_vars.push_back(L"USERPROFILE");
+ env_vars.push_back(L"SystemRoot");
+ env_vars.push_back(L"TEMP");
+ for (std::vector<string16>::const_iterator variable = env_vars.begin();
+ variable != env_vars.end(); ++variable) {
+ std::string path;
+ if (environment->GetVar(WideToASCII(*variable).c_str(), &path)) {
+ path_mapping_.push_back(
+ std::make_pair(l10n_util::ToLower(UTF8ToWide(path)) + L"\\",
+ L"%" + l10n_util::ToLower(*variable) + L"%"));
+ }
+ }
+}
+
+void ModuleEnumerator::CollapsePath(Module* entry) {
+ // Take the path and see if we can use any of the substitution values
+ // from the vector constructed above to replace c:\windows with, for
+ // example, %systemroot%.
+ for (PathMapping::const_iterator mapping = path_mapping_.begin();
+ mapping != path_mapping_.end(); ++mapping) {
+ string16 prefix = mapping->first;
+ if (StartsWith(entry->location, prefix, false)) {
+ entry->location = mapping->second +
+ entry->location.substr(prefix.length() - 1);
+ return;
+ }
+ }
+}
+
+void ModuleEnumerator::MatchAgainstBlacklist() {
+ for (size_t m = 0; m < enumerated_modules_->size(); ++m) {
+ // Match this module against the blacklist.
+ Module* module = &(*enumerated_modules_)[m];
+ module->status = GOOD; // We change this below potentially.
+ for (size_t i = 0; i < arraysize(kModuleBlacklist); ++i) {
+ #if !defined(NDEBUG)
+ // This saves time when constructing the blacklist.
+ std::string hashes(kModuleBlacklist[i].filename);
+ std::string hash1, hash2, hash3;
+ GenerateHash(kModuleBlacklist[i].filename, &hash1);
+ hashes += " - " + hash1;
+ GenerateHash(kModuleBlacklist[i].location, &hash2);
+ hashes += " - " + hash2;
+ GenerateHash(kModuleBlacklist[i].desc_or_signer, &hash3);
+ hashes += " - " + hash3;
+ #endif
+
+ ModuleStatus status = Match(*module, kModuleBlacklist[i]);
+ if (status != NOT_MATCHED) {
+ // We have a match against the blacklist. Mark it as such.
+ module->status = status;
+ module->recommended_action = kModuleBlacklist[i].help_tip;
+ break;
+ }
+ }
+ }
+}
+
+void ModuleEnumerator::ReportBack() {
+ DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
+ observer_->DoneScanning();
+}
+
+string16 ModuleEnumerator::GetSubjectNameFromDigitalSignature(
+ const FilePath& filename) {
+ HCERTSTORE store = NULL;
+ HCRYPTMSG message = NULL;
+
+ // Find the crypto message for this filename.
+ bool result = !!CryptQueryObject(CERT_QUERY_OBJECT_FILE,
+ filename.value().c_str(),
+ CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
+ CERT_QUERY_FORMAT_FLAG_BINARY,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ &store,
+ &message,
+ NULL);
+ if (!result)
+ return string16();
+
+ // Determine the size of the signer info data.
+ DWORD signer_info_size = 0;
+ result = !!CryptMsgGetParam(message,
+ CMSG_SIGNER_INFO_PARAM,
+ 0,
+ NULL,
+ &signer_info_size);
+ if (!result)
+ return string16();
+
+ // Allocate enough space to hold the signer info.
+ scoped_array<BYTE> signer_info_buffer(new BYTE[signer_info_size]);
+ CMSG_SIGNER_INFO* signer_info =
+ reinterpret_cast<CMSG_SIGNER_INFO*>(signer_info_buffer.get());
+
+ // Obtain the signer info.
+ result = !!CryptMsgGetParam(message,
+ CMSG_SIGNER_INFO_PARAM,
+ 0,
+ signer_info,
+ &signer_info_size);
+ if (!result)
+ return string16();
+
+ // Search for the signer certificate.
+ CERT_INFO CertInfo = {0};
+ PCCERT_CONTEXT cert_context = NULL;
+ CertInfo.Issuer = signer_info->Issuer;
+ CertInfo.SerialNumber = signer_info->SerialNumber;
+
+ cert_context = CertFindCertificateInStore(
+ store,
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ 0,
+ CERT_FIND_SUBJECT_CERT,
+ &CertInfo,
+ NULL);
+ if (!cert_context)
+ return string16();
+
+ // Determine the size of the Subject name.
+ DWORD subject_name_size = 0;
+ if (!(subject_name_size = CertGetNameString(cert_context,
+ CERT_NAME_SIMPLE_DISPLAY_TYPE,
+ 0,
+ NULL,
+ NULL,
+ 0))) {
+ return string16();
+ }
+
+ string16 subject_name;
+ subject_name.resize(subject_name_size);
+
+ // Get subject name.
+ if (!(CertGetNameString(cert_context,
+ CERT_NAME_SIMPLE_DISPLAY_TYPE,
+ 0,
+ NULL,
+ const_cast<LPWSTR>(subject_name.c_str()),
+ subject_name_size))) {
+ return string16();
+ }
+
+ return subject_name;
+}
+
+// ----------------------------------------------------------------------------
+
+void EnumerateModulesModel::ScanNow() {
+ if (scanning_)
+ return; // A scan is already in progress.
+
+ lock->Acquire(); // Balanced in DoneScanning();
+
+ scanning_ = true;
+
+ // Instruct the ModuleEnumerator class to load this on the File thread.
+ // ScanNow does not block.
+ if (!module_enumerator_)
+ module_enumerator_ = new ModuleEnumerator(this);
+ module_enumerator_->ScanNow(&enumerated_modules_);
+}
+
+ListValue* EnumerateModulesModel::GetModuleList() {
+ if (scanning_)
+ return NULL;
+
+ lock->Acquire();
+
+ if (enumerated_modules_.empty()) {
+ lock->Release();
+ return NULL;
+ }
+
+ ListValue* list = new ListValue();
+
+ for (ModuleEnumerator::ModulesVector::const_iterator module =
+ enumerated_modules_.begin();
+ module != enumerated_modules_.end(); ++module) {
+ DictionaryValue* data = new DictionaryValue();
+ data->SetInteger("type", module->type);
+ data->SetString("type_description",
+ (module->type == ModuleEnumerator::WINSOCK_MODULE_REGISTRATION) ?
+ ASCIIToWide("Winsock") : ASCIIToWide(""));
+ data->SetInteger("status", module->status);
+ data->SetString("location", module->location);
+ data->SetString("name", module->name);
+ data->SetString("product_name", module->product_name);
+ data->SetString("description", module->description);
+ data->SetString("version", module->version.empty() ? ASCIIToWide("") :
+ l10n_util::GetStringF(IDS_CONFLICTS_CHECK_VERSION_STRING,
+ module->version));
+ data->SetString("digital_signer", module->digital_signer);
+
+ // Figure out the possible resolution help string.
+ string16 actions;
+ string16 separator = ASCIIToWide(" ") + l10n_util::GetStringUTF16(
+ IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_SEPERATOR) +
+ ASCIIToWide(" ");
+
+ if (module->recommended_action & ModuleEnumerator::NONE) {
+ actions = l10n_util::GetStringUTF16(
+ IDS_CONFLICTS_CHECK_INVESTIGATING);
+ }
+ if (module->recommended_action & ModuleEnumerator::UNINSTALL) {
+ if (!actions.empty())
+ actions += separator;
+ actions = l10n_util::GetStringUTF16(
+ IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UNINSTALL);
+ }
+ if (module->recommended_action & ModuleEnumerator::UPDATE) {
+ if (!actions.empty())
+ actions += separator;
+ actions += l10n_util::GetStringUTF16(
+ IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UPDATE);
+ }
+ if (module->recommended_action & ModuleEnumerator::DISABLE) {
+ if (!actions.empty())
+ actions += separator;
+ actions += l10n_util::GetStringUTF16(
+ IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_DISABLE);
+ }
+ string16 possible_resolution = actions.empty() ? ASCIIToWide("") :
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_POSSIBLE_ACTIONS) +
+ ASCIIToWide(" ") +
+ actions;
+ data->SetString("possibleResolution", possible_resolution);
+ data->SetString("help_url", ConstructHelpCenterUrl(*module).spec().c_str());
+
+ list->Append(data);
+ }
+
+ lock->Release();
+ return list;
+}
+
+EnumerateModulesModel::EnumerateModulesModel()
+: scanning_(false),
+confirmed_bad_modules_detected_(0),
+suspected_bad_modules_detected_(0) {
+ const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
+ if (cmd_line.HasSwitch(switches::kConflictingModulesCheck)) {
+ check_modules_timer_.Start(
+ base::TimeDelta::FromMilliseconds(kModuleCheckDelayMs),
+ this, &EnumerateModulesModel::ScanNow);
+ }
+
+ lock = new Lock();
+}
+
+EnumerateModulesModel::~EnumerateModulesModel() {
+ delete lock;
+}
+
+void EnumerateModulesModel::DoneScanning() {
+ confirmed_bad_modules_detected_ = 0;
+ suspected_bad_modules_detected_ = 0;
+ for (ModuleEnumerator::ModulesVector::const_iterator module =
+ enumerated_modules_.begin();
+ module != enumerated_modules_.end(); ++module) {
+ if (module->status == ModuleEnumerator::CONFIRMED_BAD)
+ ++confirmed_bad_modules_detected_;
+ if (module->status == ModuleEnumerator::SUSPECTED_BAD)
+ ++suspected_bad_modules_detected_;
+ }
+
+ scanning_ = false;
+ lock->Release();
+
+ NotificationService::current()->Notify(
+ NotificationType::MODULE_LIST_ENUMERATED,
+ Source<EnumerateModulesModel>(this),
+ NotificationService::NoDetails());
+
+ if (suspected_bad_modules_detected_ || confirmed_bad_modules_detected_) {
+ bool found_confirmed_bad_modules = confirmed_bad_modules_detected_ > 0;
+ NotificationService::current()->Notify(
+ NotificationType::MODULE_INCOMPATIBILITY_DETECTED,
+ Source<EnumerateModulesModel>(this),
+ Details<bool>(&found_confirmed_bad_modules));
+ }
+}
+
+GURL EnumerateModulesModel::ConstructHelpCenterUrl(
+ const ModuleEnumerator::Module& module) {
+ if (!(module.recommended_action & ModuleEnumerator::SEE_LINK))
+ return GURL();
+
+ // Construct the needed hashes.
+ std::string filename, location, description, signer;
+ GenerateHash(WideToUTF8(module.name), &filename);
+ GenerateHash(WideToUTF8(module.location), &location);
+ GenerateHash(WideToUTF8(module.description), &description);
+ GenerateHash(WideToUTF8(module.digital_signer), &signer);
+
+ string16 url = l10n_util::GetStringF(IDS_HELP_CENTER_VIEW_CONFLICTS,
+ ASCIIToWide(filename), ASCIIToWide(location),
+ ASCIIToWide(description), ASCIIToWide(signer));
+ return GURL(WideToUTF8(url));
+}
diff --git a/chrome/browser/enumerate_modules_model_win.h b/chrome/browser/enumerate_modules_model_win.h
new file mode 100644
index 0000000..54684d0
--- /dev/null
+++ b/chrome/browser/enumerate_modules_model_win.h
@@ -0,0 +1,259 @@
+// 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.
+
+#ifndef CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_
+#define CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_
+#pragma once
+
+#include <utility>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/singleton.h"
+#include "base/string16.h"
+#include "base/timer.h"
+#include "chrome/browser/browser_thread.h"
+#include "googleurl/src/gurl.h"
+
+class EnumerateModulesModel;
+class FilePath;
+class ListValue;
+
+// A helper class that implements the enumerate module functionality on the File
+// thread.
+class ModuleEnumerator : public base::RefCountedThreadSafe<ModuleEnumerator> {
+ public:
+ // What type of module we are dealing with. Loaded modules are modules we
+ // detect as loaded in the process at the time of scanning. The others are
+ // modules of interest and may or may not be loaded in the process at the
+ // time of scan.
+ enum ModuleType {
+ LOADED_MODULE,
+ WINSOCK_MODULE_REGISTRATION,
+ };
+
+ // The blacklist status of the module. Suspected Bad modules have been
+ // partially matched (ie. name matches and location, but not description)
+ // whereas Confirmed Bad modules have been identified further (ie.
+ // AuthentiCode signer matches).
+ enum ModuleStatus {
+ // This is returned by the matching function when comparing against the
+ // blacklist and the module does not match the current entry in the
+ // blacklist.
+ NOT_MATCHED,
+ // The module is not on the blacklist. Assume it is good.
+ GOOD,
+ // Module is a suspected bad module.
+ SUSPECTED_BAD,
+ // Module is a bad bad dog.
+ CONFIRMED_BAD,
+ };
+
+ // A bitmask with the possible resolutions for bad modules.
+ enum RecommendedAction {
+ NONE = 0,
+ INVESTIGATING = 1 << 0,
+ UNINSTALL = 1 << 1,
+ DISABLE = 1 << 2,
+ UPDATE = 1 << 3,
+ SEE_LINK = 1 << 4,
+ };
+
+ // The structure we populate when enumerating modules.
+ struct Module {
+ // The type of module found
+ ModuleType type;
+ // The module status (benign/bad/etc).
+ ModuleStatus status;
+ // The module path, not including filename.
+ string16 location;
+ // The name of the module (filename).
+ string16 name;
+ // The name of the product the module belongs to.
+ string16 product_name;
+ // The module file description.
+ string16 description;
+ // The module version.
+ string16 version;
+ // The signer of the digital certificate for the module.
+ string16 digital_signer;
+ // The help tips bitmask.
+ RecommendedAction recommended_action;
+ // Whether this module has been normalized (necessary before checking it
+ // against blacklist).
+ bool normalized;
+ };
+
+ // A vector typedef of all modules enumerated.
+ typedef std::vector<Module> ModulesVector;
+
+ // A structure we populate with the blacklist entries.
+ struct BlacklistEntry {
+ const char* filename;
+ const char* location;
+ const char* desc_or_signer;
+ const char* version_from;
+ const char* version_to;
+ RecommendedAction help_tip;
+ };
+
+ // A static function that normalizes the module information in the |module|
+ // struct. Module information needs to be normalized before comparing against
+ // the blacklist. This is because the same module can be described in many
+ // different ways, ie. file paths can be presented in long/short name form,
+ // and are not case sensitive on Windows. Also, the version string returned
+ // can include appended text, which we don't want to use during comparison
+ // against the blacklist.
+ static void NormalizeModule(Module* module);
+
+ // A static function that checks whether |module| has been |blacklisted|.
+ static ModuleStatus Match(const Module& module,
+ const BlacklistEntry& blacklisted);
+
+ explicit ModuleEnumerator(EnumerateModulesModel* observer);
+ virtual ~ModuleEnumerator();
+
+ // Start scanning the loaded module list (if a scan is not already in
+ // progress). This function does not block while reading the module list, but
+ // will notify when done through the MODULE_LIST_ENUMERATED notification.
+ // The process will also send MODULE_INCOMPATIBILITY_DETECTED if an
+ // incompatible module was detected.
+ void ScanNow(ModulesVector* list);
+
+ private:
+ // The (currently) hard coded blacklist of known bad modules.
+ static const BlacklistEntry kModuleBlacklist[];
+
+ // This function does the actual file scanning work on the FILE thread. It
+ // enumerates all loaded modules in the process and other modules of
+ // interest, such as the registered Winsock LSP modules and stores them in
+ // |enumerated_modules_|. It then normalizes the module info and matches
+ // them against a blacklist of known bad modules. Finally, it calls
+ // ReportBack to let the observer know we are done.
+ void ScanOnFileThread();
+
+ // Builds up a vector of path values mapping to environment variable,
+ // with pairs like [c:\windows\, %systemroot%]. This is later used to
+ // collapse paths like c:\windows\system32 into %systemroot%\system32, which
+ // we can use for comparison against our blacklist (which uses only env vars).
+ // NOTE: The vector will not contain an exhaustive list of environment
+ // variables, only the ones currently found on the blacklist or ones that are
+ // likely to appear there.
+ void PreparePathMappings();
+
+ // For a given |module|, collapse the path from c:\windows to %systemroot%,
+ // based on the |path_mapping_| vector.
+ void CollapsePath(Module* module);
+
+ // Takes each module in the |enumerated_modules_| vector and matches it
+ // against a fixed blacklist of bad and suspected bad modules.
+ void MatchAgainstBlacklist();
+
+ // This function executes on the UI thread when the scanning and matching
+ // process is done. It notifies the observer.
+ void ReportBack();
+
+ // Given a filename, returns the Subject (who signed it) retrieved from
+ // the digital signature (Authenticode).
+ string16 GetSubjectNameFromDigitalSignature(const FilePath& filename);
+
+ // The typedef for the vector that maps a regular file path to %env_var%.
+ typedef std::vector< std::pair<string16, string16> > PathMapping;
+
+ // The vector of paths to %env_var%, used to account for differences in
+ // where people keep there files, c:\windows vs. d:\windows, etc.
+ PathMapping path_mapping_;
+
+ // The vector containing all the enumerated modules (loaded and modules of
+ // interest).
+ ModulesVector* enumerated_modules_;
+
+ // The observer, who needs to be notified when we are done.
+ EnumerateModulesModel* observer_;
+
+ // The thread that we need to call back on to report that we are done.
+ BrowserThread::ID callback_thread_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleEnumerator);
+};
+
+// This is a singleton class that enumerates all modules loaded into Chrome,
+// both currently loaded modules (called DLLs on Windows) and modules 'of
+// interest', such as WinSock LSP modules. This class also marks each module
+// as benign or suspected bad or outright bad, using a supplied blacklist that
+// is currently hard-coded.
+//
+// To use this class, grab the singleton pointer and call ScanNow().
+// Then wait to get notified through MODULE_LIST_ENUMERATED when the list is
+// ready.
+//
+// This class can be used on the UI thread as it asynchronously offloads the
+// file work over to the FILE thread and reports back to the caller with a
+// notification.
+class EnumerateModulesModel {
+ public:
+ static EnumerateModulesModel* GetSingleton() {
+ return Singleton<EnumerateModulesModel>::get();
+ }
+
+ // Returns the number of suspected bad modules found in the last scan.
+ // Returns 0 if no scan has taken place yet.
+ int suspected_bad_modules_detected() {
+ return suspected_bad_modules_detected_;
+ }
+
+ // Returns the number of confirmed bad modules found in the last scan.
+ // Returns 0 if no scan has taken place yet.
+ int confirmed_bad_modules_detected() {
+ return confirmed_bad_modules_detected_;
+ }
+
+ // Asynchronously start the scan for the loaded module list.
+ // When the list is ready.
+ void ScanNow();
+
+ // Gets the whole module list as a ListValue.
+ ListValue* GetModuleList();
+
+ private:
+ friend struct DefaultSingletonTraits<EnumerateModulesModel>;
+ friend class ModuleEnumerator;
+
+ EnumerateModulesModel();
+ virtual ~EnumerateModulesModel();
+
+ // Called on the UI thread when the helper class is done scanning.
+ void DoneScanning();
+
+ // Constructs a Help Center article URL for help with a particular module.
+ // The module must have the SEE_LINK attribute for |recommended_action| set,
+ // otherwise this returns a blank string.
+ GURL ConstructHelpCenterUrl(const ModuleEnumerator::Module& module);
+
+ // The vector containing all the modules enumerated. Will be normalized and
+ // any bad modules will be marked.
+ ModuleEnumerator::ModulesVector enumerated_modules_;
+
+ // The object responsible for enumerating the modules on the File thread.
+ scoped_refptr<ModuleEnumerator> module_enumerator_;
+
+ // When this singleton object is constructed we go and fire off this timer to
+ // start scanning for modules after a certain amount of time has passed.
+ base::OneShotTimer<EnumerateModulesModel> check_modules_timer_;
+
+ // True if we are currently scanning for modules.
+ bool scanning_;
+
+ // The number of confirmed bad modules (not including suspected bad ones)
+ // found during last scan.
+ int confirmed_bad_modules_detected_;
+
+ // The number of suspected bad modules (not including confirmed bad ones)
+ // found during last scan.
+ int suspected_bad_modules_detected_;
+
+ DISALLOW_COPY_AND_ASSIGN(EnumerateModulesModel);
+};
+
+#endif // CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_
diff --git a/chrome/browser/resources/about_conflicts.html b/chrome/browser/resources/about_conflicts.html
new file mode 100644
index 0000000..2bf6a28
--- /dev/null
+++ b/chrome/browser/resources/about_conflicts.html
@@ -0,0 +1,292 @@
+<!DOCTYPE HTML>
+<html i18n-values="dir:textdirection;">
+<head>
+<meta charset="utf-8">
+<style>
+body {
+ margin: 10px;
+ min-width: 47em;
+}
+
+a {
+ color: blue;
+ font-size: 103%;
+}
+
+div#header {
+ margin-bottom: 1.05em;
+ /* 67px is the height of the header's background image. */
+ min-height: 67px;
+ overflow: hidden;
+ padding-bottom: 20px;
+ padding-left: 0;
+ padding-top: 20px;
+ position: relative;
+ -webkit-box-sizing: border-box;
+}
+
+html[dir=rtl] #header {
+ padding-right: 0;
+}
+
+#header h1 {
+ background: url('../../app/theme/conflicts_section.png') 0px 20px no-repeat;
+ display: inline;
+ margin: 0;
+ padding-bottom: 43px;
+ padding-left: 75px;
+ padding-top: 40px;
+}
+
+html[dir=rtl] #header h1 {
+ background: url('../../app/theme/conflicts_section.png') right no-repeat;
+ padding-right: 95px;
+ padding-left: 0;
+}
+
+h1 {
+ font-size: 156%;
+ font-weight: bold;
+ padding: 0;
+ margin: 0;
+}
+
+#blurb-container {
+ padding-bottom: 1.5em;
+ font-size: 120%;
+}
+
+div.content {
+ font-size: 88%;
+ margin-top: 5px;
+}
+
+.section-header {
+ background: #ebeff9;
+ border-top: 1px solid #b5c7de;
+ font-size: 99%;
+ padding-bottom: 2px;
+ padding-left: 5px;
+ padding-top: 3px;
+ width: 100%;
+}
+
+html[dir=rtl] .section-header {
+ padding-right: 5px;
+ padding-left: 0;
+}
+
+.section-header > table > tr > td:first-child {
+ width: 100%;
+}
+
+.section-header > table {
+ width: 100%;
+}
+
+.section-header-title {
+ font-weight: bold;
+}
+
+.vbox-container {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+}
+
+.wbox {
+ display: -webkit-box;
+ -webkit-box-align: stretch;
+ -webkit-box-flex: 1;
+}
+
+#top {
+ padding-right: 5px;
+}
+
+html[dir=rtl] #top {
+ padding-left: 5px;
+ padding-right: 0;
+}
+
+.module-loaded > td {
+ padding-bottom: 4px;
+ padding-top: 5px;
+}
+
+.module {
+ border-bottom: 1px solid #cdcdcd;
+}
+
+.module-name {
+ font-weight: bold;
+}
+
+.no-modules {
+ margin: 6em 0 0;
+ text-align: center;
+ font-size: 1.2em;
+}
+
+.suspected-bad {
+ color: orange;
+}
+
+.confirmed-bad {
+ color: red;
+}
+</style>
+<script>
+
+ /**
+ * This variable structure is here to document the structure that the template
+ * expects to correctly populate the page.
+ */
+ var moduleListDataFormat = {
+ 'moduleList': [
+ {
+ 'type': 'The type of module found',
+ 'type_description':
+ 'The type of module (string), defaults to blank for regular modules',
+ 'status': 'The module status',
+ 'location': 'The module path, not including filename',
+ 'name': 'The name of the module',
+ 'product_name': 'The name of the product the module belongs to',
+ 'description': 'The module description',
+ 'version': 'The module version',
+ 'digital_signer': 'The signer of the digital certificate for the module',
+ 'recommended_action': 'The help tips bitmask',
+ 'possible_resolution': 'The help tips in string form',
+ 'help_url': 'The link to the Help Center article'
+ }
+ ]
+ };
+
+ /**
+ * Takes the |moduleListData| input argument which represents data about
+ * the currently available modules and populates the html jstemplate
+ * with that data. It expects an object structure like the above.
+ * @param {Object} moduleListData Information about available modules
+ */
+ function renderTemplate(moduleListData) {
+ // This is the javascript code that processes the template:
+ var input = new JsEvalContext(moduleListData);
+ var output = document.getElementById('modulesTemplate');
+ jstProcess(input, output);
+ }
+
+ /**
+ * Asks the C++ ConflictsDOMHandler to get details about the available modules
+ * and return detailed data about the configuration. The ConflictsDOMHandler
+ * should reply to returnModuleList() (below).
+ */
+ function requestModuleListData() {
+ chrome.send('requestModuleList', []);
+ }
+
+ /**
+ * Called by the dom_ui to re-populate the page with data representing the
+ * current state of installed modules.
+ */
+ function returnModuleList(moduleListData) {
+ var bodyContainer = document.getElementById('body-container');
+ renderTemplate(moduleListData);
+ bodyContainer.style.visibility = 'visible';
+ }
+
+ // Get data and have it displayed upon loading.
+ document.addEventListener('DOMContentLoaded', requestModuleListData);
+
+</script>
+</head>
+<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
+<div id="body-container" style="visibility:hidden">
+
+ <div id="header"><h1 i18n-content="modulesLongTitle">TITLE</h1></div>
+
+ <div id="blurb-container">
+ <span i18n-content="modulesBlurb">MODULES BLURB</span>
+ </div>
+
+ <div id="modulesTemplate">
+
+ <div id="container" class="vbox-container">
+ <div id="top" class="wbox">
+
+ <div class="section-header">
+ <table cellpadding="0" cellspacing="0"><tr valign="center">
+ <td>
+ <span class="section-header-title"
+ jscontent="modulesTableTitle">TITLE</span>
+ </td>
+ </tr></table>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="content">
+ <div class="module-name no-modules"
+ jsdisplay="moduleList.length === 0">
+ <div i18n-content="modulesNoneLoaded">NO_MODULES_ARE_AVAILABLE</div>
+ </div>
+
+ <div jsdisplay="moduleList.length > 0">
+ <div class="module" jsselect="moduleList">
+ <table width="100%" cellpadding="0" cellspacing="0">
+ <tr class="module-loaded">
+ <td valign="top">
+ <table cellpadding="2" cellspacing="0">
+ <tr>
+ <td colspan="2"><span class="module-name" dir="ltr"
+ jscontent="name">NAME</span>
+ </td>
+ </tr>
+ <tr>
+ <td width="75"><span i18n-content="headerSoftware" /></td>
+ <td><span dir="ltr" jsdisplay="type_description.length > 0">
+ <span dir="ltr"
+ jscontent="type_description">MODULE_TYPE</span>: </span>
+ <span dir="ltr" jsvalues=".innerHTML:description"></span>
+ <span dir="ltr" jsdisplay="version.length > 0"> - </span>
+ <span dir="ltr" jscontent="version">VERSION</span></td>
+ </tr>
+ <tr jsdisplay="digital_signer.length > 0">
+ <td><span i18n-content="headerSignedBy" /></td>
+ <td><span dir="ltr" jscontent="digital_signer">SIGNER</span></td>
+ </tr>
+ <tr>
+ <td><span i18n-content="headerLocation" /></td>
+ <td><span dir="ltr"
+ jscontent="location">LOCATION</span><span
+ dir="ltr" jscontent="name">NAME</span></td>
+ </tr>
+ <tr jsdisplay="status == 2 || status == 3">
+ <td><span i18n-content="headerWarning" /></td>
+ <td><span jsdisplay="status == 2"
+ i18n-content="moduleSuspectedBad"
+ class="suspected-bad">SUSPECTED_BAD</span>
+ <span jsdisplay="status == 3"
+ i18n-content="moduleConfirmedBad"
+ class="confirmed-bad">CONFIRMED_BAD</span>
+ <a jsdisplay="help_url.length > 0"
+ jsvalues=".href:help_url"><span
+ i18n-content="helpCenterLink">HELP_CENTER</span></a>
+ </td>
+ </tr>
+ <tr jsdisplay="possibleResolution.length > 0">
+ <td><span i18n-content="headerHelpTip" /></td>
+ <td><span
+ jscontent="possibleResolution">POSSIBLE_RESOLUTION</span></td>
+ </tr>
+ </table>
+
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</body>
+</html>
diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc
index ded8a63..f026d03 100644
--- a/chrome/browser/views/toolbar_view.cc
+++ b/chrome/browser/views/toolbar_view.cc
@@ -41,6 +41,10 @@
#endif
#include "chrome/browser/views/wrench_menu.h"
+#if defined(OS_WIN)
+#include "chrome/browser/enumerate_modules_model_win.h"
+#endif
+
// The space between items is 4 px in general.
const int ToolbarView::kStandardSpacing = 4;
// The top of the toolbar has an edge we have to skip over in addition to the 4
@@ -102,6 +106,8 @@ ToolbarView::ToolbarView(Browser* browser)
registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED,
NotificationService::AllSources());
}
+ registrar_.Add(this, NotificationType::MODULE_INCOMPATIBILITY_DETECTED,
+ NotificationService::AllSources());
}
ToolbarView::~ToolbarView() {
@@ -175,8 +181,8 @@ void ToolbarView::Init(Profile* profile) {
app_menu_->SetID(VIEW_ID_APP_MENU);
// Catch the case where the window is created after we detect a new version.
- if (IsUpgradeRecommended())
- ShowUpgradeReminder();
+ if (IsUpgradeRecommended() || ShouldShowIncompatibilityWarning())
+ ShowNotificationDot();
LoadImages();
@@ -317,8 +323,8 @@ cleanup:
return;
destroyed_flag_ = NULL;
- // Stop pulsating the upgrade reminder on the app menu, if active.
- upgrade_reminder_pulse_timer_.Stop();
+ // Stop pulsating the notification dot on the app menu (if active).
+ notification_dot_pulse_timer_.Stop();
}
////////////////////////////////////////////////////////////////////////////////
@@ -401,7 +407,11 @@ void ToolbarView::Observe(NotificationType type,
SchedulePaint();
}
} else if (type == NotificationType::UPGRADE_RECOMMENDED) {
- ShowUpgradeReminder();
+ ShowNotificationDot();
+ } else if (type == NotificationType::MODULE_INCOMPATIBILITY_DETECTED) {
+ bool confirmed_bad = *Details<bool>(details).ptr();
+ if (confirmed_bad)
+ ShowNotificationDot();
}
}
@@ -580,6 +590,15 @@ bool ToolbarView::IsUpgradeRecommended() {
#endif
}
+bool ToolbarView::ShouldShowIncompatibilityWarning() {
+#if defined(OS_WIN)
+ EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetSingleton();
+ return loaded_modules->confirmed_bad_modules_detected() > 0;
+#else
+ return false;
+#endif
+}
+
int ToolbarView::PopupTopSpacing() const {
return GetWindow()->GetNonClientView()->UseNativeFrame() ?
0 : kPopupTopSpacingNonGlass;
@@ -629,20 +648,21 @@ void ToolbarView::LoadImages() {
app_menu_->SetPushedIcon(GetAppMenuIcon(views::CustomButton::BS_PUSHED));
}
-void ToolbarView::ShowUpgradeReminder() {
- update_reminder_animation_.reset(new SlideAnimation(this));
- update_reminder_animation_->SetSlideDuration(kPulseDuration);
+void ToolbarView::ShowNotificationDot() {
+ notification_dot_animation_.reset(new SlideAnimation(this));
+ notification_dot_animation_->SetSlideDuration(kPulseDuration);
// Then start the recurring timer for pulsating it.
- upgrade_reminder_pulse_timer_.Start(
+ notification_dot_pulse_timer_.Stop();
+ notification_dot_pulse_timer_.Start(
base::TimeDelta::FromMilliseconds(kPulsateEveryMs),
- this, &ToolbarView::PulsateUpgradeNotifier);
+ this, &ToolbarView::PulsateNotificationDot);
}
-void ToolbarView::PulsateUpgradeNotifier() {
+void ToolbarView::PulsateNotificationDot() {
// Start the pulsating animation.
- update_reminder_animation_->Reset(0.0);
- update_reminder_animation_->Show();
+ notification_dot_animation_->Reset(0.0);
+ notification_dot_animation_->Show();
}
SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) {
@@ -657,7 +677,8 @@ SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) {
}
SkBitmap icon = *tp->GetBitmapNamed(id);
- if (!IsUpgradeRecommended())
+ bool add_badge = IsUpgradeRecommended() || ShouldShowIncompatibilityWarning();
+ if (!add_badge)
return icon;
// Draw the chrome app menu icon onto the canvas.
@@ -668,27 +689,48 @@ SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) {
SkBitmap badge;
static bool has_faded_in = false;
if (!has_faded_in) {
- SkBitmap* dot = tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE);
+ SkBitmap* dot = NULL;
+ if (ShouldShowIncompatibilityWarning()) {
+#if defined(OS_WIN)
+ dot = tp->GetBitmapNamed(IDR_INCOMPATIBILITY_DOT_INACTIVE);
+#else
+ NOTREACHED();
+#endif
+ } else {
+ dot = tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE);
+ }
SkBitmap transparent;
transparent.setConfig(dot->getConfig(), dot->width(), dot->height());
transparent.allocPixels();
transparent.eraseARGB(0, 0, 0, 0);
badge = SkBitmapOperations::CreateBlendedBitmap(
- *dot, transparent, 1.0 - update_reminder_animation_->GetCurrentValue());
- if (update_reminder_animation_->GetCurrentValue() == 1.0)
+ *dot, transparent,
+ 1.0 - notification_dot_animation_->GetCurrentValue());
+ if (notification_dot_animation_->GetCurrentValue() == 1.0)
has_faded_in = true;
} else {
// Convert animation values that start from 0.0 and incrementally go
// up to 1.0 into values that start in 0.0, go to 1.0 and then back
// to 0.0 (to create a pulsing effect).
double value =
- 1.0 - abs(2.0 * update_reminder_animation_->GetCurrentValue() - 1.0);
+ 1.0 - abs(2.0 * notification_dot_animation_->GetCurrentValue() - 1.0);
// Add the badge to it.
- badge = SkBitmapOperations::CreateBlendedBitmap(
- *tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE),
- *tp->GetBitmapNamed(IDR_UPGRADE_DOT_ACTIVE),
- value);
+ if (ShouldShowIncompatibilityWarning()) {
+#if defined(OS_WIN)
+ badge = SkBitmapOperations::CreateBlendedBitmap(
+ *tp->GetBitmapNamed(IDR_INCOMPATIBILITY_DOT_INACTIVE),
+ *tp->GetBitmapNamed(IDR_INCOMPATIBILITY_DOT_ACTIVE),
+ value);
+#else
+ NOTREACHED();
+#endif
+ } else {
+ badge = SkBitmapOperations::CreateBlendedBitmap(
+ *tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE),
+ *tp->GetBitmapNamed(IDR_UPGRADE_DOT_ACTIVE),
+ value);
+ }
}
static const int kBadgeLeftSpacing = 8;
diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h
index 8715dc5..c9b92bd 100644
--- a/chrome/browser/views/toolbar_view.h
+++ b/chrome/browser/views/toolbar_view.h
@@ -142,6 +142,9 @@ class ToolbarView : public AccessiblePaneView,
// Returns true if we should show the upgrade recommended dot.
bool IsUpgradeRecommended();
+ // Returns true if we should show the warning for incompatible software.
+ bool ShouldShowIncompatibilityWarning();
+
// Returns the number of pixels above the location bar in non-normal display.
int PopupTopSpacing() const;
@@ -158,12 +161,12 @@ class ToolbarView : public AccessiblePaneView,
return display_mode_ == DISPLAYMODE_NORMAL;
}
- // Starts the recurring timer that periodically asks the upgrade notifier
+ // Starts the recurring timer that periodically asks the notification dot
// to pulsate.
- void ShowUpgradeReminder();
+ void ShowNotificationDot();
- // Show the reminder, tempting the user to upgrade by pulsating.
- void PulsateUpgradeNotifier();
+ // Show the reminder, tempting the user to take a look.
+ void PulsateNotificationDot();
// Gets a canvas with the icon for the app menu. It will possibly contain
// an overlaid badge if an update is recommended.
@@ -210,12 +213,12 @@ class ToolbarView : public AccessiblePaneView,
// Vector of listeners to receive callbacks when the menu opens.
std::vector<views::MenuListener*> menu_listeners_;
- // The animation that makes the update reminder pulse.
- scoped_ptr<SlideAnimation> update_reminder_animation_;
+ // The animation that makes the notification dot pulse.
+ scoped_ptr<SlideAnimation> notification_dot_animation_;
// We periodically restart the animation after it has been showed
// once, to create a pulsating effect.
- base::RepeatingTimer<ToolbarView> upgrade_reminder_pulse_timer_;
+ base::RepeatingTimer<ToolbarView> notification_dot_pulse_timer_;
// Used to post tasks to switch to the next/previous menu.
ScopedRunnableMethodFactory<ToolbarView> method_factory_;
diff --git a/chrome/browser/wrench_menu_model.cc b/chrome/browser/wrench_menu_model.cc
index 8b77a4e..9492557 100644
--- a/chrome/browser/wrench_menu_model.cc
+++ b/chrome/browser/wrench_menu_model.cc
@@ -49,6 +49,10 @@
#include "chrome/browser/chromeos/cros/update_library.h"
#endif
+#if defined(OS_WIN)
+#include "chrome/browser/enumerate_modules_model_win.h"
+#endif
+
////////////////////////////////////////////////////////////////////////////////
// EncodingMenuModel
@@ -270,7 +274,16 @@ bool WrenchMenuModel::IsCommandIdVisible(int command_id) const {
#else
return Singleton<UpgradeDetector>::get()->notify_upgrade();
#endif
+ } else if (command_id == IDC_VIEW_INCOMPATIBILITIES) {
+#if defined(OS_WIN)
+ EnumerateModulesModel* loaded_modules =
+ EnumerateModulesModel::GetSingleton();
+ return loaded_modules->confirmed_bad_modules_detected() > 0;
+#else
+ return false;
+#endif
}
+
return true;
}
@@ -407,6 +420,9 @@ void WrenchMenuModel::Build() {
AddItem(IDC_UPGRADE_DIALOG, l10n_util::GetStringFUTF16(
IDS_UPDATE_NOW, product_name));
+ AddItem(IDC_VIEW_INCOMPATIBILITIES, l10n_util::GetStringUTF16(
+ IDS_VIEW_INCOMPATIBILITIES));
+
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
SetIcon(GetIndexOfCommandId(IDC_UPGRADE_DIALOG),
*rb.GetBitmapNamed(IDR_UPDATE_AVAILABLE));
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 2288438..a494f84 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2181,6 +2181,8 @@
'browser/language_order_table_model.h',
'browser/load_from_memory_cache_details.h',
'browser/load_notification_details.h',
+ 'browser/enumerate_modules_model_win.cc',
+ 'browser/enumerate_modules_model_win.h',
'browser/location_bar.h',
'browser/location_bar_util.cc',
'browser/location_bar_util.h',
@@ -3706,6 +3708,8 @@
},
'sources': [
# Using built-in rule in vstudio for midl.
+ 'browser/dom_ui/conflicts_ui.cc',
+ 'browser/dom_ui/conflicts_ui.h',
'browser/history/history_indexer.idl',
],
'sources!': [
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 44b084b..f003777 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1260,6 +1260,7 @@
'browser/download/download_util_unittest.cc',
'browser/download/save_package_unittest.cc',
'browser/encoding_menu_controller_unittest.cc',
+ 'browser/enumerate_modules_model_unittest_win.cc',
'browser/extensions/convert_user_script_unittest.cc',
'browser/extensions/default_apps_unittest.cc',
'browser/extensions/extension_icon_manager_unittest.cc',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 438c0c0..4160bc0 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -100,6 +100,10 @@ const char kCloudPrintProxyId[] = "cloud-print-proxy-id";
// print service has been enabled (see enable-cloud-print).
const char kCloudPrintServiceURL[] = "cloud-print-service";
+// Causes the browser process to inspect loaded and registered DLLs for
+// known conflicts and warn the user.
+const char kConflictingModulesCheck[] = "conflicting-modules-check";
+
// The Country we should use. This is normally obtained from the operating
// system during first run and cached in the preferences afterwards. This is a
// string value, the 2 letter code from ISO 3166-1.
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index c68b16f..5a537ce 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -42,6 +42,7 @@ extern const char kCheckForUpdateIntervalSec[];
extern const char kChromeFrame[];
extern const char kCloudPrintProxyId[];
extern const char kCloudPrintServiceURL[];
+extern const char kConflictingModulesCheck[];
extern const char kCountry[];
extern const char kDebugPrint[];
extern const char kDiagnostics[];
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index e9127a7b..afbb5f6 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -1017,6 +1017,17 @@ class NotificationType {
// expected.
UPGRADE_RECOMMENDED,
+ // Software incompatibility notifications ----------------------------------
+
+ // Sent when Chrome has finished compiling the list of loaded modules (and
+ // other modules of interest). No details are expected.
+ MODULE_LIST_ENUMERATED,
+
+ // Sent when Chrome detects an incompatible module. Details is a boolean
+ // specifying true if one or more confirmed bad modules were found or false
+ // if only suspected bad modules were found.
+ MODULE_INCOMPATIBILITY_DETECTED,
+
// Accessibility Notifications ---------------------------------------------
// Notification that a window in the browser UI (not the web content)
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 897fa72..427c933 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -49,6 +49,7 @@ const char kAboutAboutURL[] = "about:about";
const char kAboutAppCacheInternalsURL[] = "about:appcache-internals";
const char kAboutBlankURL[] = "about:blank";
const char kAboutCacheURL[] = "about:cache";
+const char kAboutConflicts[] = "about:conflicts";
const char kAboutCrashURL[] = "about:crash";
const char kAboutCreditsURL[] = "about:credits";
const char kAboutDNSURL[] = "about:dns";
@@ -74,6 +75,7 @@ const char kChromeUIAboutURL[] = "chrome://settings/about";
const char kChromeUIAppLauncherURL[] = "chrome://newtab/#mode=app-launcher";
const char kChromeUIBookmarksURL[] = "chrome://bookmarks/";
const char kChromeUIBugReportURL[] = "chrome://bugreport/";
+const char kChromeUIConflictsURL[] = "chrome://conflicts/";
const char kChromeUIConstrainedHTMLTestURL[] = "chrome://constrained-test/";
const char kChromeUIDevToolsURL[] = "chrome-devtools://devtools/";
const char kChromeUIDownloadsURL[] = "chrome://downloads/";
@@ -104,6 +106,7 @@ const char kChromeUISystemInfoURL[] = "chrome://system/";
// Keep this list sorted please.
const char kChromeUIBookmarksHost[] = "bookmarks";
const char kChromeUIBugReportHost[] = "bugreport";
+const char kChromeUIConflictsHost[] = "conflicts";
const char kChromeUIDevToolsHost[] = "devtools";
const char kChromeUIDialogHost[] = "dialog";
const char kChromeUIDownloadsHost[] = "downloads";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 620cae6..05e32e6 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -41,6 +41,7 @@ extern const char kAboutAboutURL[];
extern const char kAboutAppCacheInternalsURL[];
extern const char kAboutBlankURL[];
extern const char kAboutBrowserCrash[];
+extern const char kAboutConflicts[];
extern const char kAboutCacheURL[];
extern const char kAboutCrashURL[];
extern const char kAboutCreditsURL[];
@@ -65,6 +66,7 @@ extern const char kChromeUIAboutURL[];
extern const char kChromeUIAppLauncherURL[];
extern const char kChromeUIBookmarksURL[];
extern const char kChromeUIBugReportURL[];
+extern const char kChromeUIConflictsURL[];
extern const char kChromeUIConstrainedHTMLTestURL[];
extern const char kChromeUIDevToolsURL[];
extern const char kChromeUIDownloadsURL[];
@@ -96,6 +98,7 @@ extern const char kChromeUISystemInfoURL[];
// above.
extern const char kChromeUIBookmarksHost[];
extern const char kChromeUIBugReportHost[];
+extern const char kChromeUIConflictsHost[];
extern const char kChromeUIDevToolsHost[];
extern const char kChromeUIDialogHost[];
extern const char kChromeUIDownloadsHost[];