0

mac: remove standalone installer app

This change removes //chrome/installer/mac/app. This directory contained a
cancelled project from 2016 to build a standalone installer and has never been
shipped. Since its tests are broken on 10.14, we can just delete it rather than
fixing those.

Bug: 892172
Change-Id: Ib3d9e9c072a12e66cb7863a3e40eed8a13e948d6
Reviewed-on: https://chromium-review.googlesource.com/c/1315954
Reviewed-by: John Budorick <jbudorick@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Sidney San Martín <sdy@chromium.org>
Commit-Queue: Elly Fong-Jones <ellyjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606514}
This commit is contained in:
Elly Fong-Jones
2018-11-08 17:31:12 +00:00
committed by Commit Bot
parent 2429b3291a
commit 351c48cef0
39 changed files with 0 additions and 2261 deletions

@ -11,7 +11,6 @@ group("mac") {
public_deps = [
":copies",
":make_signers",
"app:mac_installer_app",
]
}

@ -1,13 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_APPDELEGATE_H_
#define CHROME_INSTALLER_MAC_APP_APPDELEGATE_H_
#import <AppKit/AppKit.h>
@interface AppDelegate : NSObject<NSApplicationDelegate>
@end
#endif // CHROME_INSTALLER_MAC_APP_APPDELEGATE_H_

@ -1,245 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "AppDelegate.h"
#include <Security/Security.h>
#include "chrome/common/chrome_switches.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
completionHandler:
(void (^__nullable)(NSModalResponse returnCode))handler;
@end
@interface AppDelegate ()<NSWindowDelegate,
OmahaCommunicationDelegate,
DownloaderDelegate,
UnpackDelegate> {
InstallerWindowController* installerWindowController_;
AuthorizedInstall* authorizedInstall_;
BOOL preventTermination_;
}
@property(strong) NSWindow* window;
- (void)exit;
@end
@implementation AppDelegate
@synthesize window = window_;
// Sets up the main window and begins the downloading process.
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
// TODO: Despite what the code implies -- when the installer is run, the main
// window of the application is not visible until the user has taken action on
// the Authorization modal.
window_.delegate = self;
installerWindowController_ =
[[InstallerWindowController alloc] initWithWindow:window_];
authorizedInstall_ = [[AuthorizedInstall alloc] init];
if ([authorizedInstall_ loadInstallationTool]) {
[self startDownload];
} else {
[self onLoadInstallationToolFailure];
}
}
- (void)applicationWillTerminate:(NSNotification*)aNotification {
}
- (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. If the user quits the application, the below delegate
// method gets called. However, when orderOut is called, the below delegate
// method does not get called.
- (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)onLoadInstallationToolFailure {
NSError* loadToolError = [NSError
errorForAlerts:@"Internal Error"
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 downloadChromeImageFrom:[URLs firstObject]];
}
- (void)omahaCommunication:(OmahaCommunication*)messenger
onFailure:(NSError*)error {
NSError* networkError =
[NSError errorForAlerts:@"Network Error"
withDescription:@"Could not connect to Chrome server."
isRecoverable:YES];
[self displayError:networkError];
}
// Bridge method from Downloader to InstallerWindowController. Allows Downloader
// to update the progressbar without having direct access to any UI obejcts.
- (void)downloader:(Downloader*)download percentProgress:(double)percentage {
[installerWindowController_ updateDownloadProgress:(double)percentage];
}
- (void)downloader:(Downloader*)download onSuccess:(NSURL*)diskImageURL {
[installerWindowController_ updateStatusDescription:@"Installing..."];
[installerWindowController_ enableLaunchButton];
Unpacker* unpacker = [[Unpacker alloc] init];
unpacker.delegate = self;
[unpacker mountDMGFromURL:diskImageURL];
}
- (void)downloader:(Downloader*)download onFailure:(NSError*)error {
NSError* downloadError =
[NSError errorForAlerts:@"Download Failure"
withDescription:@"Unable to download Google Chrome."
isRecoverable:NO];
[self displayError:downloadError];
}
- (void)unpacker:(Unpacker*)unpacker onMountSuccess:(NSString*)tempAppPath {
SecStaticCodeRef diskStaticCode;
SecRequirementRef diskRequirement;
// TODO: Include some better error handling below than NSLog
OSStatus oserror;
oserror = SecStaticCodeCreateWithPath(
(__bridge CFURLRef)[NSURL fileURLWithPath:tempAppPath isDirectory:NO],
kSecCSDefaultFlags, &diskStaticCode);
if (oserror != errSecSuccess)
NSLog(@"code %d", oserror);
// TODO: The below requirement is too general as most signed entities have the
// below requirement; replace it with something adequately specific.
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;
NSString* chromeInApplicationsFolder =
[authorizedInstall_ startInstall:tempAppPath];
NSMutableArray* installerSettings = [[NSMutableArray alloc] init];
if ([installerWindowController_ isUserMetricsChecked])
[installerSettings
addObject:[NSString stringWithUTF8String:switches::kEnableUserMetrics]];
if ([installerWindowController_ isDefaultBrowserChecked])
[installerSettings
addObject:[NSString
// NOTE: the |kMakeDefaultBrowser| constant used as a
// command-line switch here only will apply at a user
// level, since the application itself is not running with
// privileges. grt@ suggested this constant should be
// renamed |kMakeDefaultBrowserforUser|.
stringWithUTF8String:switches::kMakeDefaultBrowser]];
NSError* error = nil;
[[NSWorkspace sharedWorkspace]
launchApplicationAtURL:[NSURL fileURLWithPath:chromeInApplicationsFolder
isDirectory:NO]
options:NSWorkspaceLaunchDefault
configuration:@{
NSWorkspaceLaunchConfigurationArguments : installerSettings
}
error:&error];
if (error) {
NSLog(@"Chrome failed to launch: %@", error);
}
// Begin teardown step!
dispatch_async(dispatch_get_main_queue(), ^{
[window_ orderOut:nil];
});
[unpacker unmountDMG];
}
- (void)unpacker:(Unpacker*)unpacker onMountFailure:(NSError*)error {
NSError* extractError =
[NSError errorForAlerts:@"Install Error"
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 quitResponse]) {
[self startDownload];
} else {
[NSApp terminate:nil];
}
}];
});
}
@end

@ -1,23 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_AUTHORIZEDINSTALL_H_
#define CHROME_INSTALLER_MAC_APP_AUTHORIZEDINSTALL_H_
#import <Foundation/Foundation.h>
#import <Security/Security.h>
@interface AuthorizedInstall : NSObject
// Attempts to gain elevated permissions, then starts the subprocess with the
// appropriate level of privilege.
- (BOOL)loadInstallationTool;
// Signals the tool to begin the installation. Returns the path to the
// installed app.
- (NSString*)startInstall:(NSString*)appBundlePath;
@end
#endif // CHROME_INSTALLER_MAC_APP_AUTHORIZEDINSTALL_H_

