From d5c964c68c6cee99af54a0053322bda6d793cda8 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 14:23:05 +0800 Subject: [PATCH 1/9] Fix passing Error object in remote Closes #3089 --- atom/browser/lib/rpc-server.coffee | 27 +++++++++++++++------------ atom/renderer/api/lib/remote.coffee | 3 ++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/atom/browser/lib/rpc-server.coffee b/atom/browser/lib/rpc-server.coffee index a1e159a16a7..6a20d09d119 100644 --- a/atom/browser/lib/rpc-server.coffee +++ b/atom/browser/lib/rpc-server.coffee @@ -10,6 +10,7 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) -> meta.type = 'buffer' if Buffer.isBuffer value meta.type = 'value' if value is null meta.type = 'array' if Array.isArray value + meta.type = 'error' if value instanceof Error meta.type = 'promise' if value? and value.constructor.name is 'Promise' # Treat simple objects as value. @@ -36,6 +37,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) -> meta.value = Array::slice.call value, 0 else if meta.type is 'promise' meta.then = valueToMeta(sender, value.then.bind(value)) + else if meta.type is 'error' + meta.message = value.message else meta.type = 'value' meta.value = value @@ -43,8 +46,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) -> meta # Convert Error into meta data. -errorToMeta = (error) -> - type: 'error', message: error.message, stack: (error.stack || error) +exceptionToMeta = (error) -> + type: 'exception', message: error.message, stack: (error.stack || error) # Convert array of meta data from renderer into array of real values. unwrapArgs = (sender, args) -> @@ -100,19 +103,19 @@ ipc.on 'ATOM_BROWSER_REQUIRE', (event, module) -> try event.returnValue = valueToMeta event.sender, process.mainModule.require(module) catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e ipc.on 'ATOM_BROWSER_GLOBAL', (event, name) -> try event.returnValue = valueToMeta event.sender, global[name] catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) -> try event.returnValue = valueToMeta event.sender, event.sender.getOwnerBrowserWindow() catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e ipc.on 'ATOM_BROWSER_CURRENT_WEB_CONTENTS', (event) -> event.returnValue = valueToMeta event.sender, event.sender @@ -126,7 +129,7 @@ ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) -> obj = new (Function::bind.apply(constructor, [null].concat(args))) event.returnValue = valueToMeta event.sender, obj catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) -> try @@ -134,7 +137,7 @@ ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) -> func = objectsRegistry.get id callFunction event, func, global, args catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) -> try @@ -144,7 +147,7 @@ ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) -> obj = new (Function::bind.apply(constructor, [null].concat(args))) event.returnValue = valueToMeta event.sender, obj catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) -> try @@ -152,7 +155,7 @@ ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) -> obj = objectsRegistry.get id callFunction event, obj[method], obj, args catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) -> try @@ -160,14 +163,14 @@ ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) -> obj[name] = value event.returnValue = null catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) -> try obj = objectsRegistry.get id event.returnValue = valueToMeta event.sender, obj[name] catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, id) -> objectsRegistry.remove event.sender.getId(), id @@ -177,4 +180,4 @@ ipc.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) -> guestViewManager = require './guest-view-manager' event.returnValue = valueToMeta event.sender, guestViewManager.getGuest(guestInstanceId) catch e - event.returnValue = errorToMeta e + event.returnValue = exceptionToMeta e diff --git a/atom/renderer/api/lib/remote.coffee b/atom/renderer/api/lib/remote.coffee index 1f17cf34002..4eb56bcf027 100644 --- a/atom/renderer/api/lib/remote.coffee +++ b/atom/renderer/api/lib/remote.coffee @@ -46,7 +46,8 @@ metaToValue = (meta) -> when 'array' then (metaToValue(el) for el in meta.members) when 'buffer' then new Buffer(meta.value) when 'promise' then Promise.resolve(then: metaToValue(meta.then)) - when 'error' + when 'error' then new Error(meta.message) + when 'exception' throw new Error("#{meta.message}\n#{meta.stack}") else if meta.type is 'function' From 95fe4beda83318949c51e0100d46f221ab2b265e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 14:36:36 +0800 Subject: [PATCH 2/9] Pass real Error object in error event --- atom/browser/api/atom_api_auto_updater.cc | 11 +++++++++-- docs/api/auto-updater.md | 3 +-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/atom/browser/api/atom_api_auto_updater.cc b/atom/browser/api/atom_api_auto_updater.cc index 250aa3e45f6..d8407f60b32 100644 --- a/atom/browser/api/atom_api_auto_updater.cc +++ b/atom/browser/api/atom_api_auto_updater.cc @@ -23,8 +23,15 @@ AutoUpdater::~AutoUpdater() { auto_updater::AutoUpdater::SetDelegate(NULL); } -void AutoUpdater::OnError(const std::string& error) { - Emit("error", error); +void AutoUpdater::OnError(const std::string& message) { + 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() { diff --git a/docs/api/auto-updater.md b/docs/api/auto-updater.md index 9734f592c8f..ea731cd869a 100644 --- a/docs/api/auto-updater.md +++ b/docs/api/auto-updater.md @@ -110,8 +110,7 @@ The `autoUpdater` object emits the following events: Returns: -* `event` Event -* `message` String +* `error` Error Emitted when there is an error while updating. From a3f62da615221a3e98c43d518b0c03a45783aa4d Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 14:51:25 +0800 Subject: [PATCH 3/9] Fix passing Date object in remote Close #2017. --- atom/browser/lib/rpc-server.coffee | 3 +++ atom/renderer/api/lib/remote.coffee | 1 + 2 files changed, 4 insertions(+) diff --git a/atom/browser/lib/rpc-server.coffee b/atom/browser/lib/rpc-server.coffee index 6a20d09d119..6b1f95a841a 100644 --- a/atom/browser/lib/rpc-server.coffee +++ b/atom/browser/lib/rpc-server.coffee @@ -11,6 +11,7 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) -> meta.type = 'value' if value is null 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' # Treat simple objects as value. @@ -39,6 +40,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) -> 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 meta.type = 'value' meta.value = value diff --git a/atom/renderer/api/lib/remote.coffee b/atom/renderer/api/lib/remote.coffee index 4eb56bcf027..8a5565f0656 100644 --- a/atom/renderer/api/lib/remote.coffee +++ b/atom/renderer/api/lib/remote.coffee @@ -47,6 +47,7 @@ metaToValue = (meta) -> when 'buffer' then new Buffer(meta.value) when 'promise' then Promise.resolve(then: metaToValue(meta.then)) when 'error' then new Error(meta.message) + when 'date' then new Date(meta.value) when 'exception' throw new Error("#{meta.message}\n#{meta.stack}") else From 85c84a0eb034ce552e4ed48ed56ed513d365a555 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 14:58:19 +0800 Subject: [PATCH 4/9] Emit Date object in C++ directly --- atom/browser/api/atom_api_auto_updater.cc | 22 +++++++++++++++++++--- atom/browser/api/lib/auto-updater.coffee | 19 ++++++++----------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/atom/browser/api/atom_api_auto_updater.cc b/atom/browser/api/atom_api_auto_updater.cc index d8407f60b32..9a240a27903 100644 --- a/atom/browser/api/atom_api_auto_updater.cc +++ b/atom/browser/api/atom_api_auto_updater.cc @@ -11,6 +11,23 @@ #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" +namespace mate { + +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const base::Time& val) { + v8::MaybeLocal date = v8::Date::New( + isolate->GetCurrentContext(), val.ToJsTime()); + if (date.IsEmpty()) + return v8::Null(isolate); + else + return date.ToLocalChecked(); + } +}; + +} // namespace mate + namespace atom { namespace api { @@ -49,11 +66,10 @@ void AutoUpdater::OnUpdateNotAvailable() { void AutoUpdater::OnUpdateDownloaded(const std::string& release_notes, const std::string& release_name, const base::Time& release_date, - const std::string& update_url, + const std::string& url, const base::Closure& quit_and_install) { quit_and_install_ = quit_and_install; - Emit("update-downloaded-raw", release_notes, release_name, - release_date.ToJsTime(), update_url); + Emit("update-downloaded", release_notes, release_name, release_date, url); } mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder( diff --git a/atom/browser/api/lib/auto-updater.coffee b/atom/browser/api/lib/auto-updater.coffee index c039bd12dc4..7e3fa99a76d 100644 --- a/atom/browser/api/lib/auto-updater.coffee +++ b/atom/browser/api/lib/auto-updater.coffee @@ -1,15 +1,12 @@ -switch process.platform - when 'win32' - autoUpdater = require './auto-updater/auto-updater-win' - else - # take the default binding for the current platform - autoUpdater = process.atomBinding('auto_updater').autoUpdater - EventEmitter = require('events').EventEmitter - autoUpdater.__proto__ = EventEmitter.prototype +if process.platform is 'win32' + module.exports = require './auto-updater/auto-updater-win' + return -autoUpdater.on 'update-downloaded-raw', (args...) -> - args[3] = new Date(args[3]) # releaseDate - @emit 'update-downloaded', args..., => @quitAndInstall() +# Implementation on OS X. +autoUpdater = process.atomBinding('auto_updater').autoUpdater +EventEmitter = require('events').EventEmitter + +autoUpdater.__proto__ = EventEmitter.prototype autoUpdater.quitAndInstall = -> # If we don't have any window then quitAndInstall immediately. From d74ef5c078e2a13074147281cbed51378ee5315d Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 15:13:24 +0800 Subject: [PATCH 5/9] Move implementation of auto-updater on OS X to another file --- atom/browser/api/lib/auto-updater.coffee | 32 ++++--------------- .../lib/auto-updater/auto-updater-mac.coffee | 20 ++++++++++++ filenames.gypi | 1 + 3 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 atom/browser/api/lib/auto-updater/auto-updater-mac.coffee diff --git a/atom/browser/api/lib/auto-updater.coffee b/atom/browser/api/lib/auto-updater.coffee index 7e3fa99a76d..41b78a00d7e 100644 --- a/atom/browser/api/lib/auto-updater.coffee +++ b/atom/browser/api/lib/auto-updater.coffee @@ -1,25 +1,7 @@ -if process.platform is 'win32' - module.exports = require './auto-updater/auto-updater-win' - return - -# Implementation on OS X. -autoUpdater = process.atomBinding('auto_updater').autoUpdater -EventEmitter = require('events').EventEmitter - -autoUpdater.__proto__ = EventEmitter.prototype - -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 +switch process.platform + when 'win32' + module.exports = require './auto-updater/auto-updater-win' + when 'darwin' + module.exports = require './auto-updater/auto-updater-mac' + else + throw new Error('auto-updater is not implemented on this platform') diff --git a/atom/browser/api/lib/auto-updater/auto-updater-mac.coffee b/atom/browser/api/lib/auto-updater/auto-updater-mac.coffee new file mode 100644 index 00000000000..9ec9f3f9174 --- /dev/null +++ b/atom/browser/api/lib/auto-updater/auto-updater-mac.coffee @@ -0,0 +1,20 @@ +{EventEmitter} = require 'events' +{autoUpdater} = process.atomBinding 'auto_updater' + +autoUpdater.__proto__ = EventEmitter.prototype + +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 diff --git a/filenames.gypi b/filenames.gypi index 389c245ffa8..f6a7dbd0868 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -11,6 +11,7 @@ 'atom/browser/api/lib/app.coffee', 'atom/browser/api/lib/atom-delegate.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/squirrel-update-win.coffee', 'atom/browser/api/lib/browser-window.coffee', From f89d28a63ebdaff666a2d22cec50f27a753562da Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 15:40:56 +0800 Subject: [PATCH 6/9] Simplify the auto-updater implementations We used to use Sparkle on OS X, and the design was reserved to be extended to all platforms, which are all wrong now. --- atom/browser/api/atom_api_auto_updater.cc | 30 ++++++++----- atom/browser/api/atom_api_auto_updater.h | 24 +++++----- .../lib/auto-updater/auto-updater-mac.coffee | 14 ------ atom/browser/auto_updater.cc | 11 +++-- atom/browser/auto_updater.h | 35 +++++++++++++-- atom/browser/auto_updater_delegate.h | 45 ------------------- atom/browser/auto_updater_linux.cc | 17 ------- atom/browser/auto_updater_mac.mm | 26 +++++------ atom/browser/auto_updater_win.cc | 17 ------- filenames.gypi | 3 -- 10 files changed, 81 insertions(+), 141 deletions(-) delete mode 100644 atom/browser/auto_updater_delegate.h delete mode 100644 atom/browser/auto_updater_linux.cc delete mode 100644 atom/browser/auto_updater_win.cc diff --git a/atom/browser/api/atom_api_auto_updater.cc b/atom/browser/api/atom_api_auto_updater.cc index 9a240a27903..1b7c0cee7d9 100644 --- a/atom/browser/api/atom_api_auto_updater.cc +++ b/atom/browser/api/atom_api_auto_updater.cc @@ -5,8 +5,9 @@ #include "atom/browser/api/atom_api_auto_updater.h" #include "base/time/time.h" -#include "atom/browser/auto_updater.h" #include "atom/browser/browser.h" +#include "atom/browser/native_window.h" +#include "atom/browser/window_list.h" #include "atom/common/node_includes.h" #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" @@ -37,7 +38,7 @@ AutoUpdater::AutoUpdater() { } AutoUpdater::~AutoUpdater() { - auto_updater::AutoUpdater::SetDelegate(NULL); + auto_updater::AutoUpdater::SetDelegate(nullptr); } void AutoUpdater::OnError(const std::string& message) { @@ -66,25 +67,34 @@ void AutoUpdater::OnUpdateNotAvailable() { void AutoUpdater::OnUpdateDownloaded(const std::string& release_notes, const std::string& release_name, const base::Time& release_date, - const std::string& url, - const base::Closure& quit_and_install) { - quit_and_install_ = quit_and_install; + const std::string& url) { Emit("update-downloaded", release_notes, release_name, release_date, url); } +void AutoUpdater::OnWindowAllClosed() { + QuitAndInstall(); +} + mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder( v8::Isolate* isolate) { return mate::ObjectTemplateBuilder(isolate) .SetMethod("setFeedUrl", &auto_updater::AutoUpdater::SetFeedURL) .SetMethod("checkForUpdates", &auto_updater::AutoUpdater::CheckForUpdates) - .SetMethod("_quitAndInstall", &AutoUpdater::QuitAndInstall); + .SetMethod("quitAndInstall", &AutoUpdater::QuitAndInstall); } void AutoUpdater::QuitAndInstall() { - if (quit_and_install_.is_null()) - Browser::Get()->Shutdown(); - else - quit_and_install_.Run(); + // If we don't have any window then quitAndInstall immediately. + WindowList* window_list = WindowList::GetInstance(); + if (window_list->size() == 0) { + 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 diff --git a/atom/browser/api/atom_api_auto_updater.h b/atom/browser/api/atom_api_auto_updater.h index 50c3989703a..95b91041e9e 100644 --- a/atom/browser/api/atom_api_auto_updater.h +++ b/atom/browser/api/atom_api_auto_updater.h @@ -7,9 +7,9 @@ #include -#include "base/callback.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" namespace atom { @@ -17,7 +17,8 @@ namespace atom { namespace api { class AutoUpdater : public mate::EventEmitter, - public auto_updater::AutoUpdaterDelegate { + public auto_updater::Delegate, + public WindowListObserver { public: static mate::Handle Create(v8::Isolate* isolate); @@ -25,17 +26,18 @@ class AutoUpdater : public mate::EventEmitter, AutoUpdater(); virtual ~AutoUpdater(); - // AutoUpdaterDelegate implementations. + // Delegate implementations. void OnError(const std::string& error) override; void OnCheckingForUpdate() override; void OnUpdateAvailable() override; void OnUpdateNotAvailable() override; - 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) override; + void OnUpdateDownloaded(const std::string& release_notes, + const std::string& release_name, + const base::Time& release_date, + const std::string& update_url) override; + + // WindowListObserver: + void OnWindowAllClosed() override; // mate::Wrappable implementations: mate::ObjectTemplateBuilder GetObjectTemplateBuilder( @@ -44,8 +46,6 @@ class AutoUpdater : public mate::EventEmitter, private: void QuitAndInstall(); - base::Closure quit_and_install_; - DISALLOW_COPY_AND_ASSIGN(AutoUpdater); }; diff --git a/atom/browser/api/lib/auto-updater/auto-updater-mac.coffee b/atom/browser/api/lib/auto-updater/auto-updater-mac.coffee index 9ec9f3f9174..187be64f5ad 100644 --- a/atom/browser/api/lib/auto-updater/auto-updater-mac.coffee +++ b/atom/browser/api/lib/auto-updater/auto-updater-mac.coffee @@ -3,18 +3,4 @@ autoUpdater.__proto__ = EventEmitter.prototype -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 diff --git a/atom/browser/auto_updater.cc b/atom/browser/auto_updater.cc index 7ebae510e9f..7dbfc5a6feb 100644 --- a/atom/browser/auto_updater.cc +++ b/atom/browser/auto_updater.cc @@ -6,22 +6,25 @@ namespace auto_updater { -AutoUpdaterDelegate* AutoUpdater::delegate_ = NULL; +Delegate* AutoUpdater::delegate_ = nullptr; -AutoUpdaterDelegate* AutoUpdater::GetDelegate() { +Delegate* AutoUpdater::GetDelegate() { return delegate_; } -void AutoUpdater::SetDelegate(AutoUpdaterDelegate* delegate) { +void AutoUpdater::SetDelegate(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::CheckForUpdates() { } + +void AutoUpdater::QuitAndInstall() { +} #endif } // namespace auto_updater diff --git a/atom/browser/auto_updater.h b/atom/browser/auto_updater.h index e31aa0978a1..9e479d4220d 100644 --- a/atom/browser/auto_updater.h +++ b/atom/browser/auto_updater.h @@ -9,21 +9,48 @@ #include "base/basictypes.h" +namespace base { +class Time; +} + 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 { public: // Gets/Sets the delegate. - static AutoUpdaterDelegate* GetDelegate(); - static void SetDelegate(AutoUpdaterDelegate* delegate); + static Delegate* GetDelegate(); + static void SetDelegate(Delegate* delegate); static void SetFeedURL(const std::string& url); static void CheckForUpdates(); + static void QuitAndInstall(); private: - static AutoUpdaterDelegate* delegate_; + static Delegate* delegate_; DISALLOW_IMPLICIT_CONSTRUCTORS(AutoUpdater); }; diff --git a/atom/browser/auto_updater_delegate.h b/atom/browser/auto_updater_delegate.h deleted file mode 100644 index 804bc8503a1..00000000000 --- a/atom/browser/auto_updater_delegate.h +++ /dev/null @@ -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 - -#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_ diff --git a/atom/browser/auto_updater_linux.cc b/atom/browser/auto_updater_linux.cc deleted file mode 100644 index 00c95d0d45d..00000000000 --- a/atom/browser/auto_updater_linux.cc +++ /dev/null @@ -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 diff --git a/atom/browser/auto_updater_mac.mm b/atom/browser/auto_updater_mac.mm index 42ee8413632..a55cdd28126 100644 --- a/atom/browser/auto_updater_mac.mm +++ b/atom/browser/auto_updater_mac.mm @@ -12,9 +12,6 @@ #include "base/bind.h" #include "base/time/time.h" #include "base/strings/sys_string_conversions.h" -#include "atom/browser/auto_updater_delegate.h" - -#include namespace auto_updater { @@ -23,20 +20,12 @@ namespace { // The gloal SQRLUpdater object. 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 // static void AutoUpdater::SetFeedURL(const std::string& feed) { if (g_updater == nil) { - AutoUpdaterDelegate* delegate = GetDelegate(); + Delegate* delegate = GetDelegate(); if (!delegate) return; @@ -67,7 +56,7 @@ void AutoUpdater::SetFeedURL(const std::string& feed) { // static void AutoUpdater::CheckForUpdates() { - AutoUpdaterDelegate* delegate = GetDelegate(); + Delegate* delegate = GetDelegate(); if (!delegate) return; @@ -86,8 +75,7 @@ void AutoUpdater::CheckForUpdates() { base::SysNSStringToUTF8(update.releaseNotes), base::SysNSStringToUTF8(update.releaseName), base::Time::FromDoubleT(update.releaseDate.timeIntervalSince1970), - base::SysNSStringToUTF8(update.updateURL.absoluteString), - base::Bind(RelaunchToInstallUpdate)); + base::SysNSStringToUTF8(update.updateURL.absoluteString)); } else { // When the completed event is sent with no update, then we know there // 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 diff --git a/atom/browser/auto_updater_win.cc b/atom/browser/auto_updater_win.cc deleted file mode 100644 index 00c95d0d45d..00000000000 --- a/atom/browser/auto_updater_win.cc +++ /dev/null @@ -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 diff --git a/filenames.gypi b/filenames.gypi index f6a7dbd0868..e6d180d652e 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -115,10 +115,7 @@ 'atom/browser/api/save_page_handler.h', 'atom/browser/auto_updater.cc', '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_win.cc', 'atom/browser/atom_access_token_store.cc', 'atom/browser/atom_access_token_store.h', 'atom/browser/atom_browser_client.cc', From aeafc46dede25f05e28ca87efa961637e6118011 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 15:50:55 +0800 Subject: [PATCH 7/9] Keep compatibility with old API style --- atom/browser/api/atom_api_auto_updater.cc | 5 ++++- docs/api/auto-updater.md | 13 ++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/atom/browser/api/atom_api_auto_updater.cc b/atom/browser/api/atom_api_auto_updater.cc index 1b7c0cee7d9..1c80f73f7a7 100644 --- a/atom/browser/api/atom_api_auto_updater.cc +++ b/atom/browser/api/atom_api_auto_updater.cc @@ -8,6 +8,7 @@ #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 "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" @@ -68,7 +69,9 @@ void AutoUpdater::OnUpdateDownloaded(const std::string& release_notes, const std::string& release_name, const base::Time& release_date, const std::string& url) { - Emit("update-downloaded", release_notes, release_name, release_date, url); + Emit("update-downloaded", release_notes, release_name, release_date, url, + // Keep compatibility with old APIs. + base::Bind(&AutoUpdater::QuitAndInstall, base::Unretained(this))); } void AutoUpdater::OnWindowAllClosed() { diff --git a/docs/api/auto-updater.md b/docs/api/auto-updater.md index ea731cd869a..55c605762e3 100644 --- a/docs/api/auto-updater.md +++ b/docs/api/auto-updater.md @@ -136,10 +136,8 @@ Returns: * `releaseName` String * `releaseDate` Date * `updateUrl` String -* `quitAndUpdate` Function -Emitted when an update has been downloaded. Calling `quitAndUpdate()` will -restart the application and install the update. +Emitted when an update has been downloaded. ## Methods @@ -149,10 +147,15 @@ The `autoUpdater` object has the following methods: * `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. ### `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. + +### `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. From fae2c7bc7a129e86b17e8222b61e3c6aca96afbd Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 19:41:54 +0800 Subject: [PATCH 8/9] win: Make auto-updater really work Apparently that PR was never tested. --- .../lib/auto-updater/auto-updater-win.coffee | 55 ++++++------- .../auto-updater/squirrel-update-win.coffee | 80 +++++++++++-------- 2 files changed, 71 insertions(+), 64 deletions(-) diff --git a/atom/browser/api/lib/auto-updater/auto-updater-win.coffee b/atom/browser/api/lib/auto-updater/auto-updater-win.coffee index 764f76a2253..a9a61d8efe3 100644 --- a/atom/browser/api/lib/auto-updater/auto-updater-win.coffee +++ b/atom/browser/api/lib/auto-updater/auto-updater-win.coffee @@ -1,45 +1,42 @@ +app = require 'app' +url = require 'url' {EventEmitter} = require 'events' -SquirrelUpdate = require './auto-updater/squirrel-update-win' -app = require 'app' -url = require 'url' + +squirrelUpdate = require './squirrel-update-win' class AutoUpdater extends EventEmitter - quitAndInstall: -> - SquirrelUpdate.processStart -> - app.quit() + squirrelUpdate.processStart() + app.quit() setFeedUrl: (updateUrl) -> - # set feed URL only when it hasn't been set before - unless @updateUrl - @updateUrl = updateUrl + @updateUrl = updateUrl 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' - unless SquirrelUpdate.supported() - @emit 'update-not-available' - return - - SquirrelUpdate.download (error, update) => - if error? - @emit 'update-not-available' - return - - unless update? - @emit 'update-not-available' - return + squirrelUpdate.download @updateUrl, (error, update) => + return @emitError error if error? + return @emit 'update-not-available' unless update? @emit 'update-available' - SquirrelUpdate.update @updateUrl, (error) => - if error? - @emit 'update-not-available' - return + squirrelUpdate.update @updateUrl, (error) => + return @emitError error if error? - # info about the newly installed version and a function any of the event listeners can call to restart the application - @emit 'update-downloaded', {}, update.releaseNotes, update.version, new Date(), @updateUrl, => @quitAndInstall() + {releaseNotes, version} = update + # 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 diff --git a/atom/browser/api/lib/auto-updater/squirrel-update-win.coffee b/atom/browser/api/lib/auto-updater/squirrel-update-win.coffee index 95d9e1465d8..ed302124e52 100644 --- a/atom/browser/api/lib/auto-updater/squirrel-update-win.coffee +++ b/atom/browser/api/lib/auto-updater/squirrel-update-win.coffee @@ -1,57 +1,67 @@ -ChildProcess = require 'child_process' -fs = require 'fs' -path = require 'path' +fs = require 'fs' +path = require 'path' +{spawn} = require 'child_process' -appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/ -rootApplicationFolder = path.resolve appFolder, '..' # i.e. my-app/ -updateDotExe = path.join rootApplicationFolder, 'Update.exe' -exeName = path.basename process.execPath +appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/ +updateExe = path.resolve appFolder, '..', 'Update.exe' # i.e. my-app/Update.exe +exeName = path.basename process.execPath # Spawn a command and invoke the callback when it completes with an error # and the output from standard out. -spawnUpdate = (args, callback) -> - stdout = '' - +spawnUpdate = (args, detached, callback) -> try - spawnedProcess = ChildProcess.spawn(updateDotExe, args) + spawnedProcess = spawn updateExe, args, {detached} catch error - # Spawn can throw an error - process.nextTick -> callback?(error, stdout) + # Shouldn't happen, but still guard it. + process.nextTick -> callback error return + stdout = '' + stderr = '' spawnedProcess.stdout.on 'data', (data) -> stdout += data + spawnedProcess.stderr.on 'data', (data) -> stderr += data - error = null - spawnedProcess.on 'error', (processError) -> error ?= processError - spawnedProcess.on 'close', (code, signal) -> - error ?= new Error("Command failed: #{signal ? code}") if code isnt 0 - error?.code ?= code - error?.stdout ?= stdout - callback?(error, stdout) + errorEmitted = false + spawnedProcess.on 'error', (error) -> + errorEmitted = true + callback error + spawnedProcess.on 'exit', (code, signal) -> + # We may have already emitted an error. + return if errorEmitted -processStart = (callback) -> - spawnUpdate(['--processStart', exeName], callback) + # Process terminated with error. + if code isnt 0 + return callback "Command failed: #{signal ? code}\n#{stderr}" -download = (callback) -> - spawnUpdate ['--download', @updateUrl], (error, stdout) -> + # Success. + 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? try # 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?() - catch error - error.stdout = stdout - return callback(error) + catch + return callback "Invalid result:\n#{stdout}" - callback(null, update) + callback null, update -update = (updateUrl, callback) -> - spawnUpdate ['--update', updateUrl], callback +# Update the application to the latest remote version specified by URL. +exports.update = (updateUrl, callback) -> + spawnUpdate ['--update', updateUrl], false, callback # Is the Update.exe installed with the current application? exports.supported = -> - fs.accessSync(updateDotExe, fs.R_OK) -exports.processStart = processStart -exports.download = download -exports.update = update + try + fs.accessSync updateExe, fs.R_OK + return true + catch + return false From 88b05cff3e21627fcf0ed1a7223cf325f68cd48a Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 20:11:59 +0800 Subject: [PATCH 9/9] docs: auto-updater on Windows --- docs/api/auto-updater.md | 116 ++++++++------------------------------- 1 file changed, 24 insertions(+), 92 deletions(-) diff --git a/docs/api/auto-updater.md b/docs/api/auto-updater.md index 55c605762e3..7649f0f1048 100644 --- a/docs/api/auto-updater.md +++ b/docs/api/auto-updater.md @@ -1,106 +1,31 @@ # 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) -to build a Windows installer for your app. +## Platform notices -The `auto-updater` module is a simple wrapper around the -[Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac) framework. +Though `autoUpdater` provides an uniform API for different platforms, there are +still some subtle differences on each platform. -Squirrel.Mac requires that your `.app` folder is signed using the -[codesign](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/codesign.1.html) -utility for updates to be installed. +### OS X -## 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 -and transparent as updates to a website**. +### Windows -Instead of publishing a feed of versions from which your app must select, -Squirrel updates to the version your server tells it to. This allows you to -intelligently update your clients based on the request you give to Squirrel. +On Windows you have to install your app into user's machine before you can use +the auto-updater, it is recommended to use [grunt-electron-installer][installer] +module to generate a Windows installer. -Your request can include authentication details, custom headers or a request -body so that your server has the context it needs in order to supply the most -suitable update. +The server-side setup is also different from OS X, you can read the documents of +[Squirrel.Windows][squirrel-windows] to get more details. -The update JSON Squirrel requests should be dynamically generated based on -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). +### Linux -Squirrel's installer is designed to be fault tolerant and ensures that any -updates installed are valid. - -## 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, it’s compatible with `Squirrel.Mac` and `Squirrel.Windows` so you get cross-platform support out of the box. +There is not built-in support for auto-updater on Linux, it is recommended to +use the distribution's package manager to update your app. ## Events @@ -139,6 +64,8 @@ Returns: Emitted when an update has been downloaded. +On Windows only `releaseName` is available. + ## Methods The `autoUpdater` object has the following methods: @@ -159,3 +86,8 @@ using this API. 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