var cp = require('child_process');
var http = require('http');
var kew = require('kew');
var path = require('path');
var url = require('url');
var rimraf = require('rimraf').sync;
var AdmZip = require('adm-zip');
var helper = require('./lib/chromedriver');
var ncp = require('ncp');
var npmconf = require('npmconf');
var util = require('util');
var libPath = path.join(__dirname, 'lib', 'bin', 'chromedriver');
var downloadUrl = 'http://chromedriver.storage.googleapis.com/2.9/chromedriver_';
if (process.platform === 'linux' && process.arch === 'x64') {
downloadUrl += 'linux64.zip';
} else if (process.platform === 'linux') {
downloadUrl += 'linux32.zip';
} else if (process.platform === 'darwin') {
downloadUrl += 'mac32.zip';
} else if (process.platform === 'win32') {
downloadUrl += 'win32.zip';
console.log('Unexpected platform or architecture:', process.platform, process.arch);
var fileName = downloadUrl.split('/').pop();
function getRequestOptions(proxyUrl) {
var options = url.parse(proxyUrl);
options.path = downloadUrl;
options.headers = { Host: url.parse(downloadUrl).host };
options.headers['Proxy-Authorization'] = 'Basic ' + new Buffer(options.auth).toString('base64');
return url.parse(downloadUrl);
function requestBinary(requestOptions, filePath) {
var deferred = kew.defer();
var outFile = fs.openSync(filePath, 'w');
var client = http.get(requestOptions, function (response) {
var status = response.statusCode;
console.log('Receiving...');
response.addListener('data', function (data) {
fs.writeSync(outFile, data, 0, data.length, null);
if ((count - notifiedCount) > 800000) {
console.log('Received ' + Math.floor(count / 1024) + 'K...');
response.addListener('end', function () {
console.log('Received ' + Math.floor(count / 1024) + 'K total.');
deferred.reject('Error with http request: ' + util.inspect(response.headers));
function extractDownload(filePath, tmpPath) {
var deferred = kew.defer();
var options = {cwd: tmpPath};
if (filePath.substr(-4) === '.zip') {
console.log('Extracting zip contents');
var zip = new AdmZip(filePath);
zip.extractAllTo(path.dirname(filePath), true);
deferred.reject('Error extracting archive ' + e.stack);
console.log('Extracting tar contents (via spawned process)');
cp.execFile('tar', ['jxf', filePath], options, function (err) {
deferred.reject('Error extracting archive ' + err.stack);
function copyIntoPlace(tmpPath, targetPath) {
var deferred = kew.defer();
var files = fs.readdirSync(tmpPath);
for (var i = 0; i < files.length; i++) {
var file = path.join(tmpPath, files[i]);
if (fs.statSync(file).isFile() && file.search('zip') === -1) {
console.log('Renaming extracted folder', file, '->', targetPath);
if (process.platform === 'win32') {
targetPath = targetPath + '.exe';
ncp(file, targetPath, deferred.makeNodeResolver());
function fixFilePermissions() {
if (process.platform !== 'win32') {
var stat = fs.statSync(helper.path);
console.log('Fixing file permissions');
fs.chmodSync(helper.path, '755');
var dir = path.dirname(name);
if (!fs.existsSync(dir)) {
npmconf.load(function(err, conf) {
console.log('Error loading npm config');
var tmpPath = path.join(conf.get('tmp'), 'chromedriver');
var downloadedFile = path.join(tmpPath, fileName);
var promise= kew.resolve(true);
if (!fs.existsSync(downloadedFile)) {
promise = promise.then(function () {
console.log('Downlaoading', downloadUrl);
console.log('Saving to', downloadedFile);
return requestBinary(getRequestOptions(conf.get('proxy')), downloadedFile);
console.log('Download already available at', downloadedFile);
promise.then(function () {
return extractDownload(downloadedFile, tmpPath);
return copyIntoPlace(tmpPath, libPath);
return fixFilePermissions();
console.log('Done. Chromedriver binary available at', helper.path);
console.error('Chromedriver installation failed', err.stack);