1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/memory/ref_counted.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/memory/weak_ptr.h"
8 #include "base/strings/string16.h"
9 #include "base/version.h"
10 #include "base/win/win_util.h"
11 #include "base/win/windows_version.h"
12 #include "chrome/browser/google/google_update_win.h"
13 #include "chrome/browser/lifetime/application_lifetime.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/webui/help/version_updater.h"
16 #include "chrome/common/chrome_version_info.h"
17 #include "chrome/grit/chromium_strings.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "chrome/installer/util/browser_distribution.h"
20 #include "chrome/installer/util/install_util.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/gfx/native_widget_types.h"
24 #include "ui/views/widget/widget.h"
26 using content::BrowserThread
;
30 // Windows implementation of version update functionality, used by the WebUI
32 class VersionUpdaterWin
: public VersionUpdater
{
34 friend class VersionReader
;
35 friend class VersionUpdater
;
37 // Clients must use VersionUpdater::Create().
39 virtual ~VersionUpdaterWin();
41 // VersionUpdater implementation.
42 virtual void CheckForUpdate(const StatusCallback
& callback
) override
;
43 virtual void RelaunchBrowser() const override
;
45 // chrome::UpdateCheckCallback.
46 void OnUpdateCheckResults(GoogleUpdateUpgradeResult result
,
47 GoogleUpdateErrorCode error_code
,
48 const base::string16
& error_message
,
49 const base::string16
& version
);
51 // Update the UI to show the status of the upgrade.
52 void UpdateStatus(GoogleUpdateUpgradeResult result
,
53 GoogleUpdateErrorCode error_code
,
54 const base::string16
& error_message
);
56 // Got the intalled version so the handling of the UPGRADE_ALREADY_UP_TO_DATE
57 // result case can now be completeb on the UI thread.
58 void GotInstalledVersion(const Version
& version
);
60 // Returns a window that can be used for elevation.
61 gfx::AcceleratedWidget
GetElevationParent();
63 void BeginUpdateCheckOnFileThread(bool install_if_newer
);
65 // Used for callbacks.
66 base::WeakPtrFactory
<VersionUpdaterWin
> weak_factory_
;
68 // Callback used to communicate update status to the client.
69 StatusCallback callback_
;
71 DISALLOW_COPY_AND_ASSIGN(VersionUpdaterWin
);
74 // This class is used to read the version on the FILE thread and then call back
75 // the version updater in the UI thread. Using a class helps better control
76 // the lifespan of the Version independently of the lifespan of the version
77 // updater, which may die while asynchonicity is happening, thus the usage of
78 // the WeakPtr, which can only be used from the thread that created it.
80 : public base::RefCountedThreadSafe
<VersionReader
> {
82 explicit VersionReader(
83 const base::WeakPtr
<VersionUpdaterWin
>& version_updater
)
84 : version_updater_(version_updater
) {
87 void GetVersionFromFileThread() {
88 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
89 InstallUtil::GetChromeVersion(dist
, false, &installed_version_
);
90 if (!installed_version_
.IsValid()) {
91 // User-level Chrome is not installed, check system-level.
92 InstallUtil::GetChromeVersion(dist
, true, &installed_version_
);
94 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
95 &VersionReader::SetVersionInUIThread
, this));
98 void SetVersionInUIThread() {
99 if (version_updater_
.get() != NULL
)
100 version_updater_
->GotInstalledVersion(installed_version_
);
104 friend class base::RefCountedThreadSafe
<VersionReader
>;
106 // The version updater that must be called back when we are done.
107 // We use a weak pointer in case the updater gets destroyed while waiting.
108 base::WeakPtr
<VersionUpdaterWin
> version_updater_
;
110 // This is the version that gets read in the FILE thread and set on the
111 // the updater in the UI thread.
112 Version installed_version_
;
115 VersionUpdaterWin::VersionUpdaterWin()
116 : weak_factory_(this) {
119 VersionUpdaterWin::~VersionUpdaterWin() {
122 void VersionUpdaterWin::CheckForUpdate(const StatusCallback
& callback
) {
123 callback_
= callback
;
125 // On-demand updates for Chrome don't work in Vista RTM when UAC is turned
126 // off. So, in this case, the version updater must not mention
127 // on-demand updates. Silent updates (in the background) should still
128 // work as before - enabling UAC or installing the latest service pack
129 // for Vista is another option.
130 if (!(base::win::GetVersion() == base::win::VERSION_VISTA
&&
131 (base::win::OSInfo::GetInstance()->service_pack().major
== 0) &&
132 !base::win::UserAccountControlIsEnabled())) {
133 UpdateStatus(UPGRADE_CHECK_STARTED
, GOOGLE_UPDATE_NO_ERROR
,
135 // Specify false to not upgrade yet.
136 BeginUpdateCheckOnFileThread(false);
140 void VersionUpdaterWin::RelaunchBrowser() const {
141 chrome::AttemptRestart();
144 void VersionUpdaterWin::OnUpdateCheckResults(
145 GoogleUpdateUpgradeResult result
,
146 GoogleUpdateErrorCode error_code
,
147 const base::string16
& error_message
,
148 const base::string16
& version
) {
149 UpdateStatus(result
, error_code
, error_message
);
152 void VersionUpdaterWin::UpdateStatus(GoogleUpdateUpgradeResult result
,
153 GoogleUpdateErrorCode error_code
,
154 const base::string16
& error_message
) {
155 // For Chromium builds it would show an error message.
156 // But it looks weird because in fact there is no error,
157 // just the update server is not available for non-official builds.
158 #if defined(GOOGLE_CHROME_BUILD)
159 Status status
= UPDATED
;
160 base::string16 message
;
163 case UPGRADE_CHECK_STARTED
: {
167 case UPGRADE_STARTED
: {
171 case UPGRADE_IS_AVAILABLE
: {
172 UpdateStatus(UPGRADE_STARTED
, GOOGLE_UPDATE_NO_ERROR
, base::string16());
173 // Specify true to upgrade now.
174 BeginUpdateCheckOnFileThread(true);
177 case UPGRADE_ALREADY_UP_TO_DATE
: {
178 // Google Update reported that Chrome is up-to-date.
179 // To confirm the updated version is running, the reading
180 // must be done on the file thread. The rest of this case
181 // will be handled within GotInstalledVersion.
182 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
, base::Bind(
183 &VersionReader::GetVersionFromFileThread
,
184 new VersionReader(weak_factory_
.GetWeakPtr())));
187 case UPGRADE_SUCCESSFUL
: {
188 status
= NEARLY_UPDATED
;
191 case UPGRADE_ERROR
: {
193 if (error_code
== GOOGLE_UPDATE_DISABLED_BY_POLICY
) {
195 l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY
);
196 } else if (error_code
== GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY
) {
198 l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY_MANUAL
);
201 l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR
, error_code
);
204 if (!error_message
.empty()) {
206 l10n_util::GetStringFUTF16(IDS_ABOUT_BOX_ERROR_DURING_UPDATE_CHECK
,
213 // TODO(mad): Get proper progress value instead of passing 0.
214 // http://crbug.com/136117
215 callback_
.Run(status
, 0, message
);
216 #endif // defined(GOOGLE_CHROME_BUILD)
219 void VersionUpdaterWin::GotInstalledVersion(const Version
& version
) {
220 // This must be called on the UI thread so that callback_ can be called.
221 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
223 // Make sure that the latest version is running and if not,
224 // notify the user by setting the status to NEARLY_UPDATED.
226 // The extra version check is necessary on Windows because the application
227 // may be already up to date on disk though the running app is still
229 chrome::VersionInfo version_info
;
230 Version
running_version(version_info
.Version());
231 callback_
.Run((version
.IsValid() && version
.CompareTo(running_version
) > 0)
238 BOOL CALLBACK
WindowEnumeration(HWND window
, LPARAM param
) {
239 if (IsWindowVisible(window
)) {
240 HWND
* returned_window
= reinterpret_cast<HWND
*>(param
);
241 *returned_window
= window
;
247 gfx::AcceleratedWidget
VersionUpdaterWin::GetElevationParent() {
248 // Look for a visible window belonging to the UI thread.
249 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
251 EnumThreadWindows(GetCurrentThreadId(),
253 reinterpret_cast<LPARAM
>(&window
));
257 void VersionUpdaterWin::BeginUpdateCheckOnFileThread(bool install_if_newer
) {
258 scoped_refptr
<base::TaskRunner
> task_runner(
259 content::BrowserThread::GetMessageLoopProxyForThread(
260 content::BrowserThread::FILE));
261 BeginUpdateCheck(task_runner
, install_if_newer
, GetElevationParent(),
262 base::Bind(&VersionUpdaterWin::OnUpdateCheckResults
,
263 weak_factory_
.GetWeakPtr()));
268 VersionUpdater
* VersionUpdater::Create(content::BrowserContext
* /* context */) {
269 return new VersionUpdaterWin
;