feat: add query-session-end and improve session-end events on Windows (#44598)
* feat: add query-session-end event for Windows * fix: remove debug line * feat: notify with reason on session-end * docs: add comments and return params * docs: add same docs to the BrowserWindow * fix: add shutdown reason if lParam == 0 * docs: remove 'force' word * docs: revert multithreading.md change * docs: add reasons documentation, reason variable renamed to reasons * docs: improve 'shutdown' reason wording * docs: reword with 'can be' * fix: pass reasons by reference * fix: use newer approach which expose reasons value directly on Event object * docs: add escaping * style: linter fixes * fix: project now should compile * fix: EmitWithoutEvent method added, EmitWithEvent moved to private again * docs: typo fix Co-authored-by: Sam Maddock <samuel.maddock@gmail.com> * docs: dedicated WindowSessionEndEvent type created * docs: better wording for session-end event description Co-authored-by: Will Anderson <will@itsananderson.com> --------- Co-authored-by: Sam Maddock <samuel.maddock@gmail.com> Co-authored-by: Will Anderson <will@itsananderson.com>
This commit is contained in:
parent
0285592d61
commit
c5ea177b3d
11 changed files with 135 additions and 12 deletions
|
@ -144,10 +144,24 @@ _**Note**: There is a subtle difference between the behaviors of `window.onbefor
|
|||
Emitted when the window is closed. After you have received this event you should
|
||||
remove the reference to the window and avoid using it any more.
|
||||
|
||||
#### Event: 'query-session-end' _Windows_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` [WindowSessionEndEvent][window-session-end-event]
|
||||
|
||||
Emitted when a session is about to end due to a shutdown, machine restart, or user log-off.
|
||||
Calling `event.preventDefault()` can delay the system shutdown, though it’s generally best
|
||||
to respect the user’s choice to end the session. However, you may choose to use it if
|
||||
ending the session puts the user at risk of losing data.
|
||||
|
||||
#### Event: 'session-end' _Windows_
|
||||
|
||||
Emitted when window session is going to end due to force shutdown or machine restart
|
||||
or session log off.
|
||||
Returns:
|
||||
|
||||
* `event` [WindowSessionEndEvent][window-session-end-event]
|
||||
|
||||
Emitted when a session is about to end due to a shutdown, machine restart, or user log-off. Once this event fires, there is no way to prevent the session from ending.
|
||||
|
||||
#### Event: 'blur'
|
||||
|
||||
|
@ -1429,3 +1443,4 @@ On Linux, the `symbolColor` is automatically calculated to have minimum accessib
|
|||
[vibrancy-docs]: https://developer.apple.com/documentation/appkit/nsvisualeffectview?preferredLanguage=objc
|
||||
[window-levels]: https://developer.apple.com/documentation/appkit/nswindow/level
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[window-session-end-event]:../api/structures/window-session-end-event.md
|
||||
|
|
|
@ -207,10 +207,24 @@ _**Note**: There is a subtle difference between the behaviors of `window.onbefor
|
|||
Emitted when the window is closed. After you have received this event you should
|
||||
remove the reference to the window and avoid using it any more.
|
||||
|
||||
#### Event: 'query-session-end' _Windows_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` [WindowSessionEndEvent][window-session-end-event]
|
||||
|
||||
Emitted when a session is about to end due to a shutdown, machine restart, or user log-off.
|
||||
Calling `event.preventDefault()` can delay the system shutdown, though it’s generally best
|
||||
to respect the user’s choice to end the session. However, you may choose to use it if
|
||||
ending the session puts the user at risk of losing data.
|
||||
|
||||
#### Event: 'session-end' _Windows_
|
||||
|
||||
Emitted when window session is going to end due to force shutdown or machine restart
|
||||
or session log off.
|
||||
Returns:
|
||||
|
||||
* `event` [WindowSessionEndEvent][window-session-end-event]
|
||||
|
||||
Emitted when a session is about to end due to a shutdown, machine restart, or user log-off. Once this event fires, there is no way to prevent the session from ending.
|
||||
|
||||
#### Event: 'unresponsive'
|
||||
|
||||
|
@ -1672,3 +1686,4 @@ On Linux, the `symbolColor` is automatically calculated to have minimum accessib
|
|||
[vibrancy-docs]: https://developer.apple.com/documentation/appkit/nsvisualeffectview?preferredLanguage=objc
|
||||
[window-levels]: https://developer.apple.com/documentation/appkit/nswindow/level
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[window-session-end-event]:../api/structures/window-session-end-event.md
|
||||
|
|
7
docs/api/structures/window-session-end-event.md
Normal file
7
docs/api/structures/window-session-end-event.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# WindowSessionEndEvent Object extends `Event`
|
||||
|
||||
* `reasons` string[] - List of reasons for shutdown. Can be 'shutdown', 'close-app', 'critical', or 'logoff'.
|
||||
|
||||
Unfortunately, Windows does not offer a way to differentiate between a shutdown and a reboot, meaning the 'shutdown'
|
||||
reason is triggered in both scenarios. For more details on the `WM_ENDSESSION` message and its associated reasons,
|
||||
refer to the [MSDN documentation](https://learn.microsoft.com/en-us/windows/win32/shutdown/wm-endsession).
|
|
@ -151,6 +151,7 @@ auto_filenames = {
|
|||
"docs/api/structures/web-request-filter.md",
|
||||
"docs/api/structures/web-source.md",
|
||||
"docs/api/structures/window-open-handler-response.md",
|
||||
"docs/api/structures/window-session-end-event.md",
|
||||
]
|
||||
|
||||
sandbox_bundle_deps = [
|
||||
|
|
|
@ -177,8 +177,37 @@ void BaseWindow::OnWindowClosed() {
|
|||
FROM_HERE, GetDestroyClosure());
|
||||
}
|
||||
|
||||
void BaseWindow::OnWindowEndSession() {
|
||||
Emit("session-end");
|
||||
void BaseWindow::OnWindowQueryEndSession(
|
||||
const std::vector<std::string>& reasons,
|
||||
bool* prevent_default) {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
gin::Handle<gin_helper::internal::Event> event =
|
||||
gin_helper::internal::Event::New(isolate);
|
||||
v8::Local<v8::Object> event_object = event.ToV8().As<v8::Object>();
|
||||
|
||||
gin::Dictionary dict(isolate, event_object);
|
||||
dict.Set("reasons", reasons);
|
||||
|
||||
EmitWithoutEvent("query-session-end", event);
|
||||
if (event->GetDefaultPrevented()) {
|
||||
*prevent_default = true;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseWindow::OnWindowEndSession(const std::vector<std::string>& reasons) {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
gin::Handle<gin_helper::internal::Event> event =
|
||||
gin_helper::internal::Event::New(isolate);
|
||||
v8::Local<v8::Object> event_object = event.ToV8().As<v8::Object>();
|
||||
|
||||
gin::Dictionary dict(isolate, event_object);
|
||||
dict.Set("reasons", reasons);
|
||||
|
||||
EmitWithoutEvent("session-end", event);
|
||||
}
|
||||
|
||||
void BaseWindow::OnWindowBlur() {
|
||||
|
|
|
@ -57,7 +57,9 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
|||
// NativeWindowObserver:
|
||||
void WillCloseWindow(bool* prevent_default) override;
|
||||
void OnWindowClosed() override;
|
||||
void OnWindowEndSession() override;
|
||||
void OnWindowQueryEndSession(const std::vector<std::string>& reasons,
|
||||
bool* prevent_default) override;
|
||||
void OnWindowEndSession(const std::vector<std::string>& reasons) override;
|
||||
void OnWindowBlur() override;
|
||||
void OnWindowFocus() override;
|
||||
void OnWindowShow() override;
|
||||
|
|
|
@ -532,9 +532,17 @@ void NativeWindow::NotifyWindowClosed() {
|
|||
WindowList::RemoveWindow(this);
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowEndSession() {
|
||||
void NativeWindow::NotifyWindowQueryEndSession(
|
||||
const std::vector<std::string>& reasons,
|
||||
bool* prevent_default) {
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.OnWindowEndSession();
|
||||
observer.OnWindowQueryEndSession(reasons, prevent_default);
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowEndSession(
|
||||
const std::vector<std::string>& reasons) {
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.OnWindowEndSession(reasons);
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowBlur() {
|
||||
|
|
|
@ -302,7 +302,9 @@ class NativeWindow : public base::SupportsUserData,
|
|||
void NotifyWindowRequestPreferredWidth(int* width);
|
||||
void NotifyWindowCloseButtonClicked();
|
||||
void NotifyWindowClosed();
|
||||
void NotifyWindowEndSession();
|
||||
void NotifyWindowQueryEndSession(const std::vector<std::string>& reasons,
|
||||
bool* prevent_default);
|
||||
void NotifyWindowEndSession(const std::vector<std::string>& reasons);
|
||||
void NotifyWindowBlur();
|
||||
void NotifyWindowFocus();
|
||||
void NotifyWindowShow();
|
||||
|
|
|
@ -50,8 +50,12 @@ class NativeWindowObserver : public base::CheckedObserver {
|
|||
// Called when the window is closed.
|
||||
virtual void OnWindowClosed() {}
|
||||
|
||||
// Called when Windows sends WM_QUERYENDSESSION message.
|
||||
virtual void OnWindowQueryEndSession(const std::vector<std::string>& reasons,
|
||||
bool* prevent_default) {}
|
||||
|
||||
// Called when Windows sends WM_ENDSESSION message
|
||||
virtual void OnWindowEndSession() {}
|
||||
virtual void OnWindowEndSession(const std::vector<std::string>& reasons) {}
|
||||
|
||||
// Called when window loses focus.
|
||||
virtual void OnWindowBlur() {}
|
||||
|
|
|
@ -27,6 +27,24 @@ namespace electron {
|
|||
|
||||
namespace {
|
||||
|
||||
// Convert Win32 WM_QUERYENDSESSIONS to strings.
|
||||
const std::vector<std::string> EndSessionToStringVec(LPARAM end_session_id) {
|
||||
std::vector<std::string> params;
|
||||
if (end_session_id == 0) {
|
||||
params.push_back("shutdown");
|
||||
return params;
|
||||
}
|
||||
|
||||
if (end_session_id & ENDSESSION_CLOSEAPP)
|
||||
params.push_back("close-app");
|
||||
if (end_session_id & ENDSESSION_CRITICAL)
|
||||
params.push_back("critical");
|
||||
if (end_session_id & ENDSESSION_LOGOFF)
|
||||
params.push_back("logoff");
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
// Convert Win32 WM_APPCOMMANDS to strings.
|
||||
constexpr std::string_view AppCommandToString(int command_id) {
|
||||
switch (command_id) {
|
||||
|
@ -389,9 +407,20 @@ bool NativeWindowViews::PreHandleMSG(UINT message,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
case WM_QUERYENDSESSION: {
|
||||
bool prevent_default = false;
|
||||
std::vector<std::string> reasons = EndSessionToStringVec(l_param);
|
||||
NotifyWindowQueryEndSession(reasons, &prevent_default);
|
||||
// Result should be TRUE by default, otherwise WM_ENDSESSION will not be
|
||||
// fired in some cases: More:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/rstmgr/guidelines-for-applications
|
||||
*result = !prevent_default;
|
||||
return prevent_default;
|
||||
}
|
||||
case WM_ENDSESSION: {
|
||||
std::vector<std::string> reasons = EndSessionToStringVec(l_param);
|
||||
if (w_param) {
|
||||
NotifyWindowEndSession();
|
||||
NotifyWindowEndSession(reasons);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,17 @@ class EventEmitter : public gin_helper::Wrappable<T> {
|
|||
return EmitWithEvent(name, event, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// this.emit(name, args...);
|
||||
template <typename... Args>
|
||||
void EmitWithoutEvent(const std::string_view name, Args&&... args) {
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
v8::Local<v8::Object> wrapper = GetWrapper();
|
||||
if (wrapper.IsEmpty())
|
||||
return;
|
||||
gin_helper::EmitEvent(isolate(), GetWrapper(), name,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// disable copy
|
||||
EventEmitter(const EventEmitter&) = delete;
|
||||
EventEmitter& operator=(const EventEmitter&) = delete;
|
||||
|
|
Loading…
Reference in a new issue