Fix crash in DocumentData destructor.

Keep AuthSession pointer in DocumentData for loader destruction.
This commit is contained in:
John Preston 2018-01-27 12:21:57 +03:00
parent 63c1212ef1
commit a858ab5d0b
8 changed files with 187 additions and 180 deletions

View File

@ -268,14 +268,14 @@ AuthSession &Auth() {
AuthSession::AuthSession(UserId userId)
: _userId(userId)
, _autoLockTimer([this] { checkAutoLock(); })
, _data(std::make_unique<Data::Session>(this))
, _api(std::make_unique<ApiWrap>(this))
, _calls(std::make_unique<Calls::Instance>())
, _downloader(std::make_unique<Storage::Downloader>())
, _uploader(std::make_unique<Storage::Uploader>())
, _storage(std::make_unique<Storage::Facade>())
, _notifications(std::make_unique<Window::Notifications::System>(this))
, _changelogs(Core::Changelogs::Create(this)) {
, _changelogs(Core::Changelogs::Create(this))
, _data(std::make_unique<Data::Session>(this)) {
Expects(_userId != 0);
_saveDataTimer.setCallback([this] {

View File

@ -252,7 +252,6 @@ private:
TimeMs _shouldLockAt = 0;
base::Timer _autoLockTimer;
const std::unique_ptr<Data::Session> _data;
const std::unique_ptr<ApiWrap> _api;
const std::unique_ptr<Calls::Instance> _calls;
const std::unique_ptr<Storage::Downloader> _downloader;
@ -261,6 +260,9 @@ private:
const std::unique_ptr<Window::Notifications::System> _notifications;
const std::unique_ptr<Core::Changelogs> _changelogs;
// _data depends on _downloader / _uploader, including destructor.
const std::unique_ptr<Data::Session> _data;
rpl::lifetime _lifetime;
};

View File

@ -152,28 +152,6 @@ QString saveFileName(const QString &title, const QString &filter, const QString
return name;
}
bool StickerData::setInstalled() const {
switch (set.type()) {
case mtpc_inputStickerSetID: {
auto it = Auth().data().stickerSets().constFind(
set.c_inputStickerSetID().vid.v);
return (it != Auth().data().stickerSets().cend())
&& !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
} break;
case mtpc_inputStickerSetShortName: {
auto name = qs(set.c_inputStickerSetShortName().vshort_name).toLower();
for (auto it = Auth().data().stickerSets().cbegin(), e = Auth().data().stickerSets().cend(); it != e; ++it) {
if (it->shortName.toLower() == name) {
return !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
}
}
} break;
}
return false;
}
QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = false, const QString already = QString(), const QDir &dir = QDir()) {
auto alreadySavingFilename = data->loadingFilePath();
if (!alreadySavingFilename.isEmpty()) {
@ -253,9 +231,7 @@ void DocumentOpenClickHandler::doOpen(
auto audio = AudioMsgId(data, msgId);
Media::Player::mixer()->play(audio);
Media::Player::Updated().notify(audio);
if (App::main()) {
Auth().data().markMediaRead(data);
}
data->session()->data().markMediaRead(data);
}
} else if (playMusic) {
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song);
@ -282,24 +258,24 @@ void DocumentOpenClickHandler::doOpen(
File::Launch(filepath);
}
}
Auth().data().markMediaRead(data);
data->session()->data().markMediaRead(data);
} else if (data->isVoiceMessage() || data->isAudioFile() || data->isVideoFile()) {
auto filepath = location.name();
if (documentIsValidMediaFile(filepath)) {
File::Launch(filepath);
}
Auth().data().markMediaRead(data);
data->session()->data().markMediaRead(data);
} else if (data->size < App::kImageSizeLimit) {
if (!data->data().isEmpty() && playAnimation) {
if (action == ActionOnLoadPlayInline && context) {
Auth().data().requestAnimationPlayInline(context);
data->session()->data().requestAnimationPlayInline(context);
} else {
Messenger::Instance().showDocument(data, context);
}
} else if (location.accessEnable()) {
if (playAnimation || QImageReader(location.name()).canRead()) {
if (playAnimation && action == ActionOnLoadPlayInline && context) {
Auth().data().requestAnimationPlayInline(context);
data->session()->data().requestAnimationPlayInline(context);
} else {
Messenger::Instance().showDocument(data, context);
}
@ -378,35 +354,22 @@ void DocumentCancelClickHandler::onClickImpl() const {
}
VoiceData::~VoiceData() {
if (!waveform.isEmpty() && waveform.at(0) == -1 && waveform.size() > int32(sizeof(TaskId))) {
if (!waveform.isEmpty()
&& waveform[0] == -1
&& waveform.size() > int32(sizeof(TaskId))) {
TaskId taskId = 0;
memcpy(&taskId, waveform.constData() + 1, sizeof(taskId));
Local::cancelTask(taskId);
}
}
DocumentData::DocumentData(DocumentId id, int32 dc, uint64 accessHash, int32 version, const QString &url, const QVector<MTPDocumentAttribute> &attributes)
DocumentData::DocumentData(DocumentId id, not_null<AuthSession*> session)
: id(id)
, _dc(dc)
, _access(accessHash)
, _version(version)
, _url(url) {
setattributes(attributes);
if (_dc && _access) {
_location = Local::readFileLocation(mediaKey());
}
, _session(session) {
}
DocumentData *DocumentData::create(DocumentId id) {
return new DocumentData(id, 0, 0, 0, QString(), QVector<MTPDocumentAttribute>());
}
DocumentData *DocumentData::create(DocumentId id, int32 dc, uint64 accessHash, int32 version, const QVector<MTPDocumentAttribute> &attributes) {
return new DocumentData(id, dc, accessHash, version, QString(), attributes);
}
DocumentData *DocumentData::create(DocumentId id, const QString &url, const QVector<MTPDocumentAttribute> &attributes) {
return new DocumentData(id, 0, 0, 0, url, attributes);
not_null<AuthSession*> DocumentData::session() const {
return _session;
}
void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes) {
@ -577,7 +540,7 @@ void DocumentData::performActionOnLoad() {
}
} else if (Media::Player::IsStopped(state.state)) {
Media::Player::mixer()->play(AudioMsgId(this, _actionOnLoadMsgId));
Auth().data().markMediaRead(this);
_session->data().markMediaRead(this);
}
}
} else if (playMusic) {
@ -598,7 +561,7 @@ void DocumentData::performActionOnLoad() {
} else if (playAnimation) {
if (loaded()) {
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
Auth().data().requestAnimationPlayInline(item);
_session->data().requestAnimationPlayInline(item);
} else {
Messenger::Instance().showDocument(this, item);
}
@ -613,7 +576,7 @@ void DocumentData::performActionOnLoad() {
if (documentIsValidMediaFile(already)) {
File::Launch(already);
}
Auth().data().markMediaRead(this);
_session->data().markMediaRead(this);
} else if (loc.accessEnable()) {
if (showImage && QImageReader(loc.name()).canRead()) {
Messenger::Instance().showDocument(this, item);
@ -642,7 +605,7 @@ bool DocumentData::loaded(FilePathResolveType type) const {
}
destroyLoaderDelayed();
}
Auth().data().notifyDocumentLayoutChanged(this);
_session->data().notifyDocumentLayoutChanged(this);
}
return !data().isEmpty() || !filepath(type).isEmpty();
}
@ -650,7 +613,7 @@ bool DocumentData::loaded(FilePathResolveType type) const {
void DocumentData::destroyLoaderDelayed(mtpFileLoader *newValue) const {
_loader->stop();
auto loader = std::unique_ptr<FileLoader>(std::exchange(_loader, newValue));
Auth().downloader().delayedDestroyLoader(std::move(loader));
_session->downloader().delayedDestroyLoader(std::move(loader));
}
bool DocumentData::loading() const {
@ -752,7 +715,7 @@ void DocumentData::save(
_loader->connect(_loader, SIGNAL(failed(FileLoader*,bool)), App::main(), SLOT(documentLoadFailed(FileLoader*,bool)));
_loader->start();
}
Auth().data().notifyDocumentLayoutChanged(this);
_session->data().notifyDocumentLayoutChanged(this);
}
void DocumentData::cancel() {
@ -763,9 +726,8 @@ void DocumentData::cancel() {
auto loader = std::unique_ptr<FileLoader>(std::exchange(_loader, CancelledMtpFileLoader));
loader->cancel();
loader->stop();
Auth().downloader().delayedDestroyLoader(std::move(loader));
Auth().data().notifyDocumentLayoutChanged(this);
_session->downloader().delayedDestroyLoader(std::move(loader));
_session->data().notifyDocumentLayoutChanged(this);
if (auto main = App::main()) {
main->documentLoadProgress(this);
}
@ -870,6 +832,31 @@ QString DocumentData::filepath(FilePathResolveType type, bool forceSavingAs) con
return result;
}
bool DocumentData::isStickerSetInstalled() const {
Expects(sticker() != nullptr);
const auto &set = sticker()->set;
const auto &sets = _session->data().stickerSets();
switch (set.type()) {
case mtpc_inputStickerSetID: {
auto it = sets.constFind(set.c_inputStickerSetID().vid.v);
return (it != sets.cend())
&& !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
} break;
case mtpc_inputStickerSetShortName: {
auto name = qs(set.c_inputStickerSetShortName().vshort_name).toLower();
for (auto it = sets.cbegin(), e = sets.cend(); it != e; ++it) {
if (it->shortName.toLower() == name) {
return !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
}
}
} break;
}
return false;
}
ImagePtr DocumentData::makeReplyPreview() {
if (replyPreview->isNull() && !thumb->isNull()) {
if (thumb->loaded()) {
@ -889,6 +876,105 @@ ImagePtr DocumentData::makeReplyPreview() {
return replyPreview;
}
StickerData *DocumentData::sticker() const {
return (type == StickerDocument)
? static_cast<StickerData*>(_additional.get())
: nullptr;
}
void DocumentData::checkSticker() {
const auto data = sticker();
if (!data) return;
automaticLoad(nullptr);
if (data->img->isNull() && loaded()) {
if (_data.isEmpty()) {
const FileLocation &loc(location(true));
if (loc.accessEnable()) {
data->img = ImagePtr(loc.name());
loc.accessDisable();
}
} else {
data->img = ImagePtr(_data);
}
}
}
SongData *DocumentData::song() {
return isSong()
? static_cast<SongData*>(_additional.get())
: nullptr;
}
const SongData *DocumentData::song() const {
return const_cast<DocumentData*>(this)->song();
}
VoiceData *DocumentData::voice() {
return isVoiceMessage()
? static_cast<VoiceData*>(_additional.get())
: nullptr;
}
const VoiceData *DocumentData::voice() const {
return const_cast<DocumentData*>(this)->voice();
}
bool DocumentData::hasRemoteLocation() const {
return (_dc != 0 && _access != 0);
}
bool DocumentData::isValid() const {
return hasRemoteLocation() || !_url.isEmpty();
}
MTPInputDocument DocumentData::mtpInput() const {
if (_access) {
return MTP_inputDocument(
MTP_long(id),
MTP_long(_access));
}
return MTP_inputDocumentEmpty();
}
QString DocumentData::filename() const {
return _filename;
}
QString DocumentData::mimeString() const {
return _mimeString;
}
bool DocumentData::hasMimeType(QLatin1String mime) const {
return !_mimeString.compare(mime, Qt::CaseInsensitive);
}
void DocumentData::setMimeString(const QString &mime) {
_mimeString = mime;
}
MediaKey DocumentData::mediaKey() const {
return ::mediaKey(locationType(), _dc, id, _version);
}
QString DocumentData::composeNameString() const {
if (auto songData = song()) {
return ComposeNameString(
_filename,
songData->title,
songData->performer);
}
return ComposeNameString(_filename, QString(), QString());
}
LocationType DocumentData::locationType() const {
return isVoiceMessage()
? AudioFileLocation
: isVideoFile()
? VideoFileLocation
: DocumentFileLocation;
}
bool DocumentData::isVoiceMessage() const {
return (type == VoiceDocument);
}

View File

@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_types.h"
class AuthSession;
inline uint64 mediaMix32To64(int32 a, int32 b) {
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32)
| uint64(*reinterpret_cast<uint32*>(&b));
@ -42,10 +44,7 @@ struct DocumentAdditionalData {
struct StickerData : public DocumentAdditionalData {
ImagePtr img;
QString alt;
MTPInputStickerSet set = MTP_inputStickerSetEmpty();
bool setInstalled() const;
StorageImageLocation loc; // doc thumb location
};
@ -72,17 +71,9 @@ class Document;
class DocumentData {
public:
static DocumentData *create(DocumentId id);
static DocumentData *create(
DocumentId id,
int32 dc,
uint64 accessHash,
int32 version,
const QVector<MTPDocumentAttribute> &attributes);
static DocumentData *create(
DocumentId id,
const QString &url,
const QVector<MTPDocumentAttribute> &attributes);
DocumentData(DocumentId id, not_null<AuthSession*> session);
not_null<AuthSession*> session() const;
void setattributes(
const QVector<MTPDocumentAttribute> &attributes);
@ -130,44 +121,14 @@ public:
void forget();
ImagePtr makeReplyPreview();
StickerData *sticker() {
return (type == StickerDocument)
? static_cast<StickerData*>(_additional.get())
: nullptr;
}
void checkSticker() {
StickerData *s = sticker();
if (!s) return;
StickerData *sticker() const;
void checkSticker();
bool isStickerSetInstalled() const;
SongData *song();
const SongData *song() const;
VoiceData *voice();
const VoiceData *voice() const;
automaticLoad(nullptr);
if (s->img->isNull() && loaded()) {
if (_data.isEmpty()) {
const FileLocation &loc(location(true));
if (loc.accessEnable()) {
s->img = ImagePtr(loc.name());
loc.accessDisable();
}
} else {
s->img = ImagePtr(_data);
}
}
}
SongData *song() {
return isSong()
? static_cast<SongData*>(_additional.get())
: nullptr;
}
const SongData *song() const {
return const_cast<DocumentData*>(this)->song();
}
VoiceData *voice() {
return isVoiceMessage()
? static_cast<VoiceData*>(_additional.get())
: nullptr;
}
const VoiceData *voice() const {
return const_cast<DocumentData*>(this)->voice();
}
bool isVoiceMessage() const;
bool isVideoMessage() const;
bool isSong() const;
@ -187,20 +148,9 @@ public:
bool setRemoteVersion(int32 version); // Returns true if version has changed.
void setRemoteLocation(int32 dc, uint64 access);
void setContentUrl(const QString &url);
bool hasRemoteLocation() const {
return (_dc != 0 && _access != 0);
}
bool isValid() const {
return hasRemoteLocation() || !_url.isEmpty();
}
MTPInputDocument mtpInput() const {
if (_access) {
return MTP_inputDocument(
MTP_long(id),
MTP_long(_access));
}
return MTP_inputDocumentEmpty();
}
bool hasRemoteLocation() const;
bool isValid() const;
MTPInputDocument mtpInput() const;
// When we have some client-side generated document
// (for example for displaying an external inline bot result)
@ -208,18 +158,18 @@ public:
// to (this) received from the server "same" document.
void collectLocalData(DocumentData *local);
QString filename() const {
return _filename;
}
QString mimeString() const {
return _mimeString;
}
bool hasMimeType(QLatin1String mime) const {
return !_mimeString.compare(mime, Qt::CaseInsensitive);
}
void setMimeString(const QString &mime) {
_mimeString = mime;
}
QString filename() const;
QString mimeString() const;
bool hasMimeType(QLatin1String mime) const;
void setMimeString(const QString &mime);
MediaKey mediaKey() const;
static QString ComposeNameString(
const QString &filename,
const QString &songTitle,
const QString &songPerformer);
QString composeNameString() const;
~DocumentData();
@ -234,44 +184,12 @@ public:
std::unique_ptr<Data::UploadState> uploadingData;
int32 md5[8];
MediaKey mediaKey() const {
return ::mediaKey(locationType(), _dc, id, _version);
}
static QString ComposeNameString(
const QString &filename,
const QString &songTitle,
const QString &songPerformer);
QString composeNameString() const {
if (auto songData = song()) {
return ComposeNameString(
_filename,
songData->title,
songData->performer);
}
return ComposeNameString(_filename, QString(), QString());
}
private:
DocumentData(
DocumentId id,
int32 dc,
uint64 accessHash,
int32 version,
const QString &url,
const QVector<MTPDocumentAttribute> &attributes);
friend class Serialize::Document;
LocationType locationType() const {
return isVoiceMessage()
? AudioFileLocation
: isVideoFile()
? VideoFileLocation
: DocumentFileLocation;
}
LocationType locationType() const;
void destroyLoaderDelayed(mtpFileLoader *newValue = nullptr) const;
// Two types of location: from MTProto by dc+access+version or from web by url
int32 _dc = 0;
@ -281,6 +199,8 @@ private:
QString _filename;
QString _mimeString;
not_null<AuthSession*> _session;
FileLocation _location;
QByteArray _data;
std::unique_ptr<DocumentAdditionalData> _additional;
@ -290,9 +210,6 @@ private:
FullMsgId _actionOnLoadMsgId;
mutable FileLoader *_loader = nullptr;
void destroyLoaderDelayed(
mtpFileLoader *newValue = nullptr) const;
};
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit);

View File

@ -699,7 +699,9 @@ void Session::photoApplyFields(
not_null<DocumentData*> Session::document(DocumentId id) {
auto i = _documents.find(id);
if (i == _documents.cend()) {
i = _documents.emplace(id, DocumentData::create(id)).first;
i = _documents.emplace(
id,
std::make_unique<DocumentData>(id, _session)).first;
}
return i->second.get();
}

View File

@ -1004,7 +1004,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (media->type() == MediaTypeSticker) {
if (const auto document = media->getDocument()) {
if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
_menu->addAction(lang(document->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] {
_menu->addAction(lang(document->isStickerSetInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] {
showStickerPackInfo(document);
});
}

View File

@ -1517,7 +1517,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (media->type() == MediaTypeSticker) {
if (auto document = media->getDocument()) {
if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
_menu->addAction(lang(document->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] {
_menu->addAction(lang(document->isStickerSetInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] {
showStickerPackInfo(document);
});
_menu->addAction(lang(Stickers::IsFaved(document) ? lng_faved_stickers_remove : lng_faved_stickers_add), [=] {

View File

@ -204,7 +204,7 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu(
if (media->type() == MediaTypeSticker) {
if (auto document = media->getDocument()) {
if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
result->addAction(lang(document->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] {
result->addAction(lang(document->isStickerSetInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] {
ShowStickerPackInfo(document);
});
}