Index: chrome/installer/mac/app/AppDelegate.m |
diff --git a/chrome/installer/mac/app/AppDelegate.m b/chrome/installer/mac/app/AppDelegate.m |
index e7a11a5ddc84c428dc68360dc30e5df972994b0f..a8e956877a7b6776c00b43f3fd3dfb21f2fd94b5 100644 |
--- a/chrome/installer/mac/app/AppDelegate.m |
+++ b/chrome/installer/mac/app/AppDelegate.m |
@@ -4,10 +4,15 @@ |
#import "AppDelegate.h" |
+#include <Security/Security.h> |
+ |
+#import "Downloader.h" |
#import "InstallerWindowController.h" |
#import "NSError+ChromeInstallerAdditions.h" |
#import "NSAlert+ChromeInstallerAdditions.h" |
#import "AuthorizedInstall.h" |
+#import "OmahaCommunication.h" |
+#import "Unpacker.h" |
@interface NSAlert () |
- (void)beginSheetModalForWindow:(NSWindow*)sheetWindow |
@@ -15,11 +20,16 @@ |
(void (^__nullable)(NSModalResponse returnCode))handler; |
@end |
-@interface AppDelegate ()<OmahaCommunicationDelegate, DownloaderDelegate> { |
+@interface AppDelegate ()<NSWindowDelegate, |
+ OmahaCommunicationDelegate, |
+ DownloaderDelegate, |
+ UnpackDelegate> { |
InstallerWindowController* installerWindowController_; |
AuthorizedInstall* authorizedInstall_; |
+ BOOL preventTermination_; |
} |
@property(strong) NSWindow* window; |
+- (void)exit; |
@end |
@implementation AppDelegate |
@@ -28,6 +38,7 @@ |
// Sets up the main window and begins the downloading process. |
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { |
// TODO: fix UI not loading until after asking for authorization. |
+ window_.delegate = self; |
installerWindowController_ = |
[[InstallerWindowController alloc] initWithWindow:window_]; |
authorizedInstall_ = [[AuthorizedInstall alloc] init]; |
@@ -41,26 +52,54 @@ |
- (void)applicationWillTerminate:(NSNotification*)aNotification { |
} |
-- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { |
+- (NSApplicationTerminateReply)applicationShouldTerminate: |
+ (NSApplication*)sender { |
+ return preventTermination_ ? NSTerminateCancel : NSTerminateNow; |
+} |
+ |
+// This function effectively takes the place of |
+// applicationShouldTerminateAfterLastWindowClosed: to make sure that the |
+// application does correctly terminate after closing the installer, but does |
+// not terminate when we call orderOut: to hide the installer during its |
+// tear-down steps. |
+- (BOOL)windowShouldClose:(id)sender { |
+ [self exit]; |
return YES; |
} |
+- (void)exit { |
+ preventTermination_ = NO; |
+ [NSApp terminate:nil]; |
+} |
+ |
- (void)startDownload { |
[installerWindowController_ updateStatusDescription:@"Initializing..."]; |
+ |
OmahaCommunication* omahaMessenger = [[OmahaCommunication alloc] init]; |
omahaMessenger.delegate = self; |
- |
[omahaMessenger fetchDownloadURLs]; |
} |
-- (void)onOmahaSuccessWithURLs:(NSArray*)URLs { |
+- (void)onLoadInstallationToolFailure { |
+ NSError* loadToolError = [NSError |
+ errorForAlerts:@"Could not load installion tool" |
+ withDescription: |
+ @"Your Chrome Installer may be corrupted. Download and try again." |
+ isRecoverable:NO]; |
+ [self displayError:loadToolError]; |
+} |
+ |
+- (void)omahaCommunication:(OmahaCommunication*)messenger |
+ onSuccess:(NSArray*)URLs { |
[installerWindowController_ updateStatusDescription:@"Downloading..."]; |
+ |
Downloader* download = [[Downloader alloc] init]; |
download.delegate = self; |
- [download downloadChromeImageToDownloadsDirectory:[URLs firstObject]]; |
+ [download downloadChromeImageFrom:[URLs firstObject]]; |
} |
-- (void)onOmahaFailureWithError:(NSError*)error { |
+- (void)omahaCommunication:(OmahaCommunication*)messenger |
+ onFailure:(NSError*)error { |
NSError* networkError = |
[NSError errorForAlerts:@"Network Error" |
withDescription:@"Could not connect to Chrome server." |
@@ -70,24 +109,25 @@ |
// Bridge method from Downloader to InstallerWindowController. Allows Downloader |
// to update the progressbar without having direct access to any UI obejcts. |
-- (void)didDownloadData:(double)downloadProgressPercentage { |
- [installerWindowController_ |
- updateDownloadProgress:(double)downloadProgressPercentage]; |
+- (void)downloader:(Downloader*)download percentProgress:(double)percentage { |
+ [installerWindowController_ updateDownloadProgress:(double)percentage]; |
} |
-- (void)downloader:(Downloader*)download |
- onDownloadSuccess:(NSURL*)diskImagePath { |
- [installerWindowController_ updateStatusDescription:@"Done."]; |
+- (void)downloader:(Downloader*)download onSuccess:(NSURL*)diskImageURL { |
+ [installerWindowController_ updateStatusDescription:@"Installing..."]; |
[installerWindowController_ enableLaunchButton]; |
// TODO: Add unpacking step here and pass the path to the app bundle inside |
// the mounted disk image path to startInstall. Currently passing hardcoded |
// path to preunpacked app bundle. |
//[authorizedInstall_ |
// startInstall:@"$HOME/Downloads/Google Chrome.app"]; |
+ |
+ Unpacker* unpacker = [[Unpacker alloc] init]; |
+ unpacker.delegate = self; |
+ [unpacker mountDMGFromURL:diskImageURL]; |
} |
-- (void)downloader:(Downloader*)download |
- onDownloadFailureWithError:(NSError*)error { |
+- (void)downloader:(Downloader*)download onFailure:(NSError*)error { |
NSError* downloadError = |
[NSError errorForAlerts:@"Download Failure" |
withDescription:@"Unable to download Google Chrome." |
@@ -95,24 +135,100 @@ |
[self displayError:downloadError]; |
} |
-- (void)onLoadInstallationToolFailure { |
- NSError* loadToolError = [NSError |
- errorForAlerts:@"Could not load installion tool" |
- withDescription: |
- @"Your Chrome Installer may be corrupted. Download and try again." |
- isRecoverable:NO]; |
- [self displayError:loadToolError]; |
+- (void)unpacker:(Unpacker*)unpacker onMountSuccess:(NSString*)tempAppPath { |
+ SecStaticCodeRef diskStaticCode; |
+ SecRequirementRef diskRequirement; |
+ // TODO: flush out error handling more |
+ OSStatus oserror; |
+ oserror = SecStaticCodeCreateWithPath( |
+ (__bridge CFURLRef)[NSURL fileURLWithPath:tempAppPath isDirectory:NO], |
+ kSecCSDefaultFlags, &diskStaticCode); |
+ if (oserror != errSecSuccess) |
+ NSLog(@"code %d", oserror); |
+ // TODO: add in a more specific code sign requirement |
+ oserror = |
+ SecRequirementCreateWithString((CFStringRef) @"anchor apple generic", |
+ kSecCSDefaultFlags, &diskRequirement); |
+ if (oserror != errSecSuccess) |
+ NSLog(@"requirement %d", oserror); |
+ oserror = SecStaticCodeCheckValidity(diskStaticCode, kSecCSDefaultFlags, |
+ diskRequirement); |
+ if (oserror != errSecSuccess) |
+ NSLog(@"static code %d", oserror); |
+ |
+ // Calling this function will change the progress bar into an indeterminate |
+ // one. We won't need to update the progress bar any more after this point. |
+ [installerWindowController_ updateDownloadProgress:-1.0]; |
+ // By disabling closing the window or quitting, we can tell the user that |
+ // closing the application at this point is not a good idea. |
+ window_.styleMask &= ~NSClosableWindowMask; |
+ preventTermination_ = YES; |
+ |
+ // TODO: move the below code into AuthorizedInstall |
+ NSString* chromeInApplicationsFolder = @"/Applications/Google Chromo.app"; |
+ |
+ NSError* error = nil; |
+ if ([[NSFileManager defaultManager] |
+ fileExistsAtPath:chromeInApplicationsFolder]) { |
+ [[NSFileManager defaultManager] moveItemAtPath:chromeInApplicationsFolder |
+ toPath:tempAppPath |
+ error:nil]; |
+ } |
+ if (![[NSFileManager defaultManager] moveItemAtPath:tempAppPath |
+ toPath:chromeInApplicationsFolder |
+ error:&error]) { |
+ NSLog(@"%@", error); |
+ } |
+ // TODO: move the above code into AuthorizedInstall |
+ |
+ [[NSWorkspace sharedWorkspace] |
+ launchApplicationAtURL:[NSURL fileURLWithPath:chromeInApplicationsFolder |
+ isDirectory:NO] |
+ options:NSWorkspaceLaunchDefault |
+ configuration:@{} |
+ error:&error]; |
+ if (error) { |
+ NSLog(@"Chrome failed to launch: %@", error); |
+ } |
+ |
+ // Begin teardown stuff! |
+ dispatch_async(dispatch_get_main_queue(), ^{ |
+ [window_ orderOut:nil]; |
+ }); |
+ |
+ [unpacker unmountDMG]; |
+} |
+ |
+- (void)unpacker:(Unpacker*)unpacker onMountFailure:(NSError*)error { |
+ NSError* extractError = |
+ [NSError errorForAlerts:@"Install Failure" |
+ withDescription:@"Unable to add Google Chrome to Applications." |
+ isRecoverable:NO]; |
+ [self displayError:extractError]; |
+} |
+ |
+- (void)unpacker:(Unpacker*)unpacker onUnmountSuccess:(NSString*)mountpath { |
+ NSLog(@"we're done here!"); |
+ [self exit]; |
+} |
+ |
+- (void)unpacker:(Unpacker*)unpacker onUnmountFailure:(NSError*)error { |
+ NSLog(@"error unmounting"); |
+ // NOTE: since we are not deleting the temporary folder if the unmount fails, |
+ // we'll just leave it up to the computer to delete the temporary folder on |
+ // its own time, and to unmount the disk during a restart at some point. There |
+ // is no other work to be done in the mean time. |
+ [self exit]; |
} |
// Displays an alert on the main window using the contents of the passed in |
// error. |
- (void)displayError:(NSError*)error { |
NSAlert* alertForUser = [NSAlert alertWithError:error]; |
- |
dispatch_async(dispatch_get_main_queue(), ^{ |
[alertForUser beginSheetModalForWindow:window_ |
completionHandler:^(NSModalResponse returnCode) { |
- if (returnCode != [alertForUser quitButton]) { |
+ if (returnCode != [alertForUser quitResponse]) { |
[self startDownload]; |
} else { |
[NSApp terminate:nil]; |