@ -1,131 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "AuthorizedInstall.h"
@interface AuthorizedInstall () {
NSFileHandle* communicationFile_;
NSString* destinationAppBundlePath_;
}
@end
@implementation AuthorizedInstall
// Does the setup needed to authorize a subprocess to run as root.
- (OSStatus)setUpAuthorization:(AuthorizationRef*)authRef {
OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
kAuthorizationFlagDefaults, authRef);
AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights rights = {1, &items};
AuthorizationFlags flags =
kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
status = AuthorizationCopyRights(*authRef, &rights, NULL, flags, NULL);
return status;
}
// Starts up the proccess with privileged permissions.
- (void)startPrivilegedTool:(const char*)toolPath
withArguments:(const char**)args
authorization:(AuthorizationRef)authRef
status:(OSStatus)status {
if (status != errAuthorizationSuccess)
return;
FILE* file;
// AuthorizationExecuteWithPrivileges is deprecated in macOS 10.7, but no good
// replacement exists. https://crbug.com/593133.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
status = AuthorizationExecuteWithPrivileges(
authRef, toolPath, kAuthorizationFlagDefaults, (char* const*)args, &file);
#pragma clang diagnostic pop
communicationFile_ = [[NSFileHandle alloc] initWithFileDescriptor:fileno(file)
closeOnDealloc:YES];
}
// Starts up same proccess as above without privileged permissions.
- (void)startUnprivilegedTool:(NSString*)toolPath withArguments:(NSArray*)args {
NSPipe* pipe = [NSPipe pipe];
NSTask* task = [[NSTask alloc] init];
[task setArguments:args];
[task setLaunchPath:toolPath];
[task setStandardInput:pipe];
[task launch];
communicationFile_ = [pipe fileHandleForWriting];
}
// Determines which "Applications" folder to use based on authorization.
// There are three possible scenarios and two possible return values.
// 1) /Applications is returned if:
// a) The user authenticates the app.
// b) The user doesn't authenticate but is an admin.
// 2) $HOME/Applications is returned if:
// c) The user doesn't authenticate and is not an admin.
- (NSString*)getApplicationsFolder:(BOOL)isAuthorized {
NSFileManager* manager = [NSFileManager defaultManager];
NSArray* applicationDirectories = NSSearchPathForDirectoriesInDomains(
NSApplicationDirectory, NSLocalDomainMask, YES);
if (isAuthorized ||
[manager isWritableFileAtPath:applicationDirectories.firstObject]) {
return applicationDirectories.firstObject;
} else {
NSString* usersApplicationsDirectory =
[NSString pathWithComponents:@[ NSHomeDirectory(), @"Applications" ]];
if (![manager fileExistsAtPath:usersApplicationsDirectory]) {
[manager createDirectoryAtPath:usersApplicationsDirectory
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
return usersApplicationsDirectory;
}
}
- (BOOL)loadInstallationTool {
AuthorizationRef authRef = NULL;
OSStatus status = [self setUpAuthorization:&authRef];
BOOL isAuthorized = (status == errAuthorizationSuccess);
NSString* toolPath =
[[NSBundle mainBundle] pathForResource:@"copy_to_disk" ofType:@"sh"];
NSFileManager* manager = [NSFileManager defaultManager];
if (![manager fileExistsAtPath:toolPath]) {
return false;
}
NSString* applicationsDirectory = [self getApplicationsFolder:isAuthorized];
destinationAppBundlePath_ = [NSString pathWithComponents: @[
applicationsDirectory, @"Google Chrome.app"]];
if (isAuthorized) {
const char* args[] = {[applicationsDirectory UTF8String], NULL};
[self startPrivilegedTool:[toolPath UTF8String]
withArguments:args
authorization:authRef
status:status];
} else {
NSArray* args = @[ applicationsDirectory ];
[self startUnprivilegedTool:toolPath withArguments:args];
}
AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
return true;
}
// Sends a message to the tool's stdin. The tool is using 'read' to wait for
// input. 'read' adds to its buffer until it receives a newline to continue so
// append '\n' to the message to end the read.
- (void)sendMessageToTool:(NSString*)message {
[communicationFile_ writeData:[[message stringByAppendingString:@"\n"]
dataUsingEncoding:NSUTF8StringEncoding]];
}
- (NSString*)startInstall:(NSString*)appBundlePath {
[self sendMessageToTool:appBundlePath];
return destinationAppBundlePath_;
}
@end

@ -1,102 +0,0 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/mac/rules.gni")
import("//build/util/branding.gni")
import("//testing/test.gni")
source_set("mac_installer_base") {
visibility = [ ":*" ]
sources = [
"Downloader.h",
"Downloader.m",
"NSAlert+ChromeInstallerAdditions.h",
"NSAlert+ChromeInstallerAdditions.m",
"NSError+ChromeInstallerAdditions.h",
"NSError+ChromeInstallerAdditions.m",
"OmahaCommunication.h",
"OmahaCommunication.m",
"OmahaXMLParser.h",
"OmahaXMLParser.m",
"OmahaXMLRequest.h",
"OmahaXMLRequest.m",
"SystemInfo.h",
"SystemInfo.m",
"Unpacker.h",
"Unpacker.m",
]
public_configs = [ "//build/config/compiler:enable_arc" ]
}
mac_app_bundle("mac_installer_app") {
output_name = "$chrome_product_installer_full_name"
info_plist = "Info.plist"
extra_substitutions = [
"PRODUCT_INSTALLER_FULLNAME=$chrome_product_installer_full_name",
"CHROMIUM_BUNDLE_ID=$chrome_mac_bundle_id",
]
sources = [
"AppDelegate.h",
"AppDelegate.mm",
"AuthorizedInstall.h",
"AuthorizedInstall.m",
"InstallerWindowController.h",
"InstallerWindowController.m",
"main.m",
]
deps = [
":mac_installer_base",
":mac_installer_resources",
":mac_installer_xibs",
"//chrome/common:constants",
]
libs = [
"Cocoa.framework",
"DiskArbitration.framework",
"Security.framework",
]
}
mac_xib_bundle_data("mac_installer_xibs") {
sources = [
"MainMenu.xib",
]
}
bundle_data("mac_installer_resources") {
sources = [
"copy_to_disk.sh",
]
outputs = [
"{{bundle_resources_dir}}/copy_to_disk.sh",
]
}
test("mac_installer_unittests") {
sources = [
"testing/OmahaXMLRequest_test.mm",
"testing/SystemInfo_test.mm",
"testing/Unpacker_test.mm",
]
deps = [
":mac_installer_base",
"//base:base",
"//base/test:run_all_unittests",
"//chrome/common:constants",
"//testing/gtest:gtest",
]
libs = [
"Cocoa.framework",
"DiskArbitration.framework",
"Security.framework",
]
data = [
"//chrome/test/data/mac_installer/requestCheck.dtd",
"//chrome/test/data/mac_installer/requestSample.xml",
"//chrome/test/data/mac_installer/responseExample.xml",
"//chrome/test/data/mac_installer/test-dmg.dmg",
]
}

@ -1,26 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_DOWNLOADER_H_
#define CHROME_INSTALLER_MAC_APP_DOWNLOADER_H_
#import <Foundation/Foundation.h>
@class Downloader;
@protocol DownloaderDelegate
- (void)downloader:(Downloader*)download percentProgress:(double)percentage;
- (void)downloader:(Downloader*)download onSuccess:(NSURL*)diskImageURL;
- (void)downloader:(Downloader*)download onFailure:(NSError*)error;
@end
@interface Downloader : NSObject<NSURLSessionDownloadDelegate>
@property(nonatomic, assign) id<DownloaderDelegate> delegate;
// Downloads Chrome from |chromeImageURL| to the local hard drive.
- (void)downloadChromeImageFrom:(NSURL*)chromeImageURL;
@end
#endif // CHROME_INSTALLER_MAC_APP_DOWNLOADER_H_

@ -1,51 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "Downloader.h"
#include <assert.h>
@implementation Downloader
@synthesize delegate = delegate_;
// Downloads contents of chromeURL to downloads folders and delegates the work
// to the DownloadDelegate class.
- (void)downloadChromeImageFrom:(NSURL*)chromeImageURL {
NSURLSession* session =
[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration
defaultSessionConfiguration]
delegate:self
delegateQueue:nil];
[[session downloadTaskWithURL:chromeImageURL] resume];
[session finishTasksAndInvalidate];
}
// Provides updates to download progress.
- (void)URLSession:(NSURLSession*)session
downloadTask:(NSURLSessionDownloadTask*)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
double downloadProgressPercentage =
(double)totalBytesWritten / totalBytesExpectedToWrite * 100.0;
[delegate_ downloader:self percentProgress:downloadProgressPercentage];
}
- (void)URLSession:(NSURLSession*)session
downloadTask:(NSURLSessionDownloadTask*)downloadTask
didFinishDownloadingToURL:(NSURL*)location {
assert([location isFileURL]);
[delegate_ downloader:self onSuccess:location];
}
- (void)URLSession:(NSURLSession*)session
task:(NSURLSessionTask*)task
didCompleteWithError:(NSError*)error {
if (error) {
[delegate_ downloader:self onFailure:error];
}
}
@end

