diff --git a/extension/content-script.js b/extension/content-script.js --- a/extension/content-script.js +++ b/extension/content-script.js @@ -683,21 +683,19 @@ var oldSetActionHandler = navigator.mediaSession.setActionHandler || noop; navigator.mediaSession.setActionHandler = function (name, cb) { - // Call the original native implementation - // "call()" is needed as the real setActionHandler is a class member - // and calling it directly is illegal as it lacks the context - // We'll register the callback for ourself after this since it may - // throw for unsupported callback names. - var ret = oldSetActionHandler.call(navigator.mediaSession, name, cb); - if (cb) { ${mediaSessionsClassName}.callbacks[name] = cb; } else { delete ${mediaSessionsClassName}.callbacks[name]; } ${mediaSessionsClassName}.sendMessage("callbacks", Object.keys(${mediaSessionsClassName}.callbacks)); - return ret; + // Call the original native implementation + // "call()" is needed as the real setActionHandler is a class member + // and calling it directly is illegal as it lacks the context + // This may throw for unsupported actions but we registered the callback + // ourselves before + return oldSetActionHandler.call(navigator.mediaSession, name, cb); }; Object.defineProperty(navigator.mediaSession, "metadata", { 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,22 @@ port.postMessage(message); } +function sendPortMessageWithReply(subsystem, event, payload) +{ + return new Promise((resolve, reject) => { + let message = payload || {}; + message.subsystem = subsystem; + message.event = event; + message.serial = ++currentMessageSerial; + if (message.serial >= Math.pow(2, 32) - 1) { // INT_MAX + message.serial = 0; + } + 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 @@ -104,13 +104,25 @@ port.onMessage.addListener(function (message) { var subsystem = message.subsystem; var action = message.action; + let replyToSerial = message.replyToSerial; - if (!subsystem || !action) { + if (replyToSerial < 0 && (!subsystem || !action)) { return; } receivedMessageOnce = true; + if (replyToSerial >= 0) { + 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/pluginmanager.cpp b/host/pluginmanager.cpp --- a/host/pluginmanager.cpp +++ b/host/pluginmanager.cpp @@ -124,6 +124,15 @@ 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 int requestSerial = json.value(QStringLiteral("serial")).toInt(); + + if (requestSerial) { + 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); + } }