From 56b53e71aabad179eecc8285dbae16aaaa18a6a3 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Thu, 7 Dec 2017 10:10:40 -0300 Subject: [PATCH 01/11] Implement powerMonitor 'shutdown' event for Linux. The event is emitted when the OS is rebooting/shutting down, and allows an electron app to call `e.preventDefault()` in order to delay shutdown and exit cleanly. --- atom/browser/api/atom_api_power_monitor.cc | 15 ++++++ atom/browser/api/atom_api_power_monitor.h | 6 +++ atom/browser/lib/power_observer.h | 14 +++++- atom/browser/lib/power_observer_linux.cc | 54 +++++++++++++++++++++- atom/browser/lib/power_observer_linux.h | 5 ++ lib/browser/api/power-monitor.js | 12 +++++ 6 files changed, 104 insertions(+), 2 deletions(-) diff --git a/atom/browser/api/atom_api_power_monitor.cc b/atom/browser/api/atom_api_power_monitor.cc index 429d3fc26e80..95b5624951b9 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -24,6 +24,14 @@ PowerMonitor::~PowerMonitor() { base::PowerMonitor::Get()->RemoveObserver(this); } +void PowerMonitor::BlockShutdown(mate::Arguments* args) { + atom::PowerObserver::BlockShutdown(); +} + +void PowerMonitor::UnblockShutdown(mate::Arguments* args) { + atom::PowerObserver::UnblockShutdown(); +} + void PowerMonitor::OnPowerStateChange(bool on_battery_power) { if (on_battery_power) Emit("on-battery"); @@ -39,6 +47,10 @@ void PowerMonitor::OnResume() { Emit("resume"); } +bool PowerMonitor::OnShutdown() { + return Emit("shutdown"); +} + // static v8::Local PowerMonitor::Create(v8::Isolate* isolate) { if (!Browser::Get()->is_ready()) { @@ -55,6 +67,9 @@ v8::Local PowerMonitor::Create(v8::Isolate* isolate) { void PowerMonitor::BuildPrototype( v8::Isolate* isolate, v8::Local prototype) { prototype->SetClassName(mate::StringToV8(isolate, "PowerMonitor")); + mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) + .SetMethod("blockShutdown", &PowerMonitor::BlockShutdown) + .SetMethod("unblockShutdown", &PowerMonitor::UnblockShutdown); } } // namespace api diff --git a/atom/browser/api/atom_api_power_monitor.h b/atom/browser/api/atom_api_power_monitor.h index 1a5f1ec6bd3b..c8174494ade8 100644 --- a/atom/browser/api/atom_api_power_monitor.h +++ b/atom/browser/api/atom_api_power_monitor.h @@ -26,11 +26,17 @@ class PowerMonitor : public mate::TrackableObject, explicit PowerMonitor(v8::Isolate* isolate); ~PowerMonitor() override; + void BlockShutdown(mate::Arguments* args); + void UnblockShutdown(mate::Arguments* args); + // base::PowerObserver implementations: void OnPowerStateChange(bool on_battery_power) override; void OnSuspend() override; void OnResume() override; + // atom::PowerObserver + bool OnShutdown() override; + private: DISALLOW_COPY_AND_ASSIGN(PowerMonitor); }; diff --git a/atom/browser/lib/power_observer.h b/atom/browser/lib/power_observer.h index 0935b221e33d..f729235e46c0 100644 --- a/atom/browser/lib/power_observer.h +++ b/atom/browser/lib/power_observer.h @@ -18,7 +18,19 @@ namespace atom { #if defined(OS_LINUX) typedef PowerObserverLinux PowerObserver; #else -typedef base::PowerObserver PowerObserver; +class PowerObserver : public base::PowerObserver { + public: + PowerObserver() {} + void BlockShutdown() {} + void UnblockShutdown() {} + // Notification that the system is rebooting or shutting down. If the + // implementation returns true, the PowerObserver instance should try to delay + // OS shutdown so the application can perform cleanup before exiting. + virtual bool OnShutdown() { return false; } + + private: + DISALLOW_COPY_AND_ASSIGN(PowerObserver); +}; #endif // defined(OS_LINUX) } // namespace atom diff --git a/atom/browser/lib/power_observer_linux.cc b/atom/browser/lib/power_observer_linux.cc index da4b8ea82a7b..ac641a2fe0a6 100644 --- a/atom/browser/lib/power_observer_linux.cc +++ b/atom/browser/lib/power_observer_linux.cc @@ -16,6 +16,10 @@ const char kLogindServiceName[] = "org.freedesktop.login1"; const char kLogindObjectPath[] = "/org/freedesktop/login1"; const char kLogindManagerInterface[] = "org.freedesktop.login1.Manager"; +// Store shutdown lock as a global, since we only want to release it when the +// main process exits. +base::ScopedFD shutdown_lock; + std::string get_executable_basename() { char buf[4096]; size_t buf_size = sizeof(buf); @@ -54,12 +58,18 @@ void PowerObserverLinux::OnLoginServiceAvailable(bool service_available) { LOG(WARNING) << kLogindServiceName << " not available"; return; } - // listen sleep + // Connect to PrepareForShutdown/PrepareForSleep signals + logind_->ConnectToSignal(kLogindManagerInterface, "PrepareForShutdown", + base::Bind(&PowerObserverLinux::OnPrepareForShutdown, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&PowerObserverLinux::OnSignalConnected, + weak_ptr_factory_.GetWeakPtr())); logind_->ConnectToSignal(kLogindManagerInterface, "PrepareForSleep", base::Bind(&PowerObserverLinux::OnPrepareForSleep, weak_ptr_factory_.GetWeakPtr()), base::Bind(&PowerObserverLinux::OnSignalConnected, weak_ptr_factory_.GetWeakPtr())); + // Take sleep inhibit lock TakeSleepLock(); } @@ -78,6 +88,32 @@ void PowerObserverLinux::TakeSleepLock() { weak_ptr_factory_.GetWeakPtr(), &sleep_lock_)); } +void PowerObserverLinux::BlockShutdown() { + if (shutdown_lock.is_valid()) { + LOG(WARNING) << "Trying to subscribe to shutdown multiple times"; + return; + } + dbus::MethodCall shutdown_inhibit_call(kLogindManagerInterface, "Inhibit"); + dbus::MessageWriter inhibit_writer(&shutdown_inhibit_call); + inhibit_writer.AppendString("shutdown"); // what + inhibit_writer.AppendString(lock_owner_name_); // who + inhibit_writer.AppendString("Ensure a clean shutdown"); // why + inhibit_writer.AppendString("delay"); // mode + logind_->CallMethod( + &shutdown_inhibit_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&PowerObserverLinux::OnInhibitResponse, + weak_ptr_factory_.GetWeakPtr(), &shutdown_lock)); +} + +void PowerObserverLinux::UnblockShutdown() { + if (!shutdown_lock.is_valid()) { + LOG(WARNING) + << "Trying to unsubscribe to shutdown without being subscribed"; + return; + } + shutdown_lock.reset(); +} + void PowerObserverLinux::OnInhibitResponse(base::ScopedFD* scoped_fd, dbus::Response* response) { dbus::MessageReader reader(response); @@ -100,6 +136,22 @@ void PowerObserverLinux::OnPrepareForSleep(dbus::Signal* signal) { } } +void PowerObserverLinux::OnPrepareForShutdown(dbus::Signal* signal) { + dbus::MessageReader reader(signal); + bool status; + if (!reader.PopBool(&status)) { + LOG(ERROR) << "Invalid signal: " << signal->ToString(); + return; + } + if (status) { + if (!OnShutdown()) { + // The user didn't try to prevent shutdown. Release the lock and allow the + // shutdown to continue normally. + shutdown_lock.reset(); + } + } +} + void PowerObserverLinux::OnSignalConnected(const std::string& interface, const std::string& signal, bool success) { diff --git a/atom/browser/lib/power_observer_linux.h b/atom/browser/lib/power_observer_linux.h index 60691f721183..2ea8f36a9cbd 100644 --- a/atom/browser/lib/power_observer_linux.h +++ b/atom/browser/lib/power_observer_linux.h @@ -20,11 +20,16 @@ class PowerObserverLinux : public base::PowerObserver { public: PowerObserverLinux(); + void BlockShutdown(); + void UnblockShutdown(); + virtual bool OnShutdown() { return false; } + private: void TakeSleepLock(); void OnLoginServiceAvailable(bool available); void OnInhibitResponse(base::ScopedFD* scoped_fd, dbus::Response* response); void OnPrepareForSleep(dbus::Signal* signal); + void OnPrepareForShutdown(dbus::Signal* signal); void OnSignalConnected(const std::string& interface, const std::string& signal, bool success); diff --git a/lib/browser/api/power-monitor.js b/lib/browser/api/power-monitor.js index e1dff2c3b73c..18bc7b7c2095 100644 --- a/lib/browser/api/power-monitor.js +++ b/lib/browser/api/power-monitor.js @@ -5,4 +5,16 @@ const {powerMonitor, PowerMonitor} = process.atomBinding('power_monitor') Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype) EventEmitter.call(powerMonitor) +powerMonitor.on('newListener', (event) => { + if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { + powerMonitor.blockShutdown() + } +}) + +powerMonitor.on('removeListener', (event) => { + if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { + powerMonitor.unblockShutdown() + } +}) + module.exports = powerMonitor From df6328e5d74917c20fffd89688e12071f47aca01 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Tue, 12 Dec 2017 14:06:45 -0300 Subject: [PATCH 02/11] Add some tests for powerMonitor "shutdown" event --- spec/api-power-monitor-spec.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/spec/api-power-monitor-spec.js b/spec/api-power-monitor-spec.js index 5b8b84fc655d..3b8d9ab5e4a9 100644 --- a/spec/api-power-monitor-spec.js +++ b/spec/api-power-monitor-spec.js @@ -85,5 +85,34 @@ const skip = process.platform !== 'linux' || !process.env.DBUS_SYSTEM_BUS_ADDRES }) }) }) + + describe('when a listener is added to shutdown event', () => { + before(async () => { + const calls = await getCalls() + assert.equal(calls.length, 2) + powerMonitor.once('shutdown', () => { }) + }) + + it('should call Inhibit to delay shutdown', async () => { + const calls = await getCalls() + assert.equal(calls.length, 3) + assert.deepEqual(calls[2].slice(1), [ + 'Inhibit', [ + [[{type: 's', child: []}], ['shutdown']], + [[{type: 's', child: []}], ['electron']], + [[{type: 's', child: []}], ['Ensure a clean shutdown']], + [[{type: 's', child: []}], ['delay']] + ] + ]) + }) + + describe('when PrepareForShutdown(true) signal is sent by logind', () => { + it('should emit "shutdown" event', (done) => { + powerMonitor.once('shutdown', () => { done() }) + emitSignal('org.freedesktop.login1.Manager', 'PrepareForShutdown', + 'b', [['b', true]]) + }) + }) + }) }) }) From 45763c0afb375fa2f6072dc6d85e85296d2df054 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Thu, 14 Dec 2017 08:39:00 -0300 Subject: [PATCH 03/11] fixup! Implement powerMonitor 'shutdown' event for Linux --- atom/browser/lib/power_observer_linux.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/atom/browser/lib/power_observer_linux.cc b/atom/browser/lib/power_observer_linux.cc index ac641a2fe0a6..d34b11f524f8 100644 --- a/atom/browser/lib/power_observer_linux.cc +++ b/atom/browser/lib/power_observer_linux.cc @@ -122,12 +122,12 @@ void PowerObserverLinux::OnInhibitResponse(base::ScopedFD* scoped_fd, void PowerObserverLinux::OnPrepareForSleep(dbus::Signal* signal) { dbus::MessageReader reader(signal); - bool status; - if (!reader.PopBool(&status)) { + bool suspending; + if (!reader.PopBool(&suspending)) { LOG(ERROR) << "Invalid signal: " << signal->ToString(); return; } - if (status) { + if (suspending) { OnSuspend(); sleep_lock_.reset(); } else { @@ -138,12 +138,12 @@ void PowerObserverLinux::OnPrepareForSleep(dbus::Signal* signal) { void PowerObserverLinux::OnPrepareForShutdown(dbus::Signal* signal) { dbus::MessageReader reader(signal); - bool status; - if (!reader.PopBool(&status)) { + bool shutting_down; + if (!reader.PopBool(&shutting_down)) { LOG(ERROR) << "Invalid signal: " << signal->ToString(); return; } - if (status) { + if (shutting_down) { if (!OnShutdown()) { // The user didn't try to prevent shutdown. Release the lock and allow the // shutdown to continue normally. From f0e210360f9d6eec2a588ad10a1ef7711e2f79cf Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Thu, 21 Dec 2017 07:49:54 -0300 Subject: [PATCH 04/11] fixup! Implement powerMonitor 'shutdown' event for Linux --- atom/browser/lib/power_observer_linux.cc | 12 ++++++++---- atom/browser/lib/power_observer_linux.h | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/atom/browser/lib/power_observer_linux.cc b/atom/browser/lib/power_observer_linux.cc index d34b11f524f8..b94f35540bb3 100644 --- a/atom/browser/lib/power_observer_linux.cc +++ b/atom/browser/lib/power_observer_linux.cc @@ -70,10 +70,10 @@ void PowerObserverLinux::OnLoginServiceAvailable(bool service_available) { base::Bind(&PowerObserverLinux::OnSignalConnected, weak_ptr_factory_.GetWeakPtr())); // Take sleep inhibit lock - TakeSleepLock(); + BlockSleep(); } -void PowerObserverLinux::TakeSleepLock() { +void PowerObserverLinux::BlockSleep() { dbus::MethodCall sleep_inhibit_call(kLogindManagerInterface, "Inhibit"); dbus::MessageWriter inhibit_writer(&sleep_inhibit_call); inhibit_writer.AppendString("sleep"); // what @@ -88,6 +88,10 @@ void PowerObserverLinux::TakeSleepLock() { weak_ptr_factory_.GetWeakPtr(), &sleep_lock_)); } +void PowerObserverLinux::UnblockSleep() { + sleep_lock_.reset(); +} + void PowerObserverLinux::BlockShutdown() { if (shutdown_lock.is_valid()) { LOG(WARNING) << "Trying to subscribe to shutdown multiple times"; @@ -129,9 +133,9 @@ void PowerObserverLinux::OnPrepareForSleep(dbus::Signal* signal) { } if (suspending) { OnSuspend(); - sleep_lock_.reset(); + UnblockSleep(); } else { - TakeSleepLock(); + BlockSleep(); OnResume(); } } diff --git a/atom/browser/lib/power_observer_linux.h b/atom/browser/lib/power_observer_linux.h index 2ea8f36a9cbd..66b97158d660 100644 --- a/atom/browser/lib/power_observer_linux.h +++ b/atom/browser/lib/power_observer_linux.h @@ -20,12 +20,13 @@ class PowerObserverLinux : public base::PowerObserver { public: PowerObserverLinux(); + void BlockSleep(); + void UnblockSleep(); void BlockShutdown(); void UnblockShutdown(); virtual bool OnShutdown() { return false; } private: - void TakeSleepLock(); void OnLoginServiceAvailable(bool available); void OnInhibitResponse(base::ScopedFD* scoped_fd, dbus::Response* response); void OnPrepareForSleep(dbus::Signal* signal); From 176c03fa15a91994dc467409da6efa6d2c181aa9 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Thu, 21 Dec 2017 08:01:34 -0300 Subject: [PATCH 05/11] Add doc for powerMonitor shutdown event --- docs/api/power-monitor.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/api/power-monitor.md b/docs/api/power-monitor.md index 0c012c64ac8c..eac6695c571c 100644 --- a/docs/api/power-monitor.md +++ b/docs/api/power-monitor.md @@ -39,3 +39,10 @@ Emitted when the system changes to AC power. ### Event: 'on-battery' _Windows_ Emitted when system changes to battery power. + +### Event: 'shutdown' _Linux_ + +Emitted when the system is about to reboot or shut down. If the event handler +invokes `e.preventDefault()`, Electron will attempt to delay system shutdown in +order for the app to exit cleanly. If `e.preventDefault()` is called, the app +should exit as soon as possible by calling something like `app.quit()`. From e0e7dd2a8f303e6826963e38efdba4d87b72fea6 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Thu, 21 Dec 2017 08:12:25 -0300 Subject: [PATCH 06/11] fixup! Implement powerMonitor 'shutdown' event for Linux. --- atom/browser/lib/power_observer_linux.cc | 14 +++++--------- atom/browser/lib/power_observer_linux.h | 1 + 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/atom/browser/lib/power_observer_linux.cc b/atom/browser/lib/power_observer_linux.cc index b94f35540bb3..ad3cc82f478f 100644 --- a/atom/browser/lib/power_observer_linux.cc +++ b/atom/browser/lib/power_observer_linux.cc @@ -16,10 +16,6 @@ const char kLogindServiceName[] = "org.freedesktop.login1"; const char kLogindObjectPath[] = "/org/freedesktop/login1"; const char kLogindManagerInterface[] = "org.freedesktop.login1.Manager"; -// Store shutdown lock as a global, since we only want to release it when the -// main process exits. -base::ScopedFD shutdown_lock; - std::string get_executable_basename() { char buf[4096]; size_t buf_size = sizeof(buf); @@ -93,7 +89,7 @@ void PowerObserverLinux::UnblockSleep() { } void PowerObserverLinux::BlockShutdown() { - if (shutdown_lock.is_valid()) { + if (shutdown_lock_.is_valid()) { LOG(WARNING) << "Trying to subscribe to shutdown multiple times"; return; } @@ -106,16 +102,16 @@ void PowerObserverLinux::BlockShutdown() { logind_->CallMethod( &shutdown_inhibit_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, base::Bind(&PowerObserverLinux::OnInhibitResponse, - weak_ptr_factory_.GetWeakPtr(), &shutdown_lock)); + weak_ptr_factory_.GetWeakPtr(), &shutdown_lock_)); } void PowerObserverLinux::UnblockShutdown() { - if (!shutdown_lock.is_valid()) { + if (!shutdown_lock_.is_valid()) { LOG(WARNING) << "Trying to unsubscribe to shutdown without being subscribed"; return; } - shutdown_lock.reset(); + shutdown_lock_.reset(); } void PowerObserverLinux::OnInhibitResponse(base::ScopedFD* scoped_fd, @@ -151,7 +147,7 @@ void PowerObserverLinux::OnPrepareForShutdown(dbus::Signal* signal) { if (!OnShutdown()) { // The user didn't try to prevent shutdown. Release the lock and allow the // shutdown to continue normally. - shutdown_lock.reset(); + shutdown_lock_.reset(); } } } diff --git a/atom/browser/lib/power_observer_linux.h b/atom/browser/lib/power_observer_linux.h index 66b97158d660..c6099bce6245 100644 --- a/atom/browser/lib/power_observer_linux.h +++ b/atom/browser/lib/power_observer_linux.h @@ -39,6 +39,7 @@ class PowerObserverLinux : public base::PowerObserver { scoped_refptr logind_; std::string lock_owner_name_; base::ScopedFD sleep_lock_; + base::ScopedFD shutdown_lock_; base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(PowerObserverLinux); }; From 8ae3d9dd0b3010e6bfa5a8284d7b13a63c4be373 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 5 Feb 2018 15:28:58 +0900 Subject: [PATCH 07/11] Simplify the public PowerObserver interface --- atom/browser/api/atom_api_power_monitor.cc | 12 ++++------- atom/browser/api/atom_api_power_monitor.h | 5 ++--- atom/browser/lib/power_observer.h | 14 +------------ atom/browser/lib/power_observer_linux.h | 3 +++ lib/browser/api/power-monitor.js | 23 ++++++++++++---------- 5 files changed, 23 insertions(+), 34 deletions(-) diff --git a/atom/browser/api/atom_api_power_monitor.cc b/atom/browser/api/atom_api_power_monitor.cc index 95b5624951b9..2958c496a32b 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -24,14 +24,6 @@ PowerMonitor::~PowerMonitor() { base::PowerMonitor::Get()->RemoveObserver(this); } -void PowerMonitor::BlockShutdown(mate::Arguments* args) { - atom::PowerObserver::BlockShutdown(); -} - -void PowerMonitor::UnblockShutdown(mate::Arguments* args) { - atom::PowerObserver::UnblockShutdown(); -} - void PowerMonitor::OnPowerStateChange(bool on_battery_power) { if (on_battery_power) Emit("on-battery"); @@ -47,9 +39,11 @@ void PowerMonitor::OnResume() { Emit("resume"); } +#if defined(OS_LINUX) bool PowerMonitor::OnShutdown() { return Emit("shutdown"); } +#endif // static v8::Local PowerMonitor::Create(v8::Isolate* isolate) { @@ -67,9 +61,11 @@ v8::Local PowerMonitor::Create(v8::Isolate* isolate) { void PowerMonitor::BuildPrototype( v8::Isolate* isolate, v8::Local prototype) { prototype->SetClassName(mate::StringToV8(isolate, "PowerMonitor")); +#if defined(OS_LINUX) mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) .SetMethod("blockShutdown", &PowerMonitor::BlockShutdown) .SetMethod("unblockShutdown", &PowerMonitor::UnblockShutdown); +#endif } } // namespace api diff --git a/atom/browser/api/atom_api_power_monitor.h b/atom/browser/api/atom_api_power_monitor.h index c8174494ade8..8724aa50afc5 100644 --- a/atom/browser/api/atom_api_power_monitor.h +++ b/atom/browser/api/atom_api_power_monitor.h @@ -26,16 +26,15 @@ class PowerMonitor : public mate::TrackableObject, explicit PowerMonitor(v8::Isolate* isolate); ~PowerMonitor() override; - void BlockShutdown(mate::Arguments* args); - void UnblockShutdown(mate::Arguments* args); - // base::PowerObserver implementations: void OnPowerStateChange(bool on_battery_power) override; void OnSuspend() override; void OnResume() override; +#if defined(OS_LINUX) // atom::PowerObserver bool OnShutdown() override; +#endif private: DISALLOW_COPY_AND_ASSIGN(PowerMonitor); diff --git a/atom/browser/lib/power_observer.h b/atom/browser/lib/power_observer.h index f729235e46c0..0935b221e33d 100644 --- a/atom/browser/lib/power_observer.h +++ b/atom/browser/lib/power_observer.h @@ -18,19 +18,7 @@ namespace atom { #if defined(OS_LINUX) typedef PowerObserverLinux PowerObserver; #else -class PowerObserver : public base::PowerObserver { - public: - PowerObserver() {} - void BlockShutdown() {} - void UnblockShutdown() {} - // Notification that the system is rebooting or shutting down. If the - // implementation returns true, the PowerObserver instance should try to delay - // OS shutdown so the application can perform cleanup before exiting. - virtual bool OnShutdown() { return false; } - - private: - DISALLOW_COPY_AND_ASSIGN(PowerObserver); -}; +typedef base::PowerObserver PowerObserver; #endif // defined(OS_LINUX) } // namespace atom diff --git a/atom/browser/lib/power_observer_linux.h b/atom/browser/lib/power_observer_linux.h index c6099bce6245..a76ecaf3540d 100644 --- a/atom/browser/lib/power_observer_linux.h +++ b/atom/browser/lib/power_observer_linux.h @@ -20,10 +20,12 @@ class PowerObserverLinux : public base::PowerObserver { public: PowerObserverLinux(); + protected: void BlockSleep(); void UnblockSleep(); void BlockShutdown(); void UnblockShutdown(); + virtual bool OnShutdown() { return false; } private: @@ -41,6 +43,7 @@ class PowerObserverLinux : public base::PowerObserver { base::ScopedFD sleep_lock_; base::ScopedFD shutdown_lock_; base::WeakPtrFactory weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(PowerObserverLinux); }; diff --git a/lib/browser/api/power-monitor.js b/lib/browser/api/power-monitor.js index 18bc7b7c2095..deeda3bd4d4d 100644 --- a/lib/browser/api/power-monitor.js +++ b/lib/browser/api/power-monitor.js @@ -5,16 +5,19 @@ const {powerMonitor, PowerMonitor} = process.atomBinding('power_monitor') Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype) EventEmitter.call(powerMonitor) -powerMonitor.on('newListener', (event) => { - if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { - powerMonitor.blockShutdown() - } -}) +// On Linux we need to call blockShutdown() to subscribe to shutdown event. +if (process.platform === 'linux') { + powerMonitor.on('newListener', (event) => { + if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { + powerMonitor.blockShutdown() + } + }) -powerMonitor.on('removeListener', (event) => { - if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { - powerMonitor.unblockShutdown() - } -}) + powerMonitor.on('removeListener', (event) => { + if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { + powerMonitor.unblockShutdown() + } + }) +} module.exports = powerMonitor From 109e2c760f51d226ff4fc65e477bbc4e93da3304 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 5 Feb 2018 15:49:43 +0900 Subject: [PATCH 08/11] Do not use virtual function to request shutdown Would make it easier to port to other platforms. --- atom/browser/api/atom_api_power_monitor.cc | 25 ++++++++++++++++------ atom/browser/api/atom_api_power_monitor.h | 14 +++++++----- atom/browser/lib/power_observer_linux.cc | 6 +++++- atom/browser/lib/power_observer_linux.h | 4 +++- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/atom/browser/api/atom_api_power_monitor.cc b/atom/browser/api/atom_api_power_monitor.cc index 2958c496a32b..48194d0e3a99 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -16,6 +16,11 @@ namespace atom { namespace api { PowerMonitor::PowerMonitor(v8::Isolate* isolate) { +#if defined(OS_LINUX) + SetShutdownHandler(base::Bind(&PowerMonitor::ShouldShutdown, + // Passed to base class, no need for weak ptr. + base::Unretained(this))); +#endif base::PowerMonitor::Get()->AddObserver(this); Init(isolate); } @@ -24,6 +29,20 @@ PowerMonitor::~PowerMonitor() { base::PowerMonitor::Get()->RemoveObserver(this); } +bool PowerMonitor::ShouldShutdown() { + return Emit("shutdown"); +} + +#if defined(OS_LINUX) +void PowerMonitor::BlockShutdown() { + PowerObserverLinux::BlockShutdown(); +} + +void PowerMonitor::UnblockShutdown() { + PowerObserverLinux::UnblockShutdown(); +} +#endif + void PowerMonitor::OnPowerStateChange(bool on_battery_power) { if (on_battery_power) Emit("on-battery"); @@ -39,12 +58,6 @@ void PowerMonitor::OnResume() { Emit("resume"); } -#if defined(OS_LINUX) -bool PowerMonitor::OnShutdown() { - return Emit("shutdown"); -} -#endif - // static v8::Local PowerMonitor::Create(v8::Isolate* isolate) { if (!Browser::Get()->is_ready()) { diff --git a/atom/browser/api/atom_api_power_monitor.h b/atom/browser/api/atom_api_power_monitor.h index 8724aa50afc5..90971556de83 100644 --- a/atom/browser/api/atom_api_power_monitor.h +++ b/atom/browser/api/atom_api_power_monitor.h @@ -26,16 +26,20 @@ class PowerMonitor : public mate::TrackableObject, explicit PowerMonitor(v8::Isolate* isolate); ~PowerMonitor() override; + // Called by native calles. + bool ShouldShutdown(); + +#if defined(OS_LINUX) + // Private JS APIs. + void BlockShutdown(); + void UnblockShutdown(); +#endif + // base::PowerObserver implementations: void OnPowerStateChange(bool on_battery_power) override; void OnSuspend() override; void OnResume() override; -#if defined(OS_LINUX) - // atom::PowerObserver - bool OnShutdown() override; -#endif - private: DISALLOW_COPY_AND_ASSIGN(PowerMonitor); }; diff --git a/atom/browser/lib/power_observer_linux.cc b/atom/browser/lib/power_observer_linux.cc index ad3cc82f478f..bff50db6953f 100644 --- a/atom/browser/lib/power_observer_linux.cc +++ b/atom/browser/lib/power_observer_linux.cc @@ -114,6 +114,10 @@ void PowerObserverLinux::UnblockShutdown() { shutdown_lock_.reset(); } +void PowerObserverLinux::SetShutdownHandler(base::Callback handler) { + should_shutdown_ = std::move(handler); +} + void PowerObserverLinux::OnInhibitResponse(base::ScopedFD* scoped_fd, dbus::Response* response) { dbus::MessageReader reader(response); @@ -144,7 +148,7 @@ void PowerObserverLinux::OnPrepareForShutdown(dbus::Signal* signal) { return; } if (shutting_down) { - if (!OnShutdown()) { + if (!should_shutdown_ || !should_shutdown_.Run()) { // The user didn't try to prevent shutdown. Release the lock and allow the // shutdown to continue normally. shutdown_lock_.reset(); diff --git a/atom/browser/lib/power_observer_linux.h b/atom/browser/lib/power_observer_linux.h index a76ecaf3540d..c2cb1d801508 100644 --- a/atom/browser/lib/power_observer_linux.h +++ b/atom/browser/lib/power_observer_linux.h @@ -26,7 +26,7 @@ class PowerObserverLinux : public base::PowerObserver { void BlockShutdown(); void UnblockShutdown(); - virtual bool OnShutdown() { return false; } + void SetShutdownHandler(base::Callback should_shutdown); private: void OnLoginServiceAvailable(bool available); @@ -37,6 +37,8 @@ class PowerObserverLinux : public base::PowerObserver { const std::string& signal, bool success); + base::Callback should_shutdown_; + scoped_refptr bus_; scoped_refptr logind_; std::string lock_owner_name_; From c470e758cce6b6fa6cbf9d114bcc242eec6a157b Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 5 Feb 2018 16:13:54 +0900 Subject: [PATCH 09/11] Remove the AllocateSystemIOPorts call It is no longer needed and it is crashing. --- atom/browser/api/atom_api_power_monitor.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/atom/browser/api/atom_api_power_monitor.cc b/atom/browser/api/atom_api_power_monitor.cc index 48194d0e3a99..88fbb4c97be5 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -92,10 +92,6 @@ using atom::api::PowerMonitor; void Initialize(v8::Local exports, v8::Local unused, v8::Local context, void* priv) { -#if defined(OS_MACOSX) - base::PowerMonitorDeviceSource::AllocateSystemIOPorts(); -#endif - v8::Isolate* isolate = context->GetIsolate(); mate::Dictionary dict(isolate, exports); dict.Set("powerMonitor", PowerMonitor::Create(isolate)); From 983e1b1a70d0114f3f281d5fa5345f34a5b85e3c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 5 Feb 2018 16:13:35 +0900 Subject: [PATCH 10/11] Implement shutdown event for macOS --- atom/browser/api/atom_api_power_monitor.cc | 6 ++++-- atom/browser/browser.h | 3 +++ atom/browser/browser_mac.mm | 4 ++++ atom/browser/lib/power_observer_linux.cc | 2 +- atom/browser/mac/atom_application.h | 8 ++++++-- atom/browser/mac/atom_application.mm | 6 ++++++ docs/api/power-monitor.md | 2 +- 7 files changed, 25 insertions(+), 6 deletions(-) diff --git a/atom/browser/api/atom_api_power_monitor.cc b/atom/browser/api/atom_api_power_monitor.cc index 88fbb4c97be5..90f32393f168 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -18,8 +18,10 @@ namespace api { PowerMonitor::PowerMonitor(v8::Isolate* isolate) { #if defined(OS_LINUX) SetShutdownHandler(base::Bind(&PowerMonitor::ShouldShutdown, - // Passed to base class, no need for weak ptr. base::Unretained(this))); +#elif defined(OS_MACOSX) + Browser::Get()->SetShutdownHandler(base::Bind(&PowerMonitor::ShouldShutdown, + base::Unretained(this))); #endif base::PowerMonitor::Get()->AddObserver(this); Init(isolate); @@ -30,7 +32,7 @@ PowerMonitor::~PowerMonitor() { } bool PowerMonitor::ShouldShutdown() { - return Emit("shutdown"); + return !Emit("shutdown"); } #if defined(OS_LINUX) diff --git a/atom/browser/browser.h b/atom/browser/browser.h index b5e31160c8f8..553d3648afa3 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -105,6 +105,9 @@ class Browser : public WindowListObserver { LoginItemSettings GetLoginItemSettings(const LoginItemSettings& options); #if defined(OS_MACOSX) + // Set the handler which decides whether to shutdown. + void SetShutdownHandler(base::Callback handler); + // Hide the application. void Hide(); diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index d12de2c14c9d..0faeee36f939 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -21,6 +21,10 @@ namespace atom { +void Browser::SetShutdownHandler(base::Callback handler) { + [[AtomApplication sharedApplication] setShutdownHandler:std::move(handler)]; +} + void Browser::Focus() { [[AtomApplication sharedApplication] activateIgnoringOtherApps:YES]; } diff --git a/atom/browser/lib/power_observer_linux.cc b/atom/browser/lib/power_observer_linux.cc index bff50db6953f..27da2b917624 100644 --- a/atom/browser/lib/power_observer_linux.cc +++ b/atom/browser/lib/power_observer_linux.cc @@ -148,7 +148,7 @@ void PowerObserverLinux::OnPrepareForShutdown(dbus::Signal* signal) { return; } if (shutting_down) { - if (!should_shutdown_ || !should_shutdown_.Run()) { + if (!should_shutdown_ || should_shutdown_.Run()) { // The user didn't try to prevent shutdown. Release the lock and allow the // shutdown to continue normally. shutdown_lock_.reset(); diff --git a/atom/browser/mac/atom_application.h b/atom/browser/mac/atom_application.h index 45bb011cd772..243667b46039 100644 --- a/atom/browser/mac/atom_application.h +++ b/atom/browser/mac/atom_application.h @@ -2,8 +2,9 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#import "base/mac/scoped_sending_event.h" -#import "base/mac/scoped_nsobject.h" +#include "base/callback.h" +#include "base/mac/scoped_sending_event.h" +#include "base/mac/scoped_nsobject.h" @interface AtomApplication : NSApplication currentActivity_; NSCondition* handoffLock_; BOOL updateReceived_; + base::Callback shouldShutdown_; } + (AtomApplication*)sharedApplication; +- (void)setShutdownHandler:(base::Callback)handler; + // CrAppProtocol: - (BOOL)isHandlingSendEvent; diff --git a/atom/browser/mac/atom_application.mm b/atom/browser/mac/atom_application.mm index 96c99f5d22f1..68daa8c8b2b6 100644 --- a/atom/browser/mac/atom_application.mm +++ b/atom/browser/mac/atom_application.mm @@ -29,10 +29,16 @@ inline void dispatch_sync_main(dispatch_block_t block) { } - (void)terminate:(id)sender { + if (shouldShutdown_ && !shouldShutdown_.Run()) + return; // User will call Quit later. AtomApplicationDelegate* atomDelegate = (AtomApplicationDelegate*) [NSApp delegate]; [atomDelegate tryToTerminateApp:self]; } +- (void)setShutdownHandler:(base::Callback)handler { + shouldShutdown_ = std::move(handler); +} + - (BOOL)isHandlingSendEvent { return handlingSendEvent_; } diff --git a/docs/api/power-monitor.md b/docs/api/power-monitor.md index eac6695c571c..5d600da48a94 100644 --- a/docs/api/power-monitor.md +++ b/docs/api/power-monitor.md @@ -40,7 +40,7 @@ Emitted when the system changes to AC power. Emitted when system changes to battery power. -### Event: 'shutdown' _Linux_ +### Event: 'shutdown' _Linux_ _macOS_ Emitted when the system is about to reboot or shut down. If the event handler invokes `e.preventDefault()`, Electron will attempt to delay system shutdown in From 9337959f515fa07189a785e698076ce5887f14ff Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 5 Feb 2018 16:25:50 +0900 Subject: [PATCH 11/11] The tryToTerminateApp is not really needed --- atom/browser/mac/atom_application.mm | 7 +++++-- atom/browser/mac/atom_application_delegate.h | 2 -- atom/browser/mac/atom_application_delegate.mm | 7 ------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/atom/browser/mac/atom_application.mm b/atom/browser/mac/atom_application.mm index 68daa8c8b2b6..a7ea338857f4 100644 --- a/atom/browser/mac/atom_application.mm +++ b/atom/browser/mac/atom_application.mm @@ -31,8 +31,11 @@ inline void dispatch_sync_main(dispatch_block_t block) { - (void)terminate:(id)sender { if (shouldShutdown_ && !shouldShutdown_.Run()) return; // User will call Quit later. - AtomApplicationDelegate* atomDelegate = (AtomApplicationDelegate*) [NSApp delegate]; - [atomDelegate tryToTerminateApp:self]; + + // We simply try to close the browser, which in turn will try to close the + // windows. Termination can proceed if all windows are closed or window close + // can be cancelled which will abort termination. + atom::Browser::Get()->Quit(); } - (void)setShutdownHandler:(base::Callback)handler { diff --git a/atom/browser/mac/atom_application_delegate.h b/atom/browser/mac/atom_application_delegate.h index cbc5c71d0a73..777475213ecf 100644 --- a/atom/browser/mac/atom_application_delegate.h +++ b/atom/browser/mac/atom_application_delegate.h @@ -11,8 +11,6 @@ base::scoped_nsobject menu_controller_; } -- (void)tryToTerminateApp:(NSApplication*)app; - // Sets the menu that will be returned in "applicationDockMenu:". - (void)setApplicationDockMenu:(atom::AtomMenuModel*)model; diff --git a/atom/browser/mac/atom_application_delegate.mm b/atom/browser/mac/atom_application_delegate.mm index 5664501d1aa3..e210ad2b6a77 100644 --- a/atom/browser/mac/atom_application_delegate.mm +++ b/atom/browser/mac/atom_application_delegate.mm @@ -87,13 +87,6 @@ static base::mac::ScopedObjCClassSwizzler* g_swizzle_imk_input_session; return atom::Browser::Get()->OpenFile(filename_str) ? YES : NO; } -// We simply try to close the browser, which in turn will try to close the windows. -// Termination can proceed if all windows are closed or window close can be cancelled -// which will abort termination. -- (void)tryToTerminateApp:(NSApplication*)app { - atom::Browser::Get()->Quit(); -} - - (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication hasVisibleWindows:(BOOL)flag { atom::Browser* browser = atom::Browser::Get();