SL-14270 Crash on participant's updateName

Session was deleted before viewer had a chance to create view for listener, so listener ended up not deleted and avaiting an uptade, then tryed to call for deleted session.
master
Andrey Kleshchev 2021-01-06 00:45:07 +02:00
parent db161b6f85
commit f86014ef15
6 changed files with 71 additions and 2 deletions

View File

@ -349,6 +349,20 @@ void LLConversationItemSession::clearParticipants()
mNeedsRefresh = true;
}
void LLConversationItemSession::deleteParticipantModels()
{
// Make sure that no views exist before use and that view-owned items were removed!
//
// Normally we are not supposed to delete models directly, they should be
// owned by views and this action will result in crashes, but LLParticipantList
// creates models separately from views (it probably shouldn't) and then those
// models wait for idle cycles to be assigned to view.
// this code is meant to delete 'waiting' models
std::for_each(mChildren.begin(), mChildren.end(), DeletePointer());
mChildren.clear();
}
LLConversationItemParticipant* LLConversationItemSession::findParticipant(const LLUUID& participant_id)
{
// This is *not* a general tree parsing algorithm. It assumes that a session contains only

View File

@ -165,6 +165,7 @@ public:
void removeParticipant(LLConversationItemParticipant* participant);
void removeParticipant(const LLUUID& participant_id);
void clearParticipants();
void deleteParticipantModels(); // do not use while there are existing participant views
LLConversationItemParticipant* findParticipant(const LLUUID& participant_id);
void setParticipantIsMuted(const LLUUID& participant_id, bool is_muted);

View File

@ -103,6 +103,57 @@ LLConversationViewSession::~LLConversationViewSession()
mFlashTimer->unset();
}
void LLConversationViewSession::destroyView()
{
// Chat can create and parent models(listeners) to session's model before creating
// coresponding views, such participant's models normally will wait for idle cycles
// but since we are deleting session and won't be processing any more events, make
// sure unowned models are removed as well.
// Might be good idea to just have an LLPointer list somewhere in LLConversationItemSession
LLConversationItemSession* vmi = dynamic_cast<LLConversationItemSession*>(getViewModelItem());
// CONV_SESSION_1_ON_1 stores participants as two models that belong to views independent
// from session (nasty! These views are widgets in LLFloaterIMSessionTab, see buildConversationViewParticipant)
if (vmi && vmi->getType() != LLConversationItem::CONV_SESSION_1_ON_1)
{
// Destroy existing views
while (!mItems.empty())
{
LLFolderViewItem *itemp = mItems.back();
mItems.pop_back();
LLFolderViewModelItem* item_vmi = itemp->getViewModelItem();
if (item_vmi) // supposed to exist
{
// unparent to remove from child list
vmi->removeChild(item_vmi);
}
itemp->destroyView();
}
// Not needed in scope of sessions, but just in case
while (!mFolders.empty())
{
LLFolderViewFolder *folderp = mFolders.back();
mFolders.pop_back();
LLFolderViewModelItem* folder_vmi = folderp->getViewModelItem();
if (folder_vmi)
{
vmi->removeChild(folder_vmi);
}
folderp->destroyView();
}
// Now everything that is left in model(listener) is unowned,
// it is safe to remove
vmi->deleteParticipantModels();
}
LLFolderViewFolder::destroyView();
}
void LLConversationViewSession::setFlashState(bool flash_state)
{
if (flash_state && !mFlashStateOn)

View File

@ -67,6 +67,8 @@ protected:
public:
virtual ~LLConversationViewSession();
/*virtual*/ void destroyView();
/*virtual*/ BOOL postBuild();
/*virtual*/ void draw();
/*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );

View File

@ -1822,6 +1822,8 @@ bool LLFloaterIMContainer::removeConversationListItem(const LLUUID& uuid, bool c
{
new_selection = mConversationsRoot->getPreviousFromChild(widget, FALSE);
}
// Will destroy views and delete models that are not assigned to any views
widget->destroyView();
}

View File

@ -534,8 +534,7 @@ void LLFloaterIMSessionTab::removeConversationViewParticipant(const LLUUID& part
LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id);
if (widget)
{
mConversationsRoot->extractItem(widget);
delete widget;
widget->destroyView();
}
mConversationsWidgets.erase(participant_id);
}