From 80c53a0591707b6daee256850475750b0c640ef4 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 17 Aug 2023 16:01:33 -0500 Subject: [PATCH 1/2] Add IDE messaging support to extensions API. Close #1406 --- src/extensions.js | 24 ++++++++++++++++++++++++ src/websockets.js | 4 ++++ test/extensions.spec.js | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/src/extensions.js b/src/extensions.js index 746c1fbe6a..f8fd6b820a 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -76,6 +76,13 @@ this.registry.forEach(ext => ext.onSetStageSize(width, height)); } + onIDEMessage(name, data) { + const ext = this.registry.find(ext => ext.name === name); + if (ext) { + ext.onIDEMessage(data); + } + } + register(Extension) { if (this.isReady()) { this.load(Extension); @@ -211,6 +218,23 @@ return []; }; + /** + * Send a message to other open NetsBlox clients (IDEs). Messages will only + * be accessible to NetsBlox IDEs and not programs written in NetsBlox. + * + * Message data can be anything and will be handled only if the extension is + * loaded by the recipient, too. + */ + Extension.prototype.sendIDEMessage = function(msgData, ...clientIds) { + const msg = { + type: 'extension', + name: this.name, + data: msgData, + }; + return NetsBloxExtensions.ide.sockets.sendIDEMessage(msg, ...clientIds); + }; + + Extension.prototype.onIDEMessage = Extension.prototype.onRunScripts = Extension.prototype.onStopAllScripts = Extension.prototype.onPauseAll = diff --git a/src/websockets.js b/src/websockets.js index 1f270122e2..d960e1a71f 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -47,6 +47,10 @@ class MessageHandler { } WebSocketManager.IDEMessageHandlers = { + 'extension': function(msg) { + const {name, data} = msg; + NetsBloxExtensions.onIDEMessage(name, data); + }, 'action-rejected': function(msg) { if (msg.reason) { this.ide.showMessage(localize(msg.reason)); diff --git a/test/extensions.spec.js b/test/extensions.spec.js index c5c7b3ef42..98f09fa429 100644 --- a/test/extensions.spec.js +++ b/test/extensions.spec.js @@ -373,4 +373,38 @@ describe('extensions', function() { assert(w == 123 && h == 456); }); }); + + describe('IDE messages', function () { + it('should send IDE messages', function(done) { + const {NetsBloxExtensions} = driver.globals(); + const extension = NetsBloxExtensions.registry[0]; + const payload = "hello there!"; + extension.onIDEMessage = data => { + delete extension.onIDEMessage; + const err = data !== payload ? + new Error("Incorrect data payload: " + data) : null; + done(err); + }; + extension.sendIDEMessage(payload, driver.ide().cloud.clientId); + }); + + it('should only receive own messages', function(done) { + const {NetsBloxExtensions, Extension} = driver.globals(); + const MessagingExt = function() {}; + MessagingExt.prototype = new Extension('MessagingExt'); + + const extension = NetsBloxExtensions.registry[0]; + extension.onIDEMessage = () => { + const err = new Error("Message received by other extension!"); + done(err); + }; + + const sender = NetsBloxExtensions.registry[0]; + sender.onIDEMessage = () => done(); + + const payload = "hello there!"; + extension.sendIDEMessage(payload, driver.ide().cloud.clientId); + NetsBloxExtensions.register(MessagingExt); + }); + }); }); From 7b02f795ace30e4b847ce8cf8bc3fa59d06d5bf8 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Fri, 18 Aug 2023 12:27:29 -0500 Subject: [PATCH 2/2] refactor ext message handling --- src/extensions.js | 42 +++++++++++++++++++++-------------------- src/websockets.js | 8 ++++---- test/extensions.spec.js | 39 +++++++++++++++----------------------- 3 files changed, 41 insertions(+), 48 deletions(-) diff --git a/src/extensions.js b/src/extensions.js index f8fd6b820a..5ba7dc9aba 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -76,11 +76,10 @@ this.registry.forEach(ext => ext.onSetStageSize(width, height)); } - onIDEMessage(name, data) { - const ext = this.registry.find(ext => ext.name === name); - if (ext) { - ext.onIDEMessage(data); - } + onMessage(type, data) { + this.registry + .flatMap(ext => ext.getMessageHandlers(type)) + .forEach(fn => fn(data)) } register(Extension) { @@ -192,6 +191,7 @@ function Extension (name) { this.name = name; + this._handlers = {}; } Extension.prototype.getMenu = function() { @@ -218,23 +218,25 @@ return []; }; - /** - * Send a message to other open NetsBlox clients (IDEs). Messages will only - * be accessible to NetsBlox IDEs and not programs written in NetsBlox. - * - * Message data can be anything and will be handled only if the extension is - * loaded by the recipient, too. - */ - Extension.prototype.sendIDEMessage = function(msgData, ...clientIds) { - const msg = { - type: 'extension', - name: this.name, - data: msgData, - }; - return NetsBloxExtensions.ide.sockets.sendIDEMessage(msg, ...clientIds); + Extension.prototype.addMessageListener = function(type, fn) { + if (!this._handlers[type]) { + this._handlers[type] = []; + } + this._handlers[type].push(fn); + }; + + Extension.prototype.removeMessageListener = function(type, fn) { + const handlers = this.getMessageHandlers(type); + const index = handlers.indexOf(fn); + if (index > -1) { + handlers.splice(index, 1); + } + }; + + Extension.prototype.getMessageHandlers = function(type) { + return this._handlers[type] || []; }; - Extension.prototype.onIDEMessage = Extension.prototype.onRunScripts = Extension.prototype.onStopAllScripts = Extension.prototype.onPauseAll = diff --git a/src/websockets.js b/src/websockets.js index d960e1a71f..cb04b51187 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -47,10 +47,6 @@ class MessageHandler { } WebSocketManager.IDEMessageHandlers = { - 'extension': function(msg) { - const {name, data} = msg; - NetsBloxExtensions.onIDEMessage(name, data); - }, 'action-rejected': function(msg) { if (msg.reason) { this.ide.showMessage(localize(msg.reason)); @@ -180,6 +176,10 @@ WebSocketManager.IDEMessageHandlers = { }; WebSocketManager.MessageHandlers = { + 'extension': function(msg) { + const {type, data} = msg.data; + NetsBloxExtensions.onMessage(type, data); + }, 'ide-message': function(msg) { const {data, sender} = msg; let response, error; diff --git a/test/extensions.spec.js b/test/extensions.spec.js index 98f09fa429..947d6c313a 100644 --- a/test/extensions.spec.js +++ b/test/extensions.spec.js @@ -374,37 +374,28 @@ describe('extensions', function() { }); }); - describe('IDE messages', function () { - it('should send IDE messages', function(done) { + describe('messages', function () { + it('should pass recvd messages to ext', function(done) { const {NetsBloxExtensions} = driver.globals(); const extension = NetsBloxExtensions.registry[0]; const payload = "hello there!"; - extension.onIDEMessage = data => { - delete extension.onIDEMessage; + extension.addMessageListener('test', data => { + console.log("hello?") const err = data !== payload ? new Error("Incorrect data payload: " + data) : null; done(err); + }); + + // spoof receiving the message + const msg = { + type: 'extension', + data: { + type: 'test', + data: payload, + }, }; - extension.sendIDEMessage(payload, driver.ide().cloud.clientId); - }); - - it('should only receive own messages', function(done) { - const {NetsBloxExtensions, Extension} = driver.globals(); - const MessagingExt = function() {}; - MessagingExt.prototype = new Extension('MessagingExt'); - - const extension = NetsBloxExtensions.registry[0]; - extension.onIDEMessage = () => { - const err = new Error("Message received by other extension!"); - done(err); - }; - - const sender = NetsBloxExtensions.registry[0]; - sender.onIDEMessage = () => done(); - - const payload = "hello there!"; - extension.sendIDEMessage(payload, driver.ide().cloud.clientId); - NetsBloxExtensions.register(MessagingExt); + const rawMsg = {data: JSON.stringify(msg)}; + driver.ide().sockets.websocket.onmessage(rawMsg); }); }); });