Java in Firefox Extensions

  • Revision slug: Java_in_Firefox_Extensions
  • Revision title: Java in Firefox Extensions
  • Revision id: 69952
  • Created:
  • Creator: Brettz9
  • Is current revision? No
  • Comment +LiveConnect overview link; 2 words added

Revision Content

If you are in need of calling Java code from within a Firefox extension, you can make use of LiveConnect. LiveConnect gives your extension's JavaScript code (linked from or contained in XUL code) access to 2 objects: java and Packages (note that per this thread, although the new documentation for the LiveConnect reimplementation states that these globals will be deprecated (in the context of applets), "Firefox and the Java Plug-In will continue to support the global java/Packages keywords, in particular in the context of Firefox extensions."). These 2 objects let you make use of the standard JDK classes, e.g.,

var aJavaList = new java.util.LinkedList();

If you want to load your own JARs, then you can create your own Java class loader. 

The following technique only works in JavaScript code linked from or contained in XUL files. If you wish to call Java code from within JavaScript code that implements some XPCOM components, at this time, you need a different technique (refer to the complete Java Firefox Extension). A good reason for calling Java from within an XPCOM component rather than from XUL is to maintain a singleton (some singular Java object) across all Firefox windows. If you call Java from XUL, then each Firefox window will have its own class loader and hence its own "singleton".

The following approach is taken from the extension XqUSEme  (note you must use the latest version, currently still in the sandbox, as prior versions only worked with LiveConnect before Java 6 update 11) which borrows some of the code of the Java Firefox Extension in order to grant full privileges to Java within a Firefox extension, but it is easier to understand and doesn't require creation of a XPCOM component.

For privileges, the basic procedure is:

  1. Download and include http://simile.mit.edu/repository/jav...nsionUtils.jar within your extension (you can also build your own from the source files at http://simile.mit.edu/repository/jav...xtensionUtils/ )
  2. Build and add references leading to this JAR and all other JARs in your extension within an array of java.net.URL's, pass to java.net.URLClassLoader to get a class loader, and finally pass the classloader and array to a function which gives the necessary privileges:

 

// This function will be called to give the necessary privileges to your JAR files

function policyAdd (loader, urls) {
    try {
        //If have trouble with the policy try changing it to 
        //edu.mit.simile.javaFirefoxExtensionUtils.URLSetPolicy        
        var str = 'edu.mit.simile.firefoxClassLoader.URLSetPolicy';
        var policyClass = java.lang.Class.forName(
               str,
               true,
               loader
        );
        var policy = policyClass.newInstance();
        policy.setOuterPolicy(java.security.Policy.getPolicy());
        java.security.Policy.setPolicy(policy);
        policy.addPermission(new java.security.AllPermission());
        for (var j=0; j < urls.length; j++) {
            policy.addURL(urls[j]);
        }
    }catch(e) {
       alert(e+'::'+e.lineNumber);
    }
}
       
//Get extension folder installation path...
var extensionPath = Components.classes["@mozilla.org/extensions/manager;1"].
            getService(Components.interfaces.nsIExtensionManager).
            getInstallLocation("test@yoursite"). // guid of extension
            getItemLocation("test@yoursite");

// Get path to the JAR files (the following assumes your JARs are within a
// directory called "java" at the root of your extension's folder hierarchy)
// You must add this utilities (classloader) JAR to give your extension full privileges
var classLoaderJarpath = "file:///" + extensionPath.path.replace(/\\/g,"/") + "/java/javaFirefoxExtensionUtils.jar";
// Add the paths for all the other JAR files that you will be using
var myJarpath = "file:///" + extensionPath.path.replace(/\\/g,"/") +
"/java/TestJava.jar"; // seems you don't actually have to replace the backslashes as they work as well

var urlArray = []; // Build a regular JavaScript array (LiveConnect will auto-convert to a Java array)
urlArray[0] = new java.net.URL(myJarpath);
urlArray[1] = new java.net.URL(classLoaderJarpath);
var cl = java.net.URLClassLoader.newInstance(urlArray);
     
