diff --git a/extension/extension-utils.js b/extension/extension-utils.js --- a/extension/extension-utils.js +++ b/extension/extension-utils.js @@ -20,6 +20,9 @@ var callbacks = {}; // TODO rename to "portCallbacks"? var runtimeCallbacks = {}; +let currentMessageSerial = 0; +let pendingMessageReplyResolvers = {}; + var storage = (IS_FIREFOX ? chrome.storage.local : chrome.storage.sync); let firefoxVersionMatch = navigator.userAgent.match(/Firefox\/(\d+)/) @@ -53,6 +56,24 @@ port.postMessage(message); } +function sendPortMessageWithReply(subsystem, event, payload) +{ + return new Promise((resolve, reject) => { + let message = payload || {}; + message.subsystem = subsystem; + message.event = event; + ++currentMessageSerial; + if (currentMessageSerial >= Math.pow(2, 31) - 1) { // INT_MAX + currentMessageSerial = 0; + } + message.serial = currentMessageSerial; + + pendingMessageReplyResolvers[message.serial] = resolve; + + port.postMessage(message); + }); +} + // Callback is called with following arguments (in that order); // - The actual message data/payload // - Information about the sender of the message (including tab and frameId) diff --git a/extension/extension.js b/extension/extension.js --- a/extension/extension.js +++ b/extension/extension.js @@ -105,12 +105,26 @@ var subsystem = message.subsystem; var action = message.action; - if (!subsystem || !action) { + let isReply = message.hasOwnProperty("replyToSerial"); + let replyToSerial = message.replyToSerial; + + if (!isReply && (!subsystem || !action)) { return; } receivedMessageOnce = true; + if (isReply) { + let replyResolver = pendingMessageReplyResolvers[replyToSerial]; + if (replyResolver) { + replyResolver(message.payload); + delete pendingMessageReplyResolvers[replyToSerial]; + } else { + console.warn("There is no reply resolver for message with serial", replyToSerial); + } + return; + } + if (callbacks[subsystem] && callbacks[subsystem][action]) { callbacks[subsystem][action](message.payload, action); } else { diff --git a/host/abstractbrowserplugin.h b/host/abstractbrowserplugin.h --- a/host/abstractbrowserplugin.h +++ b/host/abstractbrowserplugin.h @@ -53,11 +53,15 @@ AbstractBrowserPlugin(const QString &subsystemId, int protocolVersion, QObject *parent); virtual void handleData(const QString &event, const QJsonObject &data); + virtual QJsonObject handleData(int serial, const QString &event, const QJsonObject &data); virtual bool onLoad(); virtual bool onUnload(); void sendData(const QString &action, const QJsonObject &payload = QJsonObject()); + + void sendReply(int requestSerial, const QJsonObject &payload = QJsonObject()); + QDebug debug() const; QJsonObject settings() const; diff --git a/host/abstractbrowserplugin.cpp b/host/abstractbrowserplugin.cpp --- a/host/abstractbrowserplugin.cpp +++ b/host/abstractbrowserplugin.cpp @@ -39,6 +39,14 @@ Q_UNUSED(data); } +QJsonObject AbstractBrowserPlugin::handleData(int serial, const QString &event, const QJsonObject &data) +{ + Q_UNUSED(serial); + Q_UNUSED(event); + Q_UNUSED(data); + return QJsonObject(); +} + void AbstractBrowserPlugin::sendData(const QString &action, const QJsonObject &payload) { QJsonObject data; @@ -50,6 +58,19 @@ Connection::self()->sendData(data); } +void AbstractBrowserPlugin::sendReply(int requestSerial, const QJsonObject &payload) +{ + QJsonObject data{ + {QStringLiteral("replyToSerial"), requestSerial}, + }; + + if (!payload.isEmpty()) { + data.insert(QStringLiteral("payload"), payload); + } + + Connection::self()->sendData(data); +} + bool AbstractBrowserPlugin::onLoad() { return true; diff --git a/host/downloadplugin.h b/host/downloadplugin.h --- a/host/downloadplugin.h +++ b/host/downloadplugin.h @@ -35,6 +35,7 @@ explicit DownloadPlugin(QObject *parent); bool onLoad() override; bool onUnload() override; + using AbstractBrowserPlugin::handleData; void handleData(const QString &event, const QJsonObject &data) override; private: QHash m_jobs; diff --git a/host/kdeconnectplugin.h b/host/kdeconnectplugin.h --- a/host/kdeconnectplugin.h +++ b/host/kdeconnectplugin.h @@ -31,6 +31,7 @@ explicit KDEConnectPlugin(QObject *parent); bool onLoad() override; bool onUnload() override; + using AbstractBrowserPlugin::handleData; void handleData(const QString &event, const QJsonObject &data) override; private Q_SLOTS: diff --git a/host/mprisplugin.h b/host/mprisplugin.h --- a/host/mprisplugin.h +++ b/host/mprisplugin.h @@ -65,6 +65,7 @@ bool onUnload() override; + using AbstractBrowserPlugin::handleData; void handleData(const QString &event, const QJsonObject &data) override; // mpris properties ____________ diff --git a/host/pluginmanager.cpp b/host/pluginmanager.cpp --- a/host/pluginmanager.cpp +++ b/host/pluginmanager.cpp @@ -124,6 +124,16 @@ return; } - //design question, should we have a JSON of subsystem, event, payload, or have all data at the root level? - plugin->handleData(event, json); + const QJsonValue requestSerialVariant = json.value(QStringLiteral("serial")); + if (!requestSerialVariant.isUndefined()) { + const int requestSerial = requestSerialVariant.toInt(); + + const auto reply = plugin->handleData(requestSerial, event, json); + if (!reply.isEmpty()) { + plugin->sendReply(requestSerial, reply); + } + } else { + //design question, should we have a JSON of subsystem, event, payload, or have all data at the root level? + plugin->handleData(event, json); + } } diff --git a/host/tabsrunnerplugin.h b/host/tabsrunnerplugin.h --- a/host/tabsrunnerplugin.h +++ b/host/tabsrunnerplugin.h @@ -36,6 +36,8 @@ explicit TabsRunnerPlugin(QObject *parent); bool onLoad() override; bool onUnload() override; + + using AbstractBrowserPlugin::handleData; void handleData(const QString &event, const QJsonObject &data) override; // dbus-exported