| Index: chrome/browser/mac/keystone_glue.mm
|
| diff --git a/chrome/browser/mac/keystone_glue.mm b/chrome/browser/mac/keystone_glue.mm
|
| index c6333e7c65031739873ab630349ace1d5f8c0f3d..79621ff9cb3bd4671a42cd457f04dddf4cad67be 100644
|
| --- a/chrome/browser/mac/keystone_glue.mm
|
| +++ b/chrome/browser/mac/keystone_glue.mm
|
| @@ -19,6 +19,7 @@
|
| #include "base/mac/mac_logging.h"
|
| #include "base/mac/scoped_nsautorelease_pool.h"
|
| #include "base/memory/ref_counted.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| #include "base/strings/sys_string_conversions.h"
|
| #include "base/threading/worker_pool.h"
|
| #include "build/build_config.h"
|
| @@ -127,7 +128,9 @@ class PerformBridge : public base::RefCountedThreadSafe<PerformBridge> {
|
| // Called when an update check or update installation is complete. Posts the
|
| // kAutoupdateStatusNotification notification to the default notification
|
| // center.
|
| -- (void)updateStatus:(AutoupdateStatus)status version:(NSString*)version;
|
| +- (void)updateStatus:(AutoupdateStatus)status
|
| + version:(NSString*)version
|
| + error:(NSString*)error;
|
|
|
| // Returns the version of the currently-installed application on disk.
|
| - (NSString*)currentlyInstalledVersion;
|
| @@ -135,7 +138,7 @@ class PerformBridge : public base::RefCountedThreadSafe<PerformBridge> {
|
| // These three methods are used to determine the version of the application
|
| // currently installed on disk, compare that to the currently-running version,
|
| // decide whether any updates have been installed, and call
|
| -// -updateStatus:version:.
|
| +// -updateStatus:version:error:.
|
| //
|
| // In order to check the version on disk, the installed application's
|
| // Info.plist dictionary must be read; in order to see changes as updates are
|
| @@ -153,8 +156,8 @@ class PerformBridge : public base::RefCountedThreadSafe<PerformBridge> {
|
| // CFBundleShortVersionString key, and performs
|
| // -determineUpdateStatusForVersion: on the main thread.
|
| // -determineUpdateStatusForVersion: does the actual comparison of the version
|
| -// on disk with the running version and calls -updateStatus:version: with the
|
| -// results of its analysis.
|
| +// on disk with the running version and calls -updateStatus:version:error: with
|
| +// the results of its analysis.
|
| - (void)determineUpdateStatusAsync;
|
| - (void)determineUpdateStatus;
|
| - (void)determineUpdateStatusForVersion:(NSString*)version;
|
| @@ -208,6 +211,7 @@ class PerformBridge : public base::RefCountedThreadSafe<PerformBridge> {
|
| NSString* const kAutoupdateStatusNotification = @"AutoupdateStatusNotification";
|
| NSString* const kAutoupdateStatusStatus = @"status";
|
| NSString* const kAutoupdateStatusVersion = @"version";
|
| +NSString* const kAutoupdateStatusErrorMessages = @"errormessages";
|
|
|
| namespace {
|
|
|
| @@ -289,21 +293,25 @@ NSString* const kVersionKey = @"KSVersion";
|
| NSBundle* appBundle = base::mac::OuterBundle();
|
| NSDictionary* infoDictionary = [self infoDictionary];
|
|
|
| - NSString* productID = [infoDictionary objectForKey:@"KSProductID"];
|
| + NSString* productID = base::mac::ObjCCast<NSString>(
|
| + [infoDictionary objectForKey:@"KSProductID"]);
|
| if (productID == nil) {
|
| productID = [appBundle bundleIdentifier];
|
| }
|
|
|
| NSString* appPath = [appBundle bundlePath];
|
| - NSString* url = [infoDictionary objectForKey:@"KSUpdateURL"];
|
| - NSString* version = [infoDictionary objectForKey:kVersionKey];
|
| + NSString* url = base::mac::ObjCCast<NSString>(
|
| + [infoDictionary objectForKey:@"KSUpdateURL"]);
|
| + NSString* version = base::mac::ObjCCast<NSString>(
|
| + [infoDictionary objectForKey:kVersionKey]);
|
|
|
| if (!productID || !appPath || !url || !version) {
|
| // If parameters required for Keystone are missing, don't use it.
|
| return;
|
| }
|
|
|
| - NSString* channel = [infoDictionary objectForKey:kChannelKey];
|
| + NSString* channel = base::mac::ObjCCast<NSString>(
|
| + [infoDictionary objectForKey:kChannelKey]);
|
| // The stable channel has no tag. If updating to stable, remove the
|
| // dev and beta tags since we've been "promoted".
|
| if (channel == nil)
|
| @@ -365,13 +373,15 @@ NSString* const kVersionKey = @"KSVersion";
|
| // User
|
|
|
| NSDictionary* infoDictionary = [self infoDictionary];
|
| - NSString* appBundleBrandID = [infoDictionary objectForKey:kBrandKey];
|
| + NSString* appBundleBrandID = base::mac::ObjCCast<NSString>(
|
| + [infoDictionary objectForKey:kBrandKey]);
|
|
|
| NSString* storedBrandID = nil;
|
| if ([fm fileExistsAtPath:userBrandFile]) {
|
| NSDictionary* storedBrandDict =
|
| [NSDictionary dictionaryWithContentsOfFile:userBrandFile];
|
| - storedBrandID = [storedBrandDict objectForKey:kBrandKey];
|
| + storedBrandID = base::mac::ObjCCast<NSString>(
|
| + [storedBrandDict objectForKey:kBrandKey]);
|
| }
|
|
|
| if ((appBundleBrandID != nil) &&
|
| @@ -494,8 +504,8 @@ NSString* const kVersionKey = @"KSVersion";
|
| }
|
|
|
| - (void)setRegistrationActive {
|
| - if (!registration_)
|
| - return;
|
| + DCHECK(registration_);
|
| +
|
| registrationActive_ = YES;
|
|
|
| // Should never have zero profiles. Do not report this value.
|
| @@ -536,12 +546,16 @@ NSString* const kVersionKey = @"KSVersion";
|
| }
|
|
|
| - (void)registerWithKeystone {
|
| - [self updateStatus:kAutoupdateRegistering version:nil];
|
| + DCHECK(registration_);
|
| +
|
| + [self updateStatus:kAutoupdateRegistering version:nil error:nil];
|
|
|
| NSDictionary* parameters = [self keystoneParameters];
|
| BOOL result = [registration_ registerWithParameters:parameters];
|
| if (!result) {
|
| - [self updateStatus:kAutoupdateRegisterFailed version:nil];
|
| + // TODO: If Keystone ever makes a variant of this API with a withError:
|
| + // parameter, include the error message here in the call to updateStatus:.
|
| + [self updateStatus:kAutoupdateRegisterFailed version:nil error:nil];
|
| return;
|
| }
|
|
|
| @@ -562,15 +576,26 @@ NSString* const kVersionKey = @"KSVersion";
|
|
|
| - (void)registrationComplete:(NSNotification*)notification {
|
| NSDictionary* userInfo = [notification userInfo];
|
| - if ([[userInfo objectForKey:ksr::KSRegistrationStatusKey] boolValue]) {
|
| + NSNumber* status = base::mac::ObjCCast<NSNumber>(
|
| + [userInfo objectForKey:ksr::KSRegistrationStatusKey]);
|
| + NSString* errorMessages = base::mac::ObjCCast<NSString>(
|
| + [userInfo objectForKey:ksr::KSRegistrationUpdateCheckRawErrorMessagesKey]);
|
| +
|
| + if ([status boolValue]) {
|
| if ([self isSystemTicketDoomed]) {
|
| - [self updateStatus:kAutoupdateNeedsPromotion version:nil];
|
| + [self updateStatus:kAutoupdateNeedsPromotion
|
| + version:nil
|
| + error:errorMessages];
|
| } else {
|
| - [self updateStatus:kAutoupdateRegistered version:nil];
|
| + [self updateStatus:kAutoupdateRegistered
|
| + version:nil
|
| + error:errorMessages];
|
| }
|
| } else {
|
| // Dump registration_?
|
| - [self updateStatus:kAutoupdateRegisterFailed version:nil];
|
| + [self updateStatus:kAutoupdateRegisterFailed
|
| + version:nil
|
| + error:errorMessages];
|
| }
|
| }
|
|
|
| @@ -583,14 +608,14 @@ NSString* const kVersionKey = @"KSVersion";
|
| }
|
|
|
| - (void)checkForUpdate {
|
| - DCHECK(![self asyncOperationPending]);
|
| + DCHECK(registration_);
|
|
|
| - if (!registration_) {
|
| - [self updateStatus:kAutoupdateCheckFailed version:nil];
|
| + if ([self asyncOperationPending]) {
|
| + // Update check already in process; return without doing anything.
|
| return;
|
| }
|
|
|
| - [self updateStatus:kAutoupdateChecking version:nil];
|
| + [self updateStatus:kAutoupdateChecking version:nil error:nil];
|
|
|
| // All checks from inside Chrome are considered user-initiated, because they
|
| // only happen following a user action, such as visiting the about page.
|
| @@ -604,15 +629,25 @@ NSString* const kVersionKey = @"KSVersion";
|
|
|
| - (void)checkForUpdateComplete:(NSNotification*)notification {
|
| NSDictionary* userInfo = [notification userInfo];
|
| -
|
| - if ([[userInfo objectForKey:ksr::KSRegistrationUpdateCheckErrorKey]
|
| - boolValue]) {
|
| - [self updateStatus:kAutoupdateCheckFailed version:nil];
|
| - } else if ([[userInfo objectForKey:ksr::KSRegistrationStatusKey] boolValue]) {
|
| + NSNumber* error = base::mac::ObjCCast<NSNumber>(
|
| + [userInfo objectForKey:ksr::KSRegistrationUpdateCheckErrorKey]);
|
| + NSNumber* status = base::mac::ObjCCast<NSNumber>(
|
| + [userInfo objectForKey:ksr::KSRegistrationStatusKey]);
|
| + NSString* errorMessages = base::mac::ObjCCast<NSString>(
|
| + [userInfo objectForKey:ksr::KSRegistrationUpdateCheckRawErrorMessagesKey]);
|
| +
|
| + if ([error boolValue]) {
|
| + [self updateStatus:kAutoupdateCheckFailed
|
| + version:nil
|
| + error:errorMessages];
|
| + } else if ([status boolValue]) {
|
| // If an update is known to be available, go straight to
|
| // -updateStatus:version:. It doesn't matter what's currently on disk.
|
| - NSString* version = [userInfo objectForKey:ksr::KSRegistrationVersionKey];
|
| - [self updateStatus:kAutoupdateAvailable version:version];
|
| + NSString* version = base::mac::ObjCCast<NSString>(
|
| + [userInfo objectForKey:ksr::KSRegistrationVersionKey]);
|
| + [self updateStatus:kAutoupdateAvailable
|
| + version:version
|
| + error:errorMessages];
|
| } else {
|
| // If no updates are available, check what's on disk, because an update
|
| // may have already been installed. This check happens on another thread,
|
| @@ -622,14 +657,14 @@ NSString* const kVersionKey = @"KSVersion";
|
| }
|
|
|
| - (void)installUpdate {
|
| - DCHECK(![self asyncOperationPending]);
|
| + DCHECK(registration_);
|
|
|
| - if (!registration_) {
|
| - [self updateStatus:kAutoupdateInstallFailed version:nil];
|
| + if ([self asyncOperationPending]) {
|
| + // Update check already in process; return without doing anything.
|
| return;
|
| }
|
|
|
| - [self updateStatus:kAutoupdateInstalling version:nil];
|
| + [self updateStatus:kAutoupdateInstalling version:nil error:nil];
|
|
|
| [registration_ startUpdate];
|
|
|
| @@ -639,14 +674,19 @@ NSString* const kVersionKey = @"KSVersion";
|
|
|
| - (void)installUpdateComplete:(NSNotification*)notification {
|
| NSDictionary* userInfo = [notification userInfo];
|
| + NSNumber* successfulInstall = base::mac::ObjCCast<NSNumber>(
|
| + [userInfo objectForKey:ksr::KSUpdateCheckSuccessfullyInstalledKey]);
|
| + NSString* errorMessages = base::mac::ObjCCast<NSString>(
|
| + [userInfo objectForKey:ksr::KSRegistrationUpdateCheckRawErrorMessagesKey]);
|
|
|
| // http://crbug.com/160308 and b/7517358: when using system Keystone and on
|
| // a user ticket, KSUpdateCheckSuccessfulKey will be NO even when an update
|
| // was installed correctly, so don't check it. It should be redudnant when
|
| // KSUpdateCheckSuccessfullyInstalledKey is checked.
|
| - if (![[userInfo objectForKey:ksr::KSUpdateCheckSuccessfullyInstalledKey]
|
| - intValue]) {
|
| - [self updateStatus:kAutoupdateInstallFailed version:nil];
|
| + if (![successfulInstall intValue]) {
|
| + [self updateStatus:kAutoupdateInstallFailed
|
| + version:nil
|
| + error:errorMessages];
|
| } else {
|
| updateSuccessfullyInstalled_ = YES;
|
|
|
| @@ -660,7 +700,8 @@ NSString* const kVersionKey = @"KSVersion";
|
| NSString* appInfoPlistPath = [self appInfoPlistPath];
|
| NSDictionary* infoPlist =
|
| [NSDictionary dictionaryWithContentsOfFile:appInfoPlistPath];
|
| - return [infoPlist objectForKey:@"CFBundleShortVersionString"];
|
| + return base::mac::ObjCCast<NSString>(
|
| + [infoPlist objectForKey:@"CFBundleShortVersionString"]);
|
| }
|
|
|
| // Runs on the main thread.
|
| @@ -709,10 +750,12 @@ NSString* const kVersionKey = @"KSVersion";
|
| }
|
| }
|
|
|
| - [self updateStatus:status version:version];
|
| + [self updateStatus:status version:version error:nil];
|
| }
|
|
|
| -- (void)updateStatus:(AutoupdateStatus)status version:(NSString*)version {
|
| +- (void)updateStatus:(AutoupdateStatus)status
|
| + version:(NSString*)version
|
| + error:(NSString*)error {
|
| NSNumber* statusNumber = [NSNumber numberWithInt:status];
|
| NSMutableDictionary* dictionary =
|
| [NSMutableDictionary dictionaryWithObject:statusNumber
|
| @@ -720,6 +763,9 @@ NSString* const kVersionKey = @"KSVersion";
|
| if (version) {
|
| [dictionary setObject:version forKey:kAutoupdateStatusVersion];
|
| }
|
| + if (error) {
|
| + [dictionary setObject:version forKey:kAutoupdateStatusErrorMessages];
|
| + }
|
|
|
| NSNotification* notification =
|
| [NSNotification notificationWithName:kAutoupdateStatusNotification
|
| @@ -736,8 +782,9 @@ NSString* const kVersionKey = @"KSVersion";
|
|
|
| - (AutoupdateStatus)recentStatus {
|
| NSDictionary* dictionary = [recentNotification_ userInfo];
|
| - return static_cast<AutoupdateStatus>(
|
| - [[dictionary objectForKey:kAutoupdateStatusStatus] intValue]);
|
| + NSNumber* status = base::mac::ObjCCastStrict<NSNumber>(
|
| + [dictionary objectForKey:kAutoupdateStatusStatus]);
|
| + return static_cast<AutoupdateStatus>([status intValue]);
|
| }
|
|
|
| - (BOOL)asyncOperationPending {
|
| @@ -749,6 +796,7 @@ NSString* const kVersionKey = @"KSVersion";
|
| }
|
|
|
| - (BOOL)isUserTicket {
|
| + DCHECK(registration_);
|
| return [registration_ ticketType] == ksr::kKSRegistrationUserTicket;
|
| }
|
|
|
| @@ -856,6 +904,8 @@ NSString* const kVersionKey = @"KSVersion";
|
|
|
| - (void)promoteTicketWithAuthorization:(AuthorizationRef)authorization_arg
|
| synchronous:(BOOL)synchronous {
|
| + DCHECK(registration_);
|
| +
|
| base::mac::ScopedAuthorizationRef authorization(authorization_arg);
|
| authorization_arg = NULL;
|
|
|
| @@ -873,7 +923,7 @@ NSString* const kVersionKey = @"KSVersion";
|
|
|
| synchronousPromotion_ = synchronous;
|
|
|
| - [self updateStatus:kAutoupdatePromoting version:nil];
|
| + [self updateStatus:kAutoupdatePromoting version:nil error:nil];
|
|
|
| // TODO(mark): Remove when able!
|
| //
|
| @@ -920,14 +970,24 @@ NSString* const kVersionKey = @"KSVersion";
|
| NULL, // pipe
|
| &exit_status);
|
| if (status != errAuthorizationSuccess) {
|
| - OSSTATUS_LOG(ERROR, status)
|
| - << "AuthorizationExecuteWithPrivileges preflight";
|
| - [self updateStatus:kAutoupdatePromoteFailed version:nil];
|
| + // It's possible to get an OS-provided error string for this return code
|
| + // using base::mac::DescriptionFromOSStatus, but most of those strings are
|
| + // not useful/actionable for users, so we stick with the error code instead.
|
| + NSString* errorMessage =
|
| + l10n_util::GetNSStringFWithFixup(IDS_PROMOTE_PREFLIGHT_LAUNCH_ERROR,
|
| + base::IntToString16(status));
|
| + [self updateStatus:kAutoupdatePromoteFailed
|
| + version:nil
|
| + error:errorMessage];
|
| return;
|
| }
|
| if (exit_status != 0) {
|
| - LOG(ERROR) << "keystone_promote_preflight status " << exit_status;
|
| - [self updateStatus:kAutoupdatePromoteFailed version:nil];
|
| + NSString* errorMessage =
|
| + l10n_util::GetNSStringFWithFixup(IDS_PROMOTE_PREFLIGHT_SCRIPT_ERROR,
|
| + base::IntToString16(status));
|
| + [self updateStatus:kAutoupdatePromoteFailed
|
| + version:nil
|
| + error:errorMessage];
|
| return;
|
| }
|
|
|
| @@ -951,7 +1011,9 @@ NSString* const kVersionKey = @"KSVersion";
|
|
|
| if (![registration_ promoteWithParameters:parameters
|
| authorization:authorization_]) {
|
| - [self updateStatus:kAutoupdatePromoteFailed version:nil];
|
| + // TODO: If Keystone ever makes a variant of this API with a withError:
|
| + // parameter, include the error message here in the call to updateStatus:.
|
| + [self updateStatus:kAutoupdatePromoteFailed version:nil error:nil];
|
| authorization_.reset();
|
| return;
|
| }
|
| @@ -968,7 +1030,10 @@ NSString* const kVersionKey = @"KSVersion";
|
|
|
| - (void)promotionComplete:(NSNotification*)notification {
|
| NSDictionary* userInfo = [notification userInfo];
|
| - if ([[userInfo objectForKey:ksr::KSRegistrationStatusKey] boolValue]) {
|
| + NSNumber* status = base::mac::ObjCCast<NSNumber>(
|
| + [userInfo objectForKey:ksr::KSRegistrationStatusKey]);
|
| +
|
| + if ([status boolValue]) {
|
| if (synchronousPromotion_) {
|
| // Short-circuit: if performing a synchronous promotion, the promotion
|
| // came from the installer, which already set the permissions properly.
|
| @@ -980,7 +1045,7 @@ NSString* const kVersionKey = @"KSVersion";
|
| }
|
| } else {
|
| authorization_.reset();
|
| - [self updateStatus:kAutoupdatePromoteFailed version:nil];
|
| + [self updateStatus:kAutoupdatePromoteFailed version:nil error:nil];
|
| }
|
|
|
| if (synchronousPromotion_) {
|
| @@ -1036,7 +1101,7 @@ NSString* const kVersionKey = @"KSVersion";
|
| - (void)changePermissionsForPromotionComplete {
|
| authorization_.reset();
|
|
|
| - [self updateStatus:kAutoupdatePromoted version:nil];
|
| + [self updateStatus:kAutoupdatePromoted version:nil error:nil];
|
| }
|
|
|
| - (void)setAppPath:(NSString*)appPath {
|
|
|