2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-09-04 00:15:42 +00:00

Fix crash in layers closing.

Sometimes AbstractBox::setClosing invoked Ui::hideLayers that
destroyed LayerStackWidget and all its children, including the
closing AbstractBox. After that a unique_ptr stored on stack
and owning that box was destroyed and it lead to a crash.

Now LayerStackWidget always owns several closing boxes.
This commit is contained in:
John Preston
2019-02-17 10:36:44 +04:00
parent 98cb85df66
commit cf275b152a
2 changed files with 37 additions and 11 deletions

View File

@@ -618,15 +618,16 @@ void LayerStackWidget::replaceBox(
object_ptr<BoxContent> box,
anim::type animated) {
const auto pointer = pushBox(std::move(box), animated);
while (!_layers.empty() && _layers.front().get() != pointer) {
auto removingLayer = std::move(_layers.front());
_layers.erase(begin(_layers));
if (removingLayer->inFocusChain()) {
setFocus();
}
removingLayer->setClosing();
}
const auto removeTill = ranges::find(
_layers,
pointer,
&std::unique_ptr<LayerWidget>::get);
_closingLayers.insert(
end(_closingLayers),
std::make_move_iterator(begin(_layers)),
std::make_move_iterator(removeTill));
_layers.erase(begin(_layers), removeTill);
clearClosingLayers();
}
void LayerStackWidget::prepareForAnimation() {
@@ -780,12 +781,35 @@ bool LayerStackWidget::takeToThirdSection() {
}
void LayerStackWidget::clearLayers() {
for (auto list = base::take(_layers); !list.empty(); list.pop_back()) {
const auto layer = std::move(list.back());
_closingLayers.insert(
end(_closingLayers),
std::make_move_iterator(begin(_layers)),
std::make_move_iterator(end(_layers)));
_layers.clear();
clearClosingLayers();
}
void LayerStackWidget::clearClosingLayers() {
const auto weak = make_weak(this);
while (!_closingLayers.empty()) {
const auto index = _closingLayers.size() - 1;
const auto layer = _closingLayers.back().get();
if (layer->inFocusChain()) {
setFocus();
}
// This may destroy LayerStackWidget (by calling Ui::hideLayer).
// So each time we check a weak pointer (if we are still alive).
layer->setClosing();
if (weak) {
// We could enqueue more closing layers, so we remove by index.
Assert(index < _closingLayers.size());
Assert(_closingLayers[index].get() == layer);
_closingLayers.erase(begin(_closingLayers) + index);
} else {
// All layers were already destroyed in ~LayerStackWidget.
break;
}
}
}