summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorryanmyers <ryanmyers@chromium.org>2016-03-24 18:21:16 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-25 01:22:55 +0000
commitfffe8c740df30ab0529e05000a6fce138075023a (patch)
treec4a8b7d804b388314b362b110fe71970e9402ef4
parentaa5f0bcbe659641c6902d69a0ca33e8eb785533d (diff)
downloadchromium_src-fffe8c740df30ab0529e05000a6fce138075023a.zip
chromium_src-fffe8c740df30ab0529e05000a6fce138075023a.tar.gz
chromium_src-fffe8c740df30ab0529e05000a6fce138075023a.tar.bz2
Add viewing of error messages from Keystone upon self-update failure.
With newer versions of Keystone's Registration Framework, errors in the update process (visible on stderr from agent/ksadmin) are supplied to Chrome in the userInfo dictionary of notifications. Pull this out if present, and display it in chrome://help underneath the error code. Example output: https://screenshot.googleplex.com/uH59OwVp5Xi BUG=512609 Review URL: https://codereview.chromium.org/1769703002 Cr-Commit-Position: refs/heads/master@{#383217}
-rw-r--r--chrome/app/generated_resources.grd9
-rw-r--r--chrome/browser/mac/keystone_glue.h4
-rw-r--r--chrome/browser/mac/keystone_glue.mm167
-rw-r--r--chrome/browser/mac/keystone_registration.h2
-rw-r--r--chrome/browser/mac/keystone_registration.mm2
-rw-r--r--chrome/browser/ui/webui/help/version_updater_mac.mm32
6 files changed, 163 insertions, 53 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 57e2273..169b3cc 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8049,6 +8049,12 @@ I don't think this site should be blocked!
<message name="IDS_PROMOTE_AUTHENTICATION_PROMPT" desc="The prompt displayed in the authentication dialog when setting up automatic updates for all users. The system will add a sentence asking for an administrator's name and password. Mac-only.">
<ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will set up automatic updates for all users of this computer.
</message>
+ <message name="IDS_PROMOTE_PREFLIGHT_LAUNCH_ERROR" desc="Status label shown when an administrator user requested automatic updates for all users, but the OS failed to start the pre-flight process. Mac-only.">
+ Failed to set up automatic updates for all users (preflight launch error: <ph name="ERROR_NUMBER">$1<ex>1</ex></ph>)
+ </message>
+ <message name="IDS_PROMOTE_PREFLIGHT_SCRIPT_ERROR" desc="Status label shown when an administrator user requested automatic updates for all users, but the pre-flight process reported an error. Mac-only.">
+ Failed to set up automatic updates for all users (preflight execution error: <ph name="ERROR_NUMBER">$1<ex>1</ex></ph>)
+ </message>
</if>
<!-- About Chrome View -->
@@ -8064,6 +8070,9 @@ I don't think this site should be blocked!
<message name="IDS_UPGRADE_ERROR" desc="Status label: Error occurred during upgrade">
Update failed (error: <ph name="ERROR_NUMBER">$1<ex>1</ex></ph>)
</message>
+ <message name="IDS_UPGRADE_ERROR_DETAILS" desc="Status label optionally shown when an error occured during upgrade. The upgrade daemon may optionally send supplemental error messages; if they're available, we append them after this message in an HTML pre block.">
+ Error details:
+ </message>
</if>
<if expr="is_win or chromeos">
<message name="IDS_UPGRADE_DISABLED_BY_POLICY" desc="Status label: Upgrades disabled by policy">
diff --git a/chrome/browser/mac/keystone_glue.h b/chrome/browser/mac/keystone_glue.h
index 912a1d9..ff0de4a 100644
--- a/chrome/browser/mac/keystone_glue.h
+++ b/chrome/browser/mac/keystone_glue.h
@@ -44,10 +44,12 @@ enum AutoupdateStatus {
// the notification. Its userInfo dictionary will contain an AutoupdateStatus
// value as an intValue at key kAutoupdateStatusStatus. If a version is
// available (see AutoupdateStatus), it will be present at key
-// kAutoupdateStatusVersion.
+// kAutoupdateStatusVersion. If any error messages were supplied by Keystone,
+// they will be present at key kAutoupdateStatusErrorMessages.
extern NSString* const kAutoupdateStatusNotification;
extern NSString* const kAutoupdateStatusStatus;
extern NSString* const kAutoupdateStatusVersion;
+extern NSString* const kAutoupdateStatusErrorMessages;
namespace {
diff --git a/chrome/browser/mac/keystone_glue.mm b/chrome/browser/mac/keystone_glue.mm
index c6333e7..79621ff 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 {
diff --git a/chrome/browser/mac/keystone_registration.h b/chrome/browser/mac/keystone_registration.h
index 72e52ed..1771ca6 100644
--- a/chrome/browser/mac/keystone_registration.h
+++ b/chrome/browser/mac/keystone_registration.h
@@ -43,6 +43,8 @@ extern NSString* KSRegistrationPromotionDidCompleteNotification;
extern NSString* KSRegistrationCheckForUpdateNotification;
extern NSString* KSRegistrationStatusKey;
extern NSString* KSRegistrationUpdateCheckErrorKey;
+extern NSString* KSRegistrationUpdateCheckRawResultsKey;
+extern NSString* KSRegistrationUpdateCheckRawErrorMessagesKey;
extern NSString* KSRegistrationStartUpdateNotification;
extern NSString* KSUpdateCheckSuccessfulKey;
diff --git a/chrome/browser/mac/keystone_registration.mm b/chrome/browser/mac/keystone_registration.mm
index 82e2659..4ff70c3 100644
--- a/chrome/browser/mac/keystone_registration.mm
+++ b/chrome/browser/mac/keystone_registration.mm
@@ -31,6 +31,8 @@ NSString* KSRegistrationCheckForUpdateNotification =
@"KSRegistrationCheckForUpdateNotification";
NSString* KSRegistrationStatusKey = @"Status";
NSString* KSRegistrationUpdateCheckErrorKey = @"Error";
+NSString* KSRegistrationUpdateCheckRawResultsKey = @"RawResults";
+NSString* KSRegistrationUpdateCheckRawErrorMessagesKey = @"RawErrorMessages";
NSString* KSRegistrationStartUpdateNotification =
@"KSRegistrationStartUpdateNotification";
diff --git a/chrome/browser/ui/webui/help/version_updater_mac.mm b/chrome/browser/ui/webui/help/version_updater_mac.mm
index be9c08f..2d0e42e 100644
--- a/chrome/browser/ui/webui/help/version_updater_mac.mm
+++ b/chrome/browser/ui/webui/help/version_updater_mac.mm
@@ -6,11 +6,17 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#import "chrome/browser/mac/keystone_glue.h"
#include "chrome/browser/obsolete_system/obsolete_system.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
+#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
// KeystoneObserver is a simple notification observer for Keystone status
@@ -131,7 +137,11 @@ void VersionUpdaterMac::RelaunchBrowser() const {
void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
AutoupdateStatus keystone_status = static_cast<AutoupdateStatus>(
- [[dictionary objectForKey:kAutoupdateStatusStatus] intValue]);
+ [base::mac::ObjCCastStrict<NSNumber>(
+ [dictionary objectForKey:kAutoupdateStatusStatus]) intValue]);
+ std::string error_messages = base::SysNSStringToUTF8(
+ base::mac::ObjCCastStrict<NSString>(
+ [dictionary objectForKey:kAutoupdateStatusErrorMessages]));
bool enable_promote_button = true;
base::string16 message;
@@ -211,6 +221,26 @@ void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
NOTREACHED();
return;
}
+
+ // If there are any detailed error messages being passed along by Keystone,
+ // log them. If we have an error to display, include the detail messages
+ // below the error in a <pre> block. Don't bother displaying detail messages
+ // on a success/in-progress/indeterminate status.
+ if (!error_messages.empty()) {
+ VLOG(1) << "Update error messages: " << error_messages;
+
+ if (status == FAILED) {
+ if (!message.empty()) {
+ message += base::UTF8ToUTF16("<br/><br/>");
+ }
+
+ message += l10n_util::GetStringUTF16(IDS_UPGRADE_ERROR_DETAILS);
+ message += base::UTF8ToUTF16("<br/><pre>");
+ message += base::UTF8ToUTF16(net::EscapeForHTML(error_messages));
+ message += base::UTF8ToUTF16("</pre>");
+ }
+ }
+
if (!status_callback_.is_null())
status_callback_.Run(status, 0, message);