gecko/mobile/android/modules/JavaAddonManager.jsm
Nick Alexander 31b4d8f041 Bug 1168407 - Implement a bidirectional Java addon interface. f=jchen,r=rnewman,r=mfinkle
There are several parts to this ticket:

1) Produce javaaddons-1.0.jar, a standalone JAR defining a (versioned)
Java interface suitable for consumption by third-party Java addon
implementations.

2) Support the new V1 interface in the JavaAddonManager.

3) Add Robocop JavascriptTests testing the JavaScript message passing
interface to and from Java.

This patch can be read as "not in tests/" and "everything in tests/".
2015-06-17 21:47:29 -07:00

116 lines
3.4 KiB
JavaScript

// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["JavaAddonManager"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components; /*global Components */
Cu.import("resource://gre/modules/Messaging.jsm"); /*global Messaging */
Cu.import("resource://gre/modules/Services.jsm"); /*global Services */
function resolveGeckoURI(uri) {
if (!uri) {
throw new Error("Can't resolve an empty uri");
}
if (uri.startsWith("chrome://")) {
let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]);
return registry.convertChromeURL(Services.io.newURI(uri, null, null)).spec;
} else if (uri.startsWith("resource://")) {
let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
return handler.resolveURI(Services.io.newURI(uri, null, null));
}
return uri;
}
/**
* A promise-based API
*/
let JavaAddonManager = Object.freeze({
classInstanceFromFile: function(classname, filename) {
if (!classname) {
throw new Error("classname cannot be null");
}
if (!filename) {
throw new Error("filename cannot be null");
}
return Messaging.sendRequestForResult({
type: "JavaAddonManagerV1:Load",
classname: classname,
filename: resolveGeckoURI(filename)
})
.then((guid) => {
if (!guid) {
throw new Error("Internal error: guid should not be null");
}
return new JavaAddonV1({classname: classname, guid: guid});
});
}
});
function JavaAddonV1(options = {}) {
if (!(this instanceof JavaAddonV1)) {
return new JavaAddonV1(options);
}
if (!options.classname) {
throw new Error("options.classname cannot be null");
}
if (!options.guid) {
throw new Error("options.guid cannot be null");
}
this._classname = options.classname;
this._guid = options.guid;
this._loaded = true;
this._listeners = {};
}
JavaAddonV1.prototype = Object.freeze({
unload: function() {
if (!this._loaded) {
return;
}
Messaging.sendRequestForResult({
type: "JavaAddonManagerV1:Unload",
guid: this._guid
})
.then(() => {
this._loaded = false;
for (let listener of this._listeners) {
// If we use this.removeListener, we prefix twice.
Messaging.removeListener(listener);
}
this._listeners = {};
});
},
_prefix: function(message) {
let newMessage = Cu.cloneInto(message, {}, { cloneFunctions: false });
newMessage.type = this._guid + ":" + message.type;
return newMessage;
},
sendRequest: function(message) {
return Messaging.sendRequest(this._prefix(message));
},
sendRequestForResult: function(message) {
return Messaging.sendRequestForResult(this._prefix(message));
},
addListener: function(listener, message) {
let prefixedMessage = this._guid + ":" + message;
this._listeners[prefixedMessage] = listener;
return Messaging.addListener(listener, prefixedMessage);
},
removeListener: function(message) {
let prefixedMessage = this._guid + ":" + message;
delete this._listeners[prefixedMessage];
return Messaging.removeListener(prefixedMessage);
}
});