/**
Copyright (C) 1995-2011 Opera Software ASA. All rights reserved.
THIS FILE CONTAINS DOCUMENTATION DESCRIBING HOW TO USE THE INTERFACE
("API") TO JSPLUGIN, THE MODULAR JAVASCRIPT PLUGIN TECHNOLOGY FOR
THE OPERA 11 BROWSER LAUNCHED BY OPERA SOFTWARE ASA ("OPERA"). THE
API TOGETHER WITH DOCUMENTATION AND EXAMPLE CODE IS PUBLICISED BY
OPERA, AND OPERA IS WILLING TO PERMIT USE OF IT BY YOU ("LICENSEE"),
ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED
IN A SEPARATE API LICENSE AGREEMENT ("AGREEMENT"). PLEASE READ THE
TERMS AND CONDITIONS OF THIS LICENSE CAREFULLY. BY READING, COPYING
OR USING THE API IN ANY WAY, YOU ACCEPT THE TERMS AND CONDITIONS OF
THIS AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND, YOU ARE NOT
ALLOWED TO STUDY THIS API OR MAKE USE OF IT IN ANY WAY.
*/
Native JavaScript Extensions are provided as a way to interface with the native environment from from the JavaScript environment. A possible use is for set top box manufacturers to control their hardware (eg tv tuners) from an HTML user interface. Other uses could be to create file objects and other functionality that is not included in JavaScript.
Or to quote the internal design draft:
[Native JavaScript Extensions] provide DOM-like functionality on objects that are not
part of DOM or JavaScript; these objects may be system-dependent,
unsafe, obscure, or otherwise not desirable to include in Opera in
general.
In the current implementation, the directory "jsplugins" under the Opera binary directory is scanned for .dll (W*ndows) files at startup. Under U**X, ~/.opera/jsplugins is scanned for .so files. If the library exports the symbol jsplugin_capabilities (see header file for details), the library is kept loaded, and the plugin is registered.
The plugin can then receive requests to do its stuff. These requests are given via callbacks that have been set during initialization.
To the plugin:
From the plugin:
struct jsplugin_cap and send it back in result. Its fields are:
global_names: a NULL terminated array of names that the plugin
responds to.
global_getter: responds to requests for the plugin to get one of the
names above. See Getters and setters.
global_setter: responds to requests for the plugin to set one of the
names above. See Getters and setters.
init and destroy: initialization and destruction functions for the
plugin itself.
js_plugin_capabilities should return 0 on success and non-zero otherwise.
Function and regular objects are created in Opera's ecmascript engine via callbacks. The arguments to these callbacks include:
refobj: used for setting up the environment for the object. You get
this in most calls. It can also be called global_context (eg in jsplugin_document_init).
getter/setter: used to get and set names (properties/functions) on the
object.
f_call: used for giving the native function to be called when the
javascript function is called.
constructor: the constructor for the object.
f_signature: what arguments the function takes.
See the About function signatures
section.
destructor: the destructor for the object.
result: here you get the object in question.
Function signatures indicate what arguments a javascript function takes. Basically, one can combine "s" (string), "n" (number), "b" (boolean) and "-" (any object) as one needs.
So, "snb" will make the function take (string, number, boolean) as arguments. If more arguments are passed than is specified in the signature, they are treated as the last type. Thus, a signature of "n" makes the function accept (number, number, number ...) The function signatures actually represents the requested argument conversion, so the "-" character represents no conversion and needs to be used where an object argument is expected. This meens it's possible to write a function that takes an argument "-", that could be of any type and the type checking is done in the function implementation.
It's up to the native implementation to check that the number and type
of arguments is meaningful. The number of arguments can usually be
found as an argc argument, and the type can be checked in the
type member of the struct jsplugin_value.
Extending the OBJECT element is straightforward, and bears many similarities to creating a custom JavaScript extension. Some notable differences are:
jsplugin_cap.object_types and jsplugin_cap.handle_object have to be filled in (see below)
jsplugin_cap.global_{getter,setter} shouldn't be filled in
jsplugin_cap.object_types is the equivalent to
jsplugin_cap.global_names, but for OBJECT extensions.
Like its colleague, it's a NULL terminated array, but in this case of
MIME types that the OBJECT plugin is able to handle.
See the example code for -- examples.
During initialization, one also has to set the field jsplugin_cap.handle_object.
When a custom OBJECT is found in the html and the plugin has the right permissions set (see the section Giving permissions to a plugin), this callback will be called.
As parameters, the callback gets a number of parameters:
<OBJECT TYPE="application/tve-trigger"
ID="triggerReceiverObj"></OBJECT> gives
attrs_count == 2 and two items in the attrs
array.
getter and setter: these can be used
to respond to getname and putname requests to the object. They behave
similarly to the getters and setters of javascript plugin objects. See Getters and setters.
Plugin permissions are used to determine what server may serve content that instansiates and executes code contained in javascript and OBJECT plugins. The default value is that content from no server may access no javascript or OBJECT plugins whatsoever.
To ease this somewhat boring (but very secure) set of default
restrictions, plugin permissions can be declared in the
jsplugins.ini ini file. It is looked for in the same
directory as the plugins themselves. This roughly means
~/.opera/jsplugins/ under U**X-like platforms, and
opera_exe_dir/jsplugins/ under W*NDOWS.
The file should contain a list of plugin permissions on the form plugin_id: protocol, server, port, where:
[ALL]. See the examples
An example jsplugins.ini could be:
triggerreceiver.so: http, myserver.company.com, 0 videoplayer.so: http, myserver.company.com, 0 videoplayer.so: http, myserver2.company.com, 8080 videoplayer.so: file, localhost, 0 videoplayer.so: http, [ALL].corporation.com, 0This will allow two of the example plugins via http to be instantiated in content from myserver.company.com on the default port for http. videoplayer.so will also be allowed in content from myserver2.company.com served via http on port 8080, and in content coming via file from localhost.
In addition, the last line indicates that all hosts under the corporation.com domain will be allowed to serve content that instantiates the videoplayer plugin.
There is also a more advanced way of handling permissions, and that is
that the plugin itself handles the security permissions, which can be
necessary for complex security schemes. If the plugin sets the
jsplugin_cap::allow_access callback and the keyword
CALLBACK is in the protocol field of one of the plugin's
lines in jsplugins.ini, the allow_access
callback will be called to determine whether the plugin should be
allowed to be instantiated or not in a particular document.
videoplayer.so: CALLBACKwill make the videoplayer's security be handled by itself. If there are multiple lines for a certain plugin where one line states CALLBACK, the behaviour is undefined.
The callback gets the protocol, the hostname of the accessing host and the port number as arguments. It should return non-zero to grant access.
There can be comments in the jsplugins.ini file, but only on lines for themselves. Comment lines start with # .
Eg
# this line is a comment
Whitespace is allowed both before the ";" on comment lines and before and in between fields on the permissions lines.
Lines that do not adhere to this (in some ways rather strict) format will be considered invalid and will be discarded.
The global context is often given as a parameter to functions in the plugin, and the plugin often has to pass it back into Opera. It can be viewed as a token or a reference to the javascript global context. It enables Opera to create the plugin in the right context, regarding global environment, execution permissions etc.
The plugin cannot do anything with the global context. Just pass it back when required and you'll be fine. ;-)
Plugin private data has to be allocated and deallocated by the plugin
itself. Good places for allocation are eg in the global getters for
jsplugin objects (or whereever needed and there exists a
A plugin gets to know when a document that is allowed to instantiate
the plugin gets created and destroyed. These are useful hooks if the
plugin needs to prepare something (for example some global data) when
it can be initialized and destroy it when the document is destroyed.
The plugin will get calls to
Getters and setters occur in a number of places in the javascript plugin api:
Note: The getters/setters are quite commonly also being referred to as
getname/putname in this documentation. This is what is used in the
EcmaScript standard, for example.
Getters and setters are nothing magical. They are used to look up
names and, if the names exist, to get or set their values (or call the
functions, should they be such creatures). So, reasonable behaviour
for a getter is typically to compare the string that is being looked
up to the functions/properties that it knows it wants to support, and
then create and return either a function object (that will be called
from the javascript interpreter) or a javascript value.
When you return a string via the jsplugin_value.u.string, the
string is copied on the Opera side, and it will be garbage collected
duly. However, if you have allocated any memory to construct the
string within the plugin itself, you have the responsibility to
deallocate it yourself.
When a value is successfully found and returned from a getter, you can
return either with the code JSP_GET_VALUE or with
JSP_GET_VALUE_CACHE. If you use the latter, then the property
will not be looked up again for the lifetime of the object, but will
be cached on the Opera side. This may be useful for performance
reasons.
The best documentation on how to do this is probably the code
itself. Please have a look at the code in the examples directory in
this distribution.
Most of the time, the API implemented by the plugin does not expect strings to
contain null bytes. In this case, string handling is simple:
jsplugin_value.type will be set to JSP_TYPE_STRING, and
their value will be stored as a null-terminated string in
jsplugin_value.u.string. This is true both for values sent to Opera
from the plugin and for values sent from Opera to the plugin.
However, it is possible in javascript to have null bytes in the middle of a
string. This is sometimes used to store binary data. This is handled
differently for strings sent to Opera and strings sent from Opera.
Strings sent from Opera that may contain null bytes will still have their
jsplugin_value.type field set to JSP_TYPE_STRING. The plugin
needs to read the jsplugin_value.u.len field, which gives the length
of the string in bytes, to know if the string extends beyond the first null
byte encountered.
To send a string containing null bytes to Opera, the plugin must set
jsplugin_value.type to JSP_TYPE_STRING_WITH_LENGTH, store the
string data in jsplugin_value.u.string and store the length of the
string, in bytes, in jsplugin_value.u.len. If
jsplugin_value.type is set to JSP_TYPE_STRING, Opera will
ignore the value ofjsplugin_value.u.len, and will truncate the string
at the first null byte.
It is possible to perform asynchronous getters and function calls which will
suspend the ES execution allowing the program to continue as normal. Once the
return value is ready the plugin can call opera_callbacks->resume()
with a reference object and the execution will continue. This results in the
getter or function to be called again and the real value can be returned.
Distinguishing a restart from a normal get or call is done by checking the
type of the result parameter. This is set to JSP_TYPE_UNDEFINED
for normal calls or JSP_TYPE_NULL for restarts.
When suspending execution the getter or function can set an object in the
result parameter. This object will be available when the getter or
function is restarted, in which case result will have the type
JSP_TYPE_OBJECT.
Async getters must return JSP_GET_DELAYED while function calls must
return JSP_CALL_DELAYED to suspend execution.
You can write plugins in C++. But remember that the default linkage of
a C++ compiled file is C++, so you'll have to put an export
"C" in front of the plugin's jsplugin_capabilities function.
The global context
Plugin private data
jsplugin_obj.plugin_private is a pointer that the plugin can use to store things that it needs to keep in between function calls (its context, basically). The struct jsplugin_obj is passed in as a parameter to most functions in the plugin.
jsplugin_obj) and in handle_object for
OBJECT plugins. Deallocation is best performed in the respective
plugin instance's destructor.
Initialization and destruction
jsplugin_cap::init and
jsplugin_cap::destroy to signal these events.
Getters and setters
jsplugin_cap.global_getter and
jsplugin_cap.global_setter if the plugin should be able
to answer to any requests at all (this applies to custom javascript
objects, extension to the OBJECT element can get away without these)
jsplugin_cap.handle_object has
to give a getter and a setter for looking up / setting attributes on
the element.
VideoPlayer.id.
String handling
Asynchronous operations
Miscellaneous tips
$Id$