summaryrefslogtreecommitdiffstats
path: root/mobile/android/modules/JavaAddonManager.jsm
blob: a24535ede40015a23153b06b581eabe9da9099ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// -*- 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
 */
var 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);
  }
});