Skip to content
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

Merged
merged 12 commits into from
Sep 7, 2018
Merged

Conversation

nitsakh
Copy link
Contributor

@nitsakh nitsakh commented Sep 4, 2018

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
  • PR description included and stakeholders cc'd
  • npm test passes
  • tests are changed or added
  • relevant documentation is changed or added
  • PR title follows semantic commit guidelines
Release Notes

Notes: Fixed windows multiple windows focus bug.

@nitsakh nitsakh requested review from a team, zcbenz and jkleinsc September 4, 2018 17:40
Copy link
Member

@ckerr ckerr left a 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?

@nitsakh
Copy link
Contributor Author

nitsakh commented Sep 4, 2018

:-( Will check that. Thanks for pointing me to that. 🙇

@jkleinsc
Copy link
Contributor

jkleinsc commented Sep 4, 2018

I just verified that this also fixes #13549.

@jkleinsc
Copy link
Contributor

jkleinsc commented Sep 4, 2018

I just tested this branch against the repro listed here #3893 (fixed by #5131). Unfortunately this fix reintroduces the original issue.

Copy link
Member

@zcbenz zcbenz left a 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.

@MarshallOfSound
Copy link
Member

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

@MarshallOfSound
Copy link
Member

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

@MarshallOfSound MarshallOfSound force-pushed the fix-win-focus branch 2 times, most recently from 8ce94fb to b91bf74 Compare September 5, 2018 05:19
@nitsakh nitsakh requested review from a team September 5, 2018 05:19
@MarshallOfSound MarshallOfSound changed the base branch from 3-0-x to master September 5, 2018 05:19
@@ -71,6 +71,21 @@ v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {

} // namespace

namespace {
Copy link
Member

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.

Copy link
Member

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 😆

content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&EmitNextTickTask,
emitter, name));
Copy link
Member

@ckerr ckerr Sep 5, 2018

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 than const std::string& here and in EmitNextTickTask

  • It's not clear to me how the std::string objects created on the stack in OnWindowBlur and OnWindowFocus stay alive through the deferred action to EmitNextTickStack? The previous bullet point will fix this, but asking separately for my own edification on base::Bind's internals

@jkleinsc
Copy link
Contributor

jkleinsc commented Sep 5, 2018

FYI... it looks like the Windows CI failures are a DCHECK failure:

# Fatal error in .\../../v8/src/v8threads.cc, line 30
# Debug check failed: (isolate) != nullptr.

Copy link
Contributor

@jkleinsc jkleinsc left a 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.
#

@jkleinsc
Copy link
Contributor

jkleinsc commented Sep 5, 2018

https://gist.github.com/7eb20129c41f6fc95999404d507fe438 reproduces the DCHECK failure above

@jkleinsc
Copy link
Contributor

jkleinsc commented Sep 5, 2018

void EmitNextTick(TopLevelWindow* emitter, const std::string& name) {
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&EmitNextTickTask,
Copy link
Member

@codebytere codebytere Sep 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nitsakh: #12639 (base::Bind is being deprecated in favor of base::BindOnce and base::BindRepeating)

Copy link
Member

@ckerr ckerr left a 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...?

@codebytere
Copy link
Member

codebytere commented Sep 5, 2018

For windows:

Tests failed with exit code -1073741819

🤔

@jkleinsc
Copy link
Contributor

jkleinsc commented Sep 5, 2018

@codebytere that is the dcheck failure I mentioned above.

@codebytere
Copy link
Member

codebytere commented Sep 5, 2018

interesting, this failure didn't happen with my changes running the test suite locally (but on MacOS)

@ckerr
Copy link
Member

ckerr commented Sep 6, 2018

The new method TopLevelWindow::EmitEvent() exists only because I couldn't make this work instead:

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) {
Copy link
Member

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?

Copy link
Member

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'

Copy link
Member

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 ☝️

Copy link
Member

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.

Copy link
Member

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 👍

@MarshallOfSound MarshallOfSound changed the title fix: Notify focus change right away, not on next tick fix: notify focus change right away, not on next tick Sep 7, 2018
…into fix-win-focus"

This reverts commit 9057680, reversing
changes made to 9c13e47.
We apologise again for the fault in the subtitles. Those responsible for sacking the people who have just been sacked have been sacked.
@ckerr ckerr changed the title fix: notify focus change right away, not on next tick fix: notify focus change right away rather not on next tick Sep 7, 2018
@ckerr ckerr merged commit a2ab0d8 into master Sep 7, 2018
@release-clerk
Copy link

release-clerk bot commented Sep 7, 2018

Release Notes Persisted

Fixed windows multiple windows focus bug.

@ckerr ckerr deleted the fix-win-focus branch September 7, 2018 18:22
@trop
Copy link
Contributor

trop bot commented Sep 7, 2018

An error occurred while attempting to backport this PR to "3-0-x",
you will need to perform this backport manually

jkleinsc pushed a commit that referenced this pull request Sep 7, 2018
* 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
7 participants