Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(483)

Side by Side Diff: chrome/app/keystone_glue.mm

Issue 338012: About box auto-update improvements (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/app/keystone_glue.h ('k') | chrome/app/keystone_glue_unittest.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/mac_util.h"
6 #import "chrome/app/keystone_glue.h" 5 #import "chrome/app/keystone_glue.h"
7 6
8 @interface KeystoneGlue(Private) 7 #include "base/logging.h"
9 8 #include "base/mac_util.h"
10 // Called periodically to announce activity by pinging the Keystone server. 9 #import "base/worker_pool_mac.h"
11 - (void)markActive:(NSTimer*)timer; 10 #include "chrome/common/chrome_constants.h"
12
13 @end
14
15 11
16 // Provide declarations of the Keystone registration bits needed here. From 12 // Provide declarations of the Keystone registration bits needed here. From
17 // KSRegistration.h. 13 // KSRegistration.h.
18 typedef enum { kKSPathExistenceChecker } KSExistenceCheckerType; 14 typedef enum { kKSPathExistenceChecker } KSExistenceCheckerType;
19 15
20 NSString *KSRegistrationCheckForUpdateNotification = 16 NSString *KSRegistrationCheckForUpdateNotification =
21 @"KSRegistrationCheckForUpdateNotification"; 17 @"KSRegistrationCheckForUpdateNotification";
22 NSString *KSRegistrationStatusKey = @"Status"; 18 NSString *KSRegistrationStatusKey = @"Status";
23 NSString *KSRegistrationVersionKey = @"Version"; 19 NSString *KSRegistrationVersionKey = @"Version";
24 20
25 NSString *KSRegistrationStartUpdateNotification = 21 NSString *KSRegistrationStartUpdateNotification =
26 @"KSRegistrationStartUpdateNotification"; 22 @"KSRegistrationStartUpdateNotification";
27 NSString *KSUpdateCheckSuccessfulKey = @"CheckSuccessful"; 23 NSString *KSUpdateCheckSuccessfulKey = @"CheckSuccessful";
28 NSString *KSUpdateCheckSuccessfullyInstalledKey = @"SuccessfullyInstalled"; 24 NSString *KSUpdateCheckSuccessfullyInstalledKey = @"SuccessfullyInstalled";
29 25
30 NSString *KSRegistrationRemoveExistingTag = @""; 26 NSString *KSRegistrationRemoveExistingTag = @"";
31 #define KSRegistrationPreserveExistingTag nil 27 #define KSRegistrationPreserveExistingTag nil
32 28
33 @interface KSRegistration : NSObject 29 @interface KSRegistration : NSObject
30
34 + (id)registrationWithProductID:(NSString*)productID; 31 + (id)registrationWithProductID:(NSString*)productID;
32
35 // Older API 33 // Older API
36 - (BOOL)registerWithVersion:(NSString*)version 34 - (BOOL)registerWithVersion:(NSString*)version
37 existenceCheckerType:(KSExistenceCheckerType)xctype 35 existenceCheckerType:(KSExistenceCheckerType)xctype
38 existenceCheckerString:(NSString*)xc 36 existenceCheckerString:(NSString*)xc
39 serverURLString:(NSString*)serverURLString; 37 serverURLString:(NSString*)serverURLString;
40 // Newer API 38 // Newer API
41 - (BOOL)registerWithVersion:(NSString*)version 39 - (BOOL)registerWithVersion:(NSString*)version
42 existenceCheckerType:(KSExistenceCheckerType)xctype 40 existenceCheckerType:(KSExistenceCheckerType)xctype
43 existenceCheckerString:(NSString*)xc 41 existenceCheckerString:(NSString*)xc
44 serverURLString:(NSString*)serverURLString 42 serverURLString:(NSString*)serverURLString
45 preserveTTToken:(BOOL)preserveToken 43 preserveTTToken:(BOOL)preserveToken
46 tag:(NSString *)tag; 44 tag:(NSString*)tag;
45
47 - (void)setActive; 46 - (void)setActive;
48 - (void)checkForUpdate; 47 - (void)checkForUpdate;
49 - (void)startUpdate; 48 - (void)startUpdate;
50 @end
51 49
50 @end // @interface KSRegistration
51
52 @interface KeystoneGlue(Private)
53
54 // Called periodically to announce activity by pinging the Keystone server.
55 - (void)markActive:(NSTimer*)timer;
56
57 // Called when an update check or update installation is complete. Posts the
58 // kAutoupdateStatusNotification notification to the default notification
59 // center.
60 - (void)updateStatus:(AutoupdateStatus)status version:(NSString*)version;
61
62 // These three methods are used to determine the version of the application
63 // currently installed on disk, compare that to the currently-running version,
64 // decide whether any updates have been installed, and call
65 // -updateStatus:version:.
66 //
67 // In order to check the version on disk, the installed application's
68 // Info.plist dictionary must be read; in order to see changes as updates are
69 // applied, the dictionary must be read each time, bypassing any caches such
70 // as the one that NSBundle might be maintaining. Reading files can be a
71 // blocking operation, and blocking operations are to be avoided on the main
72 // thread. I'm not quite sure what jank means, but I bet that a blocked main
73 // thread would cause some of it.
74 //
75 // -determineUpdateStatusAsync is called on the main thread to initiate the
76 // operation. It performs initial set-up work that must be done on the main
77 // thread and arranges for -determineUpdateStatusAtPath: to be called on a
78 // work queue thread managed by NSOperationQueue.
79 // -determineUpdateStatusAtPath: then reads the Info.plist, gets the version
80 // from the CFBundleShortVersionString key, and performs
81 // -determineUpdateStatusForVersion: on the main thread.
82 // -determineUpdateStatusForVersion: does the actual comparison of the version
83 // on disk with the running version and calls -updateStatus:version: with the
84 // results of its analysis.
85 - (void)determineUpdateStatusAsync;
86 - (void)determineUpdateStatusAtPath:(NSString*)appPath;
87 - (void)determineUpdateStatusForVersion:(NSString*)version;
88
89 @end // @interface KeystoneGlue(Private)
90
91 const NSString* const kAutoupdateStatusNotification =
92 @"AutoupdateStatusNotification";
93 const NSString* const kAutoupdateStatusStatus = @"status";
94 const NSString* const kAutoupdateStatusVersion = @"version";
52 95
53 @implementation KeystoneGlue 96 @implementation KeystoneGlue
54 97
98 // TODO(jrg): use base::SingletonObjC<KeystoneGlue>
99 static KeystoneGlue* sDefaultKeystoneGlue = nil; // leaked
100
55 + (id)defaultKeystoneGlue { 101 + (id)defaultKeystoneGlue {
56 // TODO(jrg): use base::SingletonObjC<KeystoneGlue> 102 if (!sDefaultKeystoneGlue) {
57 static KeystoneGlue* sDefaultKeystoneGlue = nil; // leaked
58
59 if (sDefaultKeystoneGlue == nil) {
60 sDefaultKeystoneGlue = [[KeystoneGlue alloc] init]; 103 sDefaultKeystoneGlue = [[KeystoneGlue alloc] init];
61 [sDefaultKeystoneGlue loadParameters]; 104 [sDefaultKeystoneGlue loadParameters];
62 if (![sDefaultKeystoneGlue loadKeystoneRegistration]) { 105 if (![sDefaultKeystoneGlue loadKeystoneRegistration]) {
63 [sDefaultKeystoneGlue release]; 106 [sDefaultKeystoneGlue release];
64 sDefaultKeystoneGlue = nil; 107 sDefaultKeystoneGlue = nil;
65 } 108 }
66 } 109 }
67 return sDefaultKeystoneGlue; 110 return sDefaultKeystoneGlue;
68 } 111 }
69 112
113 + (void)releaseDefaultKeystoneGlue {
114 [sDefaultKeystoneGlue release];
115 sDefaultKeystoneGlue = nil;
116 }
117
118 - (id)init {
119 if ((self = [super init])) {
120 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
121
122 [center addObserver:self
123 selector:@selector(checkForUpdateComplete:)
124 name:KSRegistrationCheckForUpdateNotification
125 object:nil];
126
127 [center addObserver:self
128 selector:@selector(installUpdateComplete:)
129 name:KSRegistrationStartUpdateNotification
130 object:nil];
131 }
132
133 return self;
134 }
135
70 - (void)dealloc { 136 - (void)dealloc {
71 [url_ release]; 137 [url_ release];
72 [productID_ release]; 138 [productID_ release];
73 [version_ release]; 139 [version_ release];
74 [channel_ release]; 140 [channel_ release];
75 [registration_ release]; 141 [registration_ release];
76 [[NSNotificationCenter defaultCenter] removeObserver:self]; 142 [[NSNotificationCenter defaultCenter] removeObserver:self];
77 [super dealloc]; 143 [super dealloc];
78 } 144 }
79 145
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 233
168 - (void)stopTimer { 234 - (void)stopTimer {
169 [timer_ invalidate]; 235 [timer_ invalidate];
170 } 236 }
171 237
172 - (void)markActive:(NSTimer*)timer { 238 - (void)markActive:(NSTimer*)timer {
173 KSRegistration* ksr = [timer userInfo]; 239 KSRegistration* ksr = [timer userInfo];
174 [ksr setActive]; 240 [ksr setActive];
175 } 241 }
176 242
177 - (void)checkComplete:(NSNotification *)notification { 243 - (void)checkForUpdate {
178 NSDictionary *userInfo = [notification userInfo]; 244 if (!registration_) {
179 BOOL updatesAvailable = [[userInfo objectForKey:KSRegistrationStatusKey] 245 [self updateStatus:kAutoupdateCheckFailed version:nil];
180 boolValue]; 246 return;
181 NSString *latestVersion = [userInfo objectForKey:KSRegistrationVersionKey]; 247 }
182 248
183 [checkTarget_ upToDateCheckCompleted:updatesAvailable 249 [registration_ checkForUpdate];
184 latestVersion:latestVersion];
185 [checkTarget_ release];
186 checkTarget_ = nil;
187 250
188 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 251 // Upon completion, KSRegistrationCheckForUpdateNotification will be posted,
189 [center removeObserver:self 252 // and -checkForUpdateComplete: will be called.
190 name:KSRegistrationCheckForUpdateNotification
191 object:nil];
192 } 253 }
193 254
194 - (BOOL)checkForUpdate:(NSObject<KeystoneGlueCallbacks>*)target { 255 - (void)checkForUpdateComplete:(NSNotification*)notification {
195 if (registration_ == nil) 256 NSDictionary* userInfo = [notification userInfo];
196 return NO; 257 BOOL updatesAvailable =
197 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 258 [[userInfo objectForKey:KSRegistrationStatusKey] boolValue];
198 [center addObserver:self 259
199 selector:@selector(checkComplete:) 260 if (updatesAvailable) {
200 name:KSRegistrationCheckForUpdateNotification 261 // If an update is known to be available, go straight to
201 object:nil]; 262 // -updateStatus:version:. It doesn't matter what's currently on disk.
202 checkTarget_ = [target retain]; 263 NSString* version = [userInfo objectForKey:KSRegistrationVersionKey];
203 [registration_ checkForUpdate]; 264 [self updateStatus:kAutoupdateAvailable version:version];
204 return YES; 265 } else {
266 // If no updates are available, check what's on disk, because an update
267 // may have already been installed. This check happens on another thread,
268 // and -updateStatus:version: will be called on the main thread when done.
269 [self determineUpdateStatusAsync];
270 }
205 } 271 }
206 272
207 - (void)startUpdateComplete:(NSNotification *)notification { 273 - (void)installUpdate {
208 NSDictionary *userInfo = [notification userInfo]; 274 if (!registration_) {
209 BOOL checkSuccessful = [[userInfo objectForKey:KSUpdateCheckSuccessfulKey] 275 [self updateStatus:kAutoupdateInstallFailed version:nil];
210 boolValue]; 276 return;
211 int installs = [[userInfo objectForKey:KSUpdateCheckSuccessfullyInstalledKey] 277 }
212 intValue];
213 278
214 [startTarget_ updateCompleted:checkSuccessful installs:installs]; 279 [registration_ startUpdate];
215 [startTarget_ release];
216 startTarget_ = nil;
217 280
218 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 281 // Upon completion, KSRegistrationStartUpdateNotification will be posted,
219 [center removeObserver:self 282 // and -installUpdateComplete: will be called.
220 name:KSRegistrationStartUpdateNotification
221 object:nil];
222 } 283 }
223 284
224 - (BOOL)startUpdate:(NSObject<KeystoneGlueCallbacks>*)target { 285 - (void)installUpdateComplete:(NSNotification*)notification {
225 if (registration_ == nil) 286 NSDictionary* userInfo = [notification userInfo];
226 return NO; 287 BOOL checkSuccessful =
227 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 288 [[userInfo objectForKey:KSUpdateCheckSuccessfulKey] boolValue];
228 [center addObserver:self 289 int installs =
229 selector:@selector(startUpdateComplete:) 290 [[userInfo objectForKey:KSUpdateCheckSuccessfullyInstalledKey] intValue];
230 name:KSRegistrationStartUpdateNotification 291
231 object:nil]; 292 if (!checkSuccessful || !installs) {
232 startTarget_ = [target retain]; 293 [self updateStatus:kAutoupdateInstallFailed version:nil];
233 [registration_ startUpdate]; 294 } else {
234 return YES; 295 updateSuccessfullyInstalled_ = YES;
296
297 // Nothing in the notification dictionary reports the version that was
298 // installed. Figure it out based on what's on disk.
299 [self determineUpdateStatusAsync];
300 }
235 } 301 }
236 302
237 @end 303 // Runs on the main thread.
304 - (void)determineUpdateStatusAsync {
305 // NSBundle is not documented as being thread-safe. Do NSBundle operations
306 // on the main thread before jumping over to a NSOperationQueue-managed
307 // thread to do blocking file input.
308 DCHECK([NSThread isMainThread]);
309
310 SEL selector = @selector(determineUpdateStatusAtPath:);
311 NSString* appPath = [[NSBundle mainBundle] bundlePath];
312 NSInvocationOperation* operation =
313 [[[NSInvocationOperation alloc] initWithTarget:self
314 selector:selector
315 object:appPath] autorelease];
316
317 NSOperationQueue* operationQueue = [WorkerPoolObjC sharedOperationQueue];
318 [operationQueue addOperation:operation];
319 }
320
321 // Runs on a thread managed by NSOperationQueue.
322 - (void)determineUpdateStatusAtPath:(NSString*)appPath {
323 DCHECK(![NSThread isMainThread]);
324
325 NSString* appInfoPlistPath =
326 [[appPath stringByAppendingPathComponent:@"Contents"]
327 stringByAppendingPathComponent:@"Info.plist"];
328 NSDictionary* infoPlist =
329 [NSDictionary dictionaryWithContentsOfFile:appInfoPlistPath];
330 NSString* version = [infoPlist objectForKey:@"CFBundleShortVersionString"];
331
332 [self performSelectorOnMainThread:@selector(determineUpdateStatusForVersion:)
333 withObject:version
334 waitUntilDone:NO];
335 }
336
337 // Runs on the main thread.
338 - (void)determineUpdateStatusForVersion:(NSString*)version {
339 DCHECK([NSThread isMainThread]);
340
341 AutoupdateStatus status;
342 if (updateSuccessfullyInstalled_) {
343 // If an update was successfully installed and this object saw it happen,
344 // then don't even bother comparing versions.
345 status = kAutoupdateInstalled;
346 } else {
347 NSString* currentVersion =
348 [NSString stringWithUTF8String:chrome::kChromeVersion];
349 if (!version) {
350 // If the version on disk could not be determined, assume that
351 // whatever's running is current.
352 version = currentVersion;
353 status = kAutoupdateCurrent;
354 } else if ([version isEqualToString:currentVersion]) {
355 status = kAutoupdateCurrent;
356 } else {
357 // If the version on disk doesn't match what's currently running, an
358 // update must have been applied in the background, without this app's
359 // direct participation. Leave updateSuccessfullyInstalled_ alone
360 // because there's no direct knowledge of what actually happened.
361 status = kAutoupdateInstalled;
362 }
363 }
364
365 [self updateStatus:status version:version];
366 }
367
368 - (void)updateStatus:(AutoupdateStatus)status version:(NSString*)version {
369 NSNumber* statusNumber = [NSNumber numberWithInt:status];
370 NSMutableDictionary* dictionary =
371 [NSMutableDictionary dictionaryWithObject:statusNumber
372 forKey:kAutoupdateStatusStatus];
373 if (version) {
374 [dictionary setObject:version forKey:kAutoupdateStatusVersion];
375 }
376
377 NSNotification* notification =
378 [NSNotification notificationWithName:kAutoupdateStatusNotification
379 object:self
380 userInfo:dictionary];
381 recentNotification_.reset([notification retain]);
382
383 [[NSNotificationCenter defaultCenter] postNotification:notification];
384 }
385
386 - (NSNotification*)recentNotification {
387 return [[recentNotification_ retain] autorelease];
388 }
389
390 - (void)clearRecentNotification {
391 recentNotification_.reset(nil);
392 }
393
394 @end // @implementation KeystoneGlue
OLDNEW
« no previous file with comments | « chrome/app/keystone_glue.h ('k') | chrome/app/keystone_glue_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698