-
Notifications
You must be signed in to change notification settings - Fork 15.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: notify focus change right away rather not on next tick #14453
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is almost verbatim the reverse of #5131 and so I think it would reintroduce that bug?
:-( Will check that. Thanks for pointing me to that. 🙇 |
I just verified that this also fixes #13549. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The focus
event is meant to be emitted after the window is focused. So behaviors on all platforms are matched, and issues like #3893 can be avoided.
The OnWidgetActivationChanged
method is called when the window is in the middle of changing focus, so emitting focus
here breaks the behavior, and can crash Electron if user decides to close the window now.
My solution was to delay the event one tick later, which fixed part of the problem but it was not the right solution.
We probably have to find out when the views library has just finished the focus changing, and emitting the event there.
I'm going to work on that today, then we can land this fix in combination with that change |
OK so did a bunch of work on this today, there are other events we can use to get focus but they introduce other race conditions with the window creation and the first observer being added. The easiest solution to get this fixed in 3.0 is to reintroduce the next tick delay but later in the call stack so it only affects the JS emit calls |
8ce94fb
to
b91bf74
Compare
@@ -71,6 +71,21 @@ v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) { | |||
|
|||
} // namespace | |||
|
|||
namespace { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is already an anonymous namespace right above, not need to create a new one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels bad... Didn't even realize 😆
b91bf74
to
4416b2b
Compare
4416b2b
to
92de895
Compare
content::BrowserThread::PostTask( | ||
content::BrowserThread::UI, FROM_HERE, | ||
base::Bind(&EmitNextTickTask, | ||
emitter, name)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
Looks like this is nonrepeating; should this be
bind::BindOnce()
? -
Could avoid bit of unnecessary heap action by using
base::StringPiece
rather thanconst std::string&
here and in EmitNextTickTask -
It's not clear to me how the
std::string
objects created on the stack inOnWindowBlur
andOnWindowFocus
stay alive through the deferred action toEmitNextTickStack
? The previous bullet point will fix this, but asking separately for my own edification on base::Bind's internals
FYI... it looks like the Windows CI failures are a DCHECK failure:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Windows CI is failing with a DCHECK failure:
#
# Fatal error in .\../../v8/src/v8threads.cc, line 30
# Debug check failed: (isolate) != nullptr.
#
https://gist.github.com/7eb20129c41f6fc95999404d507fe438 reproduces the DCHECK failure above |
Looks like Mac CI is failing with same DCHECK error: |
void EmitNextTick(TopLevelWindow* emitter, const std::string& name) { | ||
content::BrowserThread::PostTask( | ||
content::BrowserThread::UI, FROM_HERE, | ||
base::Bind(&EmitNextTickTask, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming CI is happy, I'm 👍 on this approach.
I'd still love to hear if the std::string& being bound was somehow staying in scope in 92de895 due to magic in base::BindOnce
...?
For windows:
🤔 |
@codebytere that is the dcheck failure I mentioned above. |
interesting, this failure didn't happen with my changes running the test suite locally (but on MacOS) |
This is so that the deferred Emit() calls will be canceled if the TopLevelWindow is destroyed.
The new method base::BindOnce(&TopLevelWindow::Emit, weak_factory_.GetWeakPtr(), eventName)); If some C++ guru could explain how to make that work, that would make my day. 🤞 |
Emit(eventName); | ||
} | ||
|
||
void TopLevelWindow::EmitEventSoon(base::StringPiece eventName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Can we avoid the EmitEvent
method and make this more generic by putting EmitSoon
onto the EventEmitter
base class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now the function is slightly brittle because it requires exactly one argument. It would be preferable if that could be resolved before this is opened up for a wider scope in the code.
The issue I mentioned above is the cause for this: EmitEvent()
exists solely to make the compiler happy because I didn't see how to use the existing EventEmitter::Emit()
.
To be more specific, this was the compiler error:
base::BindOnce(&TopLevelWindow::Emit, weak_factory_.GetWeakPtr(), "blur"));
^~~~~~~~~~~~~~
../../base/bind.h:179:1: note: candidate template ignored: couldn't infer template argument 'Functor'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base::BindOnce(&TopLevelWindow::Emit<base::StringPiece>, weak_factory_.GetWeakPtr(), "blur"));
My understanding of that error is you need to do ☝️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just invoke the default template variation of Emit
for this scenario
void TopLevelWindow::EmitEventSoon(base::StringPiece eventName) {
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::BindOnce(base::IgnoreResult(&TopLevelWindow::Emit<>),
weak_factory_.GetWeakPtr(), eventName));
}
Or
template <typename... Args>
void TopLevelWindow::EmitEventSoon(base::StringPiece eventName) {
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::BindOnce(base::IgnoreResult(&TopLevelWindow::Emit<Args...>),
weak_factory_.GetWeakPtr(), eventName));
}
For a generic version in EventEmitter
class you would essentially do the above by capturing the variadic args and passing them on. But I would suggest avoid adding a generic version for this delayed event emit, we are just working around a problem due to a some design issues with the framework, it shouldn't set out as a code suggestion for similar problems that may arise in the future rather we should rethink the class design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha! I didn't know about base::IgnoreResult()
and was unsure how to get rid of the bool return value from Emit, since weak ptrs can only be bound to functions returning void. Thanks for the pointer 👍
We apologise again for the fault in the subtitles. Those responsible for sacking the people who have just been sacked have been sacked.
Release Notes Persisted
|
An error occurred while attempting to backport this PR to "3-0-x", |
* fix: Notify focus change right away, not on next tick * fix: emit the JS blur/focus events on next tick to avoid race condition * address feedback from review * fix: bind deferred Emit() calls to a WeakPtr This is so that the deferred Emit() calls will be canceled if the TopLevelWindow is destroyed. * chore: remove wip/test code cruft * fix: make linter happy * Enable disabled tests * refactor: cleaner impl of EmitEventSoon() * Revert "Merge branch 'fix-win-focus' of github.com:electron/electron into fix-win-focus" This reverts commit 9057680, reversing changes made to 9c13e47. * Restore 704722c, which was removed in error. We apologise again for the fault in the subtitles. Those responsible for sacking the people who have just been sacked have been sacked.
Description of Change
This issue fixes #13913. There was a problem with two windows fighting for focus because the focus notification was being sent on next tick.
This PR changes that, so that the focus notification is sent right away.
Also fixes #13549.
Checklist
npm test
passesRelease Notes
Notes: Fixed windows multiple windows focus bug.