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 refresh-related crashes in Settings UI #8773

Merged
8 commits merged into from
Jan 19, 2021

Conversation

carlos-zamora
Copy link
Member

@carlos-zamora carlos-zamora commented Jan 13, 2021

Removes the visibility hack in UpdateSettings where we were hiding
Profile menu items instead of removing them. This hack was removed using
ReplaceAll. For an unknown reason, calling Remove() would result in
an out-of-bounds error in XAML code.

The "Discard" button would improperly refresh the Settings UI. Both of
the bugs were caused by holding a reference to a hidden menu item then
trying to set the SelectedItem to that menu item.

Additionally, 9283375 adds a check for the selected item in
SettingsNav_ItemInvoked(). This prevents navigation to an already
selected item. This was the heuristic used by the XAML Controls Gallery.

References #6800 - Settings UI Epic

Validation Steps Performed

(Repeated for each menu item)

  1. Select the menu item
  2. click "Discard changes"
  3. Verify navigated to same page

Also performed repro steps for #8747 and #8748.

Closes #8747
Closes #8748

@carlos-zamora carlos-zamora added the Area-Settings UI Anything specific to the SUI label Jan 13, 2021
@ghost ghost added Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-0 Bugs that we consider release-blocking/recall-class (P0) Product-Terminal The new Windows Terminal. Severity-Crash Crashes are real bad news. labels Jan 13, 2021
src/cascadia/TerminalSettingsEditor/MainPage.cpp Outdated Show resolved Hide resolved
src/cascadia/TerminalSettingsEditor/MainPage.cpp Outdated Show resolved Hide resolved
src/cascadia/TerminalSettingsEditor/MainPage.cpp Outdated Show resolved Hide resolved
src/cascadia/TerminalSettingsEditor/MainPage.cpp Outdated Show resolved Hide resolved
src/cascadia/TerminalSettingsEditor/MainPage.cpp Outdated Show resolved Hide resolved
@ghost ghost added Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something and removed Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something labels Jan 13, 2021
Comment on lines 150 to 152
const auto& firstItem{ menuItems.GetAt(0) };
SettingsNav().SelectedItem(firstItem);
if (const auto tag{ SettingsNav().SelectedItem().as<MUX::Controls::NavigationViewItem>().Tag() })
if (const auto& tag{ SettingsNav().SelectedItem().try_as<MUX::Controls::NavigationViewItem>().Tag() })
Copy link
Member

Choose a reason for hiding this comment

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

don't go too crazy with this -- some of these things generate temporaries and we don't want to capture references to those. Those should not have the & if we can help it.

Copy link
Member

Choose a reason for hiding this comment

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

The ones you create from try_as, and the ones you create from GetAt, probably aren't safe to take references to. The ones you create from iteration over an iterator, however, are safe to take a reference to.

Copy link
Member

Choose a reason for hiding this comment

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

I apologize - i know i asked you to flip it one way and then the other, but... some nuance 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok. So just to get this straight, try_as and GetAt aren't safe to take refs of because MenuItems might change from underneath us (especially when looking at the selected item stuff). So it's safer to take a copy so that we can use it later. Right?

Copy link
Member

Choose a reason for hiding this comment

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

Ok. So just to get this straight, try_as and GetAt aren't safe to take refs of because MenuItems might change from underneath us (especially when looking at the selected item stuff). So it's safer to take a copy so that we can use it later. Right?

So, it's a little more complicated than that.

If this were just plain C++, that would be why. For a vector, at returns a reference to the memory location inside the vector's storage, which is durable until the vector changes.

For C++/WinRT, the story is a little different. Sorta. I still don't totally know the C++ reasons for why this works the way it does.

GetAt() returns a new object that was constructed to hold the internal WinRT object pointer. try_as does the same thing. Those objects are projected type wrappers, and they are constructed on return from the function. You can't take a reference to them, because they are not referenceable. They don't exist.

Copy link
Member

Choose a reason for hiding this comment

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

HOWEVER, I think I led you astray. I did some reading on Stack Overflow, and I finally understand it. What you had written before was completely legal, and now I understand why. You can safely revert the code that removed all the &s.

Here's the skinny, on Herb Sutter's blog.

C++ deliberately specifies that binding a temporary object to a reference to const on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error.

@ghost ghost added the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label Jan 13, 2021
@ghost ghost removed the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label Jan 13, 2021
Copy link
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

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

love it love it