//Set security policies using the above policyAdd() function
policyAdd(cl, urlArray);

 

Now you can begin using LiveConnect, including referencing the classes in your JARs. Be aware that if you have added a namespace within your classes (via "package"), you will need to reference the namespace as well.

var aClass = java.lang.Class.forName("org.mozilla.developer.HelloWorld", true, cl);
var aStaticMethod = aClass.getMethod("getGreeting", []);
var greeting = aStaticMethod.invoke(null, []);
alert(greeting);

Another, perhaps simpler approach is as follows:

var myClass = loader.loadClass('com.example.myClass'); // use the same loader from above
var myObj = myClass.newInstance();
var binval = myObj.myMethod(arg1, arg2); // Pass whatever arguments you need (they'll be auto-converted to Java form, taking into account the LiveConnect conversion rules)

 

For more complex cases, in which you need to call a specific constructor with arguments, you will need reflection. The following is a somewhat simplified snippet from XqUSEme:

var reflect = java.lang.reflect;
// Build an array of the class types which are expected by our constructor (in this case, java.io.File and a class from another JAR we loaded, com.sleepycat.db.EnvironmentConfig)
var paramtypes = reflect.Array.newInstance(java.lang.Class, 2); // 2nd argument should indicate the number of items in following array
paramtypes[0] = java.io.File;
var envconfigClass = loader.loadClass('com.sleepycat.db.EnvironmentConfig');
paramtypes[1] = envconfigClass;
// Get the constructor
var constructor = envClass.getConstructor(paramtypes);
// Now that we have the constructor with the right parameter types, we can build the specific arguments we wish to pass to it
var arglist = reflect.Array.newInstance(java.lang.Object, 2); // 2nd argument should indicate the number of items in the following array
var mydir = new java.io.File(dirURL); // a file URL
arglist[0] = mydir;
var envconfig = envconfigClass.newInstance();
arglist[1] = envconfig;
// Call our constructor with our arguments
var env = constructor.newInstance(arglist);

Be aware that LiveConnect, while now actively supported by Sun has only recently been reimplemented for use in Mozilla, so there may still be some bugs (though many prior LiveConnect bugs, such as try-catch not catching Java exceptions, the failure of auto-converting JavaScript arrays properly, etc., have now been fixed). One example seems to be the fact that the "java" global is not immediately available if the extension operates in a sidebar (using setTimeout solved it at least). I've had problems with stability in the latest XqUSEme when testing in Firefox 3.5b4, especially apparently on Linux (I haven't so far been able to find any workarounds/incompatibilities, but everything is working in Firefox 3.0.10.). I've also experienced crashes in referencing the 'java' global within the XUL Editor of the Extension Developer's Extension which seems to point to some stability problem.

See also

Revision Source

