diff options
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 &<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&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 &<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&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&hl=[GRITLANGCODE]&n=<ph name="NAME">$1<ex>Hash</ex></ph>&l=<ph name="LOCATION">$2<ex>Hash</ex></ph>&d=<ph name="DESC">$3<ex>Hash</ex></ph>&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[]; |