Index: chrome/browser/component_updater/sw_reporter_installer_win.cc |
diff --git a/chrome/browser/component_updater/sw_reporter_installer_win.cc b/chrome/browser/component_updater/sw_reporter_installer_win.cc |
index c58eb885d1248238e7ea8d55d0e70c675a5379f8..f01f48544c4cfc104ec91fdf58990d45a0022369 100644 |
--- a/chrome/browser/component_updater/sw_reporter_installer_win.cc |
+++ b/chrome/browser/component_updater/sw_reporter_installer_win.cc |
@@ -28,12 +28,18 @@ |
#include "base/win/registry.h" |
#include "chrome/browser/browser_process.h" |
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h" |
+#include "chrome/browser/profiles/profile.h" |
+#include "chrome/browser/safe_browsing/srt_global_error_win.h" |
+#include "chrome/browser/ui/browser_finder.h" |
+#include "chrome/browser/ui/global_error/global_error_service.h" |
+#include "chrome/browser/ui/global_error/global_error_service_factory.h" |
#include "chrome/common/pref_names.h" |
#include "components/component_updater/component_updater_paths.h" |
#include "components/component_updater/component_updater_service.h" |
#include "components/component_updater/component_updater_utils.h" |
#include "components/component_updater/default_component_installer.h" |
#include "components/component_updater/pref_names.h" |
+#include "components/pref_registry/pref_registry_syncable.h" |
#include "content/public/browser/browser_thread.h" |
using content::BrowserThread; |
@@ -77,16 +83,98 @@ const wchar_t kSoftwareRemovalToolRegistryKey[] = |
L"Software\\Google\\Software Removal Tool"; |
const wchar_t kExitCodeRegistryValueName[] = L"ExitCode"; |
+// Exit codes that identify that a cleanup is needed. |
+const int kCleanupNeeded = 0; |
+const int kPostRebootCleanupNeeded = 4; |
+ |
void ReportUmaStep(SwReporterUmaValue value) { |
UMA_HISTOGRAM_ENUMERATION("SoftwareReporter.Step", value, SW_REPORTER_MAX); |
} |
+void ReportUmaVersion(const base::Version& version) { |
+ DCHECK(!version.components().empty()); |
+ UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.MinorVersion", |
+ version.components().back()); |
+ // The major version uses the 1st component value (when there is more than |
+ // one, since the last one is always the minor version) as a hi word in a |
+ // double word. The low word is either the second component (when there are |
+ // only three) or the 3rd one if there are at least 4. E.g., for W.X.Y.Z, we |
+ // ignore X, and Z is the minor version. We compute the major version with W |
+ // as the hi word, and Y as the low word. For X.Y.Z, we use X and Y as hi and |
+ // low words, and if we would have Y.Z we would use Y as the hi word and 0 as |
+ // the low word. major version is 0 if the version only has one component. |
+ uint32_t major_version = 0; |
+ if (version.components().size() > 1) |
+ major_version = 0x10000 * version.components()[0]; |
+ if (version.components().size() < 4 && version.components().size() > 2) |
+ major_version += version.components()[1]; |
+ else if (version.components().size() > 3) |
+ major_version += version.components()[2]; |
+ UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.MajorVersion", major_version); |
+} |
+ |
// This function is called on the UI thread to report the SwReporter exit code |
// and then clear it from the registry as well as clear the execution state |
// from the local state. This could be called from an interruptible worker |
-// thread so should be resilient to unexpected shutdown. |
-void ReportAndClearExitCode(int exit_code) { |
+// thread so should be resilient to unexpected shutdown. |version| is provided |
+// so the kSwReporterPromptVersion prefs can be set. |
+void ReportAndClearExitCode(int exit_code, const std::string& version) { |
UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.ExitCode", exit_code); |
+ if (g_browser_process && g_browser_process->local_state()) { |
+ g_browser_process->local_state()->SetInteger(prefs::kSwReporterLastExitCode, |
+ exit_code); |
+ } |
+ |
+ if (exit_code == kPostRebootCleanupNeeded || exit_code == kCleanupNeeded) { |
+ // Find the last active browser, which may be NULL, in which case we won't |
+ // show the prompt this time and will wait until the next run of the |
+ // reporter. We can't use other ways of finding a browser because we don't |
+ // have a profile. |
+ chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop(); |
+ Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type); |
+ if (browser) { |
+ Profile* profile = browser->profile(); |
+ DCHECK(profile); |
+ // Now that we have a profile, make sure we have a tabbed browser since we |
+ // need to anchor the bubble to the toolbar's wrench menu. Create one if |
+ // none exist already. |
+ if (browser->type() != Browser::TYPE_TABBED) { |
+ browser = chrome::FindTabbedBrowser(profile, false, desktop_type); |
+ if (!browser) |
+ browser = new Browser(Browser::CreateParams(profile, desktop_type)); |
+ } |
+ const std::string prompt_version = |
+ profile->GetPrefs()->GetString(prefs::kSwReporterPromptVersion); |
+ // Don't show the prompt again if it's been shown before. |
+ if (prompt_version.empty()) { |
+ profile->GetPrefs()->SetString(prefs::kSwReporterPromptVersion, |
+ version); |
+ profile->GetPrefs()->SetInteger(prefs::kSwReporterPromptReason, |
+ exit_code); |
+ GlobalErrorService* global_error_service = |
+ GlobalErrorServiceFactory::GetForProfile(profile); |
+ SRTGlobalError* global_error = new SRTGlobalError(global_error_service); |
+ // |global_error_service| takes ownership of |global_error| and keeps it |
+ // alive until RemoveGlobalError() is called, and even then, the object |
+ // is not destroyed, the caller of RemoveGlobalError is responsible to |
+ // destroy it, and in the case of the SRTGlobalError, it deletes itself |
+ // but only after the bubble has been interacted with. |
+ global_error_service->AddGlobalError(global_error); |
+ |
+ // Do not try to show bubble if another GlobalError is already showing |
+ // one. The bubble will be shown once the others have been dismissed. |
+ const GlobalErrorService::GlobalErrorList& global_errors( |
+ global_error_service->errors()); |
+ GlobalErrorService::GlobalErrorList::const_iterator it; |
+ for (it = global_errors.begin(); it != global_errors.end(); ++it) { |
+ if ((*it)->GetBubbleView()) |
+ break; |
+ } |
+ if (it == global_errors.end()) |
+ global_error->ShowBubbleView(browser); |
+ } |
+ } |
+ } |
base::win::RegKey srt_key( |
HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_WRITE); |
@@ -97,7 +185,8 @@ void ReportAndClearExitCode(int exit_code) { |
// wait for termination to collect its exit code. This task could be interrupted |
// by a shutdown at anytime, so it shouldn't depend on anything external that |
// could be shutdown beforehand. |
-void LaunchAndWaitForExit(const base::FilePath& exe_path) { |
+void LaunchAndWaitForExit(const base::FilePath& exe_path, |
+ const std::string& version) { |
const base::CommandLine reporter_command_line(exe_path); |
base::ProcessHandle scan_reporter_process = base::kNullProcessHandle; |
if (!base::LaunchProcess(reporter_command_line, |
@@ -114,16 +203,10 @@ void LaunchAndWaitForExit(const base::FilePath& exe_path) { |
base::CloseProcessHandle(scan_reporter_process); |
scan_reporter_process = base::kNullProcessHandle; |
// It's OK if this doesn't complete, the work will continue on next startup. |
- BrowserThread::PostTask(BrowserThread::UI, |
- FROM_HERE, |
- base::Bind(&ReportAndClearExitCode, exit_code)); |
-} |
- |
-void ExecuteReporter(const base::FilePath& install_dir) { |
- base::WorkerPool::PostTask( |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, |
FROM_HERE, |
- base::Bind(&LaunchAndWaitForExit, install_dir.Append(kSwReporterExeName)), |
- true); |
+ base::Bind(&ReportAndClearExitCode, exit_code, version)); |
} |
class SwReporterInstallerTraits : public ComponentInstallerTraits { |
@@ -147,6 +230,7 @@ class SwReporterInstallerTraits : public ComponentInstallerTraits { |
const base::FilePath& install_dir, |
scoped_ptr<base::DictionaryValue> manifest) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ ReportUmaVersion(version); |
wcsncpy_s(version_dir_, |
_MAX_PATH, |
@@ -155,6 +239,7 @@ class SwReporterInstallerTraits : public ComponentInstallerTraits { |
// A previous run may have results in the registry, so check and report |
// them if present. |
+ std::string version_string(version.GetString()); |
base::win::RegKey srt_key( |
HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_READ); |
DWORD exit_code; |
@@ -162,7 +247,7 @@ class SwReporterInstallerTraits : public ComponentInstallerTraits { |
srt_key.ReadValueDW(kExitCodeRegistryValueName, &exit_code) == |
ERROR_SUCCESS) { |
ReportUmaStep(SW_REPORTER_REGISTRY_EXIT_CODE); |
- ReportAndClearExitCode(exit_code); |
+ ReportAndClearExitCode(exit_code, version_string); |
} |
// If we can't access local state, we can't see when we last ran, so |
@@ -181,7 +266,12 @@ class SwReporterInstallerTraits : public ComponentInstallerTraits { |
prefs::kSwReporterLastTimeTriggered, |
base::Time::Now().ToInternalValue()); |
- ExecuteReporter(install_dir); |
+ base::WorkerPool::PostTask( |
+ FROM_HERE, |
+ base::Bind(&LaunchAndWaitForExit, |
+ install_dir.Append(kSwReporterExeName), |
+ version_string), |
+ true); |
} |
} |
@@ -224,10 +314,6 @@ wchar_t SwReporterInstallerTraits::version_dir_[] = {}; |
void RegisterSwReporterComponent(ComponentUpdateService* cus, |
PrefService* prefs) { |
- // The Sw reporter shouldn't run if the user isn't reporting metrics. |
- if (!ChromeMetricsServiceAccessor::IsMetricsReportingEnabled()) |
- return; |
- |
// Install the component. |
scoped_ptr<ComponentInstallerTraits> traits( |
new SwReporterInstallerTraits(prefs)); |
@@ -239,6 +325,20 @@ void RegisterSwReporterComponent(ComponentUpdateService* cus, |
void RegisterPrefsForSwReporter(PrefRegistrySimple* registry) { |
registry->RegisterInt64Pref(prefs::kSwReporterLastTimeTriggered, 0); |
+ registry->RegisterIntegerPref(prefs::kSwReporterLastExitCode, -1); |
+} |
+ |
+void RegisterProfilePrefsForSwReporter( |
+ user_prefs::PrefRegistrySyncable* registry) { |
+ registry->RegisterIntegerPref( |
+ prefs::kSwReporterPromptReason, |
+ -1, |
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
+ |
+ registry->RegisterStringPref( |
+ prefs::kSwReporterPromptVersion, |
+ "", |
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
} |
} // namespace component_updater |