Abstract
dwb can be extended with different types of userscripts. This api documentation describes the javascript interface.
Getting Started
Scripts that use the javascript api must be located in $XDG_CONFIG_HOME/dwb/userscripts like any other script. To include a userscript put a script with shebang
#!javascript
in the userscripts directory.
All native javascript methods can be used in scripts, however there are limitations:
-
The execution context of userscripts is completely seperated from the web execution context. Due to security concerns it is not possible to communicate with the web execution context, it is only possible to inject scripts into the web context.
-
In contrast to the global window object in the web execution context, the global object is a readonly object, i.e. it is not possible to set properties on the global object, see also global data for details.
Global
Functions from the global object.
Functions
execute("tabopen ixquick.com"); // Execute a function once, similar to window.setTimeout() timerStart(2000, function() { tabs.current.inject("alert('Hello world')"); return false; });
Note
|
Scripts included with include are either visible in the global scope or invisible, even in the including script. To use an included script it can return an object with its public objects: |
// included script var private = 37; return { getPrivate : function () { return private; } }; // Scripts that includes the above var i = include("/path/to/script"); var p = i.getPrivate(); // 37
Global Objects
data
The data object can be used to determine internally used data securely. All properties are readonly Strings.
// Get contents of the currently used bookmark file var bookmarks = io.read(data.bookmarks);
io
The io object implements functions for input and output.
var text = io.read("/home/foo/textfile.txt"); io.print(text);
system
The system object implements system functions.
var home = system.getEnv("HOME"); // asynchronous wrapped read function asyncread(filename) { system.spawn("cat " + filename, function (response) { ... }); } asyncread(home + "/.bashrc");
tabs
The tabs object implements functions and properties to get webview objects.
Properties
Functions
var c = tabs.current; tabs.nth(2).loadUri(c.uri);
util
The util object implements helper functions.
Webkit objects
All webkit objects correspond to gobject objects, i.e. they have the same properties, but the javascript properties are all camelcase. For example, a WebKitWebView has the property zoom-level, the corresponding javascript property is zoomLevel:
var webview = tabs.current webview.zoomLevel = webview.zoomLevel * 2;
All Objects derived from GObjets implement the following functions.
Example
tabs.current.connect("notify::load-status", function() { io.print("Status changed"); });
webview
The webview object represents the widget that actually displays the site content.
Properties
The properties correspond to the gobject properties in camelcase. Additional properties are
Functions
Example
var wv = tabs.current; wv.loadUri("http://www.example.com", function() { if (wv.loadStatus == LoadStatus.finished) { wv.inject("alert('Hello ' + wv.uri + '!!!');"); return true; } });
Note
|
If a script is injected from a [loadStatus]-callback the script must be injected after LoadStatus.committed has been emitted. On LoadStatus.committed the document hasn’t been created, if the script modifies the DOM it should be injected on LoadStatus.finished. If only LoadStatus.committed or loadFinished.committed are used it is better to use the corresponding signals instead to reduce overhead. |
frame
A frame represents a frame or iframe. Due to same origin policy it is not possible to inject scripts from a [webview] into iframes with a different domain. For this purpose the frame object can be used.
Properties
The properties correspond to the gobject properties in camelcase. Additional properties are
To get the domain or hostname of a webview
webview.mainFrame.domain webview.mainFrame.host
can be used.
Functions
download
Corresponds to a WebKitDownload.
Constructor
Functions
var download = new Download("http://www.example.org/foo.pdf"); var filename = "/tmp/" + download.suggestedFilename; download.destinationUri = "file://" + filename; download.start(function () { if (download.status == DownloadStatus.finished) { system.spawn("xpdf " + filename); } });
request
Corresponds to a WebKitNavigationRequest.
navigationAction
Corresponds to a WebKitWebNavigationAction.
Signals
With the signals object dwb communicates with the script on certain events. To connect to a signal one can call the connect function that is implemented by the signals object, that takes 2 arguments, the name of the signal and a callback function.
The callback function has a varying number of parameters. The last paramter is always a json-object which might be empty or contain additional data relevant to the signal. A callback function should either return true or false or nothing which is equivalent to false. If multiple callbacks are connected to the same signal and one callback function returns true the overall return value will be true.
dwb only emits signals as long as one callback is connected to a signal. To reduce overhead one should disconnect from signals when no longer needed.
The signals object is not a readonly object, properties can be added to the object which are visible in all scripts but it should be avoided to add properties on the signals object. signals should only be used to connect to signals or define custom signals.
The signals object implements the following functions
Functions
connect()
Emitted signals
Custom signals can be created by simply calling
signals.connect("nameOfNewSignal", callbackFunction);
Signals emitted by dwb are the following:
// Prevent example.com from being loaded function navigationCallback(wv, frame, request, action) { if (/.*\.example\.com.*/.test(request.uri)) { return true; } } signals.connect("navigation", navigationCallback);
Enum objects
Enum objects are objects that have only readonly properties, mapping gtk/webkit enums to javascript objects.
Global data
Since all scripts share the same execution context, they are encapsulated in a function. To avoid conflicts with other scripts it is not allowed to set properties on the global object, i.e.
#!javascript // not allowed, the global object is readonly number = 0; io.print(number); // undefined // always use var instead var number = 0; io.print(number2); // 0 // won't work either function foo() { bar = 1; } foo(); io.print(bar); // undefined
For sharing data between scripts either signals can be created or the globals-object can be used. To share data with the globals object securely when the scripts are loaded the script can return an init function that will be called after all scripts have been initialized:
#!javascript // set when the script is initialized. globals.foo = "bar";
#!javascript // The behaviour is undefined, depending on if Script 1 was initialized before // Script 2 or the other way round. io.print(globals.foo) // undefined or "bar" // Will be called after all scripts have been initialized return { init : function () { // globals.foo has been initialized io.print(globals.foo); // "bar" } };
One exception is [include], scripts that are explicitly included into the global scope setting the second parameter to true are visible in every script.
var foo = "bar";
#!javascript include("/path/to/foo", true); // visible in every script
#!javascript // Make sure Script 1 has been initialized return { init : function() { io.print(foo); // "bar"; } };
Extensions
dwb provides the possibility to load extensions. It is recommended to implement javascript-userscripts as an extension to have consistent configuration locations for scripts.
The extensions object has the following properties and functions
Properties
Functions
#!javascript extensions.load("foobar");
The default searchpath for extensions isn $XDG_DATA_HOME/dwb/extensions and SHARE_DIR/dwb/extensions where the SHARE_DIR with sharedirectory being the share directory of the installation, most likely /usr/share.
The configuration for extensions is in $XDG_CONFIG_HOME/dwb/extensionsrc and should return a javascript object.
Every extension must implement one function named init that takes one argument, the config for the extension. The function should return true if the extension was successfully loaded, false otherwise. Every extension also may implement a function end that will be called when an extension is unloaded. If an extension registers to signals and binds shortcuts the extension should unregister all signals and unbind all shortcuts in this function. init and end should be returned from the extension.
A extension called foobar in $HOME/.local/share/dwb/extensions/foobar.
function foo(val) { .... } function bar(val) { .... } function loadStatusCallback(w) { ... } return { init : function (config) { if (config.foo > 36) { bar(config.foo); foo(config.bar); bind("XX", bar, "dobar"); signals.connect("loadStatus", loadStatusCallback); return true; } return false; }, end : function () { unbind(bar); signals.disconnectByFunction(loadStatusCallback); return true; } };
return { foobar : { bar : "X", foo : 37 }, // config for extension foobar barfoo : { } // config for extension barfoo };