Chromium Updater
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

642 lines
26 KiB

13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  1. //
  2. // MGMController.m
  3. // Chromatic
  4. //
  5. // Created by Mr. Gecko on 6/9/11.
  6. // Copyright (c) 2011 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
  7. //
  8. // Permission to use, copy, modify, and/or distribute this software for any purpose
  9. // with or without fee is hereby granted, provided that the above copyright notice
  10. // and this permission notice appear in all copies.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  13. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  14. // FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  15. // OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  16. // DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  17. // ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18. //
  19. #import "MGMController.h"
  20. #import "MGMAddons.h"
  21. #import <MGMUsers/MGMUsers.h>
  22. #import <GeckoReporter/GeckoReporter.h>
  23. #import <WebKit/WebKit.h>
  24. NSString * const MGMCopyright = @"Copyright (c) 2011 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/";
  25. NSString * const MGMVersion = @"MGMVersion";
  26. NSString * const MGMLaunchCount = @"MGMLaunchCount";
  27. NSString * const MGMChannel = @"MGMChannel";
  28. NSString * const MGMChromiumPath = @"MGMChromiumPath";
  29. NSString * const MGMCustomSnapshotURL = @"MGMCustomSnapshotURL";
  30. NSString * const MGMMoveToTrash = @"MGMMoveToTrash";
  31. NSString * const MGMDoneSound = @"MGMDoneSound";
  32. NSString * const MGMLaunchWhenDone = @"MGMLaunchWhenDone";
  33. NSString * const MGMQuitAfterLaunch = @"MGMQuitAfterLaunch";
  34. NSString * const MGMCPApplications = @"/Applications";
  35. NSString * const MGMCPUserApplications = @"~/Applications";
  36. NSString * const MGMCPChromium = @"Chromium.app";
  37. NSString * const MGMChromiumZip = @"chrome-mac.zip";
  38. NSString * const MGMTMPPath = @"/tmp";
  39. NSString * const MGMChannelsURL = @"https://omahaproxy.appspot.com/all.json?os=mac";
  40. static NSString *MGMSnapshotURL = @"https://commondatastorage.googleapis.com/chromium-browser-snapshots/";
  41. NSString * const MGMSnapshotPrefix = @"Mac/";
  42. NSString * const MGMSVNLogsURL = @"http://build.chromium.org/f/chromium/perf/dashboard/ui/changelog.html?url=/trunk/src&range=%@:%@&mode=html&os=mac";
  43. NSString * const MGMCChannel = @"channel";
  44. NSString * const MGMCRevision = @"branch_base_position";
  45. NSString * const MGMCStable = @"stable";
  46. NSString * const MGMCBeta = @"beta";
  47. NSString * const MGMCDev = @"dev";
  48. NSString * const MGMCCanary = @"canary";
  49. NSString * const MGMUBUpdate = @"Update";
  50. NSString * const MGMUBInstall = @"Install";
  51. NSString * const MGMUBCancel = @"Cancel";
  52. @interface NSString (MGMAddonsSort)
  53. - (NSComparisonResult)numberCompare:(id)theItem;
  54. @end
  55. @implementation NSString (MGMAddonsSort)
  56. - (NSComparisonResult)numberCompare:(id)theItem {
  57. unsigned int theNumber = 0;
  58. unsigned int number = 0;
  59. if ([theItem isKindOfClass:[NSString class]])
  60. sscanf([theItem UTF8String], "%u", &theNumber);
  61. sscanf([self UTF8String], "%u", &number);
  62. if (number<theNumber)
  63. return NSOrderedAscending;
  64. else if (number>theNumber)
  65. return NSOrderedDescending;
  66. return NSOrderedSame;
  67. }
  68. @end
  69. @interface NSOpenPanel (MGMIgnore)
  70. - (void)setDirectoryURL:(NSURL *)url;
  71. - (void)setDirectory:(NSString *)path;
  72. @end
  73. @implementation MGMController
  74. - (void)awakeFromNib {
  75. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setup) name:MGMGRDoneNotification object:nil];
  76. [MGMReporter sharedReporter];
  77. }
  78. - (void)setup {
  79. connectionManager = [[MGMURLConnectionManager manager] retain];
  80. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  81. if ([defaults objectForKey:MGMVersion]==nil) {
  82. NSString *oldPathKey = @"cPathKey";
  83. [defaults setObject:[defaults stringForKey:oldPathKey] forKey:MGMChromiumPath];
  84. [defaults removeObjectForKey:oldPathKey];
  85. [defaults setObject:[[MGMSystemInfo info] applicationVersion] forKey:MGMVersion];
  86. }
  87. [self registerDefaults];
  88. if ([defaults integerForKey:MGMLaunchCount]!=5) {
  89. [defaults setInteger:[defaults integerForKey:MGMLaunchCount]+1 forKey:MGMLaunchCount];
  90. if ([defaults integerForKey:MGMLaunchCount]==5) {
  91. NSAlert *alert = [[NSAlert new] autorelease];
  92. [alert setMessageText:@"Donations"];
  93. [alert setInformativeText:@"Thank you for using Chromatic. Chromatic is donation supported software. If you like using it, please consider giving a donation to help with development."];
  94. [alert addButtonWithTitle:@"Yes"];
  95. [alert addButtonWithTitle:@"No"];
  96. int result = [alert runModal];
  97. if (result==1000)
  98. [self donate:self];
  99. }
  100. }
  101. if ([defaults objectForKey:MGMCustomSnapshotURL]!=nil && ![[defaults objectForKey:MGMCustomSnapshotURL] isEqual:@""])
  102. MGMSnapshotURL = [defaults objectForKey:MGMCustomSnapshotURL];
  103. about = [MGMAbout new];
  104. preferences = [MGMPreferences new];
  105. [preferences addPreferencesPaneClassName:@"MGMGeneralPane"];
  106. [preferences addPreferencesPaneClassName:@"MGMAdvancedPane"];
  107. startingUp = YES;
  108. [mainWindow makeKeyAndOrderFront:self];
  109. [progress startAnimation:self];
  110. [updateButton setEnabled:NO];
  111. [channelPopUp selectItemAtIndex:[defaults integerForKey:MGMChannel]];
  112. channelRevisions = [NSMutableDictionary new];
  113. NSString *path = [defaults objectForKey:MGMChromiumPath];
  114. if (path==nil)
  115. path = [[NSWorkspace sharedWorkspace] fullPathForApplication:@"Chromium"];
  116. if (path==nil) {
  117. NSFileManager *manager = [NSFileManager defaultManager];
  118. if ([manager isWritableFileAtPath:MGMCPApplications]) {
  119. path = [MGMCPApplications stringByAppendingPathComponent:MGMCPChromium];
  120. } else {
  121. if ([manager fileExistsAtPath:[MGMCPUserApplications stringByExpandingTildeInPath]])
  122. [manager createDirectoryAtPath:[MGMCPUserApplications stringByExpandingTildeInPath] withAttributes:nil];
  123. path = [[MGMCPUserApplications stringByExpandingTildeInPath] stringByAppendingPathComponent:MGMCPChromium];
  124. }
  125. }
  126. chromiumPath = [path copy];
  127. [self updateChromiumPath];
  128. MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:MGMChannelsURL]] delegate:self];
  129. [handler setFailWithError:@selector(channels:didFailWithError:)];
  130. [handler setFinish:@selector(channelsFinished:)];
  131. [connectionManager addHandler:handler];
  132. revisionsArray = [NSMutableArray new];
  133. NSString *url = [MGMSnapshotURL stringByAppendingFormat:@"?delimiter=/&prefix=%@", MGMSnapshotPrefix];
  134. handler = [MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]] delegate:self];
  135. [handler setFailWithError:@selector(revisions:didFailWithError:)];
  136. [handler setFinish:@selector(revisionsFinished:)];
  137. [statusField setStringValue:@"Loading revisions..."];
  138. [connectionManager addHandler:handler];
  139. }
  140. - (void)registerDefaults {
  141. NSMutableDictionary *defaults = [NSMutableDictionary dictionary];
  142. [defaults setObject:[NSNumber numberWithInt:1] forKey:MGMLaunchCount];
  143. [defaults setObject:[NSNumber numberWithInt:4] forKey:MGMChannel];
  144. [defaults setObject:[NSNumber numberWithBool:YES] forKey:MGMMoveToTrash];
  145. [defaults setObject:[NSNumber numberWithBool:YES] forKey:MGMDoneSound];
  146. [defaults setObject:[NSNumber numberWithBool:NO] forKey:MGMLaunchWhenDone];
  147. [defaults setObject:[NSNumber numberWithBool:NO] forKey:MGMQuitAfterLaunch];
  148. [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
  149. }
  150. - (IBAction)about:(id)sender {
  151. [about show];
  152. }
  153. - (IBAction)preferences:(id)sender {
  154. [preferences showPreferences];
  155. }
  156. - (IBAction)donate:(id)sender {
  157. [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9184741"]];
  158. }
  159. - (IBAction)openSource:(id)sender {
  160. [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://opensource.mrgeckosmedia.com/Chromatic?application"]];
  161. }
  162. - (void)channels:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
  163. NSLog(@"%@", theError);
  164. NSAlert *alert = [[NSAlert new] autorelease];
  165. [alert setMessageText:@"Error loading channel info"];
  166. [alert setInformativeText:[theError localizedDescription]];
  167. [alert runModal];
  168. }
  169. - (void)channelsFinished:(MGMURLBasicHandler *)theHandler {
  170. NSArray *versions = [[[[theHandler data] parseJSON] objectAtIndex:0] objectForKey:@"versions"];
  171. if (versions==nil) {
  172. NSAlert *alert = [[NSAlert new] autorelease];
  173. [alert setMessageText:@"Error loading channel info"];
  174. [alert setInformativeText:@"The JSON was unable to be parsed."];
  175. [alert runModal];
  176. }
  177. for (int i=0; i<[versions count]; i++) {
  178. NSDictionary *versionInfo = [versions objectAtIndex:i];
  179. id channel = [versionInfo objectForKey:MGMCRevision];
  180. [channelRevisions setObject:([channel isKindOfClass:[NSString class]] ? channel : [channel stringValue]) forKey:[versionInfo objectForKey:MGMCChannel]];
  181. }
  182. [progress setIndeterminate:NO];
  183. [progress startAnimation:self];
  184. [progress setDoubleValue:0.1];
  185. }
  186. - (void)revisions:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
  187. NSLog(@"%@", theError);
  188. NSAlert *alert = [[NSAlert new] autorelease];
  189. [alert setMessageText:@"Error loading revisions"];
  190. [alert setInformativeText:[theError localizedDescription]];
  191. [alert runModal];
  192. NSMenu *items = [NSMenu new];
  193. [revisionsArray sortUsingSelector:@selector(numberCompare:)];
  194. for (unsigned int i=0; i<[revisionsArray count]; i++) {
  195. NSMenuItem *item = [NSMenuItem new];
  196. [item setTitle:[revisionsArray objectAtIndex:i]];
  197. [items addItem:item];
  198. [item release];
  199. }
  200. [buildPopUp removeAllItems];
  201. [buildPopUp setMenu:items];
  202. [items release];
  203. [revisionsArray release];
  204. revisionsArray = nil;
  205. [self channelSelect:self];
  206. }
  207. - (void)revisionsFinished:(MGMURLBasicHandler *)theHandler {
  208. NSError *error = nil;
  209. NSXMLDocument *xml = [[NSXMLDocument alloc] initWithData:[theHandler data] options:NSXMLDocumentTidyXML error:&error];
  210. if ([progress doubleValue] < 0.9) {
  211. [progress incrementBy:0.02];
  212. }
  213. if (error!=nil) {
  214. NSLog(@"%@", error);
  215. NSAlert *alert = [[NSAlert new] autorelease];
  216. [alert setMessageText:@"Error parsing revisions"];
  217. [alert setInformativeText:[error localizedDescription]];
  218. [alert runModal];
  219. NSMenu *items = [NSMenu new];
  220. [revisionsArray sortUsingSelector:@selector(numberCompare:)];
  221. for (unsigned int i=0; i<[revisionsArray count]; i++) {
  222. NSMenuItem *item = [NSMenuItem new];
  223. [item setTitle:[revisionsArray objectAtIndex:i]];
  224. [items addItem:item];
  225. [item release];
  226. }
  227. [buildPopUp removeAllItems];
  228. [buildPopUp setMenu:items];
  229. [items release];
  230. [revisionsArray release];
  231. revisionsArray = nil;
  232. [self channelSelect:self];
  233. } else {
  234. NSXMLElement *rootElement = [xml rootElement];
  235. NSArray *isTruncated = [rootElement elementsForName:@"IsTruncated"];
  236. NSArray *commonPrefixes = [rootElement elementsForName:@"CommonPrefixes"];
  237. for (int i=0; i<[commonPrefixes count]; i++) {
  238. NSArray *prefix = [[commonPrefixes objectAtIndex:i] elementsForName:@"Prefix"];
  239. if ([prefix count]<1)
  240. continue;
  241. NSArray *parsed = [[[prefix objectAtIndex:0] stringValue] componentsSeparatedByString:@"/"];
  242. if ([parsed count]<2)
  243. continue;
  244. if ([[parsed objectAtIndex:1] length]>=40)
  245. continue;
  246. [revisionsArray addObject:[parsed objectAtIndex:1]];
  247. }
  248. NSArray *nextMarkers = [rootElement elementsForName:@"NextMarker"];
  249. if ([isTruncated count]>0 && [[[isTruncated objectAtIndex:0] stringValue] isEqual:@"true"] && [nextMarkers count]>0) {
  250. NSString *nextMarker = [[nextMarkers objectAtIndex:0] stringValue];
  251. NSString *url = [MGMSnapshotURL stringByAppendingFormat:@"?delimiter=/&prefix=%@&marker=%@", MGMSnapshotPrefix, nextMarker];
  252. MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]] delegate:self];
  253. [handler setFailWithError:@selector(revisions:didFailWithError:)];
  254. [handler setFinish:@selector(revisionsFinished:)];
  255. [connectionManager addHandler:handler];
  256. } else {
  257. NSMenu *items = [NSMenu new];
  258. [revisionsArray sortUsingSelector:@selector(numberCompare:)];
  259. for (unsigned int i=0; i<[revisionsArray count]; i++) {
  260. NSMenuItem *item = [NSMenuItem new];
  261. [item setTitle:[revisionsArray objectAtIndex:i]];
  262. [items addItem:item];
  263. [item release];
  264. }
  265. [buildPopUp removeAllItems];
  266. [buildPopUp setMenu:items];
  267. [items release];
  268. [revisionsArray release];
  269. revisionsArray = nil;
  270. [statusField setStringValue:@""];
  271. [self channelSelect:self];
  272. }
  273. }
  274. [xml release];
  275. }
  276. - (IBAction)channelSelect:(id)sender {
  277. [[NSUserDefaults standardUserDefaults] setInteger:[channelPopUp indexOfSelectedItem] forKey:MGMChannel];
  278. NSArray *revisions = [buildPopUp itemTitles];
  279. NSString *revision = nil;
  280. if ([channelPopUp indexOfSelectedItem]==0)
  281. revision = [channelRevisions objectForKey:MGMCStable];
  282. else if ([channelPopUp indexOfSelectedItem]==1)
  283. revision = [channelRevisions objectForKey:MGMCBeta];
  284. else if ([channelPopUp indexOfSelectedItem]==2)
  285. revision = [channelRevisions objectForKey:MGMCDev];
  286. else if ([channelPopUp indexOfSelectedItem]==3)
  287. revision = [channelRevisions objectForKey:MGMCCanary];
  288. if (revision==nil)
  289. revision = [revisions lastObject];
  290. long itemIndex = [revisions indexOfObject:revision];
  291. if (itemIndex==NSNotFound) {
  292. [buildWarningField setHidden:NO];
  293. for (unsigned int i=0; i<[revisions count]; i++) {
  294. if ([(NSString *)[revisions objectAtIndex:i] numberCompare:revision]==NSOrderedDescending) {
  295. itemIndex = i;
  296. break;
  297. }
  298. }
  299. if (itemIndex==NSNotFound)
  300. itemIndex = [revisions count]-1;
  301. } else {
  302. [buildWarningField setHidden:YES];
  303. }
  304. [buildPopUp selectItemAtIndex:itemIndex];
  305. if (startingUp)
  306. [progress setDoubleValue:0.9];
  307. [self buildSelect:self];
  308. }
  309. - (IBAction)buildSelect:(id)sender {
  310. NSURL *buildURL = [[[[NSURL URLWithString:MGMSnapshotURL] appendPathComponent:MGMSnapshotPrefix] appendPathComponent:[buildPopUp titleOfSelectedItem]] appendPathComponent:@"REVISIONS"];
  311. MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:buildURL] delegate:self];
  312. [handler setFailWithError:@selector(revision:didFailWithError:)];
  313. [handler setReceiveResponse:@selector(revision:didReceiveResponse:)];
  314. [handler setFinish:@selector(revisionDidFinish:)];
  315. [connectionManager addHandler:handler];
  316. if (![[yourBuildField stringValue] isEqual:@""] && [buildPopUp indexOfSelectedItem]!=-1 && ![[yourBuildField stringValue] isEqual:[buildPopUp titleOfSelectedItem]])
  317. [svnLogsButton setEnabled:YES];
  318. else
  319. [svnLogsButton setEnabled:NO];
  320. }
  321. - (void)revision:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
  322. NSLog(@"%@", theError);
  323. NSAlert *alert = [[NSAlert new] autorelease];
  324. [alert setMessageText:@"Error loading revision info"];
  325. [alert setInformativeText:[theError localizedDescription]];
  326. [alert runModal];
  327. if (startingUp) {
  328. [progress setDoubleValue:1.0];
  329. [progress setIndeterminate:YES];
  330. [progress setDoubleValue:0.0];
  331. [progress stopAnimation:self];
  332. [updateButton setEnabled:YES];
  333. startingUp = NO;
  334. }
  335. }
  336. - (void)revision:(MGMURLBasicHandler *)theHandler didReceiveResponse:(NSHTTPURLResponse *)theResponse {
  337. NSString *modified = [[theResponse allHeaderFields] objectForKey:@"Last-Modified"];
  338. NSDateFormatter *formatter = [NSDateFormatter new];
  339. [formatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease]];
  340. [formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"];
  341. NSDate *date = [formatter dateFromString:modified];
  342. [formatter release];
  343. formatter = [NSDateFormatter new];
  344. [formatter setDateFormat:@"MMMM d, yyyy hh:mm:ss a"];
  345. NSString *dateString = [formatter stringFromDate:date];
  346. [formatter release];
  347. if (dateString!=nil)
  348. [buildDateField setStringValue:dateString];
  349. else
  350. [buildDateField setStringValue:@"N/A"];
  351. }
  352. - (void)revisionDidFinish:(MGMURLBasicHandler *)theHandler {
  353. NSDictionary *revisionInfo = [[theHandler data] parseJSON];
  354. NSString *webkit = [NSString stringWithFormat:@"%@", [revisionInfo objectForKey:@"webkit_revision"]];
  355. if (webkit!=nil)
  356. [webKitBuildField setStringValue:webkit];
  357. else
  358. [webKitBuildField setStringValue:@"0"];
  359. NSString *v8 = [NSString stringWithFormat:@"%@", [revisionInfo objectForKey:@"v8_revision"]];
  360. if (v8!=nil)
  361. [v8BuildField setStringValue:v8];
  362. else
  363. [v8BuildField setStringValue:@"0"];
  364. if (startingUp) {
  365. [progress setDoubleValue:1.0];
  366. [progress setIndeterminate:YES];
  367. [progress setDoubleValue:0.0];
  368. [progress stopAnimation:self];
  369. [updateButton setEnabled:YES];
  370. startingUp = NO;
  371. }
  372. }
  373. - (IBAction)choosePath:(id)sender {
  374. NSOpenPanel *openPanel = [NSOpenPanel openPanel];
  375. if ([openPanel respondsToSelector:@selector(setDirectory:)])
  376. [openPanel setDirectory:[chromiumPath stringByDeletingLastPathComponent]];
  377. else
  378. [openPanel setDirectoryURL:[NSURL fileURLWithPath:[chromiumPath stringByDeletingLastPathComponent]]];
  379. [openPanel setCanChooseFiles:NO];
  380. [openPanel setCanChooseDirectories:YES];
  381. [openPanel setAllowsMultipleSelection:NO];
  382. [openPanel setTitle:@"Choose path for Chromium"];
  383. [openPanel setPrompt:@"Choose"];
  384. int result = [openPanel runModal];
  385. if (result==NSOKButton) {
  386. NSString *newPath = [[[openPanel URLs] objectAtIndex:0] path];
  387. NSFileManager *manager = [NSFileManager defaultManager];
  388. if ([manager isWritableFileAtPath:newPath]) {
  389. [chromiumPath release];
  390. chromiumPath = [[newPath stringByAppendingPathComponent:MGMCPChromium] retain];
  391. [[NSUserDefaults standardUserDefaults] setObject:chromiumPath forKey:MGMChromiumPath];
  392. [self updateChromiumPath];
  393. } else {
  394. NSAlert *alert = [[NSAlert new] autorelease];
  395. [alert setInformativeText:@"Not Writable"];
  396. [alert setMessageText:@"The directory you choose is not writable. To beable to use it with Chromatic, you need to change permissions."];
  397. [alert runModal];
  398. }
  399. }
  400. }
  401. - (void)updateChromiumPath {
  402. NSFileManager *manager = [NSFileManager defaultManager];
  403. if ([manager fileExistsAtPath:chromiumPath]) {
  404. NSDictionary *chromiumInfo = (NSDictionary *)CFBundleCopyInfoDictionaryInDirectory((CFURLRef)[NSURL fileURLWithPath:chromiumPath]);
  405. NSString *build = [chromiumInfo objectForKey:@"SVNRevision"];
  406. if (build==nil)
  407. build = [chromiumInfo objectForKey:@"SCMRevision"];
  408. if (build!=nil)
  409. [yourBuildField setStringValue:build];
  410. [chromiumInfo release];
  411. NSDictionary *chromeAttributes = [manager attributesOfItemAtPath:chromiumPath];
  412. NSDate *date = [chromeAttributes objectForKey:NSFileModificationDate];
  413. NSDateFormatter *formatter = [[NSDateFormatter new] autorelease];
  414. [formatter setDateFormat:@"MMMM d, yyyy hh:mm:ss a"];
  415. [installDateField setStringValue:[formatter stringFromDate:date]];
  416. [launchButton setEnabled:YES];
  417. [updateButton setTitle:MGMUBUpdate];
  418. if (![[yourBuildField stringValue] isEqual:@""] && [buildPopUp indexOfSelectedItem]!=-1 && ![[yourBuildField stringValue] isEqual:[buildPopUp titleOfSelectedItem]])
  419. [svnLogsButton setEnabled:YES];
  420. else
  421. [svnLogsButton setEnabled:NO];
  422. } else {
  423. [launchButton setEnabled:NO];
  424. [svnLogsButton setEnabled:NO];
  425. [yourBuildField setStringValue:@""];
  426. [installDateField setStringValue:@""];
  427. [updateButton setTitle:MGMUBInstall];
  428. }
  429. [pathField setStringValue:[chromiumPath stringByDeletingLastPathComponent]];
  430. }
  431. - (IBAction)launchChromium:(id)sender {
  432. [[NSWorkspace sharedWorkspace] launchApplication:chromiumPath];
  433. if ([[NSUserDefaults standardUserDefaults] boolForKey:MGMQuitAfterLaunch])
  434. [[NSApplication sharedApplication] terminate:self];
  435. }
  436. - (IBAction)viewSVNLogs:(id)sender {
  437. NSString *revision1, *revision2, *tmp;
  438. revision1 = [yourBuildField stringValue];
  439. revision2 = [buildPopUp titleOfSelectedItem];
  440. if ([revision1 numberCompare:revision2]==NSOrderedDescending) {
  441. tmp = revision1;
  442. revision1 = revision2;
  443. revision2 = tmp;
  444. }
  445. NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:MGMSVNLogsURL, revision1, revision2]];
  446. [[svnLogsBrowser mainFrame] loadRequest:[NSURLRequest requestWithURL:url]];
  447. [svnLogsWindow makeKeyAndOrderFront:self];
  448. }
  449. - (IBAction)update:(id)sender {
  450. if ([[updateButton title] isEqual:MGMUBCancel]) {
  451. [connectionManager cancelHandler:updateHandler];
  452. [updateHandler release];
  453. updateHandler = nil;
  454. [[NSFileManager defaultManager] removeItemAtPath:[MGMTMPPath stringByAppendingPathComponent:MGMChromiumZip]];
  455. [self updateDone];
  456. } else {
  457. [progress startAnimation:self];
  458. startTime = [[NSDate date] timeIntervalSince1970];
  459. NSURL *url = [[[[NSURL URLWithString:MGMSnapshotURL] appendPathComponent:MGMSnapshotPrefix] appendPathComponent:[buildPopUp titleOfSelectedItem]] appendPathComponent:MGMChromiumZip];
  460. [updateHandler release];
  461. updateHandler = [[MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:url] delegate:self] retain];
  462. [updateHandler setFile:[MGMTMPPath stringByAppendingPathComponent:MGMChromiumZip]];
  463. [updateHandler setFailWithError:@selector(update:didFailWithError:)];
  464. [updateHandler setReceiveResponse:@selector(update:didReceiveResponse:)];
  465. [updateHandler setBytesReceived:@selector(update:receivedBytes:totalBytes:expectedBytes:)];
  466. [updateHandler setFinish:@selector(updateDidFinish:)];
  467. [updateHandler setSynchronous:NO];
  468. [connectionManager addHandler:updateHandler];
  469. [channelPopUp setEnabled:NO];
  470. [buildPopUp setEnabled:NO];
  471. [updateButton setTitle:MGMUBCancel];
  472. }
  473. }
  474. - (void)windowWillClose:(NSNotification *)notification {
  475. [[svnLogsBrowser mainFrame] loadHTMLString:@"" baseURL:nil];
  476. }
  477. - (void)update:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
  478. NSLog(@"%@", theError);
  479. NSAlert *alert = [[NSAlert new] autorelease];
  480. [alert setMessageText:@"Error downloading update"];
  481. [alert setInformativeText:[theError localizedDescription]];
  482. [alert runModal];
  483. }
  484. - (void)update:(MGMURLBasicHandler *)theHandler didReceiveResponse:(NSHTTPURLResponse *)theResponse {
  485. [progress setIndeterminate:NO];
  486. [progress startAnimation:self];
  487. bytesReceivedArray = [NSMutableArray new];
  488. }
  489. - (void)update:(MGMURLBasicHandler *)theHandler receivedBytes:(unsigned long)theBytes totalBytes:(unsigned long)theTotalBytes expectedBytes:(unsigned long)theExpectedBytes {
  490. bytesReceived += theBytes;
  491. if (lastCheck==nil || [lastCheck timeIntervalSinceNow]<-1.0) {
  492. [lastCheck release];
  493. lastCheck = [NSDate new];
  494. while ([bytesReceivedArray count]>15) {
  495. [bytesReceivedArray removeObjectAtIndex:0];
  496. }
  497. [bytesReceivedArray addObject:[NSNumber numberWithUnsignedLong:bytesReceived]];
  498. averageBytesReceived = 0;
  499. for (int i=0; i<[bytesReceivedArray count]; i++) {
  500. averageBytesReceived += [[bytesReceivedArray objectAtIndex:i] unsignedLongValue];
  501. }
  502. averageBytesReceived /= [bytesReceivedArray count];
  503. [statusField setStringValue:[NSString stringWithFormat:@"%@ of %@ (%@/sec) - %@", [NSString readableMemory:theTotalBytes], [NSString readableMemory:theExpectedBytes], [NSString readableMemory:bytesReceived], [NSString readableTime:(theExpectedBytes-theTotalBytes)/averageBytesReceived]]];
  504. bytesReceived = 0;
  505. }
  506. [progress setDoubleValue:(double)theTotalBytes/(double)theExpectedBytes];
  507. totalBytes = theTotalBytes;
  508. }
  509. - (void)updateDidFinish:(MGMURLBasicHandler *)theHandler {
  510. [updateHandler release];
  511. updateHandler = nil;
  512. [bytesReceivedArray release];
  513. bytesReceivedArray = nil;
  514. [updateButton setEnabled:NO];
  515. [progress setIndeterminate:YES];
  516. [progress startAnimation:self];
  517. [statusField setStringValue:@"Uncompressing and installing update..."];
  518. unzipTask = [NSTask new];
  519. [unzipTask setLaunchPath:@"/usr/bin/ditto"];
  520. [unzipTask setArguments:[NSArray arrayWithObjects:@"-v", @"-x", @"-k", @"--rsrc", [MGMTMPPath stringByAppendingPathComponent:MGMChromiumZip], MGMTMPPath, nil]];
  521. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUnzipped:) name:NSTaskDidTerminateNotification object:unzipTask];
  522. [unzipTask launch];
  523. }
  524. - (void)updateUnzipped:(NSNotification *)theNotification {
  525. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  526. NSFileManager *manager = [NSFileManager defaultManager];
  527. if ([manager fileExistsAtPath:chromiumPath]) {
  528. if ([defaults boolForKey:MGMMoveToTrash]) {
  529. NSString *trash = [@"~/.Trash" stringByExpandingTildeInPath];
  530. NSInteger tag;
  531. [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[chromiumPath stringByDeletingLastPathComponent] destination:trash files:[NSArray arrayWithObject:[chromiumPath lastPathComponent]] tag:&tag];
  532. if (tag!=0)
  533. NSLog(@"Error Deleting: %ld", (long)tag);
  534. } else {
  535. [manager removeItemAtPath:chromiumPath];
  536. }
  537. }
  538. NSString *extractedPath = [[MGMTMPPath stringByAppendingPathComponent:[MGMChromiumZip stringByDeletingPathExtension]] stringByAppendingPathComponent:MGMCPChromium];
  539. if (![manager moveItemAtPath:extractedPath toPath:chromiumPath]) {
  540. NSBeep();
  541. [self updateDone];
  542. [statusField setStringValue:[NSString stringWithFormat:@"Unable to %@ due to an error. This may be a permissions issue.", [[updateButton title] lowercaseString]]];
  543. return;
  544. }
  545. [manager removeItemAtPath:[extractedPath stringByDeletingLastPathComponent]];
  546. [manager removeItemAtPath:[MGMTMPPath stringByAppendingPathComponent:MGMChromiumZip]];
  547. [manager setAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] ofItemAtPath:chromiumPath];
  548. [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
  549. [self updateDone];
  550. int time = [[NSDate date] timeIntervalSince1970]-startTime;
  551. [statusField setStringValue:[NSString stringWithFormat:@"%@ downloaded at %@/sec in %@", [NSString readableMemory:totalBytes], [NSString readableMemory:averageBytesReceived], [NSString readableTime:time]]];
  552. if ([defaults boolForKey:MGMDoneSound]) {
  553. NSSound *done = [NSSound soundNamed:@"Glass"];
  554. [done setDelegate:self];
  555. [done play];
  556. }
  557. [updateButton setEnabled:YES];
  558. if ([defaults boolForKey:MGMLaunchWhenDone] && [defaults boolForKey:MGMDoneSound] && ![defaults boolForKey:MGMQuitAfterLaunch])
  559. [self launchChromium:self];
  560. }
  561. - (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)didFinish {
  562. if (didFinish) {
  563. [sound release];
  564. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  565. if ([defaults boolForKey:MGMLaunchWhenDone] && [defaults boolForKey:MGMQuitAfterLaunch])
  566. [self launchChromium:self];
  567. }
  568. }
  569. - (void)updateDone {
  570. [self updateChromiumPath];
  571. [channelPopUp setEnabled:YES];
  572. [buildPopUp setEnabled:YES];
  573. [progress setIndeterminate:YES];
  574. [progress setDoubleValue:0.0];
  575. [progress stopAnimation:self];
  576. [statusField setStringValue:@""];
  577. }
  578. - (void)applicationWillTerminate:(NSNotification *)aNotification {
  579. if (updateHandler!=nil)
  580. [self update:self];
  581. }
  582. @end