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 "chrome/browser/ui/webui/help/version_updater_mac.h"
8 #include "base/bind_helpers.h"
9 #include "chrome/browser/lifetime/application_lifetime.h"
10 #import "chrome/browser/mac/keystone_glue.h"
11 #include "chrome/browser/mac/obsolete_system.h"
12 #include "chrome/grit/chromium_strings.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "ui/base/l10n/l10n_util.h"
16 // KeystoneObserver is a simple notification observer for Keystone status
17 // updates. It will be created and managed by VersionUpdaterMac.
18 @interface KeystoneObserver : NSObject {
20 VersionUpdaterMac* versionUpdater_; // Weak.
23 // Initialize an observer with an updater. The updater owns this object.
24 - (id)initWithUpdater:(VersionUpdaterMac*)updater;
26 // Notification callback, called with the status of keystone operations.
27 - (void)handleStatusNotification:(NSNotification*)notification;
29 @end // @interface KeystoneObserver
31 @implementation KeystoneObserver
33 - (id)initWithUpdater:(VersionUpdaterMac*)updater {
34 if ((self = [super init])) {
35 versionUpdater_ = updater;
36 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
37 [center addObserver:self
38 selector:@selector(handleStatusNotification:)
39 name:kAutoupdateStatusNotification
46 [[NSNotificationCenter defaultCenter] removeObserver:self];
50 - (void)handleStatusNotification:(NSNotification*)notification {
51 versionUpdater_->UpdateStatus([notification userInfo]);
54 @end // @implementation KeystoneObserver
56 VersionUpdater* VersionUpdater::Create(
57 content::WebContents* /* web_contents */) {
58 return new VersionUpdaterMac;
61 VersionUpdaterMac::VersionUpdaterMac()
62 : show_promote_button_(false),
63 keystone_observer_([[KeystoneObserver alloc] initWithUpdater:this]) {
66 VersionUpdaterMac::~VersionUpdaterMac() {
69 void VersionUpdaterMac::CheckForUpdate(
70 const StatusCallback& status_callback,
71 const PromoteCallback& promote_callback) {
72 // Copy the callbacks, we will re-use this for the remaining lifetime
74 status_callback_ = status_callback;
75 promote_callback_ = promote_callback;
77 KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
78 if (keystone_glue && ![keystone_glue isOnReadOnlyFilesystem]) {
79 AutoupdateStatus recent_status = [keystone_glue recentStatus];
80 if ([keystone_glue asyncOperationPending] ||
81 recent_status == kAutoupdateRegisterFailed ||
82 recent_status == kAutoupdateNeedsPromotion) {
83 // If an asynchronous update operation is currently pending, such as a
84 // check for updates or an update installation attempt, set the status
85 // up correspondingly without launching a new update check.
87 // If registration failed, no other operations make sense, so just go
88 // straight to the error.
89 UpdateStatus([[keystone_glue recentNotification] userInfo]);
91 // Launch a new update check, even if one was already completed, because
92 // a new update may be available or a new update may have been installed
93 // in the background since the last time the Help page was displayed.
94 [keystone_glue checkForUpdate];
96 // Immediately, kAutoupdateStatusNotification will be posted, with status
97 // kAutoupdateChecking.
99 // Upon completion, kAutoupdateStatusNotification will be posted with a
100 // status indicating the result of the check.
103 UpdateShowPromoteButton();
105 // There is no glue, or the application is on a read-only filesystem.
106 // Updates and promotions are impossible.
107 status_callback_.Run(DISABLED, 0, base::string16());
111 void VersionUpdaterMac::PromoteUpdater() const {
112 // Tell Keystone to make software updates available for all users.
113 [[KeystoneGlue defaultKeystoneGlue] promoteTicket];
115 // Immediately, kAutoupdateStatusNotification will be posted, and
116 // UpdateStatus() will be called with status kAutoupdatePromoting.
118 // Upon completion, kAutoupdateStatusNotification will be posted, and
119 // UpdateStatus() will be called with a status indicating a result of the
120 // installation attempt.
122 // If the promotion was successful, KeystoneGlue will re-register the ticket
123 // and UpdateStatus() will be called again indicating first that
124 // registration is in progress and subsequently that it has completed.
127 void VersionUpdaterMac::RelaunchBrowser() const {
128 // Tell the Broweser to restart if possible.
129 chrome::AttemptRestart();
132 void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
133 AutoupdateStatus keystone_status = static_cast<AutoupdateStatus>(
134 [[dictionary objectForKey:kAutoupdateStatusStatus] intValue]);
136 bool enable_promote_button = true;
137 base::string16 message;
140 switch (keystone_status) {
141 case kAutoupdateRegistering:
142 case kAutoupdateChecking:
144 enable_promote_button = false;
147 case kAutoupdateRegistered:
148 case kAutoupdatePromoted:
149 UpdateShowPromoteButton();
150 // Go straight into an update check. Return immediately, this routine
151 // will be re-entered shortly with kAutoupdateChecking.
152 [[KeystoneGlue defaultKeystoneGlue] checkForUpdate];
155 case kAutoupdateCurrent:
159 case kAutoupdateAvailable:
160 // Install the update automatically. Return immediately, this routine
161 // will be re-entered shortly with kAutoupdateInstalling.
162 [[KeystoneGlue defaultKeystoneGlue] installUpdate];
165 case kAutoupdateInstalling:
167 enable_promote_button = false;
170 case kAutoupdateInstalled:
171 status = NEARLY_UPDATED;
174 case kAutoupdatePromoting:
176 // TODO(mark): KSRegistration currently handles the promotion
177 // synchronously, meaning that the main thread's loop doesn't spin,
178 // meaning that animations and other updates to the window won't occur
179 // until KSRegistration is done with promotion. This looks laggy and bad
180 // and probably qualifies as "jank." For now, there just won't be any
181 // visual feedback while promotion is in progress, but it should complete
182 // (or fail) very quickly. http://b/2290009.
186 enable_promote_button = false;
189 case kAutoupdateRegisterFailed:
190 enable_promote_button = false;
192 case kAutoupdateCheckFailed:
193 case kAutoupdateInstallFailed:
194 case kAutoupdatePromoteFailed:
196 message = l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR,
200 case kAutoupdateNeedsPromotion:
203 base::string16 product_name =
204 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
205 message = l10n_util::GetStringFUTF16(IDS_PROMOTE_INFOBAR_TEXT,
214 if (!status_callback_.is_null())
215 status_callback_.Run(status, 0, message);
217 if (!promote_callback_.is_null()) {
218 PromotionState promotion_state = PROMOTE_HIDDEN;
219 if (show_promote_button_)
220 promotion_state = enable_promote_button ? PROMOTE_ENABLED
222 promote_callback_.Run(promotion_state);
226 void VersionUpdaterMac::UpdateShowPromoteButton() {
227 if (ObsoleteSystemMac::Has32BitOnlyCPU() &&
228 ObsoleteSystemMac::Is32BitEndOfTheLine()) {
229 // Promotion is moot upon reaching the end of the line.
230 show_promote_button_ = false;
234 KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
235 AutoupdateStatus recent_status = [keystone_glue recentStatus];
236 if (recent_status == kAutoupdateRegistering ||
237 recent_status == kAutoupdateRegisterFailed ||
238 recent_status == kAutoupdatePromoted) {
239 // Promotion isn't possible at this point.
240 show_promote_button_ = false;
241 } else if (recent_status == kAutoupdatePromoting ||
242 recent_status == kAutoupdatePromoteFailed) {
243 // Show promotion UI because the user either just clicked that button or
244 // because the user should be able to click it again.
245 show_promote_button_ = true;
247 // Show the promote button if promotion is a possibility.
248 show_promote_button_ = [keystone_glue wantsPromotion];