1 /*
2   remotemodelserver.cpp
3 
4   This file is part of GammaRay, the Qt application inspection and
5   manipulation tool.
6 
7   Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
8   Author: Volker Krause <volker.krause@kdab.com>
9 
10   Licensees holding valid commercial KDAB GammaRay licenses may use this file in
11   accordance with GammaRay Commercial License Agreement provided with the Software.
12 
13   Contact info@kdab.com if any conditions of this licensing are not clear to you.
14 
15   This program is free software; you can redistribute it and/or modify
16   it under the terms of the GNU General Public License as published by
17   the Free Software Foundation, either version 2 of the License, or
18   (at your option) any later version.
19 
20   This program is distributed in the hope that it will be useful,
21   but WITHOUT ANY WARRANTY; without even the implied warranty of
22   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23   GNU General Public License for more details.
24 
25   You should have received a copy of the GNU General Public License
26   along with this program.  If not, see <http://www.gnu.org/licenses/>.
27 */
28 
29 #include "remotemodelserver.h"
30 #include "server.h"
31 #include <core/probeguard.h>
32 #include <common/protocol.h>
33 #include <common/message.h>
34 #include <common/modelevent.h>
35 #include <common/sourcelocation.h>
36 
37 #include <compat/qasconst.h>
38 
39 #include <QAbstractItemModel>
40 #include <QSortFilterProxyModel>
41 #include <QDataStream>
42 #include <QDebug>
43 #include <QBuffer>
44 #include <QIcon>
45 
46 #include <iostream>
47 
48 using namespace GammaRay;
49 using namespace std;
50 
51 void(*RemoteModelServer::s_registerServerCallback)() = nullptr;
52 
RemoteModelServer(const QString & objectName,QObject * parent)53 RemoteModelServer::RemoteModelServer(const QString &objectName, QObject *parent)
54     : QObject(parent)
55     , m_model(nullptr)
56     , m_dummyBuffer(new QBuffer(&m_dummyData, this))
57     , m_monitored(false)
58 {
59     setObjectName(objectName);
60     m_dummyBuffer->open(QIODevice::WriteOnly);
61     registerServer();
62 }
63 
64 RemoteModelServer::~RemoteModelServer() = default;
65 
model() const66 QAbstractItemModel *RemoteModelServer::model() const
67 {
68     return m_model;
69 }
70 
setModel(QAbstractItemModel * model)71 void RemoteModelServer::setModel(QAbstractItemModel *model)
72 {
73     if (model == m_model)
74         return;
75 
76     if (m_model)
77         disconnectModel();
78 
79     m_model = model;
80     if (m_model && m_monitored)
81         connectModel();
82 
83     if (m_monitored)
84         modelReset();
85 }
86 
connectModel()87 void RemoteModelServer::connectModel()
88 {
89     Q_ASSERT(m_model);
90     Model::used(m_model);
91 
92     connect(m_model.data(), &QAbstractItemModel::headerDataChanged,
93             this, &RemoteModelServer::headerDataChanged);
94     connect(m_model.data(), &QAbstractItemModel::rowsInserted,
95             this, &RemoteModelServer::rowsInserted);
96     connect(m_model.data(), &QAbstractItemModel::rowsAboutToBeMoved,
97             this, &RemoteModelServer::rowsAboutToBeMoved);
98     connect(m_model.data(), &QAbstractItemModel::rowsMoved,
99             this, &RemoteModelServer::rowsMoved);
100     connect(m_model.data(), &QAbstractItemModel::rowsRemoved,
101             this, &RemoteModelServer::rowsRemoved);
102     connect(m_model.data(), &QAbstractItemModel::columnsInserted,
103             this, &RemoteModelServer::columnsInserted);
104     connect(m_model.data(), &QAbstractItemModel::columnsMoved,
105             this, &RemoteModelServer::columnsMoved);
106     connect(m_model.data(), &QAbstractItemModel::columnsRemoved,
107             this, &RemoteModelServer::columnsRemoved);
108     connect(m_model.data(), &QAbstractItemModel::dataChanged,
109             this, &RemoteModelServer::dataChanged);
110     connect(m_model.data(),
111             &QAbstractItemModel::layoutChanged,
112             this,
113             &RemoteModelServer::layoutChanged);
114     connect(m_model.data(), &QAbstractItemModel::modelReset, this, &RemoteModelServer::modelReset);
115     connect(m_model.data(), &QObject::destroyed, this, &RemoteModelServer::modelDeleted);
116 }
117 
disconnectModel()118 void RemoteModelServer::disconnectModel()
119 {
120     Q_ASSERT(m_model);
121     Model::unused(m_model);
122 
123     disconnect(m_model.data(), &QAbstractItemModel::headerDataChanged,
124                this, &RemoteModelServer::headerDataChanged);
125     disconnect(m_model.data(), &QAbstractItemModel::rowsInserted,
126                this, &RemoteModelServer::rowsInserted);
127     disconnect(m_model.data(), &QAbstractItemModel::rowsAboutToBeMoved,
128                this, &RemoteModelServer::rowsAboutToBeMoved);
129     disconnect(m_model.data(), &QAbstractItemModel::rowsMoved,
130                this, &RemoteModelServer::rowsMoved);
131     disconnect(m_model.data(), &QAbstractItemModel::rowsRemoved,
132                this, &RemoteModelServer::rowsRemoved);
133     disconnect(m_model.data(), &QAbstractItemModel::columnsInserted,
134                this, &RemoteModelServer::columnsInserted);
135     disconnect(m_model.data(), &QAbstractItemModel::columnsMoved,
136                this, &RemoteModelServer::columnsMoved);
137     disconnect(m_model.data(), &QAbstractItemModel::columnsRemoved,
138                this, &RemoteModelServer::columnsRemoved);
139     disconnect(m_model.data(), &QAbstractItemModel::dataChanged,
140                this, &RemoteModelServer::dataChanged);
141     disconnect(m_model.data(), &QAbstractItemModel::layoutChanged,
142                this, &RemoteModelServer::layoutChanged);
143     disconnect(m_model.data(), &QAbstractItemModel::modelReset, this, &RemoteModelServer::modelReset);
144     disconnect(m_model.data(), &QObject::destroyed, this, &RemoteModelServer::modelDeleted);
145 }
146 
newRequest(const GammaRay::Message & msg)147 void RemoteModelServer::newRequest(const GammaRay::Message &msg)
148 {
149     if (!m_model && msg.type() != Protocol::ModelSyncBarrier)
150         return;
151 
152     ProbeGuard g;
153     switch (msg.type()) {
154     case Protocol::ModelRowColumnCountRequest:
155     {
156         quint32 size;
157         msg >> size;
158         Q_ASSERT(size > 0);
159 
160         Message reply(m_myAddress, Protocol::ModelRowColumnCountReply);
161         reply << size;
162         for (quint32 i = 0; i < size; ++i) {
163             Protocol::ModelIndex index;
164             msg >> index;
165             const QModelIndex qmIndex = Protocol::toQModelIndex(m_model, index);
166 
167             qint32 rowCount = -1, columnCount = -1;
168             if (index.isEmpty() || qmIndex.isValid()) {
169                 rowCount = m_model->rowCount(qmIndex);
170                 columnCount = m_model->columnCount(qmIndex);
171             }
172 
173             reply << index << rowCount << columnCount;
174         }
175         sendMessage(reply);
176         break;
177     }
178 
179     case Protocol::ModelContentRequest:
180     {
181         quint32 size;
182         msg >> size;
183         Q_ASSERT(size > 0);
184 
185         QVector<QModelIndex> indexes;
186         indexes.reserve(size);
187         for (quint32 i = 0; i < size; ++i) {
188             Protocol::ModelIndex index;
189             msg >> index;
190             const QModelIndex qmIndex = Protocol::toQModelIndex(m_model, index);
191             if (!qmIndex.isValid())
192                 continue;
193             indexes.push_back(qmIndex);
194         }
195         if (indexes.isEmpty())
196             break;
197 
198         Message msg(m_myAddress, Protocol::ModelContentReply);
199         msg << quint32(indexes.size());
200         for (const auto &qmIndex : qAsConst(indexes))
201             msg << Protocol::fromQModelIndex(qmIndex)
202                           << filterItemData(m_model->itemData(qmIndex))
203                           << qint32(m_model->flags(qmIndex));
204 
205         sendMessage(msg);
206         break;
207     }
208 
209     case Protocol::ModelHeaderRequest:
210     {
211         qint8 orientation;
212         qint32 section;
213         msg >> orientation >> section;
214         Q_ASSERT(orientation == Qt::Horizontal || orientation == Qt::Vertical);
215         Q_ASSERT(section >= 0);
216 
217         QHash<qint32, QVariant> data;
218         // TODO: add all roles
219         data.insert(Qt::DisplayRole,
220                     m_model->headerData(section, static_cast<Qt::Orientation>(orientation),
221                                         Qt::DisplayRole));
222         data.insert(Qt::ToolTipRole,
223                     m_model->headerData(section, static_cast<Qt::Orientation>(orientation),
224                                         Qt::ToolTipRole));
225 
226         Message msg(m_myAddress, Protocol::ModelHeaderReply);
227         msg << orientation << section << data;
228         sendMessage(msg);
229         break;
230     }
231 
232     case Protocol::ModelSetDataRequest:
233     {
234         Protocol::ModelIndex index;
235         int role;
236         QVariant value;
237         msg >> index >> role >> value;
238 
239         m_model->setData(Protocol::toQModelIndex(m_model, index), value, role);
240         break;
241     }
242 
243     case Protocol::ModelSortRequest:
244     {
245         quint32 column, order;
246         msg >> column >> order;
247         m_model->sort(column, (Qt::SortOrder)order);
248         break;
249     }
250 
251     case Protocol::ModelSyncBarrier:
252     {
253         qint32 barrierId;
254         msg >> barrierId;
255         Message reply(m_myAddress, Protocol::ModelSyncBarrier);
256         reply << barrierId;
257         sendMessage(reply);
258         break;
259     }
260     }
261 }
262 
filterItemData(QMap<int,QVariant> && itemData) const263 QMap<int, QVariant> RemoteModelServer::filterItemData(QMap<int, QVariant> &&itemData) const
264 {
265     for (auto it = itemData.begin(); it != itemData.end();) {
266         if (!it.value().isValid()) {
267             it = itemData.erase(it);
268         } else if (it.value().userType() == qMetaTypeId<QIcon>()) {
269             // see also: https://bugreports.qt-project.org/browse/QTBUG-33321
270             const QIcon icon = it.value().value<QIcon>();
271             ///TODO: what size to use? icon.availableSizes is empty...
272             if (!icon.isNull())
273                 it.value() = icon.pixmap(QSize(16, 16));
274             ++it;
275         } else if (canSerialize(it.value())) {
276             ++it;
277         } else {
278 // qWarning() << "Cannot serialize QVariant of type" << it.value().typeName();
279             it = itemData.erase(it);
280         }
281     }
282     return std::move(itemData);
283 }
284 
canSerialize(const QVariant & value) const285 bool RemoteModelServer::canSerialize(const QVariant &value) const
286 {
287     if (qstrcmp(value.typeName(), "QJSValue") == 0 || qstrcmp(value.typeName(), "QJsonObject") == 0 || qstrcmp(value.typeName(), "QJsonValue") == 0 || qstrcmp(value.typeName(), "QJsonArray") == 0) {
288         // QJSValue tries to serialize nested elements and asserts if that fails
289         // too bad it can contain QObject* as nested element, which obviously can't be serialized...
290         // QJsonObject serialization fails due to QTBUG-73437
291         return false;
292     }
293 
294     // recurse into containers
295     if (value.canConvert<QVariantList>()) {
296         QSequentialIterable it = value.value<QSequentialIterable>();
297         for (const QVariant &v : it) {
298             if (!canSerialize(v))
299                 return false;
300         }
301         // note: do not return true here, the fact we can write every single element
302         // does not mean we can write the entire thing, or vice vesa...
303     } else if (value.canConvert<QVariantMap>()) {
304         auto iterable = value.value<QAssociativeIterable>();
305         for (auto it = iterable.begin(); it != iterable.end(); ++it) {
306             if (!canSerialize(it.value()) || !canSerialize(it.key()))
307                 return false;
308         }
309         // see above
310     }
311 
312     // whitelist a few expensive to encode types we know we can serialize
313     if (value.userType() == qMetaTypeId<QUrl>() || value.userType() == qMetaTypeId<GammaRay::SourceLocation>())
314         return true;
315 
316     // ugly, but there doesn't seem to be a better way atm to find out without trying
317     m_dummyBuffer->seek(0);
318     QDataStream stream(m_dummyBuffer);
319     return QMetaType::save(stream, value.userType(), value.constData());
320 }
321 
modelMonitored(bool monitored)322 void RemoteModelServer::modelMonitored(bool monitored)
323 {
324     if (m_monitored == monitored)
325         return;
326     m_monitored = monitored;
327     if (m_model) {
328         if (m_monitored)
329             connectModel();
330         else
331             disconnectModel();
332     }
333 }
334 
dataChanged(const QModelIndex & begin,const QModelIndex & end,const QVector<int> & roles)335 void RemoteModelServer::dataChanged(const QModelIndex &begin, const QModelIndex &end,
336                                     const QVector<int> &roles)
337 {
338     if (!isConnected())
339         return;
340     Message msg(m_myAddress, Protocol::ModelContentChanged);
341     msg << Protocol::fromQModelIndex(begin) << Protocol::fromQModelIndex(end) << roles;
342     sendMessage(msg);
343 }
344 
headerDataChanged(Qt::Orientation orientation,int first,int last)345 void RemoteModelServer::headerDataChanged(Qt::Orientation orientation, int first, int last)
346 {
347     if (!isConnected())
348         return;
349     Message msg(m_myAddress, Protocol::ModelHeaderChanged);
350     msg <<  qint8(orientation) << first << last;
351     sendMessage(msg);
352 }
353 
rowsInserted(const QModelIndex & parent,int start,int end)354 void RemoteModelServer::rowsInserted(const QModelIndex &parent, int start, int end)
355 {
356     sendAddRemoveMessage(Protocol::ModelRowsAdded, parent, start, end);
357 }
358 
rowsAboutToBeMoved(const QModelIndex & sourceParent,int sourceStart,int sourceEnd,const QModelIndex & destinationParent,int destinationRow)359 void RemoteModelServer::rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart,
360                                            int sourceEnd, const QModelIndex &destinationParent,
361                                            int destinationRow)
362 {
363     Q_UNUSED(sourceStart);
364     Q_UNUSED(sourceEnd);
365     Q_UNUSED(destinationRow);
366     m_preOpIndexes.push_back(Protocol::fromQModelIndex(sourceParent));
367     m_preOpIndexes.push_back(Protocol::fromQModelIndex(destinationParent));
368 }
369 
rowsMoved(const QModelIndex & sourceParent,int sourceStart,int sourceEnd,const QModelIndex & destinationParent,int destinationRow)370 void RemoteModelServer::rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
371                                   const QModelIndex &destinationParent, int destinationRow)
372 {
373     Q_UNUSED(sourceParent);
374     Q_UNUSED(destinationParent);
375     Q_ASSERT(m_preOpIndexes.size() >= 2);
376     const auto destParentIdx = m_preOpIndexes.takeLast();
377     const auto sourceParentIdx = m_preOpIndexes.takeLast();
378     sendMoveMessage(Protocol::ModelRowsMoved, sourceParentIdx, sourceStart, sourceEnd,
379                     destParentIdx, destinationRow);
380 }
381 
rowsRemoved(const QModelIndex & parent,int start,int end)382 void RemoteModelServer::rowsRemoved(const QModelIndex &parent, int start, int end)
383 {
384     sendAddRemoveMessage(Protocol::ModelRowsRemoved, parent, start, end);
385 }
386 
columnsInserted(const QModelIndex & parent,int start,int end)387 void RemoteModelServer::columnsInserted(const QModelIndex &parent, int start, int end)
388 {
389     sendAddRemoveMessage(Protocol::ModelColumnsAdded, parent, start, end);
390 }
391 
columnsMoved(const QModelIndex & sourceParent,int sourceStart,int sourceEnd,const QModelIndex & destinationParent,int destinationColumn)392 void RemoteModelServer::columnsMoved(const QModelIndex &sourceParent, int sourceStart,
393                                      int sourceEnd, const QModelIndex &destinationParent,
394                                      int destinationColumn)
395 {
396     sendMoveMessage(Protocol::ModelColumnsMoved,
397                     Protocol::fromQModelIndex(sourceParent), sourceStart, sourceEnd,
398                     Protocol::fromQModelIndex(destinationParent), destinationColumn);
399 }
400 
columnsRemoved(const QModelIndex & parent,int start,int end)401 void RemoteModelServer::columnsRemoved(const QModelIndex &parent, int start, int end)
402 {
403     sendAddRemoveMessage(Protocol::ModelColumnsRemoved, parent, start, end);
404 }
405 
406 
layoutChanged(const QList<QPersistentModelIndex> & parents,QAbstractItemModel::LayoutChangeHint hint)407 void RemoteModelServer::layoutChanged(const QList<QPersistentModelIndex> &parents,
408                                       QAbstractItemModel::LayoutChangeHint hint)
409 {
410     QVector<Protocol::ModelIndex> indexes;
411     indexes.reserve(parents.size());
412     for (const auto &index : parents)
413         indexes.push_back(Protocol::fromQModelIndex(index));
414     sendLayoutChanged(indexes, hint);
415 }
416 
417 
sendLayoutChanged(const QVector<Protocol::ModelIndex> & parents,quint32 hint)418 void RemoteModelServer::sendLayoutChanged(const QVector< Protocol::ModelIndex > &parents,
419                                           quint32 hint)
420 {
421     if (!isConnected())
422         return;
423     Message msg(m_myAddress, Protocol::ModelLayoutChanged);
424     msg << parents << hint;
425     sendMessage(msg);
426 }
427 
modelReset()428 void RemoteModelServer::modelReset()
429 {
430     if (!isConnected())
431         return;
432     sendMessage(Message(m_myAddress, Protocol::ModelReset));
433 }
434 
sendAddRemoveMessage(Protocol::MessageType type,const QModelIndex & parent,int start,int end)435 void RemoteModelServer::sendAddRemoveMessage(Protocol::MessageType type, const QModelIndex &parent,
436                                              int start, int end)
437 {
438     if (!isConnected())
439         return;
440     Message msg(m_myAddress, type);
441     msg << Protocol::fromQModelIndex(parent) << start << end;
442     sendMessage(msg);
443 }
444 
sendMoveMessage(Protocol::MessageType type,const Protocol::ModelIndex & sourceParent,int sourceStart,int sourceEnd,const Protocol::ModelIndex & destinationParent,int destinationIndex)445 void RemoteModelServer::sendMoveMessage(Protocol::MessageType type,
446                                         const Protocol::ModelIndex &sourceParent, int sourceStart,
447                                         int sourceEnd,
448                                         const Protocol::ModelIndex &destinationParent,
449                                         int destinationIndex)
450 {
451     if (!isConnected())
452         return;
453     Message msg(m_myAddress, type);
454     msg << sourceParent << qint32(sourceStart) << qint32(sourceEnd)
455                   << destinationParent << qint32(destinationIndex);
456     sendMessage(msg);
457 }
458 
modelDeleted()459 void RemoteModelServer::modelDeleted()
460 {
461     m_model = nullptr;
462     if (m_monitored)
463         modelReset();
464 }
465 
registerServer()466 void RemoteModelServer::registerServer()
467 {
468     if (Q_UNLIKELY(s_registerServerCallback)) { // called from the ctor, so we can't rely on virtuals
469         s_registerServerCallback();
470         return;
471     }
472     m_myAddress = Server::instance()->registerObject(objectName(), this, Server::ExportProperties);
473     Server::instance()->registerMessageHandler(m_myAddress, this, "newRequest");
474     Server::instance()->registerMonitorNotifier(m_myAddress, this, "modelMonitored");
475     connect(Endpoint::instance(), &Endpoint::disconnected, this, [this] { modelMonitored(); });
476 }
477 
isConnected() const478 bool RemoteModelServer::isConnected() const
479 {
480     return Endpoint::isConnected();
481 }
482 
sendMessage(const Message & msg) const483 void RemoteModelServer::sendMessage(const Message &msg) const
484 {
485     Endpoint::send(msg);
486 }
487 
proxyDynamicSortFilter() const488 bool RemoteModelServer::proxyDynamicSortFilter() const
489 {
490     if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
491         return proxy->dynamicSortFilter();
492     return false;
493 }
494 
setProxyDynamicSortFilter(bool dynamicSortFilter)495 void RemoteModelServer::setProxyDynamicSortFilter(bool dynamicSortFilter)
496 {
497     if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
498         proxy->setDynamicSortFilter(dynamicSortFilter);
499 }
500 
proxyFilterCaseSensitivity() const501 Qt::CaseSensitivity RemoteModelServer::proxyFilterCaseSensitivity() const
502 {
503     if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
504         return proxy->filterCaseSensitivity();
505     return Qt::CaseSensitive;
506 }
507 
setProxyFilterCaseSensitivity(Qt::CaseSensitivity caseSensitivity)508 void RemoteModelServer::setProxyFilterCaseSensitivity(Qt::CaseSensitivity caseSensitivity)
509 {
510     if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
511         proxy->setFilterCaseSensitivity(caseSensitivity);
512 }
513 
proxyFilterKeyColumn() const514 int RemoteModelServer::proxyFilterKeyColumn() const
515 {
516     if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
517         return proxy->filterKeyColumn();
518     return 0;
519 }
520 
setProxyFilterKeyColumn(int column)521 void RemoteModelServer::setProxyFilterKeyColumn(int column)
522 {
523     if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
524         proxy->setFilterKeyColumn(column);
525 }
526 
proxyFilterRegExp() const527 QRegExp RemoteModelServer::proxyFilterRegExp() const
528 {
529     if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
530         return proxy->filterRegExp();
531     return QRegExp();
532 }
533 
setProxyFilterRegExp(const QRegExp & regExp)534 void RemoteModelServer::setProxyFilterRegExp(const QRegExp &regExp)
535 {
536     if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
537         proxy->setFilterRegExp(regExp);
538 }
539