<p>If you are in need of calling Java code from within a Firefox <a href="/en/Extensions" title="en/Extensions">extension</a>, you can make use of <a class="external" href="http://en.wikipedia.org/wiki/LiveConnect">LiveConnect</a>. LiveConnect gives your extension's JavaScript code (linked from or contained in XUL code) access to 2 objects: <code>java</code> and <code>Packages</code> (note that per <a class="external" href="http://forums.java.net/jive/thread.jspa?threadID=45933&amp;tstart=0">this thread</a>, although the <a class="link-https" href="https://jdk6.dev.java.net/plugin2/liveconnect/">new documentation</a> for the LiveConnect reimplementation states that these <a class="link-https" href="https://jdk6.dev.java.net/plugin2/liveconnect/#DEPRECATED_FUNCTIONALITY">globals will be deprecated</a> (in the context of applets), "Firefox and the Java Plug-In will continue to support the global java/Packages keywords, in particular in the context of Firefox extensions."). These 2 objects let you make use of the standard JDK classes, e.g.,</p>
<pre class="brush: js">var aJavaList = new java.util.LinkedList();
</pre>
<p>If you want to load your own JARs, then you can create your own Java class loader. </p>
<p>The following technique only works in JavaScript code linked from or contained in XUL files. If you wish to call Java code from within JavaScript code that implements some XPCOM components, at this time, you need a different technique (refer to the complete <a class="external" href="http://simile.mit.edu/java-firefox-extension/">Java Firefox Extension</a>). A good reason for calling Java from within an XPCOM component rather than from XUL is to maintain a singleton (some singular Java object) across all Firefox windows. If you call Java from XUL, then each Firefox window will have its own class loader and hence its own "singleton".</p>
<p>The following approach is taken from the extension <a class="link-https" href="https://addons.mozilla.org/en-US/firefox/addon/5515">XqUSEme</a>  (note you must use the latest version, currently still in the sandbox, as prior versions only worked with LiveConnect before Java 6 update 11) which borrows some of the code of the Java Firefox Extension in order to grant full privileges to Java within a Firefox extension, but it is easier to understand and doesn't require creation of a XPCOM component.</p>
<p>For privileges, the basic procedure is:</p>
<ol> <li>Download and include <a class=" external" href="http://simile.mit.edu/repository/java-firefox-extension/tools/javaFirefoxExtensionUtils.jar" rel="freelink">http://simile.mit.edu/repository/jav...nsionUtils.jar</a> within your extension (you can also build your own from the source files at <a class=" external" href="http://simile.mit.edu/repository/java-firefox-extension/tools/java-firefox-extension-utils/edu/mit/simile/javaFirefoxExtensionUtils/" rel="freelink">http://simile.mit.edu/repository/jav...xtensionUtils/</a> )</li> <li>Build and add references leading to this JAR and all other JARs in your extension within an array of java.net.URL's, pass to java.net.URLClassLoader to get a class loader, and finally pass the classloader and array to a function which gives the necessary privileges:</li>
</ol>
<p> </p>
<pre class="brush: js">// This function will be called to give the necessary privileges to your JAR files

function policyAdd (loader, urls) {
    try {
        //If have trouble with the policy try changing it to 
        //edu.mit.simile.javaFirefoxExtensionUtils.URLSetPolicy        
        var str = 'edu.mit.simile.firefoxClassLoader.URLSetPolicy';
        var policyClass = java.lang.Class.forName(
               str,
               true,
               loader
        );
        var policy = policyClass.newInstance();
        policy.setOuterPolicy(java.security.Policy.getPolicy());
        java.security.Policy.setPolicy(policy);
        policy.addPermission(new java.security.AllPermission());
        for (var j=0; j &lt; urls.length; j++) {
            policy.addURL(urls[j]);
        }
    }catch(e) {
       alert(e+'::'+e.lineNumber);
    }
}
       
//Get extension folder installation path...
var extensionPath = Components.classes["@mozilla.org/extensions/manager;1"].
            getService(Components.interfaces.nsIExtensionManager).
            getInstallLocation("test@yoursite"). // guid of extension
            getItemLocation("test@yoursite");

// Get path to the JAR files (the following assumes your JARs are within a
// directory called "java" at the root of your extension's folder hierarchy)
// You must add this utilities (classloader) JAR to give your extension full privileges
var classLoaderJarpath = "file:///" + extensionPath.path.replace(/\\/g,"/") + "/java/javaFirefoxExtensionUtils.jar";
// Add the paths for all the other JAR files that you will be using
var myJarpath = "file:///" + extensionPath.path.replace(/\\/g,"/") +
"/java/TestJava.jar"; // seems you don't actually have to replace the backslashes as they work as well

var urlArray = []; // Build a regular JavaScript array (LiveConnect will auto-convert to a Java array)
urlArray[0] = new java.net.URL(myJarpath);
urlArray[1] = new java.net.URL(classLoaderJarpath);
var cl = java.net.URLClassLoader.newInstance(urlArray);
     
