OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/memory/ref_counted.h" | |
6 #include "base/memory/scoped_ptr.h" | |
7 #include "base/memory/weak_ptr.h" | 5 #include "base/memory/weak_ptr.h" |
8 #include "base/strings/string16.h" | 6 #include "base/strings/string16.h" |
9 #include "base/version.h" | |
10 #include "base/win/win_util.h" | 7 #include "base/win/win_util.h" |
11 #include "base/win/windows_version.h" | 8 #include "base/win/windows_version.h" |
| 9 #include "chrome/browser/browser_process.h" |
| 10 #include "chrome/browser/first_run/upgrade_util_win.h" |
12 #include "chrome/browser/google/google_update_win.h" | 11 #include "chrome/browser/google/google_update_win.h" |
13 #include "chrome/browser/lifetime/application_lifetime.h" | 12 #include "chrome/browser/lifetime/application_lifetime.h" |
14 #include "chrome/browser/ui/browser.h" | |
15 #include "chrome/browser/ui/webui/help/version_updater.h" | 13 #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" | 14 #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" | 15 #include "content/public/browser/browser_thread.h" |
| 16 #include "content/public/browser/web_contents.h" |
| 17 #include "ui/aura/window.h" |
| 18 #include "ui/aura/window_tree_host.h" |
22 #include "ui/base/l10n/l10n_util.h" | 19 #include "ui/base/l10n/l10n_util.h" |
23 #include "ui/gfx/native_widget_types.h" | 20 #include "ui/gfx/native_widget_types.h" |
24 #include "ui/views/widget/widget.h" | |
25 | |
26 using content::BrowserThread; | |
27 | 21 |
28 namespace { | 22 namespace { |
29 | 23 |
30 // Windows implementation of version update functionality, used by the WebUI | 24 // Windows implementation of version update functionality, used by the WebUI |
31 // About/Help page. | 25 // About/Help page. |
32 class VersionUpdaterWin : public VersionUpdater { | 26 class VersionUpdaterWin : public VersionUpdater, public UpdateCheckDelegate { |
33 private: | 27 public: |
34 friend class VersionReader; | 28 // |owner_widget| is the parent widget hosting the update check UI. Any UI |
35 friend class VersionUpdater; | 29 // needed to install an update (e.g., a UAC prompt for a system-level install) |
36 | 30 // will be parented to this widget. |
37 // Clients must use VersionUpdater::Create(). | 31 explicit VersionUpdaterWin(gfx::AcceleratedWidget owner_widget); |
38 VersionUpdaterWin(); | |
39 ~VersionUpdaterWin() override; | 32 ~VersionUpdaterWin() override; |
40 | 33 |
41 // VersionUpdater implementation. | 34 // VersionUpdater: |
42 void CheckForUpdate(const StatusCallback& callback) override; | 35 void CheckForUpdate(const StatusCallback& callback) override; |
43 void RelaunchBrowser() const override; | 36 void RelaunchBrowser() const override; |
44 | 37 |
45 // chrome::UpdateCheckCallback. | 38 // UpdateCheckDelegate: |
46 void OnUpdateCheckResults(GoogleUpdateUpgradeResult result, | 39 void OnUpdateCheckComplete(const base::string16& new_version) override; |
47 GoogleUpdateErrorCode error_code, | 40 void OnUpgradeProgress(int progress, |
48 const base::string16& error_message, | 41 const base::string16& new_version) override; |
49 const base::string16& version); | 42 void OnUpgradeComplete(const base::string16& new_version) override; |
| 43 void OnError(GoogleUpdateErrorCode error_code, |
| 44 const base::string16& error_message, |
| 45 const base::string16& new_version) override; |
50 | 46 |
51 // Update the UI to show the status of the upgrade. | 47 private: |
52 void UpdateStatus(GoogleUpdateUpgradeResult result, | 48 #if defined(GOOGLE_CHROME_BUILD) |
53 GoogleUpdateErrorCode error_code, | 49 void BeginUpdateCheckOnFileThread(bool install_update_if_possible); |
54 const base::string16& error_message); | 50 #endif // GOOGLE_CHROME_BUILD |
55 | 51 |
56 // Got the intalled version so the handling of the UPGRADE_ALREADY_UP_TO_DATE | 52 // The widget owning the UI for the update check. |
57 // result case can now be completeb on the UI thread. | 53 gfx::AcceleratedWidget owner_widget_; |
58 void GotInstalledVersion(const Version& version); | |
59 | |
60 // Returns a window that can be used for elevation. | |
61 gfx::AcceleratedWidget GetElevationParent(); | |
62 | |
63 void BeginUpdateCheckOnFileThread(bool install_if_newer); | |
64 | 54 |
65 // Callback used to communicate update status to the client. | 55 // Callback used to communicate update status to the client. |
66 StatusCallback callback_; | 56 StatusCallback callback_; |
67 | 57 |
68 // Used for callbacks. | 58 // Used for callbacks. |
69 base::WeakPtrFactory<VersionUpdaterWin> weak_factory_; | 59 base::WeakPtrFactory<VersionUpdaterWin> weak_factory_; |
70 | 60 |
71 DISALLOW_COPY_AND_ASSIGN(VersionUpdaterWin); | 61 DISALLOW_COPY_AND_ASSIGN(VersionUpdaterWin); |
72 }; | 62 }; |
73 | 63 |
74 // This class is used to read the version on the FILE thread and then call back | 64 VersionUpdaterWin::VersionUpdaterWin(gfx::AcceleratedWidget owner_widget) |
75 // the version updater in the UI thread. Using a class helps better control | 65 : owner_widget_(owner_widget), weak_factory_(this) { |
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. | |
79 class VersionReader | |
80 : public base::RefCountedThreadSafe<VersionReader> { | |
81 public: | |
82 explicit VersionReader( | |
83 const base::WeakPtr<VersionUpdaterWin>& version_updater) | |
84 : version_updater_(version_updater) { | |
85 } | |
86 | |
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_); | |
93 } | |
94 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | |
95 &VersionReader::SetVersionInUIThread, this)); | |
96 } | |
97 | |
98 void SetVersionInUIThread() { | |
99 if (version_updater_.get() != NULL) | |
100 version_updater_->GotInstalledVersion(installed_version_); | |
101 } | |
102 | |
103 private: | |
104 friend class base::RefCountedThreadSafe<VersionReader>; | |
105 | |
106 ~VersionReader() {} | |
107 | |
108 // The version updater that must be called back when we are done. | |
109 // We use a weak pointer in case the updater gets destroyed while waiting. | |
110 base::WeakPtr<VersionUpdaterWin> version_updater_; | |
111 | |
112 // This is the version that gets read in the FILE thread and set on the | |
113 // the updater in the UI thread. | |
114 Version installed_version_; | |
115 }; | |
116 | |
117 VersionUpdaterWin::VersionUpdaterWin() | |
118 : weak_factory_(this) { | |
119 } | 66 } |
120 | 67 |
121 VersionUpdaterWin::~VersionUpdaterWin() { | 68 VersionUpdaterWin::~VersionUpdaterWin() { |
122 } | 69 } |
123 | 70 |
124 void VersionUpdaterWin::CheckForUpdate(const StatusCallback& callback) { | 71 void VersionUpdaterWin::CheckForUpdate(const StatusCallback& callback) { |
| 72 // There is no supported integration with Google Update for Chromium. |
| 73 #if defined(GOOGLE_CHROME_BUILD) |
125 callback_ = callback; | 74 callback_ = callback; |
126 | 75 |
127 // On-demand updates for Chrome don't work in Vista RTM when UAC is turned | 76 // On-demand updates for Chrome don't work in Vista RTM when UAC is turned |
128 // off. So, in this case, the version updater must not mention | 77 // off. So, in this case, the version updater must not mention |
129 // on-demand updates. Silent updates (in the background) should still | 78 // on-demand updates. Silent updates (in the background) should still |
130 // work as before - enabling UAC or installing the latest service pack | 79 // work as before - enabling UAC or installing the latest service pack |
131 // for Vista is another option. | 80 // for Vista is another option. |
132 if (!(base::win::GetVersion() == base::win::VERSION_VISTA && | 81 if (!(base::win::GetVersion() == base::win::VERSION_VISTA && |
133 (base::win::OSInfo::GetInstance()->service_pack().major == 0) && | 82 (base::win::OSInfo::GetInstance()->service_pack().major == 0) && |
134 !base::win::UserAccountControlIsEnabled())) { | 83 !base::win::UserAccountControlIsEnabled())) { |
135 UpdateStatus(UPGRADE_CHECK_STARTED, GOOGLE_UPDATE_NO_ERROR, | 84 callback_.Run(CHECKING, 0, base::string16()); |
136 base::string16()); | 85 BeginUpdateCheckOnFileThread(false /* !install_update_if_possible */); |
137 // Specify false to not upgrade yet. | |
138 BeginUpdateCheckOnFileThread(false); | |
139 } | 86 } |
| 87 #endif |
140 } | 88 } |
141 | 89 |
142 void VersionUpdaterWin::RelaunchBrowser() const { | 90 void VersionUpdaterWin::RelaunchBrowser() const { |
143 chrome::AttemptRestart(); | 91 chrome::AttemptRestart(); |
144 } | 92 } |
145 | 93 |
146 void VersionUpdaterWin::OnUpdateCheckResults( | 94 void VersionUpdaterWin::OnUpdateCheckComplete( |
147 GoogleUpdateUpgradeResult result, | 95 const base::string16& new_version) { |
148 GoogleUpdateErrorCode error_code, | 96 #if defined(GOOGLE_CHROME_BUILD) |
149 const base::string16& error_message, | 97 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
150 const base::string16& version) { | 98 Status status = CHECKING; |
151 UpdateStatus(result, error_code, error_message); | 99 if (new_version.empty()) { |
| 100 // Google Update says that no new version is available. Check to see if a |
| 101 // restart is needed for a previously-applied update to take effect. |
| 102 status = upgrade_util::IsRunningOldChrome() ? NEARLY_UPDATED : UPDATED; |
| 103 } else { |
| 104 // Notify the caller that the update is now beginning and initiate it. |
| 105 status = UPDATING; |
| 106 BeginUpdateCheckOnFileThread(true /* install_update_if_possible */); |
| 107 } |
| 108 callback_.Run(status, 0, base::string16()); |
| 109 #endif // GOOGLE_CHROME_BUILD |
152 } | 110 } |
153 | 111 |
154 void VersionUpdaterWin::UpdateStatus(GoogleUpdateUpgradeResult result, | 112 void VersionUpdaterWin::OnUpgradeProgress(int progress, |
155 GoogleUpdateErrorCode error_code, | 113 const base::string16& new_version) { |
156 const base::string16& error_message) { | |
157 // For Chromium builds it would show an error message. | |
158 // But it looks weird because in fact there is no error, | |
159 // just the update server is not available for non-official builds. | |
160 #if defined(GOOGLE_CHROME_BUILD) | 114 #if defined(GOOGLE_CHROME_BUILD) |
161 Status status = UPDATED; | 115 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 116 callback_.Run(UPDATING, progress, base::string16()); |
| 117 #endif // GOOGLE_CHROME_BUILD |
| 118 } |
| 119 |
| 120 void VersionUpdaterWin::OnUpgradeComplete(const base::string16& new_version) { |
| 121 #if defined(GOOGLE_CHROME_BUILD) |
| 122 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 123 callback_.Run(NEARLY_UPDATED, 0, base::string16()); |
| 124 #endif // GOOGLE_CHROME_BUILD |
| 125 } |
| 126 |
| 127 void VersionUpdaterWin::OnError(GoogleUpdateErrorCode error_code, |
| 128 const base::string16& error_message, |
| 129 const base::string16& new_version) { |
| 130 #if defined(GOOGLE_CHROME_BUILD) |
| 131 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
162 base::string16 message; | 132 base::string16 message; |
163 | 133 |
164 switch (result) { | 134 // Current versions of Google Update provide a nice message for the policy |
165 case UPGRADE_CHECK_STARTED: { | 135 // case. Use this generic error for the policy case only if no message from |
166 status = CHECKING; | 136 // Google Update is present. |
167 break; | 137 if (error_code != GOOGLE_UPDATE_DISABLED_BY_POLICY || error_message.empty()) |
168 } | 138 message = l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR, error_code); |
169 case UPGRADE_STARTED: { | |
170 status = UPDATING; | |
171 break; | |
172 } | |
173 case UPGRADE_IS_AVAILABLE: { | |
174 UpdateStatus(UPGRADE_STARTED, GOOGLE_UPDATE_NO_ERROR, base::string16()); | |
175 // Specify true to upgrade now. | |
176 BeginUpdateCheckOnFileThread(true); | |
177 return; | |
178 } | |
179 case UPGRADE_ALREADY_UP_TO_DATE: { | |
180 // Google Update reported that Chrome is up-to-date. | |
181 // To confirm the updated version is running, the reading | |
182 // must be done on the file thread. The rest of this case | |
183 // will be handled within GotInstalledVersion. | |
184 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( | |
185 &VersionReader::GetVersionFromFileThread, | |
186 new VersionReader(weak_factory_.GetWeakPtr()))); | |
187 return; | |
188 } | |
189 case UPGRADE_SUCCESSFUL: { | |
190 status = NEARLY_UPDATED; | |
191 break; | |
192 } | |
193 case UPGRADE_ERROR: { | |
194 status = FAILED; | |
195 if (error_code == GOOGLE_UPDATE_DISABLED_BY_POLICY) { | |
196 message = | |
197 l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY); | |
198 } else if (error_code == GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY) { | |
199 message = | |
200 l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY_MANUAL); | |
201 } else { | |
202 message = | |
203 l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR, error_code); | |
204 } | |
205 | 139 |
206 if (!error_message.empty()) { | 140 if (!error_message.empty()) { |
207 message += | 141 message += l10n_util::GetStringFUTF16( |
208 l10n_util::GetStringFUTF16(IDS_ABOUT_BOX_ERROR_DURING_UPDATE_CHECK, | 142 IDS_ABOUT_BOX_ERROR_DURING_UPDATE_CHECK, error_message); |
209 error_message); | |
210 } | |
211 break; | |
212 } | |
213 } | 143 } |
214 | 144 callback_.Run(FAILED, 0, message); |
215 // TODO(mad): Get proper progress value instead of passing 0. | 145 #endif // GOOGLE_CHROME_BUILD |
216 // http://crbug.com/136117 | |
217 callback_.Run(status, 0, message); | |
218 #endif // defined(GOOGLE_CHROME_BUILD) | |
219 } | 146 } |
220 | 147 |
221 void VersionUpdaterWin::GotInstalledVersion(const Version& version) { | 148 #if defined(GOOGLE_CHROME_BUILD) |
222 // This must be called on the UI thread so that callback_ can be called. | 149 void VersionUpdaterWin::BeginUpdateCheckOnFileThread( |
223 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 150 bool install_update_if_possible) { |
224 | 151 BeginUpdateCheck(content::BrowserThread::GetMessageLoopProxyForThread( |
225 // Make sure that the latest version is running and if not, | 152 content::BrowserThread::FILE), |
226 // notify the user by setting the status to NEARLY_UPDATED. | 153 g_browser_process->GetApplicationLocale(), |
227 // | 154 install_update_if_possible, owner_widget_, |
228 // The extra version check is necessary on Windows because the application | 155 weak_factory_.GetWeakPtr()); |
229 // may be already up to date on disk though the running app is still | |
230 // out of date. | |
231 chrome::VersionInfo version_info; | |
232 Version running_version(version_info.Version()); | |
233 callback_.Run((version.IsValid() && version.CompareTo(running_version) > 0) | |
234 ? NEARLY_UPDATED | |
235 : UPDATED, | |
236 0, | |
237 base::string16()); | |
238 } | 156 } |
239 | 157 #endif // GOOGLE_CHROME_BUILD |
240 BOOL CALLBACK WindowEnumeration(HWND window, LPARAM param) { | |
241 if (IsWindowVisible(window)) { | |
242 HWND* returned_window = reinterpret_cast<HWND*>(param); | |
243 *returned_window = window; | |
244 return FALSE; | |
245 } | |
246 return TRUE; | |
247 } | |
248 | |
249 gfx::AcceleratedWidget VersionUpdaterWin::GetElevationParent() { | |
250 // Look for a visible window belonging to the UI thread. | |
251 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
252 HWND window = NULL; | |
253 EnumThreadWindows(GetCurrentThreadId(), | |
254 WindowEnumeration, | |
255 reinterpret_cast<LPARAM>(&window)); | |
256 return window; | |
257 } | |
258 | |
259 void VersionUpdaterWin::BeginUpdateCheckOnFileThread(bool install_if_newer) { | |
260 scoped_refptr<base::TaskRunner> task_runner( | |
261 content::BrowserThread::GetMessageLoopProxyForThread( | |
262 content::BrowserThread::FILE)); | |
263 BeginUpdateCheck(task_runner, install_if_newer, GetElevationParent(), | |
264 base::Bind(&VersionUpdaterWin::OnUpdateCheckResults, | |
265 weak_factory_.GetWeakPtr())); | |
266 } | |
267 | 158 |
268 } // namespace | 159 } // namespace |
269 | 160 |
270 VersionUpdater* VersionUpdater::Create(content::BrowserContext* /* context */) { | 161 VersionUpdater* VersionUpdater::Create(content::WebContents* web_contents) { |
271 return new VersionUpdaterWin; | 162 // Retrieve the HWND for the browser window that is hosting the update check. |
| 163 // This will be used as the parent for a UAC prompt, if needed. It's possible |
| 164 // this this window will no longer have focus by the time UAC is needed. In |
| 165 // that case, the UAC prompt will appear in the taskbar and will require a |
| 166 // user click. This is the least surprising thing we can do for the user, and |
| 167 // is the intended behavior for Windows applications. It's also possible that |
| 168 // the browser window hosting the update check will have been closed by the |
| 169 // time the UAC prompt is needed. This will behave similarly. |
| 170 return new VersionUpdaterWin(web_contents->GetTopLevelNativeWindow() |
| 171 ->GetHost() |
| 172 ->GetAcceleratedWidget()); |
272 } | 173 } |
OLD | NEW |