Fix crash when deleting item being edited in wxOSX wxDataViewCtrl

Avoid touching the model while updating the control due to the items
being deleted from it, as doing this can easily result in dereferencing
pointers to the already deleted objects and crashing.

This is rather ugly, but the original code seems to have been written
with this approach in mind (see preexisting comments in ItemDeleted())
and, to be fair, there doesn't seem any other solution with the existing
API, as it allows (or maybe even requires?) deleting the items _before_
notifying the control about their removal and so not giving it any
opportunity to stop editing the item while it's still alive.

Closes #18337.
This commit is contained in:
Vadim Zeitlin
2019-09-16 18:04:44 +02:00
parent c0df6ec475
commit 3a9869b4c4

View File

@@ -568,6 +568,14 @@ outlineView:(NSOutlineView*)outlineView
{ {
wxUnusedVar(outlineView); wxUnusedVar(outlineView);
if ( implementation->GetDataViewCtrl()->IsDeleting() )
{
// Note that returning "NO" here would result in currently expanded
// branches not being expanded any more, while returning "YES" doesn't
// seem to have any ill effects, even though this is clearly bogus.
return YES;
}
wxCHECK_MSG( model, 0, "Valid model in data source does not exist." ); wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
return model->IsContainer(wxDataViewItemFromItem(item)); return model->IsContainer(wxDataViewItemFromItem(item));
} }
@@ -597,6 +605,15 @@ outlineView:(NSOutlineView*)outlineView
{ {
wxUnusedVar(outlineView); wxUnusedVar(outlineView);
// We ignore any calls to this function happening while items are being
// deleted, as they can (only?) come from -[NSTableView textDidEndEditing:]
// called from our own textDidEndEditing, which is called by
// -[NSOutlineView reloadItem:reloadChildren:], and if the item being
// edited is one of the items being deleted, then trying to use it would
// attempt to use already freed memory and crash.
if ( implementation->GetDataViewCtrl()->IsDeleting() )
return nil;
wxCHECK_MSG( model, nil, "Valid model in data source does not exist." ); wxCHECK_MSG( model, nil, "Valid model in data source does not exist." );
wxDataViewColumn* const wxDataViewColumn* const
@@ -623,6 +640,11 @@ outlineView:(NSOutlineView*)outlineView
{ {
wxUnusedVar(outlineView); wxUnusedVar(outlineView);
// See the comment in outlineView:objectValueForTableColumn:byItem: above:
// this function can also be called in the same circumstances.
if ( implementation->GetDataViewCtrl()->IsDeleting() )
return;
wxDataViewColumn* const wxDataViewColumn* const
col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]); col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]);
@@ -1937,6 +1959,10 @@ outlineView:(NSOutlineView*)outlineView
{ {
// call method of superclass (otherwise editing does not work correctly - // call method of superclass (otherwise editing does not work correctly -
// the outline data source class is not informed about a change of data): // the outline data source class is not informed about a change of data):
//
// Note that we really, really need to do this, as otherwise we would be
// left with an orphan text control -- even though doing this forces us to
// have the checks for IsDeleting() in several other methods of this class.
[super textDidEndEditing:notification]; [super textDidEndEditing:notification];
// under OSX an event indicating the end of an editing session can be sent // under OSX an event indicating the end of an editing session can be sent