CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / toolkit / mozapps / extensions / PluginProvider.jsm
blob3e799d19512cdf340650eaac6b209142fd194d2e
1 /*
2 # ***** BEGIN LICENSE BLOCK *****
3 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 # The contents of this file are subject to the Mozilla Public License Version
6 # 1.1 (the "License"); you may not use this file except in compliance with
7 # the License. You may obtain a copy of the License at
8 # http://www.mozilla.org/MPL/
10 # Software distributed under the License is distributed on an "AS IS" basis,
11 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 # for the specific language governing rights and limitations under the
13 # License.
15 # The Original Code is the Extension Manager.
17 # The Initial Developer of the Original Code is
18 # the Mozilla Foundation.
19 # Portions created by the Initial Developer are Copyright (C) 2010
20 # the Initial Developer. All Rights Reserved.
22 # Contributor(s):
23 #   Dave Townsend <dtownsend@oxymoronical.com>
25 # Alternatively, the contents of this file may be used under the terms of
26 # either the GNU General Public License Version 2 or later (the "GPL"), or
27 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 # in which case the provisions of the GPL or the LGPL are applicable instead
29 # of those above. If you wish to allow use of your version of this file only
30 # under the terms of either the GPL or the LGPL, and not to allow others to
31 # use your version of this file under the terms of the MPL, indicate your
32 # decision by deleting the provisions above and replace them with the notice
33 # and other provisions required by the GPL or the LGPL. If you do not delete
34 # the provisions above, a recipient may use your version of this file under
35 # the terms of any one of the MPL, the GPL or the LGPL.
37 # ***** END LICENSE BLOCK *****
40 const Cc = Components.classes;
41 const Ci = Components.interfaces;
43 var EXPORTED_SYMBOLS = [];
45 Components.utils.import("resource://gre/modules/AddonManager.jsm");
46 Components.utils.import("resource://gre/modules/Services.jsm");
48 ["LOG", "WARN", "ERROR"].forEach(function(aName) {
49   this.__defineGetter__(aName, function() {
50     Components.utils.import("resource://gre/modules/AddonLogging.jsm");
52     LogManager.getLogger("addons.plugins", this);
53     return this[aName];
54   });
55 }, this);
57 var PluginProvider = {
58   // A dictionary mapping IDs to names and descriptions
59   plugins: null,
61   /**
62    * Called to get an Addon with a particular ID.
63    *
64    * @param  aId
65    *         The ID of the add-on to retrieve
66    * @param  aCallback
67    *         A callback to pass the Addon to
68    */
69   getAddonByID: function PL_getAddon(aId, aCallback) {
70     if (!this.plugins)
71       this.buildPluginList();
73     if (aId in this.plugins) {
74       let name = this.plugins[aId].name;
75       let description = this.plugins[aId].description;
77       let tags = Cc["@mozilla.org/plugin/host;1"].
78                  getService(Ci.nsIPluginHost).
79                  getPluginTags({});
80       let selected = [];
81       tags.forEach(function(aTag) {
82         if (aTag.name == name && aTag.description == description)
83           selected.push(aTag);
84       }, this);
86       aCallback(new PluginWrapper(aId, name, description, selected));
87     }
88     else {
89       aCallback(null);
90     }
91   },
93   /**
94    * Called to get Addons of a particular type.
95    *
96    * @param  aTypes
97    *         An array of types to fetch. Can be null to get all types.
98    * @param  callback
99    *         A callback to pass an array of Addons to
100    */
101   getAddonsByTypes: function PL_getAddonsByTypes(aTypes, aCallback) {
102     if (aTypes && aTypes.indexOf("plugin") < 0) {
103       aCallback([]);
104       return;
105     }
107     if (!this.plugins)
108       this.buildPluginList();
110     let results = [];
112     for (let id in this.plugins) {
113       this.getAddonByID(id, function(aAddon) {
114         results.push(aAddon);
115       });
116     }
118     aCallback(results);
119   },
121   /**
122    * Called to get Addons that have pending operations.
123    *
124    * @param  aTypes
125    *         An array of types to fetch. Can be null to get all types
126    * @param  aCallback
127    *         A callback to pass an array of Addons to
128    */
129   getAddonsWithOperationsByTypes: function PL_getAddonsWithOperationsByTypes(aTypes, aCallback) {
130     aCallback([]);
131   },
133   /**
134    * Called to get the current AddonInstalls, optionally restricting by type.
135    *
136    * @param  aTypes
137    *         An array of types or null to get all types
138    * @param  aCallback
139    *         A callback to pass the array of AddonInstalls to
140    */
141   getInstallsByTypes: function PL_getInstallsByTypes(aTypes, aCallback) {
142     aCallback([]);
143   },
145   buildPluginList: function PL_buildPluginList() {
146     let tags = Cc["@mozilla.org/plugin/host;1"].
147                getService(Ci.nsIPluginHost).
148                getPluginTags({});
150     this.plugins = {};
151     let seen = {};
152     tags.forEach(function(aTag) {
153       if (!(aTag.name in seen))
154         seen[aTag.name] = {};
155       if (!(aTag.description in seen[aTag.name])) {
156         let id = Cc["@mozilla.org/uuid-generator;1"].
157                  getService(Ci.nsIUUIDGenerator).
158                  generateUUID();
159         this.plugins[id] = {
160           name: aTag.name,
161           description: aTag.description
162         };
163         seen[aTag.name][aTag.description] = true;
164       }
165     }, this);
166   }
170  * The PluginWrapper wraps a set of nsIPluginTags to provide the data visible to
171  * public callers through the API.
172  */
173 function PluginWrapper(aId, aName, aDescription, aTags) {
174   let safedesc = aDescription.replace(/<\/?[a-z][^>]*>/gi, " ");
175   let homepageURL = null;
176   if (/<A\s+HREF=[^>]*>/i.test(aDescription))
177     homepageURL = /<A\s+HREF=["']?([^>"'\s]*)/i.exec(aDescription)[1];
179   this.__defineGetter__("id", function() aId);
180   this.__defineGetter__("type", function() "plugin");
181   this.__defineGetter__("name", function() aName);
182   this.__defineGetter__("creator", function() null);
183   this.__defineGetter__("description", function() safedesc);
184   this.__defineGetter__("version", function() aTags[0].version);
185   this.__defineGetter__("homepageURL", function() homepageURL);
187   this.__defineGetter__("isActive", function() !aTags[0].blocklisted && !aTags[0].disabled);
188   this.__defineGetter__("appDisabled", function() aTags[0].blocklisted);
189   this.__defineGetter__("userDisabled", function() aTags[0].disabled);
190   this.__defineSetter__("userDisabled", function(aVal) {
191     if (aTags[0].disabled == aVal)
192       return;
194     aTags.forEach(function(aTag) {
195       aTag.disabled = aVal;
196     });
197     AddonManagerPrivate.callAddonListeners(aVal ? "onDisabling" : "onEnabling", this, false);
198     AddonManagerPrivate.callAddonListeners(aVal ? "onDisabled" : "onEnabled", this);
199     return aVal;
200   });
202   this.__defineGetter__("blocklistState", function() {
203     let bs = Cc["@mozilla.org/extensions/blocklist;1"].
204              getService(Ci.nsIBlocklistService);
205     return bs.getPluginBlocklistState(aTags[0]);
206   });
208   this.__defineGetter__("size", function() {
209     function getDirectorySize(aFile) {
210       let size = 0;
211       let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
212       let entry;
213       while (entry = entries.nextFile) {
214         if (entry.isSymlink() || !entry.isDirectory())
215           size += entry.fileSize;
216         else
217           size += getDirectorySize(entry);
218       }
219       entries.close();
220       return size;
221     }
223     let size = 0;
224     let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
225     aTags.forEach(function(aTag) {
226       file.initWithPath(aTag.fullpath);
227       if (file.isDirectory())
228         size += getDirectorySize(file);
229       else
230         size += file.fileSize;
231     });
232     return size;
233   });
235   this.__defineGetter__("installDate", function() {
236     let date = 0;
237     let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
238     aTags.forEach(function(aTag) {
239       file.initWithPath(aTag.fullpath);
240       date = Math.max(date, file.lastModifiedTime);
241     });
242     return new Date(date);
243   });
245   this.__defineGetter__("scope", function() {
246     let path = aTags[0].fullpath;
247     // Plugins inside the application directory are in the application scope
248     let dir = Services.dirsvc.get("APlugns", Ci.nsILocalFile);
249     if (path.substring(0, dir.path.length) == dir.path)
250       return AddonManager.SCOPE_APPLICATION;
252     // Plugins inside the profile directory are in the profile scope
253     dir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
254     if (path.substring(0, dir.path.length) == dir.path)
255       return AddonManager.SCOPE_PROFILE;
257     // Plugins anywhere else in the user's home are in the user scope
258     dir = Services.dirsvc.get("Home", Ci.nsILocalFile);
259     if (path.substring(0, dir.path.length) == dir.path)
260       return AddonManager.SCOPE_USER;
262     // Any other locations are system scope
263     return AddonManager.SCOPE_SYSTEM;
264   });
266   this.__defineGetter__("pendingOperations", function() {
267     return AddonManager.PENDING_NONE;
268   });
270   this.__defineGetter__("operationsRequiringRestart", function() {
271     return AddonManager.OP_NEEDS_RESTART_NONE;
272   });
274   this.__defineGetter__("permissions", function() {
275     let permissions = 0;
276     if (!this.appDisabled) {
277       if (this.userDisabled)
278         permissions |= AddonManager.PERM_CAN_ENABLE;
279       else
280         permissions |= AddonManager.PERM_CAN_DISABLE;
281     }
282     return permissions;
283   });
286 PluginWrapper.prototype = {
287   get updateDate() {
288     return this.installDate;
289   },
291   get isCompatible() {
292     return true;
293   },
295   get isPlatformCompatible() {
296     return true;
297   },
299   get providesUpdatesSecurely() {
300     return true;
301   },
303   isCompatibleWith: function(aAppVerison, aPlatformVersion) {
304     return true;
305   },
307   findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
308     if ("onNoCompatibilityUpdateAvailable" in aListener)
309       aListener.onNoCompatibilityUpdateAvailable(this);
310     if ("onNoUpdateAvailable" in aListener)
311       aListener.onNoUpdateAvailable(this);
312     if ("onUpdateFinished" in aListener)
313       aListener.onUpdateFinished(this);
314   }
317 AddonManagerPrivate.registerProvider(PluginProvider);