diff options
author | jrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-20 21:23:58 +0000 |
---|---|---|
committer | jrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-20 21:23:58 +0000 |
commit | e2a9027c5bc0f0fa895ec0f42837a73d19b21ce0 (patch) | |
tree | 3dbf2d105d9f44093b07038a0c5f151ec6a3d6ab /chrome/app/keystone_glue.m | |
parent | 89b0600da74755aa3e19255177eb48287f4377b7 (diff) | |
download | chromium_src-e2a9027c5bc0f0fa895ec0f42837a73d19b21ce0.zip chromium_src-e2a9027c5bc0f0fa895ec0f42837a73d19b21ce0.tar.gz chromium_src-e2a9027c5bc0f0fa895ec0f42837a73d19b21ce0.tar.bz2 |
Development of keystone glue.
BUG=http://codereview.chromium.org/112044
chrome.gyp caught in a different CL but here is the relevant part of
the diff:
--- chrome.gyp (revision 16158)
+++ chrome.gyp (working copy)
@@ -2679,6 +2682,11 @@
],
'sources': [
'app/breakpad_mac_stubs.mm',
+ # *NO* files in chrome/app have unit tests (except keystone_glue)!!!
+ # It seems a waste to have an app_unittests target, so for now
+ # I add keystone_glue.m explicitly to this target.
+ 'app/keystone_glue.m',
+ 'app/keystone_glue_unittest.mm',
# All unittests in browser, common, and renderer.
'browser/autocomplete/autocomplete_unittest.cc',
'browser/autocomplete/autocomplete_popup_view_mac_unittest.mm',
Review URL: http://codereview.chromium.org/113613
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16540 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/app/keystone_glue.m')
-rw-r--r-- | chrome/app/keystone_glue.m | 180 |
1 files changed, 149 insertions, 31 deletions
diff --git a/chrome/app/keystone_glue.m b/chrome/app/keystone_glue.m index 0a01374..56a8e5f 100644 --- a/chrome/app/keystone_glue.m +++ b/chrome/app/keystone_glue.m @@ -4,9 +4,28 @@ #import "keystone_glue.h" +@interface KeystoneGlue(Private) + +// Called periodically to announce activity by pinging the Keystone server. +- (void)markActive:(NSTimer*)timer; + +@end + + // Provide declarations of the Keystone registration bits needed here. From // KSRegistration.h. typedef enum { kKSPathExistenceChecker } KSExistenceCheckerType; + +NSString *KSRegistrationCheckForUpdateNotification = + @"KSRegistrationCheckForUpdateNotification"; +NSString *KSRegistrationStatusKey = @"Status"; +NSString *KSRegistrationVersionKey = @"Version"; + +NSString *KSRegistrationStartUpdateNotification = + @"KSRegistrationStartUpdateNotification"; +NSString *KSUpdateCheckSuccessfulKey = @"CheckSuccessful"; +NSString *KSUpdateCheckSuccessfullyInstalledKey = @"SuccessfullyInstalled"; + @interface KSRegistration : NSObject + (id)registrationWithProductID:(NSString*)productID; - (BOOL)registerWithVersion:(NSString*)version @@ -14,28 +33,65 @@ typedef enum { kKSPathExistenceChecker } KSExistenceCheckerType; existenceCheckerString:(NSString*)xc serverURLString:(NSString*)serverURLString; - (void)setActive; +- (void)checkForUpdate; +- (void)startUpdate; @end + @implementation KeystoneGlue -// TODO(mmentovai): Determine if the app is writable, and don't register for -// updates if not - but keep the periodic activity pings. -+ (void)registerWithKeystone { - // Figure out who we are. - NSBundle* mainBundle = [NSBundle mainBundle]; - NSDictionary* infoDictionary = [mainBundle infoDictionary]; ++ (id)defaultKeystoneGlue { + // TODO(jrg): rename this file to .mm so I can use C++ and + // make this type a base::SingletonObjC<KeystoneGlue>. + static KeystoneGlue* sDefaultKeystoneGlue = nil; // leaked + + if (sDefaultKeystoneGlue == nil) { + sDefaultKeystoneGlue = [[KeystoneGlue alloc] init]; + [sDefaultKeystoneGlue loadParameters]; + if (![sDefaultKeystoneGlue loadKeystoneRegistration]) { + [sDefaultKeystoneGlue release]; + sDefaultKeystoneGlue = nil; + } + } + return sDefaultKeystoneGlue; +} + +- (void)dealloc { + [url_ release]; + [productID_ release]; + [version_ release]; + [registration_ release]; + [super dealloc]; +} + +- (NSDictionary*)infoDictionary { + return [[NSBundle mainBundle] infoDictionary]; +} + +- (void)loadParameters { + NSDictionary* infoDictionary = [self infoDictionary]; NSString* url = [infoDictionary objectForKey:@"KSUpdateURL"]; - NSString* bundleIdentifier = [infoDictionary objectForKey:@"KSProductID"]; - if (bundleIdentifier == nil) { - bundleIdentifier = [mainBundle bundleIdentifier]; + NSString* product = [infoDictionary objectForKey:@"KSProductID"]; + if (product == nil) { + product = [[NSBundle mainBundle] bundleIdentifier]; } NSString* version = [infoDictionary objectForKey:@"KSVersion"]; - if (!bundleIdentifier || !url || !version) { + if (!product || !url || !version) { // If parameters required for Keystone are missing, don't use it. return; } + url_ = [url retain]; + productID_ = [product retain]; + version_ = [version retain]; +} + +- (BOOL)loadKeystoneRegistration { + if (!productID_ || !url_ || !version_) + return NO; + // Load the KeystoneRegistration framework bundle. + NSBundle* mainBundle = [NSBundle mainBundle]; NSString* ksrPath = [[mainBundle privateFrameworksPath] stringByAppendingPathComponent:@"KeystoneRegistration.framework"]; @@ -44,36 +100,98 @@ typedef enum { kKSPathExistenceChecker } KSExistenceCheckerType; // Harness the KSRegistration class. Class ksrClass = [ksrBundle classNamed:@"KSRegistration"]; - KSRegistration* ksr = [ksrClass registrationWithProductID:bundleIdentifier]; - if (!ksr) { - // Strictly speaking, this isn't necessary, because it's harmless to send - // messages to nil. However, if there really isn't a - // KeystoneRegistration.framework or KSRegistration class, bailing out here - // avoids setting up the timer that will only be able to perform no-ops. - return; - } + KSRegistration* ksr = [ksrClass registrationWithProductID:productID_]; + if (!ksr) + return NO; + + registration_ = [ksr retain]; + return YES; +} - // Keystone will asynchronously handle installation and registration as - // needed. - [ksr registerWithVersion:version - existenceCheckerType:kKSPathExistenceChecker - existenceCheckerString:[mainBundle bundlePath] - serverURLString:url]; +- (void)registerWithKeystone { + [registration_ registerWithVersion:version_ + existenceCheckerType:kKSPathExistenceChecker + existenceCheckerString:[[NSBundle mainBundle] bundlePath] + serverURLString:url_]; // Mark an active RIGHT NOW; don't wait an hour for the first one. - [ksr setActive]; + [registration_ setActive]; // Set up hourly activity pings. - [NSTimer scheduledTimerWithTimeInterval:60 * 60 // One hour - target:self - selector:@selector(markActive:) - userInfo:ksr - repeats:YES]; + timer_ = [NSTimer scheduledTimerWithTimeInterval:60 * 60 // One hour + target:self + selector:@selector(markActive:) + userInfo:registration_ + repeats:YES]; +} + +- (void)stopTimer { + [timer_ invalidate]; } -+ (void)markActive:(NSTimer*)timer { +- (void)markActive:(NSTimer*)timer { KSRegistration* ksr = [timer userInfo]; [ksr setActive]; } +- (void)checkComplete:(NSNotification *)notification { + NSDictionary *userInfo = [notification userInfo]; + BOOL updatesAvailable = [[userInfo objectForKey:KSRegistrationStatusKey] + boolValue]; + NSString *latestVersion = [userInfo objectForKey:KSRegistrationVersionKey]; + + [checkTarget_ upToDateCheckCompleted:updatesAvailable + latestVersion:latestVersion]; + [checkTarget_ release]; + checkTarget_ = nil; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center removeObserver:self + name:KSRegistrationCheckForUpdateNotification + object:nil]; +} + +- (BOOL)checkForUpdate:(NSObject<KeystoneGlueCallbacks>*)target { + if (registration_ == nil) + return NO; + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center addObserver:self + selector:@selector(checkComplete:) + name:KSRegistrationCheckForUpdateNotification + object:nil]; + checkTarget_ = [target retain]; + [registration_ checkForUpdate]; + return YES; +} + +- (void)startUpdateComplete:(NSNotification *)notification { + NSDictionary *userInfo = [notification userInfo]; + BOOL checkSuccessful = [[userInfo objectForKey:KSUpdateCheckSuccessfulKey] + boolValue]; + int installs = [[userInfo objectForKey:KSUpdateCheckSuccessfullyInstalledKey] + intValue]; + + [startTarget_ updateCompleted:checkSuccessful installs:installs]; + [startTarget_ release]; + startTarget_ = nil; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center removeObserver:self + name:KSRegistrationStartUpdateNotification + object:nil]; +} + +- (BOOL)startUpdate:(NSObject<KeystoneGlueCallbacks>*)target { + if (registration_ == nil) + return NO; + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center addObserver:self + selector:@selector(startUpdateComplete:) + name:KSRegistrationStartUpdateNotification + object:nil]; + startTarget_ = [target retain]; + [registration_ startUpdate]; + return YES; +} + @end |