This is a library to userscripts (or extensions) which will enable you to inject your userscript into the page and still have access to functions like GM_setValue.
MIT License
Please use Port Library as it uses Promises for async functions and it supports synchronous functions. Moreover, there's an API.
This library will make it possible to inject your userscript into the page and still have access to functions like GM_setValue, GM_getValue, GM_log and GM_xmlhttpRequest.
This is useful for the changes made to Firefox 32+, which limits the unsafeWindow. So
to maintain the same, old unsafeWindow
you will have to inject your userscript into
the page, where you don't have access to the GM api. What UserProxy does is that it
maintains the old unsafeWindow
while the userscript still have access to the GM api.
UserProxy sets up a message system between the page and the userscript. This system
works by sending stringified objects by using JSON
. In these objects there are
information on which function to call and with what arguments. UserProxy will when
sending arguments transform them into a passable object where the functions are
stored into the memory and an unique id is generated which will be sent instead
of the function. The reciever will then be able to identify from where that
unique function id came from and how to handle it.
All this is made into a simple API where it's not needed to know how the system works and how the transformation works.
This library uses CustomEvents and if that is not supported it will fallback to postMessage.
To start using this library you need to include UserProxy.js
in your userscript.
After that you will have to move your userscript into a function which you can
pass to connect
.
You will then have to modify the userscript to handle asynchronous functions instead of synchronous functions. The difference is that you have to attach callback functions to an asynchrnous function whereas synchronous functions return their result.
So if you need to call GM_getValue
synchronously it would look like this
var myValue = GM_getValue("myKey");
// ... your code
whereas in UserProxy every function is asynchronous and needs to be called as
GM_getValue("myKey").then(function(myValue){
// ... your code
});
The API of UserProxy is made into two parts. One for the userscript and one for the injected script/function.
To inject your script into the page you will have to make a function which you can
pass to connect
. The function will not be able to access anything outside it's closure.
function exampleScript() {
// ... your code
}
The exampleScript
function will be injected into the page by calling connect
.
UserProxy.connect(exampleScript);
The injected function/script will have the variables: window
and unsafeWindow
, which
both refers to the global window.
You can pass functions from the userscript closure to the page closure by defining
an object with the name of the function as the key of a property and the function
itself as the value. For the page closure to be able to access the functions
from the userscript closure the key pairs are needed to be passed to the
UserProxy.setFunctions
function. However remember that try to keep as much
unsafe code in the userscript closure to prevent any or minimize security holes.
An example on how to make functions available to the page closure is below.
UserProxy.setFunctions({
"GM_getValue": GM_getValue,
"GM_setValue": GM_setValue
});
// ...
UserProxy.connect(exampleScript);
After UserProxy.connect
have been called it's not possible to change the functions
available to the page closure from the userscript closure. So be sure to predefine
anything that's needed.
To call a function can only be done for the functions made available through the
function setFunctions
.
UserProxy will by default define the functions in the userscript scope so that the userscript can call the functions directly without needing to call an API.
An example of calling GM_getValue
:
GM_getValue("myKey");
The calling of the defined functions are asynchronous and therefore a callback
is needed to i.e. get the value of myKey
. To set the callback a function
then
needs to be called directly after calling the function.
GM_getValue("myKey").then(function(value){
// ... your callback code
});
It's possible to also call the functions by using call
where the first argument
is the method and the rest is the argument that should be passed to the defined
function.
UserProxy.call("GM_getValue", "myKey").then(function(value){
// ... your callback code
});
If you're going to call the same function with the same callback function multiple
times. Then you can use prepareCall
, where the first argument is the method, the
second argument is the callback. The arguments after that will be passed to the
defined function.
The prepareCall
function will return a function where the arguments are passed to the defined
function.
function callback() {
// ... your code
}
var myFunc = UserProxy.prepareCall("GM_setValue", callback, "myKey");
myFunc("First value"); // Will call GM_setValue("myKey", "First value").then(callback);
myFunc("Second value"); // Will call GM_setValue("myKey", "Second value").then(callback);
To list the defined functions in the injected code you can call listFunctions
,
which will return an array of all the callable functions through UserProxy.
var myFunctions = UserProxy.listFunctions();
To get a defined function in the injected code through UserProxy you call
getFunction
, where the argument is the method.
var myFunction = UserProxy.getFunction("GM_getValue");
If your injected code somehow uses the namespace UserProxy
you can set it to
something else. This is done before the connect
function has been called.
So if you want the namespace to be MyProxy
instead of UserProxy
you can do
the following:
UserProxy.setNamespace("MyProxy");
// ...
UserProxy.connect(exampleScript);
In the injected script i.e. exampleScript
you can now call the UserProxy API
by using the MyProxy
namespace:
if (MyProxy.isDefined("GM_getValue")) {
// ... your code
}
If you decide to allow your script to use GM_xmlhttpRequest
then make a domain
check to only allow domains through that you use as there is an open connection
with the page and userscript. This connection should in theory be interceptable
but requires the intercepter to know the token.
The MIT License (MIT)
Copyright (c) 2014 Jeppe Rune Mortensen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.