@ -1,54 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>${CHROMIUM_BUNDLE_ID}.installer</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_INSTALLER_FULLNAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>${CHROMIUM_MIN_SYSTEM_VERSION}</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Google. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>google.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
<key>gvt1.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>

@ -1,21 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_INSTALLERWINDOWCONTROLLER_H_
#define CHROME_INSTALLER_MAC_APP_INSTALLERWINDOWCONTROLLER_H_
#import <AppKit/AppKit.h>
@interface InstallerWindowController : NSWindowController
- (id)initWithWindow:(NSWindow*)window;
- (void)updateStatusDescription:(NSString*)text;
- (void)updateDownloadProgress:(double)progressPercent;
- (void)enableLaunchButton;
- (BOOL)isUserMetricsChecked;
- (BOOL)isDefaultBrowserChecked;
@end
#endif // CHROME_INSTALLER_MAC_APP_INSTALLERWINDOWCONTROLLER_H_

@ -1,150 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "InstallerWindowController.h"
#import "AppDelegate.h"
@interface InstallerWindowController () {
NSButton* importButton_;
NSButton* defaultBrowserButton_;
NSButton* optInButton_;
NSButton* launchButton_;
NSTextField* statusDescription_;
NSTextField* downloadProgressDescription_;
NSProgressIndicator* progressBar_;
}
@end
@implementation InstallerWindowController
// Simplify styling and naming buttons.
- (void)stylizeButton:(NSButton*)button withTitle:(NSString*)title {
button.buttonType = NSSwitchButton;
button.bezelStyle = NSRoundedBezelStyle;
button.title = title;
}
// Positions and stylizes buttons.
- (void)setUpButtons {
importButton_ = [[NSButton alloc] initWithFrame:NSMakeRect(30, 20, 300, 25)];
[self stylizeButton:importButton_
withTitle:@"Import from... Wait import what?"];
defaultBrowserButton_.state = NSOnState;
defaultBrowserButton_ =
[[NSButton alloc] initWithFrame:NSMakeRect(30, 45, 300, 25)];
[self stylizeButton:defaultBrowserButton_
withTitle:@"Make Chrome the default browser."];
optInButton_ = [[NSButton alloc] initWithFrame:NSMakeRect(30, 70, 300, 25)];
[self stylizeButton:optInButton_ withTitle:@"Say yes to UMA."];
launchButton_ = [[NSButton alloc] initWithFrame:NSMakeRect(310, 6, 100, 50)];
launchButton_.buttonType = NSPushOnPushOffButton;
launchButton_.bezelStyle = NSRoundedBezelStyle;
launchButton_.title = @"Launch";
[launchButton_ setEnabled:NO];
[launchButton_ setAction:@selector(launchButtonClicked)];
}
// Simplfy styling NSTextField objects.
- (void)stylizeTextField:(NSTextField*)textField
withDescription:(NSString*)description {
textField.backgroundColor = NSColor.clearColor;
textField.textColor = NSColor.blackColor;
textField.stringValue = description;
textField.bezeled = NO;
textField.editable = NO;
}
// Positions and stylizes textfields.
- (void)setUpTextfields {
statusDescription_ =
[[NSTextField alloc] initWithFrame:NSMakeRect(20, 95, 300, 20)];
[self stylizeTextField:statusDescription_
withDescription:@"Working on it! While you're waiting..."];
downloadProgressDescription_ =
[[NSTextField alloc] initWithFrame:NSMakeRect(20, 160, 300, 20)];
[self stylizeTextField:downloadProgressDescription_
withDescription:@"Downloading... "];
}
// Positions and stylizes the progressbar for download and install.
- (void)setUpProgressBar {
progressBar_ =
[[NSProgressIndicator alloc] initWithFrame:NSMakeRect(15, 125, 400, 50)];
progressBar_.indeterminate = NO;
progressBar_.style = NSProgressIndicatorBarStyle;
progressBar_.maxValue = 100.0;
progressBar_.minValue = 0.0;
progressBar_.doubleValue = 0.0;
}
// Positions the main window and adds the rest of the UI elements to it.
// Prevents resizing the window so that the absolute position will look the same
// on all computers. Window is hidden until all positioning is finished.
- (id)initWithWindow:(NSWindow*)window {
if (self = [super initWithWindow:window]) {
[window setFrame:NSMakeRect(0, 0, 430, 220) display:YES];
[window center];
[window setStyleMask:[window styleMask] & ~NSResizableWindowMask];
[self setUpButtons];
[self setUpProgressBar];
[self setUpTextfields];
[window.contentView addSubview:importButton_];
[window.contentView addSubview:defaultBrowserButton_];
[window.contentView addSubview:optInButton_];
[window.contentView addSubview:launchButton_];
[window.contentView addSubview:progressBar_];
[window.contentView addSubview:statusDescription_];
[window.contentView addSubview:downloadProgressDescription_];
[NSApp activateIgnoringOtherApps:YES];
[window makeKeyAndOrderFront:self];
}
return self;
}
- (void)updateStatusDescription:(NSString*)text {
// TODO: This method somehow causes ghosting of the previous string's contents
// after a redraw. The below line of code is a temporary hack to clear the
// ghosting behavior, but it should be replaced with a legitimate bug fix.
downloadProgressDescription_.stringValue = @"";
downloadProgressDescription_.stringValue = text;
}
- (void)updateDownloadProgress:(double)progressPercent {
if (progressPercent > 0.0) {
progressBar_.doubleValue = progressPercent;
} else {
// After the progress bar is made indeterminate, it will not need to track
// determinate progress any more. Therefore, there is nothing implemented to
// set indeterminate to NO.
progressBar_.doubleValue = 0.0;
progressBar_.indeterminate = YES;
[progressBar_ startAnimation:nil];
}
}
- (void)enableLaunchButton {
[launchButton_ setEnabled:YES];
}
- (void)launchButtonClicked {
// TODO: Launch the app and start ejecting disk.
[NSApp terminate:nil];
}
- (BOOL)isUserMetricsChecked {
return optInButton_.state == NSOnState;
}
- (BOOL)isDefaultBrowserChecked {
return defaultBrowserButton_.state == NSOnState;
}
@end

