-
Notifications
You must be signed in to change notification settings - Fork 6k
Reintroduce Windows lifecycle with guard for posthumous OnWindowStateEvent
#44344
Conversation
Should I incorporate the changes in #44238 into this PR? Doing so will resolve the startup warnings issue at the same time, but will make this a rather broad request. |
shell/platform/windows/window.h
Outdated
@@ -249,6 +253,9 @@ class Window : public KeyboardManager::WindowDelegate { | |||
// Accessibility node that represents an alert. | |||
std::unique_ptr<ui::AXPlatformNodeWin> alert_node_; | |||
|
|||
// Guard against posthumous vtable access; | |||
bool vtable_is_alive = true; |
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 spooky. Some potential alternative options:
- Merge
FlutterWindow
andWindow
to make destruction simpler - Expose a
Destroy
method to destroy the window manually. This would need to be called before theWindow
is destructed to ensure no messages are receive post destruction. - Switch from an inheritance pattern to a delegate pattern so that the
Window
is destructed before theFlutterWindow
What are your thoughts?
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.
- I do not see any reason why they need to be separate classes, though I am a little nervous about trying it. As long as it's safe, and no one will complain about needing both classes for some 3rd party or future change, this seems like a valid option.
- We could also try this, though do we know from where we could call it, or also importantly, if we have any way to ensure it's called before destruction?
- If
FlutterWindow
is our only use case forWindow
, merging the classes seems like a better idea.
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.
- I do not see any reason why they need to be separate classes, though I am a little nervous about trying it. As long as it's safe, and no one will complain about needing both classes for some 3rd party or future change, this seems like a valid option.
This type hierarchy is a common win32 C++ pattern: https://learn.microsoft.com/windows/win32/learnwin32/managing-application-state-#an-object-oriented-approach
I'm not in love with this pattern though 😅
- ... though do we know from where we could call it, or also importantly, if we have any way to ensure it's called before destruction?
One potential place might FlutterDesktopViewControllerDestroy
. We could manually close the window before destroying our objects:
engine/shell/platform/windows/flutter_windows.cc
Lines 90 to 93 in 138a1ea
void FlutterDesktopViewControllerDestroy( | |
FlutterDesktopViewControllerRef controller) { | |
delete controller; | |
} |
- ... merging the classes seems like a better idea.
Agreed. Right now I'm leaning towards option 2, it seems like the least intrusive option.
/cc @cbracken For thoughts
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.
What do you think about moving the call to Window::Destroy()
from ~Window
to ~FlutterWindow
? I just do not know how we can enforce within a test that window messages are received between the two destructors.
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.
In the interest of getting this landed, agreed that option 2 (make Destroy() protected, then move the destroy call down to the subclass) is probably the quickest fix. Once that's landed, let's follow up with a patch that implements option 1 (merge Window and FlutterWindow).
I don't think there's a strong case for keeping them separate. Now that the UWP embedder is gone let's use the YAGNI rule here and merge the two. If we ever find a good reason for a separation, we can look at that in the future.
shell/platform/windows/window.h
Outdated
@@ -249,6 +253,9 @@ class Window : public KeyboardManager::WindowDelegate { | |||
// Accessibility node that represents an alert. | |||
std::unique_ptr<ui::AXPlatformNodeWin> alert_node_; | |||
|
|||
// Guard against posthumous vtable access; | |||
bool vtable_is_alive = true; |
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.
On a separate note, I'm surprised the linter didn't catch the missing underscore suffix on this field. Probably irrelevant given the change in approach proposed for this patch, but I'll file a bug to see why the linters aren't running on windows.
@@ -84,6 +85,16 @@ class FlutterEngine : public PluginRegistry { | |||
// once on the platform thread. | |||
void SetNextFrameCallback(std::function<void()> callback); | |||
|
|||
// Called to pass an external window message to the engine for lifecycle | |||
// state updates. This does not consume the window message. Non-Flutter |
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.
// state updates. This does not consume the window message. Non-Flutter | |
// state updates. Non-Flutter |
@@ -89,6 +89,17 @@ class StubFlutterWindowsApi { | |||
// FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate. | |||
virtual void PluginRegistrarUnregisterTopLevelWindowProcDelegate( | |||
FlutterDesktopWindowProcCallback delegate) {} | |||
|
|||
// Claled for FlutterDesktopEngineProcessExternalWindowMessage. |
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.
// Claled for FlutterDesktopEngineProcessExternalWindowMessage. | |
// Called for FlutterDesktopEngineProcessExternalWindowMessage. |
@@ -212,6 +212,19 @@ FLUTTER_EXPORT HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view); | |||
FLUTTER_EXPORT IDXGIAdapter* FlutterDesktopViewGetGraphicsAdapter( | |||
FlutterDesktopViewRef view); | |||
|
|||
// Called to pass an external window message to the engine for lifecycle | |||
// state updates. This does not consume the window message. Non-Flutter windows |
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.
// state updates. This does not consume the window message. Non-Flutter windows | |
// state updates. Non-Flutter windows |
// Called to pass an external window message to the engine for lifecycle | ||
// state updates. This does not consume the window message. Non-Flutter windows | ||
// must call this method in their WndProc in order to be included in the logic | ||
// for application lifecycle state updates. Returns true when the message is |
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.
// for application lifecycle state updates. Returns true when the message is | |
// for application lifecycle state updates. Returns true if the message should be |
// the logic for application lifecycle state updates. Returns a result when | ||
// the message has been consumed. |
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 logic for application lifecycle state updates. Returns a result when | |
// the message has been consumed. | |
// the logic for application lifecycle state updates. Returns a result if | |
// the message should be consumed. |
// state updates. Non-Flutter | ||
// windows must call this method in their WndProc in order to be included in |
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.
Please reformat this to fill the line
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.
Same comment for FlutterDesktopEngineProcessExternalWindowMessage
shell/platform/windows/window.h
Outdated
@@ -236,6 +240,9 @@ class Window : public KeyboardManager::WindowDelegate { | |||
// Returns the root view accessibility node, or nullptr if none. | |||
virtual gfx::NativeViewAccessible GetNativeViewAccessible() = 0; | |||
|
|||
// Release OS resources associated with window. |
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.
// Release OS resources associated with window. | |
// Release OS resources associated with the window. |
@yaakovschectman Before merging this, please verify that this latest approach also works as expected with the internal app team. |
@loic-sharma Someone from said team confirmed they experience no crash when testing with this build. |
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.
LGTM, nice work! 🎉
Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
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.
…132332) flutter/engine@a9be77e...16b01b9 2023-08-10 skia-flutter-autoroll@skia.org Roll Dart SDK from 46da53e7abe2 to a2eac00da6b8 (1 revision) (flutter/engine#44601) 2023-08-10 huxiaohu2007@gmail.com Fix unexpected pointer change issue and Add test case (flutter/engine#43949) 2023-08-10 47866232+chunhtai@users.noreply.github.com Reland "Android a11y bridge sets importantness" (flutter/engine#44589) 2023-08-10 john@johnmccutchan.com Support for Android Platform Views under Impeller/Vulkan (flutter/engine#44571) 2023-08-10 109111084+yaakovschectman@users.noreply.github.com Reintroduce Windows lifecycle with guard for posthumous `OnWindowStateEvent` (flutter/engine#44344) 2023-08-10 skia-flutter-autoroll@skia.org Roll Skia from 92e6f52b0fa8 to b6492f5ce8c3 (1 revision) (flutter/engine#44597) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC chinmaygarde@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…eEvent` (flutter#44344) Previously, destruction of `Window` called `DestroyWindow`, which may send `WM_KILLFOCUS` to the to-be-destroyed window. Because `Window`'s destructor is called after `FlutterWindow`'s, the `FlutterWindow` vtable was already destroyed at this point, and the subsequent call to the virtual method `OnWindowStateEvent` would cause a crash. This PR reintroduces the reverted changes for Windows lifecycle with a check before calling the virtual method that the `FlutterWindow` object has not yet been destructed. flutter/flutter#131872 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat --------- Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
Previously, destruction of
Window
calledDestroyWindow
, which may sendWM_KILLFOCUS
to the to-be-destroyed window. BecauseWindow
's destructor is called afterFlutterWindow
's, theFlutterWindow
vtable was already destroyed at this point, and the subsequent call to the virtual methodOnWindowStateEvent
would cause a crash. This PR reintroduces the reverted changes for Windows lifecycle with a check before calling the virtual method that theFlutterWindow
object has not yet been destructed.flutter/flutter#131872
If you had to change anything in the flutter/tests repo, include a link to the migration guide as per the breaking change policy.
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.