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 "grit/chromium_strings.h"
12 #include "grit/generated_resources.h"
13 #include "ui/base/l10n/l10n_util.h"
15 // KeystoneObserver is a simple notification observer for Keystone status
16 // updates. It will be created and managed by VersionUpdaterMac.
17 @interface KeystoneObserver : NSObject {
19 VersionUpdaterMac* versionUpdater_; // Weak.
22 // Initialize an observer with an updater. The updater owns this object.
23 - (id)initWithUpdater:(VersionUpdaterMac*)updater;
25 // Notification callback, called with the status of keystone operations.
26 - (void)handleStatusNotification:(NSNotification*)notification;
28 @end // @interface KeystoneObserver
30 @implementation KeystoneObserver
32 - (id)initWithUpdater:(VersionUpdaterMac*)updater {
33 if ((self = [super init])) {
34 versionUpdater_ = updater;
35 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
36 [center addObserver:self
37 selector:@selector(handleStatusNotification:)
38 name:kAutoupdateStatusNotification
45 [[NSNotificationCenter defaultCenter] removeObserver:self];
49 - (void)handleStatusNotification:(NSNotification*)notification {
50 versionUpdater_->UpdateStatus([notification userInfo]);
53 @end // @implementation KeystoneObserver
56 VersionUpdater* VersionUpdater::Create() {
57 return new VersionUpdaterMac;
60 VersionUpdaterMac::VersionUpdaterMac() {
61 keystone_observer_.reset([[KeystoneObserver alloc] initWithUpdater:this]);
64 VersionUpdaterMac::~VersionUpdaterMac() {
67 void VersionUpdaterMac::CheckForUpdate(
68 const StatusCallback& status_callback,
69 const PromoteCallback& promote_callback) {
70 // Copy the callbacks, we will re-use this for the remaining lifetime
72 status_callback_ = status_callback;
73 promote_callback_ = promote_callback;
75 KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
76 if (keystone_glue && ![keystone_glue isOnReadOnlyFilesystem]) {
77 AutoupdateStatus recent_status = [keystone_glue recentStatus];
78 if ([keystone_glue asyncOperationPending] ||
79 recent_status == kAutoupdateRegisterFailed ||
80 recent_status == kAutoupdateNeedsPromotion) {
81 // If an asynchronous update operation is currently pending, such as a
82 // check for updates or an update installation attempt, set the status
83 // up correspondingly without launching a new update check.
85 // If registration failed, no other operations make sense, so just go
86 // straight to the error.
87 UpdateStatus([[keystone_glue recentNotification] userInfo]);
89 // Launch a new update check, even if one was already completed, because
90 // a new update may be available or a new update may have been installed
91 // in the background since the last time the Help page was displayed.
92 [keystone_glue checkForUpdate];
94 // Immediately, kAutoupdateStatusNotification will be posted, with status
95 // kAutoupdateChecking.
97 // Upon completion, kAutoupdateStatusNotification will be posted with a
98 // status indicating the result of the check.
101 UpdateShowPromoteButton();
103 // There is no glue, or the application is on a read-only filesystem.
104 // Updates and promotions are impossible.
105 status_callback_.Run(DISABLED, 0, string16());
109 void VersionUpdaterMac::PromoteUpdater() const {
110 // Tell Keystone to make software updates available for all users.
111 [[KeystoneGlue defaultKeystoneGlue] promoteTicket];
113 // Immediately, kAutoupdateStatusNotification will be posted, and
114 // UpdateStatus() will be called with status kAutoupdatePromoting.
116 // Upon completion, kAutoupdateStatusNotification will be posted, and
117 // UpdateStatus() will be called with a status indicating a result of the
118 // installation attempt.
120 // If the promotion was successful, KeystoneGlue will re-register the ticket
121 // and UpdateStatus() will be called again indicating first that
122 // registration is in progress and subsequently that it has completed.
125 void VersionUpdaterMac::RelaunchBrowser() const {
126 // Tell the Broweser to restart if possible.
127 chrome::AttemptRestart();
130 void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
131 AutoupdateStatus keystone_status = static_cast<AutoupdateStatus>(
132 [[dictionary objectForKey:kAutoupdateStatusStatus] intValue]);
134 bool enable_promote_button = true;
138 switch (keystone_status) {
139 case kAutoupdateRegistering:
140 case kAutoupdateChecking:
142 enable_promote_button = false;
145 case kAutoupdateRegistered:
146 case kAutoupdatePromoted:
147 UpdateShowPromoteButton();
148 // Go straight into an update check. Return immediately, this routine
149 // will be re-entered shortly with kAutoupdateChecking.
150 [[KeystoneGlue defaultKeystoneGlue] checkForUpdate];
153 case kAutoupdateCurrent:
157 case kAutoupdateAvailable:
158 // Install the update automatically. Return immediately, this routine
159 // will be re-entered shortly with kAutoupdateInstalling.
160 [[KeystoneGlue defaultKeystoneGlue] installUpdate];
163 case kAutoupdateInstalling:
165 enable_promote_button = false;
168 case kAutoupdateInstalled:
169 status = NEARLY_UPDATED;
172 case kAutoupdatePromoting:
174 // TODO(mark): KSRegistration currently handles the promotion
175 // synchronously, meaning that the main thread's loop doesn't spin,
176 // meaning that animations and other updates to the window won't occur
177 // until KSRegistration is done with promotion. This looks laggy and bad
178 // and probably qualifies as "jank." For now, there just won't be any
179 // visual feedback while promotion is in progress, but it should complete
180 // (or fail) very quickly. http://b/2290009.
184 enable_promote_button = false;
187 case kAutoupdateRegisterFailed:
188 enable_promote_button = false;
190 case kAutoupdateCheckFailed:
191 case kAutoupdateInstallFailed:
192 case kAutoupdatePromoteFailed:
194 message = l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR,
198 case kAutoupdateNeedsPromotion:
201 string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
202 message = l10n_util:: GetStringFUTF16(IDS_PROMOTE_INFOBAR_TEXT,
211 if (!status_callback_.is_null())
212 status_callback_.Run(status, 0, message);
214 if (!promote_callback_.is_null()) {
215 PromotionState promotion_state = PROMOTE_HIDDEN;
216 if (show_promote_button_)
217 promotion_state = enable_promote_button ? PROMOTE_ENABLED
219 promote_callback_.Run(promotion_state);
223 void VersionUpdaterMac::UpdateShowPromoteButton() {
224 KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
225 AutoupdateStatus recent_status = [keystone_glue recentStatus];
226 if (recent_status == kAutoupdateRegistering ||
227 recent_status == kAutoupdateRegisterFailed ||
228 recent_status == kAutoupdatePromoted) {
229 // Promotion isn't possible at this point.
230 show_promote_button_ = false;
231 } else if (recent_status == kAutoupdatePromoting ||
232 recent_status == kAutoupdatePromoteFailed) {
233 // Show promotion UI because the user either just clicked that button or
234 // because the user should be able to click it again.
235 show_promote_button_ = true;
237 // Show the promote button if promotion is a possibility.
238 show_promote_button_ = [keystone_glue wantsPromotion];