Comment on lines +135 to +142
if (profileTag->Guid() == selectedItemProfileTag->Guid())
{
// found the one that was selected before the refresh
SettingsNav().SelectedItem(item);
_Navigate(*selectedItemProfileTag);
co_return;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

hey how will this work when you have BASE LAYER selected?

Copy link
Member Author

Choose a reason for hiding this comment

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

Base Layer is unique in that it has a string tag. So that's covered in the other if-statement.

@carlos-zamora carlos-zamora added the Needs-Second It's a PR that needs another sign-off label Jan 15, 2021
@@ -180,6 +203,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
if (const auto clickedItemContainer = args.InvokedItemContainer())
{
if (clickedItemContainer.IsSelected())
Copy link
Member

Choose a reason for hiding this comment

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

clever

// fallback to first menu item
const auto& firstItem{ menuItems.GetAt(0) };
SettingsNav().SelectedItem(firstItem);
if (const auto& tag{ SettingsNav().SelectedItem().try_as<MUX::Controls::NavigationViewItem>().Tag() })
{
_Navigate(unbox_value<hstring>(tag));
Copy link
Member

Choose a reason for hiding this comment

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

oh I see, yea this works because the first one is always a string. gotcha.

@carlos-zamora carlos-zamora added AutoMerge Marked for automatic merge by the bot when requirements are met and removed Needs-Second It's a PR that needs another sign-off labels Jan 19, 2021
@ghost
Copy link

ghost commented Jan 19, 2021

Hello @carlos-zamora!

Because this pull request has the AutoMerge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit 9fed14a into main Jan 19, 2021
@ghost ghost deleted the dev/cazamor/sui/bugfix-crash-del-added-prof branch January 19, 2021 22:14
ghost pushed a commit that referenced this pull request Jan 21, 2021
Upon a settings reload, we would select the correct navigation item for
a profile, but navigate to the old one. As a result, you would navigate
to the old page that points to a dead profile object. This would make it
appear like you did not discard/save the changes.

This bugfix navigates to the newly created profile, ensuring that your
changes are actually applied to the settings model's clone in use.

## References
#8773 - Introduced the bug
#6800 - Settings UI Epic

## Validation Steps Performed
- Navigate to "powershell" profile
- edit "tab title" value
- discard changes

Before: changes would persist unless you discarded changes again
Now: changes are discarded

Also verified expected behavior occurs when you click "save" instead of
"discard"
mpela81 pushed a commit to mpela81/terminal that referenced this pull request Jan 28, 2021
Removes the visibility hack in `UpdateSettings` where we were hiding
Profile menu items instead of removing them. This hack was removed using
`ReplaceAll`. For an unknown reason, calling `Remove()` would result in
an out-of-bounds error in XAML code.

The "Discard" button would improperly refresh the Settings UI. Both of
the bugs were caused by holding a reference to a hidden menu item then
trying to set the `SelectedItem` to that menu item. 

Additionally, 9283375 adds a check for the selected item in
`SettingsNav_ItemInvoked()`. This prevents navigation to an already
selected item. This was the heuristic used by the XAML Controls Gallery.

References microsoft#6800 - Settings UI Epic

## Validation Steps Performed
(Repeated for each menu item)
1. Select the menu item
2. click "Discard changes"
3. Verify navigated to same page

Also performed repro steps for microsoft#8747 and microsoft#8748.

Closes microsoft#8747
Closes microsoft#8748
mpela81 pushed a commit to mpela81/terminal that referenced this pull request Jan 28, 2021
Upon a settings reload, we would select the correct navigation item for
a profile, but navigate to the old one. As a result, you would navigate
to the old page that points to a dead profile object. This would make it
appear like you did not discard/save the changes.

This bugfix navigates to the newly created profile, ensuring that your
changes are actually applied to the settings model's clone in use.

## References
microsoft#8773 - Introduced the bug
microsoft#6800 - Settings UI Epic

## Validation Steps Performed
- Navigate to "powershell" profile
- edit "tab title" value
- discard changes

Before: changes would persist unless you discarded changes again
Now: changes are discarded

Also verified expected behavior occurs when you click "save" instead of
"discard"
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Settings UI Anything specific to the SUI AutoMerge Marked for automatic merge by the bot when requirements are met Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-0 Bugs that we consider release-blocking/recall-class (P0) Product-Terminal The new Windows Terminal. Severity-Crash Crashes are real bad news.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Crash when deleting a profile you just (un)hid Crash when deleting a profile you just added
3 participants