@ -1,294 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
<connections>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="Google Chrome Installer" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Google Chrome Installer" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide Google Chrome Installer" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit Google Chrome Installer" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="Google Chrome Installer Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="Google Chrome Installer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="430" height="220"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</window>
</objects>
</document>

@ -1,17 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_NSALERT_CHROMEINSTALLERADDITIONS_H_
#define CHROME_INSTALLER_MAC_APP_NSALERT_CHROMEINSTALLERADDITIONS_H_
#import <AppKit/AppKit.h>
typedef NSInteger NSModalResponse;
@interface NSAlert (ChromeInstallerAdditions)
// Allows the caller to determine whether to determine the app's quit button was
// pressed or not.
- (NSModalResponse)quitResponse;
@end
#endif // CHROME_INSTALLER_MAC_APP_NSALERT_CHROMEINSTALLERADDITIONS_H_

@ -1,15 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "NSAlert+ChromeInstallerAdditions.h"
@implementation NSAlert (ChromeInstallerAdditions)
// In the one-button scenario, the button would be just "Quit." In the
// two-button scenario, the first button would allow the user to "Retry" and
// the second button would provide the "Quit" option.
- (NSModalResponse)quitResponse {
return ([[self buttons] count] == 1) ? NSAlertFirstButtonReturn
: NSAlertSecondButtonReturn;
}
@end

@ -1,18 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_NSERROR_CHROMEINSTALLERADDITIONS_H_
#define CHROME_INSTALLER_MAC_APP_NSERROR_CHROMEINSTALLERADDITIONS_H_
#import <Foundation/Foundation.h>
@interface NSError (ChromeInstallerAdditions)
// Creates a custom error object to be used to create an alert to be shown to
// the user.
+ (NSError*)errorForAlerts:(NSString*)message
withDescription:(NSString*)description
isRecoverable:(BOOL)recoverable;
@end
#endif // CHROME_INSTALLER_MAC_APP_NSERROR_CHROMEINSTALLERADDITIONS_H_

@ -1,27 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "NSError+ChromeInstallerAdditions.h"
@implementation NSError (ChromeInstallerAdditions)
+ (NSError*)errorForAlerts:(NSString*)message
withDescription:(NSString*)description
isRecoverable:(BOOL)recoverable {
NSArray* options = @[];
if (recoverable) {
options = @[ @"Try Again", @"Quit" ];
} else {
options = @[ @"Quit" ];
}
NSDictionary* errorContents = @{
NSLocalizedDescriptionKey : NSLocalizedString(message, nil),
NSLocalizedRecoveryOptionsErrorKey : options,
NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(description, nil)
};
return [NSError errorWithDomain:@"ChromeErrorDomain"
code:-1
userInfo:errorContents];
}
@end

@ -1,6 +0,0 @@
ellyjones@chromium.org
ivanhernandez@google.com
mark@chromium.org
zengster@google.com
# COMPONENT: Internals>Installer

@ -1,31 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_OMAHACOMMUNICATION_H_
#define CHROME_INSTALLER_MAC_APP_OMAHACOMMUNICATION_H_
#import <Foundation/Foundation.h>
@class OmahaCommunication;
@protocol OmahaCommunicationDelegate
- (void)omahaCommunication:(OmahaCommunication*)messenger
onSuccess:(NSArray*)URLs;
- (void)omahaCommunication:(OmahaCommunication*)messenger
onFailure:(NSError*)error;
@end
@interface OmahaCommunication : NSObject<NSURLSessionDataDelegate>
@property(nonatomic, copy) NSXMLDocument* requestXMLBody;
@property(nonatomic, assign) id<OmahaCommunicationDelegate> delegate;
- (id)init;
- (id)initWithBody:(NSXMLDocument*)xmlBody;
// Asks the Omaha servers for the most updated version of Chrome.
- (void)fetchDownloadURLs;
@end
#endif // CHROME_INSTALLER_MAC_APP_OMAHACOMMUNICATION_H_

