diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 2e2be027..6b33d2c9 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -1092,6 +1092,20 @@
"optionsCustomizeUpdate": {
"message": "Updates"
},
+ "optionsExtMessaging": {
+ "message": "External control"
+ },
+ "optionsExtMessagingNote": {
+ "message": "You can give access to Stylus to other extensions. These extensions will get full power over your styles. For advanced users only, you are left on your own to figure everything out. No backwards compatibility guaranteed. Input extension IDs, not names."
+ },
+ "optionsExtMessagingAdd": {
+ "message": "Add",
+ "description": "Label for the button to add an external messaging extension"
+ },
+ "optionsExtMessagingRemove": {
+ "message": "Remove",
+ "description": "Label for the button to remove an external messaging extension"
+ },
"optionsHeading": {
"message": "Options",
"description": "Heading for options section on manage page."
diff --git a/background/background.js b/background/background.js
index 2cdbc06c..7f213bc7 100644
--- a/background/background.js
+++ b/background/background.js
@@ -163,6 +163,17 @@ chrome.runtime.onInstalled.addListener(({reason, previousVersion}) => {
}
});
+prefs.subscribe('externals.allowedExtensionIds', (id, value) => {
+ /* global onMessageExternal */// ext-msg.js
+ if (value.length > 0) {
+ require(['/background/ext-msg'], () => {
+ chrome.runtime.onMessageExternal.addListener(onMessageExternal);
+ });
+ } else if (window.onMessageExternal) {
+ chrome.runtime.onMessageExternal.removeListener(onMessageExternal);
+ }
+}, {runNow: true});
+
msg.on((msg, sender) => {
if (msg.method === 'invokeAPI') {
let res = msg.path.reduce((res, name) => res && res[name], API);
diff --git a/background/ext-msg.js b/background/ext-msg.js
new file mode 100644
index 00000000..7f904c4b
--- /dev/null
+++ b/background/ext-msg.js
@@ -0,0 +1,21 @@
+/* global msg */
+/* global prefs */
+'use strict';
+
+window.onMessageExternal = function ({data, target}, sender, sendResponse) {
+ // Check origin
+ if (!sender.id || sender.id !== chrome.runtime.id
+ && !prefs.get('externals.allowedExtensionIds').includes(sender.id)
+ ) {
+ return;
+ }
+
+ const allowedAPI =
+ ['openEditor', 'openManage', 'styles', 'sync', 'updater', 'usercss', 'usw'];
+ // Check content
+ if (target === 'extension' && data && data.method === 'invokeAPI'
+ && data.path && allowedAPI.includes(data.path[0])
+ ) {
+ msg._onRuntimeMessage({data, target}, sender, sendResponse);
+ }
+};
diff --git a/js/msg.js b/js/msg.js
index 4b40dd0c..20e8eb0c 100644
--- a/js/msg.js
+++ b/js/msg.js
@@ -108,6 +108,8 @@
}
return result;
},
+
+ _onRuntimeMessage: onRuntimeMessage,
};
function getExtBg() {
diff --git a/js/prefs.js b/js/prefs.js
index e11f5c0b..f191c224 100644
--- a/js/prefs.js
+++ b/js/prefs.js
@@ -27,6 +27,8 @@
'styleViaXhr': false, // early style injection to avoid FOUC
'patchCsp': false, // add data: and popular image hosting sites to strict CSP
+ 'externals.allowedExtensionIds': [], // extensions allowed to message Stylus
+
// checkbox in style config dialog
'config.autosave': true,
diff --git a/options.html b/options.html
index 8e9fc5f1..bd5f2789 100644
--- a/options.html
+++ b/options.html
@@ -16,6 +16,20 @@
+
+