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(content::BrowserContext* /* context */) {
57 return new VersionUpdaterMac;
60 VersionUpdaterMac::VersionUpdaterMac()
61 : show_promote_button_(false),
62 keystone_observer_([[KeystoneObserver alloc] initWithUpdater:this]) {
65 VersionUpdaterMac::~VersionUpdaterMac() {
68 void VersionUpdaterMac::CheckForUpdate(
69 const StatusCallback& status_callback,
70 const PromoteCallback& promote_callback) {
71 // Copy the callbacks, we will re-use this for the remaining lifetime
73 status_callback_ = status_callback;
74 promote_callback_ = promote_callback;
76 KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
77 if (keystone_glue && ![keystone_glue isOnReadOnlyFilesystem]) {
78 AutoupdateStatus recent_status = [keystone_glue recentStatus];
79 if ([keystone_glue asyncOperationPending] ||
80 recent_status == kAutoupdateRegisterFailed ||
81 recent_status == kAutoupdateNeedsPromotion) {
82 // If an asynchronous update operation is currently pending, such as a
83 // check for updates or an update installation attempt, set the status
84 // up correspondingly without launching a new update check.
86 // If registration failed, no other operations make sense, so just go
87 // straight to the error.
88 UpdateStatus([[keystone_glue recentNotification] userInfo]);
90 // Launch a new update check, even if one was already completed, because
91 // a new update may be available or a new update may have been installed
92 // in the background since the last time the Help page was displayed.
93 [keystone_glue checkForUpdate];
95 // Immediately, kAutoupdateStatusNotification will be posted, with status
96 // kAutoupdateChecking.
98 // Upon completion, kAutoupdateStatusNotification will be posted with a
99 // status indicating the result of the check.
102 UpdateShowPromoteButton();
104 // There is no glue, or the application is on a read-only filesystem.
105 // Updates and promotions are impossible.
106 status_callback_.Run(DISABLED, 0, base::string16());
110 void VersionUpdaterMac::PromoteUpdater() const {
111 // Tell Keystone to make software updates available for all users.
112 [[KeystoneGlue defaultKeystoneGlue] promoteTicket];
114 // Immediately, kAutoupdateStatusNotification will be posted, and
115 // UpdateStatus() will be called with status kAutoupdatePromoting.
117 // Upon completion, kAutoupdateStatusNotification will be posted, and
118 // UpdateStatus() will be called with a status indicating a result of the
119 // installation attempt.
121 // If the promotion was successful, KeystoneGlue will re-register the ticket
122 // and UpdateStatus() will be called again indicating first that
123 // registration is in progress and subsequently that it has completed.
126 void VersionUpdaterMac::RelaunchBrowser() const {
127 // Tell the Broweser to restart if possible.
128 chrome::AttemptRestart();
131 void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
132 AutoupdateStatus keystone_status = static_cast<AutoupdateStatus>(
133 [[dictionary objectForKey:kAutoupdateStatusStatus] intValue]);
135 bool enable_promote_button = true;
136 base::string16 message;
139 switch (keystone_status) {
140 case kAutoupdateRegistering:
141 case kAutoupdateChecking:
143 enable_promote_button = false;
146 case kAutoupdateRegistered:
147 case kAutoupdatePromoted:
148 UpdateShowPromoteButton();
149 // Go straight into an update check. Return immediately, this routine
150 // will be re-entered shortly with kAutoupdateChecking.
151 [[KeystoneGlue defaultKeystoneGlue] checkForUpdate];
154 case kAutoupdateCurrent:
158 case kAutoupdateAvailable:
159 // Install the update automatically. Return immediately, this routine
160 // will be re-entered shortly with kAutoupdateInstalling.
161 [[KeystoneGlue defaultKeystoneGlue] installUpdate];
164 case kAutoupdateInstalling:
166 enable_promote_button = false;
169 case kAutoupdateInstalled:
170 status = NEARLY_UPDATED;
173 case kAutoupdatePromoting:
175 // TODO(mark): KSRegistration currently handles the promotion
176 // synchronously, meaning that the main thread's loop doesn't spin,
177 // meaning that animations and other updates to the window won't occur
178 // until KSRegistration is done with promotion. This looks laggy and bad
179 // and probably qualifies as "jank." For now, there just won't be any
180 // visual feedback while promotion is in progress, but it should complete
181 // (or fail) very quickly. http://b/2290009.
185 enable_promote_button = false;
188 case kAutoupdateRegisterFailed:
189 enable_promote_button = false;
191 case kAutoupdateCheckFailed:
192 case kAutoupdateInstallFailed:
193 case kAutoupdatePromoteFailed:
195 message = l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR,
199 case kAutoupdateNeedsPromotion:
202 base::string16 product_name =
203 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
204 message = l10n_util::GetStringFUTF16(IDS_PROMOTE_INFOBAR_TEXT,
213 if (!status_callback_.is_null())
214 status_callback_.Run(status, 0, message);
216 if (!promote_callback_.is_null()) {
217 PromotionState promotion_state = PROMOTE_HIDDEN;
218 if (show_promote_button_)
219 promotion_state = enable_promote_button ? PROMOTE_ENABLED
221 promote_callback_.Run(promotion_state);
225 void VersionUpdaterMac::UpdateShowPromoteButton() {
226 if (ObsoleteSystemMac::Has32BitOnlyCPU() &&
227 ObsoleteSystemMac::Is32BitEndOfTheLine()) {
228 // Promotion is moot upon reaching the end of the line.
229 show_promote_button_ = false;
233 KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
234 AutoupdateStatus recent_status = [keystone_glue recentStatus];
235 if (recent_status == kAutoupdateRegistering ||
236 recent_status == kAutoupdateRegisterFailed ||
237 recent_status == kAutoupdatePromoted) {
238 // Promotion isn't possible at this point.
239 show_promote_button_ = false;
240 } else if (recent_status == kAutoupdatePromoting ||
241 recent_status == kAutoupdatePromoteFailed) {
242 // Show promotion UI because the user either just clicked that button or
243 // because the user should be able to click it again.
244 show_promote_button_ = true;
246 // Show the promote button if promotion is a possibility.
247 show_promote_button_ = [keystone_glue wantsPromotion];