Merge pull request #3186 from atom/auto-updater-fix

Clean up the code of auto-updater
This commit is contained in:
Cheng Zhao 2015-10-23 20:31:20 +08:00
commit 0e3e1be85c
16 changed files with 247 additions and 332 deletions

View file

@ -5,12 +5,31 @@
#include "atom/browser/api/atom_api_auto_updater.h" #include "atom/browser/api/atom_api_auto_updater.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "atom/browser/auto_updater.h"
#include "atom/browser/browser.h" #include "atom/browser/browser.h"
#include "atom/browser/native_window.h"
#include "atom/browser/window_list.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/node_includes.h" #include "atom/common/node_includes.h"
#include "native_mate/dictionary.h" #include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h" #include "native_mate/object_template_builder.h"
namespace mate {
template<>
struct Converter<base::Time> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::Time& val) {
v8::MaybeLocal<v8::Value> date = v8::Date::New(
isolate->GetCurrentContext(), val.ToJsTime());
if (date.IsEmpty())
return v8::Null(isolate);
else
return date.ToLocalChecked();
}
};
} // namespace mate
namespace atom { namespace atom {
namespace api { namespace api {
@ -20,11 +39,18 @@ AutoUpdater::AutoUpdater() {
} }
AutoUpdater::~AutoUpdater() { AutoUpdater::~AutoUpdater() {
auto_updater::AutoUpdater::SetDelegate(NULL); auto_updater::AutoUpdater::SetDelegate(nullptr);
} }
void AutoUpdater::OnError(const std::string& error) { void AutoUpdater::OnError(const std::string& message) {
Emit("error", error); v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto error = v8::Exception::Error(mate::StringToV8(isolate(), message));
EmitCustomEvent(
"error",
error->ToObject(isolate()->GetCurrentContext()).ToLocalChecked(),
// Message is also emitted to keep compatibility with old code.
message);
} }
void AutoUpdater::OnCheckingForUpdate() { void AutoUpdater::OnCheckingForUpdate() {
@ -42,11 +68,14 @@ void AutoUpdater::OnUpdateNotAvailable() {
void AutoUpdater::OnUpdateDownloaded(const std::string& release_notes, void AutoUpdater::OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_name, const std::string& release_name,
const base::Time& release_date, const base::Time& release_date,
const std::string& update_url, const std::string& url) {
const base::Closure& quit_and_install) { Emit("update-downloaded", release_notes, release_name, release_date, url,
quit_and_install_ = quit_and_install; // Keep compatibility with old APIs.
Emit("update-downloaded-raw", release_notes, release_name, base::Bind(&AutoUpdater::QuitAndInstall, base::Unretained(this)));
release_date.ToJsTime(), update_url); }
void AutoUpdater::OnWindowAllClosed() {
QuitAndInstall();
} }
mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder( mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder(
@ -54,14 +83,21 @@ mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder(
return mate::ObjectTemplateBuilder(isolate) return mate::ObjectTemplateBuilder(isolate)
.SetMethod("setFeedUrl", &auto_updater::AutoUpdater::SetFeedURL) .SetMethod("setFeedUrl", &auto_updater::AutoUpdater::SetFeedURL)
.SetMethod("checkForUpdates", &auto_updater::AutoUpdater::CheckForUpdates) .SetMethod("checkForUpdates", &auto_updater::AutoUpdater::CheckForUpdates)
.SetMethod("_quitAndInstall", &AutoUpdater::QuitAndInstall); .SetMethod("quitAndInstall", &AutoUpdater::QuitAndInstall);
} }
void AutoUpdater::QuitAndInstall() { void AutoUpdater::QuitAndInstall() {
if (quit_and_install_.is_null()) // If we don't have any window then quitAndInstall immediately.
Browser::Get()->Shutdown(); WindowList* window_list = WindowList::GetInstance();
else if (window_list->size() == 0) {
quit_and_install_.Run(); auto_updater::AutoUpdater::QuitAndInstall();
return;
}
// Otherwise do the restart after all windows have been closed.
window_list->AddObserver(this);
for (NativeWindow* window : *window_list)
window->Close();
} }
// static // static

View file

@ -7,9 +7,9 @@
#include <string> #include <string>
#include "base/callback.h"
#include "atom/browser/api/event_emitter.h" #include "atom/browser/api/event_emitter.h"
#include "atom/browser/auto_updater_delegate.h" #include "atom/browser/auto_updater.h"
#include "atom/browser/window_list_observer.h"
#include "native_mate/handle.h" #include "native_mate/handle.h"
namespace atom { namespace atom {
@ -17,7 +17,8 @@ namespace atom {
namespace api { namespace api {
class AutoUpdater : public mate::EventEmitter, class AutoUpdater : public mate::EventEmitter,
public auto_updater::AutoUpdaterDelegate { public auto_updater::Delegate,
public WindowListObserver {
public: public:
static mate::Handle<AutoUpdater> Create(v8::Isolate* isolate); static mate::Handle<AutoUpdater> Create(v8::Isolate* isolate);
@ -25,17 +26,18 @@ class AutoUpdater : public mate::EventEmitter,
AutoUpdater(); AutoUpdater();
virtual ~AutoUpdater(); virtual ~AutoUpdater();
// AutoUpdaterDelegate implementations. // Delegate implementations.
void OnError(const std::string& error) override; void OnError(const std::string& error) override;
void OnCheckingForUpdate() override; void OnCheckingForUpdate() override;
void OnUpdateAvailable() override; void OnUpdateAvailable() override;
void OnUpdateNotAvailable() override; void OnUpdateNotAvailable() override;
void OnUpdateDownloaded( void OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_notes, const std::string& release_name,
const std::string& release_name, const base::Time& release_date,
const base::Time& release_date, const std::string& update_url) override;
const std::string& update_url,
const base::Closure& quit_and_install) override; // WindowListObserver:
void OnWindowAllClosed() override;
// mate::Wrappable implementations: // mate::Wrappable implementations:
mate::ObjectTemplateBuilder GetObjectTemplateBuilder( mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
@ -44,8 +46,6 @@ class AutoUpdater : public mate::EventEmitter,
private: private:
void QuitAndInstall(); void QuitAndInstall();
base::Closure quit_and_install_;
DISALLOW_COPY_AND_ASSIGN(AutoUpdater); DISALLOW_COPY_AND_ASSIGN(AutoUpdater);
}; };

View file

@ -1,28 +1,7 @@
switch process.platform switch process.platform
when 'win32' when 'win32'
autoUpdater = require './auto-updater/auto-updater-win' module.exports = require './auto-updater/auto-updater-win'
when 'darwin'
module.exports = require './auto-updater/auto-updater-mac'
else else
# take the default binding for the current platform throw new Error('auto-updater is not implemented on this platform')
autoUpdater = process.atomBinding('auto_updater').autoUpdater
EventEmitter = require('events').EventEmitter
autoUpdater.__proto__ = EventEmitter.prototype
autoUpdater.on 'update-downloaded-raw', (args...) ->
args[3] = new Date(args[3]) # releaseDate
@emit 'update-downloaded', args..., => @quitAndInstall()
autoUpdater.quitAndInstall = ->
# If we don't have any window then quitAndInstall immediately.
BrowserWindow = require 'browser-window'
windows = BrowserWindow.getAllWindows()
if windows.length is 0
@_quitAndInstall()
return
# Do the restart after all windows have been closed.
app = require 'app'
app.removeAllListeners 'window-all-closed'
app.once 'window-all-closed', @_quitAndInstall.bind(this)
win.close() for win in windows
module.exports = autoUpdater

View file

@ -0,0 +1,6 @@
{EventEmitter} = require 'events'
{autoUpdater} = process.atomBinding 'auto_updater'
autoUpdater.__proto__ = EventEmitter.prototype
module.exports = autoUpdater

View file

@ -1,45 +1,42 @@
app = require 'app'
url = require 'url'
{EventEmitter} = require 'events' {EventEmitter} = require 'events'
SquirrelUpdate = require './auto-updater/squirrel-update-win'
app = require 'app' squirrelUpdate = require './squirrel-update-win'
url = require 'url'
class AutoUpdater extends EventEmitter class AutoUpdater extends EventEmitter
quitAndInstall: -> quitAndInstall: ->
SquirrelUpdate.processStart -> squirrelUpdate.processStart()
app.quit() app.quit()
setFeedUrl: (updateUrl) -> setFeedUrl: (updateUrl) ->
# set feed URL only when it hasn't been set before @updateUrl = updateUrl
unless @updateUrl
@updateUrl = updateUrl
checkForUpdates: -> checkForUpdates: ->
throw new Error('Update URL is not set') unless @updateUrl return @emitError 'Update URL is not set' unless @updateUrl
return @emitError 'Can not find Squirrel' unless squirrelUpdate.supported()
@emit 'checking-for-update' @emit 'checking-for-update'
unless SquirrelUpdate.supported() squirrelUpdate.download @updateUrl, (error, update) =>
@emit 'update-not-available' return @emitError error if error?
return return @emit 'update-not-available' unless update?
SquirrelUpdate.download (error, update) =>
if error?
@emit 'update-not-available'
return
unless update?
@emit 'update-not-available'
return
@emit 'update-available' @emit 'update-available'
SquirrelUpdate.update @updateUrl, (error) => squirrelUpdate.update @updateUrl, (error) =>
if error? return @emitError error if error?
@emit 'update-not-available'
return
# info about the newly installed version and a function any of the event listeners can call to restart the application {releaseNotes, version} = update
@emit 'update-downloaded', {}, update.releaseNotes, update.version, new Date(), @updateUrl, => @quitAndInstall() # Following information is not available on Windows, so fake them.
date = new Date
url = @updateUrl
module.exports = new AutoUpdater() @emit 'update-downloaded', {}, releaseNotes, version, date, url, => @quitAndInstall()
# Private: Emit both error object and message, this is to keep compatibility
# with Old APIs.
emitError: (message) ->
@emit 'error', new Error(message), message
module.exports = new AutoUpdater

View file

@ -1,57 +1,67 @@
ChildProcess = require 'child_process' fs = require 'fs'
fs = require 'fs' path = require 'path'
path = require 'path' {spawn} = require 'child_process'
appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/ appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/
rootApplicationFolder = path.resolve appFolder, '..' # i.e. my-app/ updateExe = path.resolve appFolder, '..', 'Update.exe' # i.e. my-app/Update.exe
updateDotExe = path.join rootApplicationFolder, 'Update.exe' exeName = path.basename process.execPath
exeName = path.basename process.execPath
# Spawn a command and invoke the callback when it completes with an error # Spawn a command and invoke the callback when it completes with an error
# and the output from standard out. # and the output from standard out.
spawnUpdate = (args, callback) -> spawnUpdate = (args, detached, callback) ->
stdout = ''
try try
spawnedProcess = ChildProcess.spawn(updateDotExe, args) spawnedProcess = spawn updateExe, args, {detached}
catch error catch error
# Spawn can throw an error # Shouldn't happen, but still guard it.
process.nextTick -> callback?(error, stdout) process.nextTick -> callback error
return return
stdout = ''
stderr = ''
spawnedProcess.stdout.on 'data', (data) -> stdout += data spawnedProcess.stdout.on 'data', (data) -> stdout += data
spawnedProcess.stderr.on 'data', (data) -> stderr += data
error = null errorEmitted = false
spawnedProcess.on 'error', (processError) -> error ?= processError spawnedProcess.on 'error', (error) ->
spawnedProcess.on 'close', (code, signal) -> errorEmitted = true
error ?= new Error("Command failed: #{signal ? code}") if code isnt 0 callback error
error?.code ?= code spawnedProcess.on 'exit', (code, signal) ->
error?.stdout ?= stdout # We may have already emitted an error.
callback?(error, stdout) return if errorEmitted
processStart = (callback) -> # Process terminated with error.
spawnUpdate(['--processStart', exeName], callback) if code isnt 0
return callback "Command failed: #{signal ? code}\n#{stderr}"
download = (callback) -> # Success.
spawnUpdate ['--download', @updateUrl], (error, stdout) -> callback null, stdout
# Start an instance of the installed app.
exports.processStart = (callback) ->
spawnUpdate ['--processStart', exeName], true, ->
# Download the releases specified by the URL and write new results to stdout.
exports.download = (updateUrl, callback) ->
spawnUpdate ['--download', updateUrl], false, (error, stdout) ->
return callback(error) if error? return callback(error) if error?
try try
# Last line of output is the JSON details about the releases # Last line of output is the JSON details about the releases
json = stdout.trim().split('\n').pop() json = stdout.trim().split('\n').pop()
update = JSON.parse(json)?.releasesToApply?.pop?() update = JSON.parse(json)?.releasesToApply?.pop?()
catch error catch
error.stdout = stdout return callback "Invalid result:\n#{stdout}"
return callback(error)
callback(null, update) callback null, update
update = (updateUrl, callback) -> # Update the application to the latest remote version specified by URL.
spawnUpdate ['--update', updateUrl], callback exports.update = (updateUrl, callback) ->
spawnUpdate ['--update', updateUrl], false, callback
# Is the Update.exe installed with the current application? # Is the Update.exe installed with the current application?
exports.supported = -> exports.supported = ->
fs.accessSync(updateDotExe, fs.R_OK) try
exports.processStart = processStart fs.accessSync updateExe, fs.R_OK
exports.download = download return true
exports.update = update catch
return false

View file

@ -6,22 +6,25 @@
namespace auto_updater { namespace auto_updater {
AutoUpdaterDelegate* AutoUpdater::delegate_ = NULL; Delegate* AutoUpdater::delegate_ = nullptr;
AutoUpdaterDelegate* AutoUpdater::GetDelegate() { Delegate* AutoUpdater::GetDelegate() {
return delegate_; return delegate_;
} }
void AutoUpdater::SetDelegate(AutoUpdaterDelegate* delegate) { void AutoUpdater::SetDelegate(Delegate* delegate) {
delegate_ = delegate; delegate_ = delegate;
} }
#if defined(OS_MACOSX) && defined(MAS_BUILD) #if !defined(OS_MACOSX) || defined(MAS_BUILD)
void AutoUpdater::SetFeedURL(const std::string& url) { void AutoUpdater::SetFeedURL(const std::string& url) {
} }
void AutoUpdater::CheckForUpdates() { void AutoUpdater::CheckForUpdates() {
} }
void AutoUpdater::QuitAndInstall() {
}
#endif #endif
} // namespace auto_updater } // namespace auto_updater

View file

@ -9,21 +9,48 @@
#include "base/basictypes.h" #include "base/basictypes.h"
namespace base {
class Time;
}
namespace auto_updater { namespace auto_updater {
class AutoUpdaterDelegate; class Delegate {
public:
// An error happened.
virtual void OnError(const std::string& error) {}
// Checking to see if there is an update
virtual void OnCheckingForUpdate() {}
// There is an update available and it is being downloaded
virtual void OnUpdateAvailable() {}
// There is no available update.
virtual void OnUpdateNotAvailable() {}
// There is a new update which has been downloaded.
virtual void OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url) {}
protected:
virtual ~Delegate() {}
};
class AutoUpdater { class AutoUpdater {
public: public:
// Gets/Sets the delegate. // Gets/Sets the delegate.
static AutoUpdaterDelegate* GetDelegate(); static Delegate* GetDelegate();
static void SetDelegate(AutoUpdaterDelegate* delegate); static void SetDelegate(Delegate* delegate);
static void SetFeedURL(const std::string& url); static void SetFeedURL(const std::string& url);
static void CheckForUpdates(); static void CheckForUpdates();
static void QuitAndInstall();
private: private:
static AutoUpdaterDelegate* delegate_; static Delegate* delegate_;
DISALLOW_IMPLICIT_CONSTRUCTORS(AutoUpdater); DISALLOW_IMPLICIT_CONSTRUCTORS(AutoUpdater);
}; };

View file

@ -1,45 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_
#define ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_
#include <string>
#include "base/callback_forward.h"
namespace base {
class Time;
}
namespace auto_updater {
class AutoUpdaterDelegate {
public:
// An error happened.
virtual void OnError(const std::string& error) {}
// Checking to see if there is an update
virtual void OnCheckingForUpdate() {}
// There is an update available and it is being downloaded
virtual void OnUpdateAvailable() {}
// There is no available update.
virtual void OnUpdateNotAvailable() {}
// There is a new update which has been downloaded.
virtual void OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url,
const base::Closure& quit_and_install) {}
protected:
virtual ~AutoUpdaterDelegate() {}
};
} // namespace auto_updater
#endif // ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_

View file

@ -1,17 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/auto_updater.h"
namespace auto_updater {
// static
void AutoUpdater::SetFeedURL(const std::string& url) {
}
// static
void AutoUpdater::CheckForUpdates() {
}
} // namespace auto_updater

View file

@ -12,9 +12,6 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "atom/browser/auto_updater_delegate.h"
#include <iostream>
namespace auto_updater { namespace auto_updater {
@ -23,20 +20,12 @@ namespace {
// The gloal SQRLUpdater object. // The gloal SQRLUpdater object.
SQRLUpdater* g_updater = nil; SQRLUpdater* g_updater = nil;
void RelaunchToInstallUpdate() {
[[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
AutoUpdaterDelegate* delegate = AutoUpdater::GetDelegate();
if (delegate)
delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
}];
}
} // namespace } // namespace
// static // static
void AutoUpdater::SetFeedURL(const std::string& feed) { void AutoUpdater::SetFeedURL(const std::string& feed) {
if (g_updater == nil) { if (g_updater == nil) {
AutoUpdaterDelegate* delegate = GetDelegate(); Delegate* delegate = GetDelegate();
if (!delegate) if (!delegate)
return; return;
@ -67,7 +56,7 @@ void AutoUpdater::SetFeedURL(const std::string& feed) {
// static // static
void AutoUpdater::CheckForUpdates() { void AutoUpdater::CheckForUpdates() {
AutoUpdaterDelegate* delegate = GetDelegate(); Delegate* delegate = GetDelegate();
if (!delegate) if (!delegate)
return; return;
@ -86,8 +75,7 @@ void AutoUpdater::CheckForUpdates() {
base::SysNSStringToUTF8(update.releaseNotes), base::SysNSStringToUTF8(update.releaseNotes),
base::SysNSStringToUTF8(update.releaseName), base::SysNSStringToUTF8(update.releaseName),
base::Time::FromDoubleT(update.releaseDate.timeIntervalSince1970), base::Time::FromDoubleT(update.releaseDate.timeIntervalSince1970),
base::SysNSStringToUTF8(update.updateURL.absoluteString), base::SysNSStringToUTF8(update.updateURL.absoluteString));
base::Bind(RelaunchToInstallUpdate));
} else { } else {
// When the completed event is sent with no update, then we know there // When the completed event is sent with no update, then we know there
// is no update available. // is no update available.
@ -100,4 +88,12 @@ void AutoUpdater::CheckForUpdates() {
}]; }];
} }
void AutoUpdater::QuitAndInstall() {
[[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
Delegate* delegate = AutoUpdater::GetDelegate();
if (delegate)
delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
}];
}
} // namespace auto_updater } // namespace auto_updater

View file

@ -1,17 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/auto_updater.h"
namespace auto_updater {
// static
void AutoUpdater::SetFeedURL(const std::string& url) {
}
// static
void AutoUpdater::CheckForUpdates() {
}
} // namespace auto_updater

View file

@ -10,6 +10,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
meta.type = 'buffer' if Buffer.isBuffer value meta.type = 'buffer' if Buffer.isBuffer value
meta.type = 'value' if value is null meta.type = 'value' if value is null
meta.type = 'array' if Array.isArray value meta.type = 'array' if Array.isArray value
meta.type = 'error' if value instanceof Error
meta.type = 'date' if value instanceof Date
meta.type = 'promise' if value? and value.constructor.name is 'Promise' meta.type = 'promise' if value? and value.constructor.name is 'Promise'
# Treat simple objects as value. # Treat simple objects as value.
@ -36,6 +38,10 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
meta.value = Array::slice.call value, 0 meta.value = Array::slice.call value, 0
else if meta.type is 'promise' else if meta.type is 'promise'
meta.then = valueToMeta(sender, value.then.bind(value)) meta.then = valueToMeta(sender, value.then.bind(value))
else if meta.type is 'error'
meta.message = value.message
else if meta.type is 'date'
meta.value = value.getTime()
else else
meta.type = 'value' meta.type = 'value'
meta.value = value meta.value = value
@ -43,8 +49,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
meta meta
# Convert Error into meta data. # Convert Error into meta data.
errorToMeta = (error) -> exceptionToMeta = (error) ->
type: 'error', message: error.message, stack: (error.stack || error) type: 'exception', message: error.message, stack: (error.stack || error)
# Convert array of meta data from renderer into array of real values. # Convert array of meta data from renderer into array of real values.
unwrapArgs = (sender, args) -> unwrapArgs = (sender, args) ->
@ -100,19 +106,19 @@ ipc.on 'ATOM_BROWSER_REQUIRE', (event, module) ->
try try
event.returnValue = valueToMeta event.sender, process.mainModule.require(module) event.returnValue = valueToMeta event.sender, process.mainModule.require(module)
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_GLOBAL', (event, name) -> ipc.on 'ATOM_BROWSER_GLOBAL', (event, name) ->
try try
event.returnValue = valueToMeta event.sender, global[name] event.returnValue = valueToMeta event.sender, global[name]
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) -> ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) ->
try try
event.returnValue = valueToMeta event.sender, event.sender.getOwnerBrowserWindow() event.returnValue = valueToMeta event.sender, event.sender.getOwnerBrowserWindow()
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_CURRENT_WEB_CONTENTS', (event) -> ipc.on 'ATOM_BROWSER_CURRENT_WEB_CONTENTS', (event) ->
event.returnValue = valueToMeta event.sender, event.sender event.returnValue = valueToMeta event.sender, event.sender
@ -126,7 +132,7 @@ ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
obj = new (Function::bind.apply(constructor, [null].concat(args))) obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta event.sender, obj event.returnValue = valueToMeta event.sender, obj
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) -> ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
try try
@ -134,7 +140,7 @@ ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
func = objectsRegistry.get id func = objectsRegistry.get id
callFunction event, func, global, args callFunction event, func, global, args
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) -> ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
try try
@ -144,7 +150,7 @@ ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
obj = new (Function::bind.apply(constructor, [null].concat(args))) obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta event.sender, obj event.returnValue = valueToMeta event.sender, obj
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) -> ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
try try
@ -152,7 +158,7 @@ ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
obj = objectsRegistry.get id obj = objectsRegistry.get id
callFunction event, obj[method], obj, args callFunction event, obj[method], obj, args
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) -> ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
try try
@ -160,14 +166,14 @@ ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
obj[name] = value obj[name] = value
event.returnValue = null event.returnValue = null
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) -> ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) ->
try try
obj = objectsRegistry.get id obj = objectsRegistry.get id
event.returnValue = valueToMeta event.sender, obj[name] event.returnValue = valueToMeta event.sender, obj[name]
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, id) -> ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, id) ->
objectsRegistry.remove event.sender.getId(), id objectsRegistry.remove event.sender.getId(), id
@ -177,4 +183,4 @@ ipc.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) ->
guestViewManager = require './guest-view-manager' guestViewManager = require './guest-view-manager'
event.returnValue = valueToMeta event.sender, guestViewManager.getGuest(guestInstanceId) event.returnValue = valueToMeta event.sender, guestViewManager.getGuest(guestInstanceId)
catch e catch e
event.returnValue = errorToMeta e event.returnValue = exceptionToMeta e

View file

@ -46,7 +46,9 @@ metaToValue = (meta) ->
when 'array' then (metaToValue(el) for el in meta.members) when 'array' then (metaToValue(el) for el in meta.members)
when 'buffer' then new Buffer(meta.value) when 'buffer' then new Buffer(meta.value)
when 'promise' then Promise.resolve(then: metaToValue(meta.then)) when 'promise' then Promise.resolve(then: metaToValue(meta.then))
when 'error' when 'error' then new Error(meta.message)
when 'date' then new Date(meta.value)
when 'exception'
throw new Error("#{meta.message}\n#{meta.stack}") throw new Error("#{meta.message}\n#{meta.stack}")
else else
if meta.type is 'function' if meta.type is 'function'

View file

@ -1,106 +1,31 @@
# autoUpdater # autoUpdater
**This module has only been implemented for OS X.** This module provides an interface for the `Squirrel` auto-updater framework.
Check out [atom/grunt-electron-installer](https://github.com/atom/grunt-electron-installer) ## Platform notices
to build a Windows installer for your app.
The `auto-updater` module is a simple wrapper around the Though `autoUpdater` provides an uniform API for different platforms, there are
[Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac) framework. still some subtle differences on each platform.
Squirrel.Mac requires that your `.app` folder is signed using the ### OS X
[codesign](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/codesign.1.html)
utility for updates to be installed.
## Squirrel On OS X the `autoUpdater` module is built upon [Squirrel.Mac][squirrel-mac], you
don't need any special setup to make it work. For server-side requirements, you
can read [Server Support][server-support].
Squirrel is an OS X framework focused on making application updates **as safe ### Windows
and transparent as updates to a website**.
Instead of publishing a feed of versions from which your app must select, On Windows you have to install your app into user's machine before you can use
Squirrel updates to the version your server tells it to. This allows you to the auto-updater, it is recommended to use [grunt-electron-installer][installer]
intelligently update your clients based on the request you give to Squirrel. module to generate a Windows installer.
Your request can include authentication details, custom headers or a request The server-side setup is also different from OS X, you can read the documents of
body so that your server has the context it needs in order to supply the most [Squirrel.Windows][squirrel-windows] to get more details.
suitable update.
The update JSON Squirrel requests should be dynamically generated based on ### Linux
criteria in the request and whether an update is required. Squirrel relies
on server-side support to determine whether an update is required. See
[Server Support](#server-support).
Squirrel's installer is designed to be fault tolerant and ensures that any There is not built-in support for auto-updater on Linux, it is recommended to
updates installed are valid. use the distribution's package manager to update your app.
## Update Requests
Squirrel is indifferent to the request the client application provides for
update checking. `Accept: application/json` is added to the request headers
because Squirrel is responsible for parsing the response.
For the requirements imposed on the responses and the body format of an update
response, see [Server Support](#server-support).
Your update request must *at least* include a version identifier so that the
server can determine whether an update for this specific version is required. It
may also include other identifying criteria, such as operating system version or
username, to allow the server to deliver as fine grained an update as you
would like.
How you include the version identifier or other criteria is specific to the
server that you are requesting updates from. A common approach is to use query
parameters, like this:
```javascript
// In the main process
var app = require('app');
var autoUpdater = require('auto-updater');
autoUpdater.setFeedUrl('http://mycompany.com/myapp/latest?version=' + app.getVersion());
```
## Server Support
Your server should determine whether an update is required based on the
[Update Request](#update-requests) your client issues.
If an update is required, your server should respond with a status code of
[200 OK](http://tools.ietf.org/html/rfc2616#section-10.2.1) and include the
[update JSON](#update-json-format) in the body. Squirrel **will** download and
install this update, even if the version of the update is the same as the
currently running version. To save redundantly downloading the same version
multiple times your server must not inform the client to update.
If no update is required your server must respond with a status code of
[204 No Content](http://tools.ietf.org/html/rfc2616#section-10.2.5). Squirrel
will check for an update again at the interval you specify.
## Update JSON Format
When an update is available, Squirrel expects the following schema in response
to the update request provided:
```json
{
"url": "http://mycompany.com/myapp/releases/myrelease",
"name": "My Release Name",
"notes": "Theses are some release notes innit",
"pub_date": "2013-09-18T12:29:53+01:00"
}
```
The only required key is "url"; the others are optional.
Squirrel will request "url" with `Accept: application/zip` and only supports
installing ZIP updates. If future update formats are supported their MIME type
will be added to the `Accept` header so that your server can return the
appropriate format.
`pub_date` (if present) must be formatted according to ISO 8601.
## Update server implementations
[Nuts](https://github.com/GitbookIO/nuts) is an open source implementation of the update server described above, it integrates beautifully with GitHub releases. Nuts manages downloads and updates, its compatible with `Squirrel.Mac` and `Squirrel.Windows` so you get cross-platform support out of the box.
## Events ## Events
@ -110,8 +35,7 @@ The `autoUpdater` object emits the following events:
Returns: Returns:
* `event` Event * `error` Error
* `message` String
Emitted when there is an error while updating. Emitted when there is an error while updating.
@ -137,10 +61,10 @@ Returns:
* `releaseName` String * `releaseName` String
* `releaseDate` Date * `releaseDate` Date
* `updateUrl` String * `updateUrl` String
* `quitAndUpdate` Function
Emitted when an update has been downloaded. Calling `quitAndUpdate()` will Emitted when an update has been downloaded.
restart the application and install the update.
On Windows only `releaseName` is available.
## Methods ## Methods
@ -150,10 +74,20 @@ The `autoUpdater` object has the following methods:
* `url` String * `url` String
Set the `url` and initialize the auto updater. The `url` cannot be changed Sets the `url` and initialize the auto updater. The `url` cannot be changed
once it is set. once it is set.
### `autoUpdater.checkForUpdates()` ### `autoUpdater.checkForUpdates()`
Ask the server whether there is an update. You must call `setFeedUrl` before Asks the server whether there is an update. You must call `setFeedUrl` before
using this API. using this API.
### `autoUpdater.quitAndUpdate()`
Restarts the app and install the update after it has been downloaded. It should
only be called after `update-downloaded` has been emitted.
[squirrel-mac]: https://github.com/Squirrel/Squirrel.Mac
[server-support]: https://github.com/Squirrel/Squirrel.Mac#server-support
[squirrel-windows]: https://github.com/Squirrel/Squirrel.Windows
[installer]: https://github.com/atom/grunt-electron-installer

View file

@ -11,6 +11,7 @@
'atom/browser/api/lib/app.coffee', 'atom/browser/api/lib/app.coffee',
'atom/browser/api/lib/atom-delegate.coffee', 'atom/browser/api/lib/atom-delegate.coffee',
'atom/browser/api/lib/auto-updater.coffee', 'atom/browser/api/lib/auto-updater.coffee',
'atom/browser/api/lib/auto-updater/auto-updater-mac.coffee',
'atom/browser/api/lib/auto-updater/auto-updater-win.coffee', 'atom/browser/api/lib/auto-updater/auto-updater-win.coffee',
'atom/browser/api/lib/auto-updater/squirrel-update-win.coffee', 'atom/browser/api/lib/auto-updater/squirrel-update-win.coffee',
'atom/browser/api/lib/browser-window.coffee', 'atom/browser/api/lib/browser-window.coffee',
@ -114,10 +115,7 @@
'atom/browser/api/save_page_handler.h', 'atom/browser/api/save_page_handler.h',
'atom/browser/auto_updater.cc', 'atom/browser/auto_updater.cc',
'atom/browser/auto_updater.h', 'atom/browser/auto_updater.h',
'atom/browser/auto_updater_delegate.h',
'atom/browser/auto_updater_linux.cc',
'atom/browser/auto_updater_mac.mm', 'atom/browser/auto_updater_mac.mm',
'atom/browser/auto_updater_win.cc',
'atom/browser/atom_access_token_store.cc', 'atom/browser/atom_access_token_store.cc',
'atom/browser/atom_access_token_store.h', 'atom/browser/atom_access_token_store.h',
'atom/browser/atom_browser_client.cc', 'atom/browser/atom_browser_client.cc',