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.
 
 

656 lines
27 KiB

//
// MGMController.m
// Chromatic
//
// Created by Mr. Gecko on 6/9/11.
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
//
// Permission to use, copy, modify, and/or distribute this software for any purpose
// with or without fee is hereby granted, provided that the above copyright notice
// and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#import "MGMController.h"
#import "MGMAddons.h"
#import <MGMUsers/MGMUsers.h>
#import <GeckoReporter/GeckoReporter.h>
#import <WebKit/WebKit.h>
NSString * const MGMCopyright = @"Copyright (c) 2011 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/";
NSString * const MGMVersion = @"MGMVersion";
NSString * const MGMLaunchCount = @"MGMLaunchCount";
NSString * const MGMChannel = @"MGMChannel";
NSString * const MGMChromiumPath = @"MGMChromiumPath";
NSString * const MGMCustomSnapshotURL = @"MGMCustomSnapshotURL";
NSString * const MGMCustomSnapshotPrefix = @"MGMCustomSnapshotPrefix";
NSString * const MGMMoveToTrash = @"MGMMoveToTrash";
NSString * const MGMDoneSound = @"MGMDoneSound";
NSString * const MGMLaunchWhenDone = @"MGMLaunchWhenDone";
NSString * const MGMQuitAfterLaunch = @"MGMQuitAfterLaunch";
NSString * const MGM64bit = @"MGM64bit";
NSString * const MGMCPApplications = @"/Applications";
NSString * const MGMCPUserApplications = @"~/Applications";
NSString * const MGMCPChromium = @"Chromium.app";
NSString * const MGMChromiumZip = @"chrome-mac.zip";
NSString * const MGMTMPPath = @"/tmp";
NSString * const MGMChannelsURL = @"https://omahaproxy.appspot.com/all.json?os=mac";
static NSString *MGMSnapshotURL = @"https://storage.googleapis.com/chromium-browser-continuous/";//https://commondatastorage.googleapis.com/chromium-browser-snapshots/
static NSString * MGMSnapshotPrefix = @"Mac/";
NSString * const MGMSVNLogsURL = @"http://build.chromium.org/f/chromium/perf/dashboard/ui/changelog.html?url=/trunk/src&range=%@:%@&mode=html&os=mac";
NSString * const MGMCChannel = @"channel";
NSString * const MGMCRevision = @"branch_base_position";
NSString * const MGMCStable = @"stable";
NSString * const MGMCBeta = @"beta";
NSString * const MGMCDev = @"dev";
NSString * const MGMCCanary = @"canary";
NSString * const MGMUBUpdate = @"Update";
NSString * const MGMUBInstall = @"Install";
NSString * const MGMUBCancel = @"Cancel";
@interface NSString (MGMAddonsSort)
- (NSComparisonResult)numberCompare:(id)theItem;
@end
@implementation NSString (MGMAddonsSort)
- (NSComparisonResult)numberCompare:(id)theItem {
unsigned int theNumber = 0;
unsigned int number = 0;
if ([theItem isKindOfClass:[NSString class]])
sscanf([theItem UTF8String], "%u", &theNumber);
sscanf([self UTF8String], "%u", &number);
if (number<theNumber)
return NSOrderedAscending;
else if (number>theNumber)
return NSOrderedDescending;
return NSOrderedSame;
}
@end
@interface NSOpenPanel (MGMIgnore)
- (void)setDirectoryURL:(NSURL *)url;
- (void)setDirectory:(NSString *)path;
@end
@implementation MGMController
- (void)awakeFromNib {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setup) name:MGMGRDoneNotification object:nil];
[MGMReporter sharedReporter];
}
- (void)setup {
connectionManager = [[MGMURLConnectionManager manager] retain];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:MGMVersion]==nil) {
NSString *oldPathKey = @"cPathKey";
[defaults setObject:[defaults stringForKey:oldPathKey] forKey:MGMChromiumPath];
[defaults removeObjectForKey:oldPathKey];
[defaults setObject:[[MGMSystemInfo info] applicationVersion] forKey:MGMVersion];
}
[self registerDefaults];
if ([defaults integerForKey:MGMLaunchCount]!=5) {
[defaults setInteger:[defaults integerForKey:MGMLaunchCount]+1 forKey:MGMLaunchCount];
if ([defaults integerForKey:MGMLaunchCount]==5) {
NSAlert *alert = [[NSAlert new] autorelease];
[alert setMessageText:@"Donations"];
[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."];
[alert addButtonWithTitle:@"Yes"];
[alert addButtonWithTitle:@"No"];
int result = [alert runModal];
if (result==1000)
[self donate:self];
}
}
if ([[NSUserDefaults standardUserDefaults] boolForKey:MGM64bit]) {
MGMSnapshotURL = @"https://storage.googleapis.com/chromium-qa/";
MGMSnapshotPrefix = @"Chromium_Mac_10_8_x64__experimental_/";
}
if ([defaults objectForKey:MGMCustomSnapshotURL]!=nil && ![[defaults objectForKey:MGMCustomSnapshotURL] isEqual:@""])
MGMSnapshotURL = [defaults objectForKey:MGMCustomSnapshotURL];
if ([defaults objectForKey:MGMCustomSnapshotPrefix]!=nil && ![[defaults objectForKey:MGMCustomSnapshotPrefix] isEqual:@""])
MGMSnapshotPrefix = [defaults objectForKey:MGMCustomSnapshotPrefix];
about = [MGMAbout new];
preferences = [MGMPreferences new];
[preferences addPreferencesPaneClassName:@"MGMGeneralPane"];
[preferences addPreferencesPaneClassName:@"MGMAdvancedPane"];
startingUp = YES;
[mainWindow makeKeyAndOrderFront:self];
[progress startAnimation:self];
[updateButton setEnabled:NO];
[channelPopUp selectItemAtIndex:[defaults integerForKey:MGMChannel]];
channelRevisions = [NSMutableDictionary new];
NSString *path = [defaults objectForKey:MGMChromiumPath];
if (path==nil)
path = [[NSWorkspace sharedWorkspace] fullPathForApplication:@"Chromium"];
if (path==nil) {
NSFileManager *manager = [NSFileManager defaultManager];
if ([manager isWritableFileAtPath:MGMCPApplications]) {
path = [MGMCPApplications stringByAppendingPathComponent:MGMCPChromium];
} else {
if ([manager fileExistsAtPath:[MGMCPUserApplications stringByExpandingTildeInPath]])
[manager createDirectoryAtPath:[MGMCPUserApplications stringByExpandingTildeInPath] withAttributes:nil];
path = [[MGMCPUserApplications stringByExpandingTildeInPath] stringByAppendingPathComponent:MGMCPChromium];
}
}
chromiumPath = [path copy];
[self updateChromiumPath];
MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:MGMChannelsURL]] delegate:self];
[handler setFailWithError:@selector(channels:didFailWithError:)];
[handler setFinish:@selector(channelsFinished:)];
[connectionManager addHandler:handler];
revisionsArray = [NSMutableArray new];
NSString *url = [MGMSnapshotURL stringByAppendingFormat:@"?delimiter=/&prefix=%@", MGMSnapshotPrefix];
handler = [MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]] delegate:self];
[handler setFailWithError:@selector(revisions:didFailWithError:)];
[handler setFinish:@selector(revisionsFinished:)];
[statusField setStringValue:@"Loading revisions..."];
[connectionManager addHandler:handler];
}
- (void)registerDefaults {
NSMutableDictionary *defaults = [NSMutableDictionary dictionary];
[defaults setObject:[NSNumber numberWithInt:1] forKey:MGMLaunchCount];
[defaults setObject:[NSNumber numberWithInt:4] forKey:MGMChannel];
[defaults setObject:[NSNumber numberWithBool:YES] forKey:MGMMoveToTrash];
[defaults setObject:[NSNumber numberWithBool:YES] forKey:MGMDoneSound];
[defaults setObject:[NSNumber numberWithBool:NO] forKey:MGMLaunchWhenDone];
[defaults setObject:[NSNumber numberWithBool:NO] forKey:MGMQuitAfterLaunch];
[defaults setObject:[NSNumber numberWithBool:NO] forKey:MGM64bit];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
}
- (IBAction)about:(id)sender {
[about show];
}
- (IBAction)preferences:(id)sender {
[preferences showPreferences];
}
- (IBAction)donate:(id)sender {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9184741"]];
}
- (IBAction)openSource:(id)sender {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://opensource.mrgeckosmedia.com/Chromatic?application"]];
}
- (void)channels:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
NSLog(@"%@", theError);
NSAlert *alert = [[NSAlert new] autorelease];
[alert setMessageText:@"Error loading channel info"];
[alert setInformativeText:[theError localizedDescription]];
[alert runModal];
}
- (void)channelsFinished:(MGMURLBasicHandler *)theHandler {
NSArray *versions = [[[[theHandler data] parseJSON] objectAtIndex:0] objectForKey:@"versions"];
if (versions==nil) {
NSAlert *alert = [[NSAlert new] autorelease];
[alert setMessageText:@"Error loading channel info"];
[alert setInformativeText:@"The JSON was unable to be parsed."];
[alert runModal];
}
for (int i=0; i<[versions count]; i++) {
NSDictionary *versionInfo = [versions objectAtIndex:i];
id channel = [versionInfo objectForKey:MGMCRevision];
[channelRevisions setObject:([channel isKindOfClass:[NSString class]] ? channel : [channel stringValue]) forKey:[versionInfo objectForKey:MGMCChannel]];
}
[progress setIndeterminate:NO];
[progress startAnimation:self];
[progress setDoubleValue:0.1];
}
- (void)revisions:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
NSLog(@"%@", theError);
NSAlert *alert = [[NSAlert new] autorelease];
[alert setMessageText:@"Error loading revisions"];
[alert setInformativeText:[theError localizedDescription]];
[alert runModal];
NSMenu *items = [NSMenu new];
[revisionsArray sortUsingSelector:@selector(numberCompare:)];
for (unsigned int i=0; i<[revisionsArray count]; i++) {
NSMenuItem *item = [NSMenuItem new];
[item setTitle:[revisionsArray objectAtIndex:i]];
[items addItem:item];
[item release];
}
[buildPopUp removeAllItems];
[buildPopUp setMenu:items];
[items release];
[revisionsArray release];
revisionsArray = nil;
[self channelSelect:self];
}
- (void)revisionsFinished:(MGMURLBasicHandler *)theHandler {
NSError *error = nil;
NSXMLDocument *xml = [[NSXMLDocument alloc] initWithData:[theHandler data] options:NSXMLDocumentTidyXML error:&error];
if ([progress doubleValue] < 0.9) {
[progress incrementBy:0.02];
}
if (error!=nil) {
NSLog(@"%@", error);
NSAlert *alert = [[NSAlert new] autorelease];
[alert setMessageText:@"Error parsing revisions"];
[alert setInformativeText:[error localizedDescription]];
[alert runModal];
NSMenu *items = [NSMenu new];
[revisionsArray sortUsingSelector:@selector(numberCompare:)];
for (unsigned int i=0; i<[revisionsArray count]; i++) {
NSMenuItem *item = [NSMenuItem new];
[item setTitle:[revisionsArray objectAtIndex:i]];
[items addItem:item];
[item release];
}
[buildPopUp removeAllItems];
[buildPopUp setMenu:items];
[items release];
[revisionsArray release];
revisionsArray = nil;
[self channelSelect:self];
} else {
NSXMLElement *rootElement = [xml rootElement];
NSArray *isTruncated = [rootElement elementsForName:@"IsTruncated"];
NSArray *commonPrefixes = [rootElement elementsForName:@"CommonPrefixes"];
for (int i=0; i<[commonPrefixes count]; i++) {
NSArray *prefix = [[commonPrefixes objectAtIndex:i] elementsForName:@"Prefix"];
if ([prefix count]<1)
continue;
NSArray *parsed = [[[prefix objectAtIndex:0] stringValue] componentsSeparatedByString:@"/"];
if ([parsed count]<2)
continue;
if ([[parsed objectAtIndex:1] length]>=40)
continue;
[revisionsArray addObject:[parsed objectAtIndex:1]];
}
NSArray *nextMarkers = [rootElement elementsForName:@"NextMarker"];
if ([isTruncated count]>0 && [[[isTruncated objectAtIndex:0] stringValue] isEqual:@"true"] && [nextMarkers count]>0) {
NSString *nextMarker = [[nextMarkers objectAtIndex:0] stringValue];
NSString *url = [MGMSnapshotURL stringByAppendingFormat:@"?delimiter=/&prefix=%@&marker=%@", MGMSnapshotPrefix, nextMarker];
MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]] delegate:self];
[handler setFailWithError:@selector(revisions:didFailWithError:)];
[handler setFinish:@selector(revisionsFinished:)];
[connectionManager addHandler:handler];
} else {
NSMenu *items = [NSMenu new];
[revisionsArray sortUsingSelector:@selector(numberCompare:)];
for (unsigned int i=0; i<[revisionsArray count]; i++) {
NSMenuItem *item = [NSMenuItem new];
[item setTitle:[revisionsArray objectAtIndex:i]];
[items addItem:item];
[item release];
}
[buildPopUp removeAllItems];
[buildPopUp setMenu:items];
[items release];
[revisionsArray release];
revisionsArray = nil;
[statusField setStringValue:@""];
[self channelSelect:self];
}
}
[xml release];
}
- (IBAction)channelSelect:(id)sender {
[[NSUserDefaults standardUserDefaults] setInteger:[channelPopUp indexOfSelectedItem] forKey:MGMChannel];
NSArray *revisions = [buildPopUp itemTitles];
NSString *revision = nil;
if ([channelPopUp indexOfSelectedItem]==0)
revision = [channelRevisions objectForKey:MGMCStable];
else if ([channelPopUp indexOfSelectedItem]==1)
revision = [channelRevisions objectForKey:MGMCBeta];
else if ([channelPopUp indexOfSelectedItem]==2)
revision = [channelRevisions objectForKey:MGMCDev];
else if ([channelPopUp indexOfSelectedItem]==3)
revision = [channelRevisions objectForKey:MGMCCanary];
if (revision==nil)
revision = [revisions lastObject];
long itemIndex = [revisions indexOfObject:revision];
if (itemIndex==NSNotFound) {
[buildWarningField setHidden:NO];
for (unsigned int i=0; i<[revisions count]; i++) {
if ([(NSString *)[revisions objectAtIndex:i] numberCompare:revision]==NSOrderedDescending) {
itemIndex = i;
break;
}
}
if (itemIndex==NSNotFound)
itemIndex = [revisions count]-1;
} else {
[buildWarningField setHidden:YES];
}
[buildPopUp selectItemAtIndex:itemIndex];
if (startingUp)
[progress setDoubleValue:0.9];
[self buildSelect:self];
}
- (IBAction)buildSelect:(id)sender {
NSURL *buildURL = [[[[NSURL URLWithString:MGMSnapshotURL] appendPathComponent:MGMSnapshotPrefix] appendPathComponent:[buildPopUp titleOfSelectedItem]] appendPathComponent:@"REVISIONS"];
MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:buildURL] delegate:self];
[handler setFailWithError:@selector(revision:didFailWithError:)];
[handler setReceiveResponse:@selector(revision:didReceiveResponse:)];
[handler setFinish:@selector(revisionDidFinish:)];
[connectionManager addHandler:handler];
if (![[yourBuildField stringValue] isEqual:@""] && [buildPopUp indexOfSelectedItem]!=-1 && ![[yourBuildField stringValue] isEqual:[buildPopUp titleOfSelectedItem]])
[svnLogsButton setEnabled:YES];
else
[svnLogsButton setEnabled:NO];
}
- (void)revision:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
NSLog(@"%@", theError);
NSAlert *alert = [[NSAlert new] autorelease];
[alert setMessageText:@"Error loading revision info"];
[alert setInformativeText:[theError localizedDescription]];
[alert runModal];
if (startingUp) {
[progress setDoubleValue:1.0];
[progress setIndeterminate:YES];
[progress setDoubleValue:0.0];
[progress stopAnimation:self];
[updateButton setEnabled:YES];
startingUp = NO;
}
}
- (void)revision:(MGMURLBasicHandler *)theHandler didReceiveResponse:(NSHTTPURLResponse *)theResponse {
NSString *modified = [[theResponse allHeaderFields] objectForKey:@"Last-Modified"];
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease]];
[formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"];
NSDate *date = [formatter dateFromString:modified];
[formatter release];
formatter = [NSDateFormatter new];
[formatter setDateFormat:@"MMMM d, yyyy hh:mm:ss a"];
NSString *dateString = [formatter stringFromDate:date];
[formatter release];
if (dateString!=nil)
[buildDateField setStringValue:dateString];
else
[buildDateField setStringValue:@"N/A"];
}
- (void)revisionDidFinish:(MGMURLBasicHandler *)theHandler {
NSDictionary *revisionInfo = [[theHandler data] parseJSON];
NSString *webkit = [[revisionInfo objectForKey:@"webkit_revision"] description];
if (webkit!=nil)
[webKitBuildField setStringValue:webkit];
else
[webKitBuildField setStringValue:@"0"];
NSString *v8 = [[revisionInfo objectForKey:@"v8_revision"] description];
if (v8!=nil)
[v8BuildField setStringValue:v8];
else
[v8BuildField setStringValue:@"0"];
if (startingUp) {
[progress setDoubleValue:1.0];
[progress setIndeterminate:YES];
[progress setDoubleValue:0.0];
[progress stopAnimation:self];
[updateButton setEnabled:YES];
startingUp = NO;
}
}
- (IBAction)choosePath:(id)sender {
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
if ([openPanel respondsToSelector:@selector(setDirectory:)])
[openPanel setDirectory:[chromiumPath stringByDeletingLastPathComponent]];
else
[openPanel setDirectoryURL:[NSURL fileURLWithPath:[chromiumPath stringByDeletingLastPathComponent]]];
[openPanel setCanChooseFiles:NO];
[openPanel setCanChooseDirectories:YES];
[openPanel setAllowsMultipleSelection:NO];
[openPanel setTitle:@"Choose path for Chromium"];
[openPanel setPrompt:@"Choose"];
int result = [openPanel runModal];
if (result==NSOKButton) {
NSString *newPath = [[[openPanel URLs] objectAtIndex:0] path];
NSFileManager *manager = [NSFileManager defaultManager];
if ([manager isWritableFileAtPath:newPath]) {
[chromiumPath release];
chromiumPath = [[newPath stringByAppendingPathComponent:MGMCPChromium] retain];
[[NSUserDefaults standardUserDefaults] setObject:chromiumPath forKey:MGMChromiumPath];
[self updateChromiumPath];
} else {
NSAlert *alert = [[NSAlert new] autorelease];
[alert setInformativeText:@"Not Writable"];
[alert setMessageText:@"The directory you choose is not writable. To beable to use it with Chromatic, you need to change permissions."];
[alert runModal];
}
}
}
- (void)updateChromiumPath {
NSFileManager *manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath:chromiumPath]) {
NSDictionary *chromiumInfo = (NSDictionary *)CFBundleCopyInfoDictionaryInDirectory((CFURLRef)[NSURL fileURLWithPath:chromiumPath]);
NSString *build = [chromiumInfo objectForKey:@"SVNRevision"];
if (build==nil)
build = [chromiumInfo objectForKey:@"SCMRevision"];
if (build!=nil)
[yourBuildField setStringValue:build];
[chromiumInfo release];
NSDictionary *chromeAttributes = [manager attributesOfItemAtPath:chromiumPath];
NSDate *date = [chromeAttributes objectForKey:NSFileModificationDate];
NSDateFormatter *formatter = [[NSDateFormatter new] autorelease];
[formatter setDateFormat:@"MMMM d, yyyy hh:mm:ss a"];
[installDateField setStringValue:[formatter stringFromDate:date]];
[launchButton setEnabled:YES];
[updateButton setTitle:MGMUBUpdate];
if (![[yourBuildField stringValue] isEqual:@""] && [buildPopUp indexOfSelectedItem]!=-1 && ![[yourBuildField stringValue] isEqual:[buildPopUp titleOfSelectedItem]])
[svnLogsButton setEnabled:YES];
else
[svnLogsButton setEnabled:NO];
} else {
[launchButton setEnabled:NO];
[svnLogsButton setEnabled:NO];
[yourBuildField setStringValue:@""];
[installDateField setStringValue:@""];
[updateButton setTitle:MGMUBInstall];
}
[pathField setStringValue:[chromiumPath stringByDeletingLastPathComponent]];
}
- (IBAction)launchChromium:(id)sender {
[[NSWorkspace sharedWorkspace] launchApplication:chromiumPath];
if ([[NSUserDefaults standardUserDefaults] boolForKey:MGMQuitAfterLaunch])
[[NSApplication sharedApplication] terminate:self];
}
- (IBAction)viewSVNLogs:(id)sender {
NSString *revision1, *revision2, *tmp;
revision1 = [yourBuildField stringValue];
revision2 = [buildPopUp titleOfSelectedItem];
if ([revision1 numberCompare:revision2]==NSOrderedDescending) {
tmp = revision1;
revision1 = revision2;
revision2 = tmp;
}
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:MGMSVNLogsURL, revision1, revision2]];
[[svnLogsBrowser mainFrame] loadRequest:[NSURLRequest requestWithURL:url]];
[svnLogsWindow makeKeyAndOrderFront:self];
}
- (IBAction)update:(id)sender {
if ([[updateButton title] isEqual:MGMUBCancel]) {
[connectionManager cancelHandler:updateHandler];
[updateHandler release];
updateHandler = nil;
[[NSFileManager defaultManager] removeItemAtPath:[MGMTMPPath stringByAppendingPathComponent:MGMChromiumZip]];
[self updateDone];
} else {
[progress startAnimation:self];
startTime = [[NSDate date] timeIntervalSince1970];
NSURL *url = [[[[NSURL URLWithString:MGMSnapshotURL] appendPathComponent:MGMSnapshotPrefix] appendPathComponent:[buildPopUp titleOfSelectedItem]] appendPathComponent:MGMChromiumZip];
[updateHandler release];
updateHandler = [[MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:url] delegate:self] retain];
[updateHandler setFile:[MGMTMPPath stringByAppendingPathComponent:MGMChromiumZip]];
[updateHandler setFailWithError:@selector(update:didFailWithError:)];
[updateHandler setReceiveResponse:@selector(update:didReceiveResponse:)];
[updateHandler setBytesReceived:@selector(update:receivedBytes:totalBytes:expectedBytes:)];
[updateHandler setFinish:@selector(updateDidFinish:)];
[updateHandler setSynchronous:NO];
[connectionManager addHandler:updateHandler];
[channelPopUp setEnabled:NO];
[buildPopUp setEnabled:NO];
[updateButton setTitle:MGMUBCancel];
}
}
- (void)windowWillClose:(NSNotification *)notification {
[[svnLogsBrowser mainFrame] loadHTMLString:@"" baseURL:nil];
}
- (void)update:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
NSLog(@"%@", theError);
NSAlert *alert = [[NSAlert new] autorelease];
[alert setMessageText:@"Error downloading update"];
[alert setInformativeText:[theError localizedDescription]];
[alert runModal];
}
- (void)update:(MGMURLBasicHandler *)theHandler didReceiveResponse:(NSHTTPURLResponse *)theResponse {
[progress setIndeterminate:NO];
[progress startAnimation:self];
bytesReceivedArray = [NSMutableArray new];
}
- (void)update:(MGMURLBasicHandler *)theHandler receivedBytes:(unsigned long)theBytes totalBytes:(unsigned long)theTotalBytes expectedBytes:(unsigned long)theExpectedBytes {
bytesReceived += theBytes;
if (lastCheck==nil || [lastCheck timeIntervalSinceNow]<-1.0) {
[lastCheck release];
lastCheck = [NSDate new];
while ([bytesReceivedArray count]>15) {
[bytesReceivedArray removeObjectAtIndex:0];
}
[bytesReceivedArray addObject:[NSNumber numberWithUnsignedLong:bytesReceived]];
averageBytesReceived = 0;
for (int i=0; i<[bytesReceivedArray count]; i++) {
averageBytesReceived += [[bytesReceivedArray objectAtIndex:i] unsignedLongValue];
}
averageBytesReceived /= [bytesReceivedArray count];
[statusField setStringValue:[NSString stringWithFormat:@"%@ of %@ (%@/sec) - %@", [NSString readableMemory:theTotalBytes], [NSString readableMemory:theExpectedBytes], [NSString readableMemory:bytesReceived], [NSString readableTime:(theExpectedBytes-theTotalBytes)/averageBytesReceived]]];
bytesReceived = 0;
}
[progress setDoubleValue:(double)theTotalBytes/(double)theExpectedBytes];
totalBytes = theTotalBytes;
}
- (void)updateDidFinish:(MGMURLBasicHandler *)theHandler {
[updateHandler release];
updateHandler = nil;
[bytesReceivedArray release];
bytesReceivedArray = nil;
[updateButton setEnabled:NO];
[progress setIndeterminate:YES];
[progress startAnimation:self];
[statusField setStringValue:@"Uncompressing and installing update..."];
unzipTask = [NSTask new];
[unzipTask setLaunchPath:@"/usr/bin/ditto"];
[unzipTask setArguments:[NSArray arrayWithObjects:@"-v", @"-x", @"-k", @"--rsrc", [MGMTMPPath stringByAppendingPathComponent:MGMChromiumZip], MGMTMPPath, nil]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUnzipped:) name:NSTaskDidTerminateNotification object:unzipTask];
[unzipTask launch];
}
- (void)updateUnzipped:(NSNotification *)theNotification {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSFileManager *manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath:chromiumPath]) {
if ([defaults boolForKey:MGMMoveToTrash]) {
NSString *trash = [@"~/.Trash" stringByExpandingTildeInPath];
NSInteger tag;
[[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[chromiumPath stringByDeletingLastPathComponent] destination:trash files:[NSArray arrayWithObject:[chromiumPath lastPathComponent]] tag:&tag];
if (tag!=0)
NSLog(@"Error Deleting: %ld", (long)tag);
} else {
[manager removeItemAtPath:chromiumPath];
}
}
NSString *extractedPath = [[MGMTMPPath stringByAppendingPathComponent:[MGMChromiumZip stringByDeletingPathExtension]] stringByAppendingPathComponent:MGMCPChromium];
if (![manager moveItemAtPath:extractedPath toPath:chromiumPath]) {
NSBeep();
[self updateDone];
[statusField setStringValue:[NSString stringWithFormat:@"Unable to %@ due to an error. This may be a permissions issue.", [[updateButton title] lowercaseString]]];
return;
}
[manager removeItemAtPath:[extractedPath stringByDeletingLastPathComponent]];
[manager removeItemAtPath:[MGMTMPPath stringByAppendingPathComponent:MGMChromiumZip]];
[manager setAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] ofItemAtPath:chromiumPath];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
[self updateDone];
int time = [[NSDate date] timeIntervalSince1970]-startTime;
[statusField setStringValue:[NSString stringWithFormat:@"%@ downloaded at %@/sec in %@", [NSString readableMemory:totalBytes], [NSString readableMemory:averageBytesReceived], [NSString readableTime:time]]];
if ([defaults boolForKey:MGMDoneSound]) {
NSSound *done = [NSSound soundNamed:@"Glass"];
[done setDelegate:self];
[done play];
}
[updateButton setEnabled:YES];
if ([defaults boolForKey:MGMLaunchWhenDone] && [defaults boolForKey:MGMDoneSound] && ![defaults boolForKey:MGMQuitAfterLaunch])
[self launchChromium:self];
}
- (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)didFinish {
if (didFinish) {
[sound release];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults boolForKey:MGMLaunchWhenDone] && [defaults boolForKey:MGMQuitAfterLaunch])
[self launchChromium:self];
}
}
- (void)updateDone {
[self updateChromiumPath];
[channelPopUp setEnabled:YES];
[buildPopUp setEnabled:YES];
[progress setIndeterminate:YES];
[progress setDoubleValue:0.0];
[progress stopAnimation:self];
[statusField setStringValue:@""];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
if (updateHandler!=nil)
[self update:self];
}
@end