@ -1,68 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "OmahaCommunication.h"
#import "OmahaXMLRequest.h"
#import "OmahaXMLParser.h"
// TODO: Turn the below string into a command line flag for testing.
static NSString* const omahaURLPath =
@"https://tools.google.com/service/update2";
@interface NSURLSession ()
- (NSURLSessionDataTask*)dataTaskWithRequest:(NSURLRequest*)request
completionHandler:
(void (^)(NSData* data,
NSURLResponse* response,
NSError* error))completionHandler;
@end
@implementation OmahaCommunication
@synthesize requestXMLBody = requestXMLBody_;
@synthesize delegate = delegate_;
- (id)init {
return [self initWithBody:[OmahaXMLRequest createXMLRequestBody]];
}
- (id)initWithBody:(NSXMLDocument*)xmlBody {
if ((self = [super init])) {
requestXMLBody_ = xmlBody;
}
return self;
}
- (void)fetchDownloadURLs {
// Forming the request
NSURL* requestURL = [NSURL URLWithString:omahaURLPath];
NSMutableURLRequest* request =
[NSMutableURLRequest requestWithURL:requestURL];
[request addValue:@"text/xml" forHTTPHeaderField:@"Content-Type"];
NSData* requestBody =
[[requestXMLBody_ XMLString] dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody = requestBody;
request.HTTPMethod = @"POST";
// Sending the request
[[[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData* data, NSURLResponse* response,
NSError* error) {
NSArray* completeURLs = nil;
if (!error) {
completeURLs = [OmahaXMLParser parseXML:data error:&error];
}
// Deals with errors both from the network error and the
// parsing error, as the user only needs to know there was a problem
// talking with the Google Update server.
if (error) {
[delegate_ omahaCommunication:self onFailure:error];
} else {
[delegate_ omahaCommunication:self onSuccess:completeURLs];
}
}] resume];
}
@end

@ -1,19 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_OMAHAXMLPARSER_H_
#define CHROME_INSTALLER_MAC_APP_OMAHAXMLPARSER_H_
#import <Foundation/Foundation.h>
@interface OmahaXMLParser : NSObject
// Parses the XML body from Omaha's HTTP response and extracts the URLs and name
// of the Chrome disk image. Then, returns an array with all the URLs
// concatenated with the filename.
+ (NSArray*)parseXML:(NSData*)omahaResponseXML error:(NSError**)error;
@end
#endif // CHROME_INSTALLER_MAC_APP_OMAHAXMLPARSER_H_

@ -1,78 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "OmahaXMLParser.h"
@interface OmahaXMLParser ()<NSXMLParserDelegate>
@end
@implementation OmahaXMLParser {
NSMutableArray* chromeIncompleteDownloadURLs_;
NSString* chromeImageFilename_;
}
// Sets up instance of NSXMLParser and calls on delegate methods to do actual
// parsing work.
+ (NSArray*)parseXML:(NSData*)omahaResponseXML error:(NSError**)error {
NSXMLParser* parser = [[NSXMLParser alloc] initWithData:omahaResponseXML];
OmahaXMLParser* omahaParser = [[OmahaXMLParser alloc] init];
[parser setDelegate:omahaParser];
if (![parser parse]) {
*error = [parser parserError];
return nil;
}
NSMutableArray* completeDownloadURLs = [[NSMutableArray alloc] init];
for (NSString* URL in omahaParser->chromeIncompleteDownloadURLs_) {
[completeDownloadURLs
addObject:[NSURL URLWithString:omahaParser->chromeImageFilename_
relativeToURL:[NSURL URLWithString:URL]]];
}
if ([completeDownloadURLs count] < 1) {
// TODO: The below error exists only so the caller of this method would
// catch the error created here. A better way to handle this is to make the
// error's contents inform what the installer will try next when it attempts
// to recover from an issue.
*error = [NSError errorWithDomain:@"ChromeErrorDomain" code:1 userInfo:nil];
return nil;
}
return completeDownloadURLs;
}
// Searches the XML data for the tag "URL" and the subsequent "codebase"
// attribute that indicates a URL follows. Copies each URL into an array.
// NOTE: The URLs in the XML file are incomplete. They need the filename
// appended to end. The second if statement checks for the tag "package" which
// contains the filename needed to complete the URLs.
- (void)parser:(NSXMLParser*)parser
didStartElement:(NSString*)elementName
namespaceURI:(NSString*)namespaceURI
qualifiedName:(NSString*)qName
attributes:(NSDictionary*)attributeDict {
if ([elementName isEqualToString:@"url"]) {
if (!chromeIncompleteDownloadURLs_) {
chromeIncompleteDownloadURLs_ = [[NSMutableArray alloc] init];
}
NSString* extractedURL = [attributeDict objectForKey:@"codebase"];
[chromeIncompleteDownloadURLs_ addObject:extractedURL];
}
if ([elementName isEqualToString:@"package"]) {
chromeImageFilename_ =
[[NSString alloc] initWithString:[attributeDict objectForKey:@"name"]];
}
}
// If either component of the URL is empty then the complete URL cannot
// be generated so both variables are set to nil to flag errors.
- (void)parserDidEndDocument:(NSXMLParser*)parser {
if (!chromeIncompleteDownloadURLs_ || !chromeImageFilename_) {
chromeIncompleteDownloadURLs_ = nil;
chromeImageFilename_ = nil;
}
}
@end

@ -1,17 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_OMAHAXMLREQUEST_H_
#define CHROME_INSTALLER_MAC_APP_OMAHAXMLREQUEST_H_
#import <Foundation/Foundation.h>
@interface OmahaXMLRequest : NSObject
// Creates the body of the request being prepared to send to Omaha.
+ (NSXMLDocument*)createXMLRequestBody;
@end
#endif // CHROME_INSTALLER_MAC_APP_OMAHAXMLREQUEST_H_

@ -1,73 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "OmahaXMLRequest.h"
#include "SystemInfo.h"
@implementation OmahaXMLRequest : NSObject
+ (NSXMLElement*)createElementWithName:(NSString*)name {
return [[NSXMLElement alloc] initWithName:name];
}
+ (void)forElement:(NSXMLElement*)element
AddAttribute:(NSString*)attribute
WithValue:(NSString*)value {
[element
addAttribute:[NSXMLNode attributeWithName:attribute stringValue:value]];
}
// borisv@ indicated that the OS version, platform, appid, and version are the
// user attributes that Omaha actually looks at. The other parameters are useful
// for logging purposes but otherwise not directly used.
+ (NSXMLDocument*)createXMLRequestBody {
// TODO: This protocol version number probably shouldn't be hard-coded. Check
// with borisv@ regarding changing protocol verions.
NSString* protocol = @"3.0";
NSString* platform = @"mac";
NSString* operatingSystem = [SystemInfo getOSVersion];
NSString* architecture = [SystemInfo getArch];
NSString* plat_arch =
[NSString stringWithFormat:@"%@_%@", operatingSystem, architecture];
NSString* appid = @"com.google.Chrome";
NSString* version = @"0.0.0.0";
NSString* language = @"en-us";
NSXMLElement* root = [OmahaXMLRequest createElementWithName:@"request"];
[OmahaXMLRequest forElement:root AddAttribute:@"protocol" WithValue:protocol];
NSXMLElement* osChild = [OmahaXMLRequest createElementWithName:@"os"];
[OmahaXMLRequest forElement:osChild
AddAttribute:@"platform"
WithValue:platform];
[OmahaXMLRequest forElement:osChild
AddAttribute:@"version"
WithValue:operatingSystem];
[OmahaXMLRequest forElement:osChild
AddAttribute:@"arch"
WithValue:architecture];
[OmahaXMLRequest forElement:osChild AddAttribute:@"sp" WithValue:plat_arch];
[root addChild:osChild];
NSXMLElement* appChild = [OmahaXMLRequest createElementWithName:@"app"];
[OmahaXMLRequest forElement:appChild AddAttribute:@"appid" WithValue:appid];
[OmahaXMLRequest forElement:appChild
AddAttribute:@"version"
WithValue:version];
[OmahaXMLRequest forElement:appChild AddAttribute:@"lang" WithValue:language];
[root addChild:appChild];
NSXMLElement* updateChildChild =
[OmahaXMLRequest createElementWithName:@"updatecheck"];
[appChild addChild:updateChildChild];
NSXMLDocument* requestXMLDocument =
[[NSXMLDocument alloc] initWithRootElement:root];
return requestXMLDocument;
}
@end

@ -1,177 +0,0 @@
# Chrome Installer for Mac -- the repository!
This Mac app installs Chrome on the machine it's run on. It's meant to be the
Mac app the user first downloads when they click 'Download Chrome' on a Mac
machine. After the user runs the app, Chrome would launch directly after
installation completes successfully.
## The 10,000 Foot View
The installer breaks down its task into the following steps:
1. __OmahaCommunication__: Ask Omaha for a URL of the most recent compatible
copy of the Google Chrome disk image in Google's servers.
2. __Downloader__: Download the disk image, via URL from OmahaCommunication.
3. __Unpacker__: Mount the disk image and extract the Chrome app bundle.
4. __AuthorizedInstall__: With privilege escalation if able, move the Chrome
app into the Applications folder, set permissions, and hand ownership of
the Chrome app to `root`.
5. Launch Chrome & close.
Each of the above modules are designed to carry one action before returning via
delegate method. All of these main steps occur on a primary working thread
(non-UI), with the exception of `AuthorizedInstall`, which makes use of an
authorized-if-able subprocess. If the user does not provide permission to
escalate privileges, `AuthorizedInstall` still does its job, but opts for the
User's Applications folder instead of the system Applications folder.
The OmahaXML* classes and SystemInfo class are simply classes to help
OmahaCommunication do its work.
The app's UI is made of a single window that has a determinate progress bar
during download, which turns into an indeterminate one during installation. The
options to set Chrome as a default browser and opt in for user metrics are made
available to the user before they launch Chrome. Once Chrome is ready to launch,
a Launch button will now be pressable -- this button will trigger Chrome to open
and the installer app to close its remaining window. In the background, the app
will take care of any tear-down tasks before exiting naturally.
We initialize the AuthorizedInstall class early in the life of the installer so
the user can immediately choose to authorize the installer for root installation.
The script consumes the authorization token (which expires in five minutes by
default) immediately, then waits until the installer has progressed to step 4
(above).
## The Class Breakdown
| Class | Role |
|----------------------------|----------------------------------------------------|
| AppDelegate | Controls the flow of the program |
| AuthorizedInstall | Attempts authorization to add Chrome to the Applications folder and adjust permissions as root |
| Downloader | Downloads GoogleChrome.dmg from Omaha servers |
| InstallerWindowController | Controls the user interface |
| OmahaCommunication | Talks with Omaha Servers to get URL of disk image |
| OmahaXMLParser | Extracts URLs from Omaha's XML response |
| OmahaXMLRequest | Creates an XML request to send to Omaha |
| SystemInfo | Provides system information to help craft the XML request for Omaha |
| Unpacker | Mounts the disk image and controls the temporary directory that abstracts the installer's file-manipulating activity from the user |
## The Future
Here lies a list of hopes and dreams:
* Implement resumable downloads.
* Add in adequate testing using a local test server.
* Include basic error recovery attempts -- say, if during a download a URL
does not provide a valid disk image, the installer can try re-downloading
the disk image from another URL.
* Manage potential conflicts, in the case that Google Chrome already exists in
the Applications folder when the installer is run.
* Trash the installer application after it has completed running.
## Diagram Appendix
### Task Flow
```
Exposed Errors Main Logic
+--------------------------------------+
| |
| Request authentication from users |
| |
+------------------+-------------------+
|
+------------------v-------------------+
| |
+--------+ Ask Omaha for appropriate Chrome app |
v | |
+------------------+-------------------+
Network Error |
+------------------v-------------------+
^ | |
+--------+ Parse the response from Omaha |
| |
+------------------+-------------------+
|
+------------------v-------------------+
| |
Download Error <-+ Download the Chrome disk image |
| |
+------------------+-------------------+
|
+------------------v-------------------+
| |
+--------+ Mount the disk image |
v | |
+------------------+-------------------+
Install Error |
+------------------v-------------------+
^ | |
+--------+ Install & Configure Chrome app |
| |
+------------------+-------------------+
|
+------------------v-------------------+
| |
| Unmount disk image +-> If unmount fails, system
| | restart can resolve this.
+------------------+-------------------+
|
+------------------v-------------------+
| |
Launch Error <-+ Launch Chrome |
| |
+--------------------------------------+
```
### Class Heirarchy
```
Users
^ +
| |
| |
+-------------------------------+-----v--------------------------------+
| |
| InstallerWindowController |
| |
+-------+------^------------+---^--------+----^---------+---------^----+
| | | | | | | |
| | | | | | | |
| | | | | | | |
+-------v------+------------v---+--------v----+---------v---------+----+
| |
| AppDelegate |
| |
+-------+------^------------+---^--------+----^---------+---------^----+
| | | | | | | |
| | | | | | | |
| | | | | | | |
+-------v------+-----+ +----v---+---+ +--v----+--+ +----v---------+----+
| | | | | | | |
| OmahaCommunication | | Downloader | | Unpacker | | AuthorizedInstall |
| | | | | | | |
+-------^------^-----+ +------------+ +----------+ +---------^---------+
| | |
| +------------+ |
| | |
+-------+---------+ +-------+--------+ +--------+--------+
| | | | | |
| OmahaXMLRequest | | OmahaXMLParser | | copy_to_disk.sh |
| | | | | |
+-------^---------+ +----------------+ +-----------------+
|
|
|
+-----+------+
| |
| SystemInfo |
| |
+------------+
```

@ -1,29 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_SYSTEMINFO_H_
#define CHROME_INSTALLER_MAC_APP_SYSTEMINFO_H_
#if !defined(__x86_64__)
#error "Your machine's system architecture may not be compatible with Chrome."
#endif
#import <Foundation/Foundation.h>
@interface SystemInfo : NSObject
// Gets the CPU architecture type of the client's system, which will be used
// when crafting the query to Omaha. This should return either "x84_64h" for
// systems running on Intel Haswell chips, "i486" for other Intel machines, or
// strings representing other CPU types ("amd", "pentium", and "i686", for
// example, are all possible; however, due to the above macro, the possible
// return strings are limited to either "x84_64h" or "i486").
+ (NSString*)getArch;
// Gets the operating system version of the client. This function may return
// values such as "10.11" or "10.10.5".
+ (NSString*)getOSVersion;
@end
#endif // CHROME_INSTALLER_MAC_APP_SYSTEMINFO_H_

@ -1,28 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <mach-o/arch.h>
#import "SystemInfo.h"
@implementation SystemInfo
+ (NSString*)getArch {
// NOTE: It seems the below function `NSGetLocalArchInfo` returns an
// arch->name that is either "x84_64h" or "i486".
const NXArchInfo* arch = NXGetLocalArchInfo();
NSString* archName = [NSString stringWithUTF8String:arch->name];
return archName;
}
+ (NSString*)getOSVersion {
NSDictionary* systemVersion =
[NSDictionary dictionaryWithContentsOfFile:
@"/System/Library/CoreServices/SystemVersion.plist"];
NSString* versionNumber = [systemVersion objectForKey:@"ProductVersion"];
return versionNumber;
}
@end

@ -1,30 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_MAC_APP_UNPACKER_H_
#define CHROME_INSTALLER_MAC_APP_UNPACKER_H_
#import <Foundation/Foundation.h>
@class Unpacker;
@protocol UnpackDelegate<NSObject>
- (void)unpacker:(Unpacker*)unpacker onMountSuccess:(NSString*)tempAppPath;
- (void)unpacker:(Unpacker*)unpacker onMountFailure:(NSError*)error;
- (void)unpacker:(Unpacker*)unpacker onUnmountSuccess:(NSString*)mountpath;
- (void)unpacker:(Unpacker*)unpacker onUnmountFailure:(NSError*)error;
@end
@interface Unpacker : NSObject
@property(nonatomic, assign) id<UnpackDelegate> delegate;
@property(nonatomic, copy) NSString* appPath;
// Mount a disk image at |fileURL|.
- (void)mountDMGFromURL:(NSURL*)fileURL;
// Unmount that same disk image.
- (void)unmountDMG;
@end
#endif // CHROME_INSTALLER_MAC_APP_UNPACKER_H_

@ -1,167 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "Unpacker.h"
#import <AppKit/AppKit.h>
#include <DiskArbitration/DiskArbitration.h>
#include <dispatch/dispatch.h>
#import "Downloader.h"
@interface Unpacker () {
NSURL* temporaryDirectoryURL_;
NSString* mountPath_;
NSTask* __weak mountTask_;
DASessionRef session_;
dispatch_queue_t unpack_dq_;
}
- (void)didFinishEjectingDisk:(DADiskRef)disk
withDissenter:(DADissenterRef)dissenter;
@end
static void eject_callback(DADiskRef disk,
DADissenterRef dissenter,
void* context) {
Unpacker* unpacker = (__bridge_transfer Unpacker*)context;
[unpacker didFinishEjectingDisk:disk withDissenter:dissenter];
}
static void unmount_callback(DADiskRef disk,
DADissenterRef dissenter,
void* context) {
if (dissenter) {
Unpacker* unpacker = (__bridge Unpacker*)context;
[unpacker didFinishEjectingDisk:disk withDissenter:dissenter];
} else {
DADiskEject(disk, kDADiskEjectOptionDefault, eject_callback, context);
}
}
@implementation Unpacker
@synthesize delegate = delegate_;
@synthesize appPath = appPath_;
- (void)cleanUp {
[mountTask_ terminate];
// It's not the end of the world if this temporary directory is not removed
// here. The directory will be deleted when the operating system itself
// decides to anyway.
[[NSFileManager defaultManager] removeItemAtURL:temporaryDirectoryURL_
error:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
// TODO: The failure delegate methods need to be revised to meaningfully deal
// with the errors (pipe in stderr / stdout to handle the error according to
// what the error was).
- (void)mountDMGFromURL:(NSURL*)fileURL {
NSError* error = nil;
temporaryDirectoryURL_ = [[NSFileManager defaultManager]
URLForDirectory:NSItemReplacementDirectory
inDomain:NSUserDomainMask
appropriateForURL:[NSURL fileURLWithPath:@"/" isDirectory:YES]
create:YES
error:&error];
if (error) {
[delegate_ unpacker:self onMountFailure:error];
return;
}
NSURL* temporaryDiskImageURL =
[temporaryDirectoryURL_ URLByAppendingPathComponent:@"GoogleChrome.dmg"];
mountPath_ = [[temporaryDirectoryURL_ URLByAppendingPathComponent:@"mnt"
isDirectory:YES] path];
[[NSFileManager defaultManager] createDirectoryAtPath:mountPath_
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error) {
[delegate_ unpacker:self onMountFailure:error];
return;
}
// If the user closes the app at any time, we make sure that the cleanUp
// function deletes the temporary folder we just created.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(cleanUp)
name:NSApplicationWillTerminateNotification
object:nil];
[[NSFileManager defaultManager] moveItemAtURL:fileURL
toURL:temporaryDiskImageURL
error:nil];
NSString* path = @"/usr/bin/hdiutil";
NSArray* args = @[
@"attach", temporaryDiskImageURL, @"-nobrowse", @"-noverify",
@"-mountpoint", mountPath_
];
NSTask* mountTask = [[NSTask alloc] init];
mountTask.launchPath = path;
mountTask.arguments = args;
mountTask.terminationHandler = ^void(NSTask* task) {
NSError* error = nil;
NSString* diskAppPath =
[NSString pathWithComponents:@[ mountPath_, @"Google Chrome.app" ]];
NSString* tempAppPath = [[temporaryDirectoryURL_
URLByAppendingPathComponent:@"Google Chrome.app"] path];
[[NSFileManager defaultManager] copyItemAtPath:diskAppPath
toPath:tempAppPath
error:&error];
if (error) {
[delegate_ unpacker:self onMountFailure:error];
} else {
[delegate_ unpacker:self onMountSuccess:tempAppPath];
}
};
mountTask_ = mountTask;
[mountTask launch];
}
- (void)unmountDMG {
session_ = DASessionCreate(nil);
unpack_dq_ =
dispatch_queue_create("com.google.chrome.unpack", DISPATCH_QUEUE_SERIAL);
DASessionSetDispatchQueue(session_, unpack_dq_);
DADiskRef child_disk = DADiskCreateFromVolumePath(
nil, session_,
(__bridge CFURLRef)[NSURL fileURLWithPath:mountPath_ isDirectory:YES]);
DADiskRef whole_disk = DADiskCopyWholeDisk(child_disk);
DADiskUnmount(whole_disk,
kDADiskUnmountOptionWhole | kDADiskUnmountOptionForce,
unmount_callback, (__bridge_retained void*)self);
CFRelease(whole_disk);
CFRelease(child_disk);
}
- (void)didFinishEjectingDisk:(DADiskRef)disk
withDissenter:(DADissenterRef)dissenter {
DASessionSetDispatchQueue(session_, NULL);
CFRelease(session_);
NSError* error = nil;
if (dissenter) {
DAReturn status = DADissenterGetStatus(dissenter);
error = [NSError
errorWithDomain:@"ChromeErrorDomain"
code:err_get_code(status)
userInfo:@{
NSLocalizedDescriptionKey :
(__bridge NSString*)DADissenterGetStatusString(dissenter)
}];
[delegate_ unpacker:self onUnmountFailure:error];
} else {
[self cleanUp];
[delegate_ unpacker:self onUnmountSuccess:mountPath_];
}
}
@end

@ -1,43 +0,0 @@
#!/bin/sh -p
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This script will be called by the installer application to copy Google
# Chrome.app into the proper /Applications folder. This script may run as root.
#
# When running as root, this script will be invoked with the real user ID set
# to the user's ID, but the effective user ID set to 0 (root). bash -p is
# used on the first line to prevent bash from setting the effective user ID to
# the real user ID (dropping root privileges).
# 'e': terminate if error arises
# 'u': raise an error if a variable isn't set
# 'o pipefail': set the return exit code to the last non-zero error code
set -euo pipefail
# Waits for the main app to pass the path to the app bundle inside the mounted
# disk image.
read -r SRC
DEST="${1}"
APPBUNDLENAME=$(basename "${SRC}")
FULL_DEST="${DEST}"/"${APPBUNDLENAME}"
# Starts the copy
# 'l': copy symlinks as symlinks
# 'r': recursive copy
# 'p': preserve permissions
# 't': preserve times
# 'q': quiet mode, so rynsc will only log to console if an error occurs
rsync -lrptq "${SRC}" "${DEST}"
# If this script is run as root, change ownership to root and set elevated
# permissions.
if [ "${EUID}" -eq 0 ] ; then
chown -Rh root:admin "${FULL_DEST}"
chmod -R a+rX,ug+w,o-w "${FULL_DEST}"
fi
exit 0

@ -1,9 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <AppKit/AppKit.h>
int main(int argc, const char* argv[]) {
return NSApplicationMain(argc, argv);
}

@ -1,36 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/installer/mac/app/OmahaXMLRequest.h"
#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/strings/sys_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
TEST(OmahaXMLRequestTest, CreateReturnsValidXML) {
NSXMLDocument* xml_body_ = [OmahaXMLRequest createXMLRequestBody];
ASSERT_TRUE(xml_body_);
base::FilePath path;
base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
path = path.AppendASCII("chrome/test/data/mac_installer/requestCheck.dtd");
NSString* requestDTDLocation = base::SysUTF8ToNSString(path.value());
NSData* requestDTDData = [NSData dataWithContentsOfFile:requestDTDLocation];
ASSERT_TRUE(requestDTDData);
NSError* error;
NSXMLDTD* requestXMLChecker =
[[NSXMLDTD alloc] initWithData:requestDTDData options:0 error:&error];
[requestXMLChecker setName:@"request"];
[xml_body_ setDTD:requestXMLChecker];
EXPECT_TRUE([xml_body_ validateAndReturnError:&error]);
}
} // namespace

@ -1,32 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/installer/mac/app/SystemInfo.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
TEST(SystemInfoTest, GetArchReturnsExpectedString) {
NSString* arch = [SystemInfo getArch];
EXPECT_TRUE([arch isEqualToString:@"i486"] ||
[arch isEqualToString:@"x86_64h"]);
}
TEST(SystemInfoTest, GetOSVersionMatchesRegexFormat) {
NSString* os_version = [SystemInfo getOSVersion];
NSRegularExpression* regex = [NSRegularExpression
regularExpressionWithPattern:@"^10\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
options:0
error:nil];
NSUInteger matches =
[regex numberOfMatchesInString:os_version
options:0
range:NSMakeRange(0, os_version.length)];
EXPECT_EQ(1u, matches);
}
} // namespace

