Tampermonkey / tampermonkey

Tampermonkey is the most popular userscript manager, with over 10 million users. It's available for Chrome, Microsoft Edge, Safari, Opera Next, and Firefox.
GNU General Public License v3.0
4.24k stars 419 forks source link

[suggestion] Add a method to communicate with other extensions #486

Open elypter opened 6 years ago

elypter commented 6 years ago

there are many scripts, and i have ideas as well, that modify any page if a certain pattern is detected. for example a video tag, a floating header or a count-down. these can all be implemented very well as userscripts. however if you use many of them you notice that there is a lot of duplicate code that is running and has to be coded. it is quite inefficient if 5 scripts in a row run getElementbyID, tag name or whatever and iterate over the same elements multiple times. i think this is a big waste of resources because if you want to make a good userscript you basically have to write some sort of an adblocker just for your single application.

this lead me to the idea to add a way to simply use the adblocker to trigger the userscript only when it is needed. it should also be a lot faster because adblockers are optimized for the task. to make this work tampermonkey would have to have a way for other extensions to trigger a userscript. preferably with arguments so the script already knows what elements to look at. at the moment there is no adblocker or other extension that could call userscripts, so it's a classic chicken egg problem but someone has to make the first step. some adblockers are already experimenting with script snippets so there is definitely a use on this side as well and i think it would save a lot of duplicate code if userscript managers and adblockers could work together.

tophf commented 6 years ago

Conditional execution of the installed script may be a good idea e.g. GM_executeScript.

An alternative approach would be to declare the portions within one script:

@code-section  foo
@code-section  bar1 optional
@code-section  bar1 optional
// ==/UserScript==

........

// UserScript:code-section foo
if (document.getElementByIf('bar1')) {
  GM_runCodeSection('bar1', {a: 1, b: 2}, asyncResult => {
    console.log('Asynchronous execution result:', asyncResult);
  });
} else if (document.getElementByIf('bar2')) {
  GM_runCodeSection('bar2', {a: 1, b: 2}, asyncResult => {
    console.log('Asynchronous execution result:', asyncResult);
  });
}
// /UserScript:code-section foo

// UserScript:code-section bar1
(() => {
  console.log(GM_getCodeSectionParams());
})();
// /UserScript:code-section bar1

// UserScript:code-section bar2
(() => {
  console.log(GM_getCodeSectionParams());
})();
// /UserScript:code-section bar2

This would inject only foo section's code (and the metadata block), which would check the current page and load other parts of the script if needed via GM_runCodeSection, which would run in the same sandbox. And of course the optional sections text won't be injected by default, thus reducing the time needed to build the sandbox during page load.

elypter commented 6 years ago

i also think that it would be very useful if userscripts could be executed from bookmarklets although i dont know if it is possible to do this without exposing them to the website as well.

Mottie commented 6 years ago

That seems overly complicated. I think a new GM function could be added to register namespaced code blocks like this:

// ==UserScript==
// @name utility functions
// @namespace some-unique-namespace
// ...
// ==/UserScript==
GM_registerCode('createElement', (name, options) => {
  console.log(name, options);
});

GM_registerCode('parseFile', ({outputType}) => {
  console.log(outputType)
});

Then another userscript could access the above "library" like this:

// Include 'some-unique-namespace' if the userscript namespace isn't the same
const $create = GM_executeCode('some-unique-namespace.createElement');
const div = $create('div', { /* */ });
const span = $create('span');