//Set security policies using the above policyAdd() function
policyAdd(cl, urlArray);
</pre>
<p> </p>
<p>Now you can begin using LiveConnect, including referencing the classes in your JARs. Be aware that if you have added a namespace within your classes (via "package"), you will need to reference the namespace as well.</p>
<pre class="brush: js">var aClass = java.lang.Class.forName("org.mozilla.developer.HelloWorld", true, cl);
var aStaticMethod = aClass.getMethod("getGreeting", []);
var greeting = aStaticMethod.invoke(null, []);
alert(greeting);

</pre>
<p>Another, perhaps simpler approach is as follows:</p>
<pre class="brush: js">var myClass = loader.loadClass('com.example.myClass'); // use the same loader from above
var myObj = myClass.newInstance();
var binval = myObj.myMethod(arg1, arg2); // Pass whatever arguments you need (they'll be auto-converted to Java form, taking into account the LiveConnect conversion rules)
</pre>
<p> </p>
<p>For more complex cases, in which you need to call a specific constructor with arguments, you will need reflection. The following is a somewhat simplified snippet from XqUSEme:</p>
<pre class="brush: js">var reflect = java.lang.reflect;
// Build an array of the class types which are expected by our constructor (in this case, java.io.File and a class from another JAR we loaded, com.sleepycat.db.EnvironmentConfig)
var paramtypes = reflect.Array.newInstance(java.lang.Class, 2); // 2nd argument should indicate the number of items in following array
paramtypes[0] = java.io.File;
var envconfigClass = loader.loadClass('com.sleepycat.db.EnvironmentConfig');
paramtypes[1] = envconfigClass;
// Get the constructor
var constructor = envClass.getConstructor(paramtypes);
// Now that we have the constructor with the right parameter types, we can build the specific arguments we wish to pass to it
var arglist = reflect.Array.newInstance(java.lang.Object, 2); // 2nd argument should indicate the number of items in the following array
var mydir = new java.io.File(dirURL); // a file URL
arglist[0] = mydir;
var envconfig = envconfigClass.newInstance();
arglist[1] = envconfig;
// Call our constructor with our arguments
var env = constructor.newInstance(arglist);
</pre>
<p>Be aware that LiveConnect, while now actively supported by Sun has only recently been reimplemented for use in Mozilla, so there may still be some bugs (though many prior LiveConnect bugs, such as try-catch not catching Java exceptions, the failure of auto-converting JavaScript arrays properly, etc., have now been fixed). One example seems to be the fact that the "java" global is not immediately available if the extension operates in a sidebar (using setTimeout solved it at least). I've had problems with stability in the latest XqUSEme when testing in Firefox 3.5b4, especially apparently on Linux (I haven't so far been able to find any workarounds/incompatibilities, but everything is working in Firefox 3.0.10.). I've also experienced crashes in referencing the 'java' global within the XUL Editor of the Extension Developer's Extension which seems to point to some stability problem.</p>
<h3>See also</h3>
<ul> <li><a href="en/Core_JavaScript_1.5_Guide/LiveConnect_Overview" title="en/Core_JavaScript_1.5_Guide/LiveConnect_Overview">LiveConnect Overview</a></li> <li><a class=" link-https" href="https://jdk6.dev.java.net/plugin2/#LIVECONNECT" rel="freelink">https://jdk6.dev.java.net/plugin2/#LIVECONNECT</a></li> <li><a class=" link-https" href="https://jdk6.dev.java.net/plugin2/liveconnect/" rel="freelink">https://jdk6.dev.java.net/plugin2/liveconnect/</a></li> <li><a class=" external" href="http://forums.sun.com/thread.jspa?threadID=5366932&amp;start=15" rel="freelink">http://forums.sun.com/thread.jspa?th...66932&amp;start=15</a></li>
</ul>
<input id="gwProxy" type="hidden"><!--Session data--><input id="jsProxy" type="hidden"> <input id="gwProxy" type="hidden"><!--Session data--><input id="jsProxy" type="hidden">
Revert to this revision