@ -1,113 +0,0 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/installer/mac/app/Unpacker.h"
#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/sys_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "chrome/installer/mac/app/Downloader.h"
@interface TestDelegate : NSObject<UnpackDelegate>
@property(nonatomic) BOOL pass;
@property(nonatomic) dispatch_semaphore_t test_semaphore;
- (void)fail;
- (void)succeed;
- (void)wait;
@end
@implementation TestDelegate
@synthesize pass = pass_;
@synthesize test_semaphore = test_semaphore_;
- (id)init {
if ((self = [super init])) {
test_semaphore_ = dispatch_semaphore_create(0);
pass_ = NO;
}
return self;
}
- (void)succeed {
pass_ = YES;
dispatch_semaphore_signal(test_semaphore_);
}
- (void)fail {
pass_ = NO;
dispatch_semaphore_signal(test_semaphore_);
}
- (void)wait {
dispatch_semaphore_wait(test_semaphore_, DISPATCH_TIME_FOREVER);
}
- (void)unpacker:(Unpacker*)unpacker onMountSuccess:(NSString*)tempAppPath {
if ([[NSFileManager defaultManager] fileExistsAtPath:tempAppPath]) {
[self succeed];
} else {
[self fail];
}
}
- (void)unpacker:(Unpacker*)unpacker onMountFailure:(NSError*)error {
[self fail];
}
- (void)unpacker:(Unpacker*)unpacker onUnmountSuccess:(NSString*)mountpath {
if (![[NSFileManager defaultManager]
fileExistsAtPath:[NSString pathWithComponents:@[
mountpath, @"Google Chrome.app"
]]]) {
[self succeed];
} else {
[self fail];
}
}
- (void)unpacker:(Unpacker*)unpacker onUnmountFailure:(NSError*)error {
[self fail];
}
@end
namespace {
TEST(UnpackerTest, IntegrationTest) {
// Create objects and semaphore
Unpacker* unpack = [[Unpacker alloc] init];
TestDelegate* test_delegate = [[TestDelegate alloc] init];
unpack.delegate = test_delegate;
// Get a disk image to use to test
base::FilePath originalPath;
base::PathService::Get(base::DIR_SOURCE_ROOT, &originalPath);
originalPath = originalPath.AppendASCII("chrome/test/data/mac_installer/");
base::FilePath copiedPath = base::FilePath(originalPath);
NSString* diskImageOriginalPath = base::SysUTF8ToNSString(
(originalPath.AppendASCII("test-dmg.dmg")).value());
NSString* diskImageCopiedPath = base::SysUTF8ToNSString(
(originalPath.AppendASCII("test-dmg2.dmg")).value());
// The unpacker moves (not copies) a downloaded disk image directly into its
// own temporary directory, so if the below copy didn't happen, `test-dmg.dmg`
// would disappear every time this test was run
[[NSFileManager defaultManager] copyItemAtPath:diskImageOriginalPath
toPath:diskImageCopiedPath
error:nil];
NSURL* dmgURL = [NSURL fileURLWithPath:diskImageCopiedPath isDirectory:NO];
// Start mount step
[unpack mountDMGFromURL:dmgURL];
[test_delegate wait];
// Is the disk image mounted?
ASSERT_TRUE([test_delegate pass]);
// Start unmount step
[unpack unmountDMG];
[test_delegate wait];
// Is the disk image gone?
EXPECT_TRUE([test_delegate pass]);
}
} // namespace

@ -13434,12 +13434,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true
},
"test": "mac_installer_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true
@ -14153,15 +14147,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"args": [
"--test-launcher-print-test-stdio=always"
],
"swarming": {
"can_use_on_swarming_builders": true
},
"test": "mac_installer_unittests"
},
{
"args": [
"--test-launcher-print-test-stdio=always"

@ -658,12 +658,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true
},
"test": "mac_installer_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true
@ -8170,19 +8164,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
"dimension_sets": [
{
"gpu": "8086:0a2e",
"os": "Mac-10.14"
}
],
"expiration": 21600
},
"test": "mac_installer_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
@ -9215,15 +9196,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"args": [
"--enable-features=ViewsBrowserWindows"
],
"swarming": {
"can_use_on_swarming_builders": true
},
"test": "mac_installer_unittests"
},
{
"args": [
"--enable-features=ViewsBrowserWindows"

@ -580,18 +580,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
"dimension_sets": [
{
"gpu": "none",
"os": "Mac-10.10"
}
]
},
"test": "mac_installer_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
@ -1717,18 +1705,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
"dimension_sets": [
{
"gpu": "none",
"os": "Mac-10.11"
}
]
},
"test": "mac_installer_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
@ -2939,18 +2915,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
"dimension_sets": [
{
"gpu": "8086:0a2e",
"os": "Mac-10.12.6"
}
]
},
"test": "mac_installer_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
@ -4076,18 +4040,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
"dimension_sets": [
{
"gpu": "none",
"os": "Mac-10.13"
}
]
},
"test": "mac_installer_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
@ -5201,18 +5153,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,
"dimension_sets": [
{
"gpu": "none",
"os": "Mac-10.13"
}
]
},
"test": "mac_installer_unittests"
},
{
"swarming": {
"can_use_on_swarming_builders": true,

@ -9198,15 +9198,6 @@
},
"test": "libjingle_xmpp_unittests"
},
{
"args": [
"--test-launcher-print-test-stdio=always"
],
"swarming": {
"can_use_on_swarming_builders": true
},
"test": "mac_installer_unittests"
},
{
"args": [
"--test-launcher-print-test-stdio=always"

@ -793,10 +793,6 @@
"label": "//third_party/libjingle_xmpp:libjingle_xmpp_unittests",
"type": "console_test_launcher",
},
"mac_installer_unittests": {
"label": "//chrome/installer/mac/app:mac_installer_unittests",
"type": "console_test_launcher",
},
"mash:all" : {
"label": "//mash:all",
"type": "additional_compile_target",

@ -1828,7 +1828,6 @@
},
'mac_specific_chromium_gtests': {
'mac_installer_unittests': {},
'sandbox_mac_unittests': {},
},