Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / toolkit / components / exthelper / extApplication.js
blob28a6cb45b81dd4e9d9448c07792c0f6fe5c2b7b9
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is FUEL.
15  *
16  * The Initial Developer of the Original Code is Mozilla Corporation.
17  * Portions created by the Initial Developer are Copyright (C) 2006
18  * the Initial Developer. All Rights Reserved.
19  *
20  * Contributor(s):
21  *  Mark Finkle <mfinkle@mozilla.com> (Original Author)
22  *  John Resig  <jresig@mozilla.com> (Original Author)
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either the GNU General Public License Version 2 or later (the "GPL"), or
26  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
38 //=================================================
39 // Shutdown - used to store cleanup functions which will
40 //            be called on Application shutdown
41 var gShutdown = [];
43 //=================================================
44 // Console constructor
45 function Console() {
46   this._console = Components.classes["@mozilla.org/consoleservice;1"]
47     .getService(Ci.nsIConsoleService);
50 //=================================================
51 // Console implementation
52 Console.prototype = {
53   log : function cs_log(aMsg) {
54     this._console.logStringMessage(aMsg);
55   },
57   open : function cs_open() {
58     var wMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"]
59                               .getService(Ci.nsIWindowMediator);
60     var console = wMediator.getMostRecentWindow("global:console");
61     if (!console) {
62       var wWatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
63                              .getService(Ci.nsIWindowWatcher);
64       wWatch.openWindow(null, "chrome://global/content/console.xul", "_blank",
65                         "chrome,dialog=no,all", null);
66     } else {
67       // console was already open
68       console.focus();
69     }
70   },
72   QueryInterface : XPCOMUtils.generateQI([Ci.extIConsole])
76 //=================================================
77 // EventItem constructor
78 function EventItem(aType, aData) {
79   this._type = aType;
80   this._data = aData;
83 //=================================================
84 // EventItem implementation
85 EventItem.prototype = {
86   _cancel : false,
88   get type() {
89     return this._type;
90   },
92   get data() {
93     return this._data;
94   },
96   preventDefault : function ei_pd() {
97     this._cancel = true;
98   },
100   QueryInterface : XPCOMUtils.generateQI([Ci.extIEventItem])
104 //=================================================
105 // Events constructor
106 function Events() {
107   this._listeners = [];
110 //=================================================
111 // Events implementation
112 Events.prototype = {
113   addListener : function evts_al(aEvent, aListener) {
114     if (this._listeners.some(hasFilter))
115       return;
117     this._listeners.push({
118       event: aEvent,
119       listener: aListener
120     });
122     function hasFilter(element) {
123       return element.event == aEvent && element.listener == aListener;
124     }
125   },
127   removeListener : function evts_rl(aEvent, aListener) {
128     this._listeners = this._listeners.filter(hasFilter);
130     function hasFilter(element) {
131       return element.event != aEvent && element.listener != aListener;
132     }
133   },
135   dispatch : function evts_dispatch(aEvent, aEventItem) {
136     eventItem = new EventItem(aEvent, aEventItem);
138     this._listeners.forEach(function(key){
139       if (key.event == aEvent) {
140         key.listener.handleEvent ?
141           key.listener.handleEvent(eventItem) :
142           key.listener(eventItem);
143       }
144     });
146     return !eventItem._cancel;
147   },
149   QueryInterface : XPCOMUtils.generateQI([Ci.extIEvents])
153 //=================================================
154 // PreferenceBranch constructor
155 function PreferenceBranch(aBranch) {
156   if (!aBranch)
157     aBranch = "";
159   this._root = aBranch;
160   this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
161                           .getService(Ci.nsIPrefService);
163   if (aBranch)
164     this._prefs = this._prefs.getBranch(aBranch);
166   this._prefs.QueryInterface(Ci.nsIPrefBranch);
167   this._prefs.QueryInterface(Ci.nsIPrefBranch2);
169   // we want to listen to "all" changes for this branch, so pass in a blank domain
170   this._prefs.addObserver("", this, true);
171   this._events = new Events();
173   var self = this;
174   gShutdown.push(function() { self._shutdown(); });
177 //=================================================
178 // PreferenceBranch implementation
179 PreferenceBranch.prototype = {
180   // cleanup observer so we don't leak
181   _shutdown: function prefs_shutdown() {
182     this._prefs.removeObserver(this._root, this);
184     this._prefs = null;
185     this._events = null;
186   },
188   // for nsIObserver
189   observe: function prefs_observe(aSubject, aTopic, aData) {
190     if (aTopic == "nsPref:changed")
191       this._events.dispatch("change", aData);
192   },
194   get root() {
195     return this._root;
196   },
198   get all() {
199     return this.find({});
200   },
202   get events() {
203     return this._events;
204   },
206   // XXX: Disabled until we can figure out the wrapped object issues
207   // name: "name" or /name/
208   // path: "foo.bar." or "" or /fo+\.bar/
209   // type: Boolean, Number, String (getPrefType)
210   // locked: true, false (prefIsLocked)
211   // modified: true, false (prefHasUserValue)
212   find : function prefs_find(aOptions) {
213     var retVal = [];
214     var items = this._prefs.getChildList("", []);
216     for (var i = 0; i < items.length; i++) {
217       retVal.push(new Preference(items[i], this));
218     }
220     return retVal;
221   },
223   has : function prefs_has(aName) {
224     return (this._prefs.getPrefType(aName) != Ci.nsIPrefBranch.PREF_INVALID);
225   },
227   get : function prefs_get(aName) {
228     return this.has(aName) ? new Preference(aName, this) : null;
229   },
231   getValue : function prefs_gv(aName, aValue) {
232     var type = this._prefs.getPrefType(aName);
234     switch (type) {
235       case Ci.nsIPrefBranch2.PREF_STRING:
236         aValue = this._prefs.getComplexValue(aName, Ci.nsISupportsString).data;
237         break;
238       case Ci.nsIPrefBranch2.PREF_BOOL:
239         aValue = this._prefs.getBoolPref(aName);
240         break;
241       case Ci.nsIPrefBranch2.PREF_INT:
242         aValue = this._prefs.getIntPref(aName);
243         break;
244     }
246     return aValue;
247   },
249   setValue : function prefs_sv(aName, aValue) {
250     var type = aValue != null ? aValue.constructor.name : "";
252     switch (type) {
253       case "String":
254         var str = Components.classes["@mozilla.org/supports-string;1"]
255                             .createInstance(Ci.nsISupportsString);
256         str.data = aValue;
257         this._prefs.setComplexValue(aName, Ci.nsISupportsString, str);
258         break;
259       case "Boolean":
260         this._prefs.setBoolPref(aName, aValue);
261         break;
262       case "Number":
263         this._prefs.setIntPref(aName, aValue);
264         break;
265       default:
266         throw("Unknown preference value specified.");
267     }
268   },
270   reset : function prefs_reset() {
271     this._prefs.resetBranch("");
272   },
274   QueryInterface : XPCOMUtils.generateQI([Ci.extIPreferenceBranch, Ci.nsISupportsWeakReference])
278 //=================================================
279 // Preference constructor
280 function Preference(aName, aBranch) {
281   this._name = aName;
282   this._branch = aBranch;
283   this._events = new Events();
285   var self = this;
287   this.branch.events.addListener("change", function(aEvent){
288     if (aEvent.data == self.name)
289       self.events.dispatch(aEvent.type, aEvent.data);
290   });
293 //=================================================
294 // Preference implementation
295 Preference.prototype = {
296   get name() {
297     return this._name;
298   },
300   get type() {
301     var value = "";
302     var type = this.branch._prefs.getPrefType(this._name);
304     switch (type) {
305       case Ci.nsIPrefBranch2.PREF_STRING:
306         value = "String";
307         break;
308       case Ci.nsIPrefBranch2.PREF_BOOL:
309         value = "Boolean";
310         break;
311       case Ci.nsIPrefBranch2.PREF_INT:
312         value = "Number";
313         break;
314     }
316     return value;
317   },
319   get value() {
320     return this.branch.getValue(this._name, null);
321   },
323   set value(aValue) {
324     return this.branch.setValue(this._name, aValue);
325   },
327   get locked() {
328     return this.branch._prefs.prefIsLocked(this.name);
329   },
331   set locked(aValue) {
332     this.branch._prefs[ aValue ? "lockPref" : "unlockPref" ](this.name);
333   },
335   get modified() {
336     return this.branch._prefs.prefHasUserValue(this.name);
337   },
339   get branch() {
340     return this._branch;
341   },
343   get events() {
344     return this._events;
345   },
347   reset : function pref_reset() {
348     this.branch._prefs.clearUserPref(this.name);
349   },
351   QueryInterface : XPCOMUtils.generateQI([Ci.extIPreference])
355 //=================================================
356 // SessionStorage constructor
357 function SessionStorage() {
358   this._storage = {};
359   this._events = new Events();
362 //=================================================
363 // SessionStorage implementation
364 SessionStorage.prototype = {
365   get events() {
366     return this._events;
367   },
369   has : function ss_has(aName) {
370     return this._storage.hasOwnProperty(aName);
371   },
373   set : function ss_set(aName, aValue) {
374     this._storage[aName] = aValue;
375     this._events.dispatch("change", aName);
376   },
378   get : function ss_get(aName, aDefaultValue) {
379     return this.has(aName) ? this._storage[aName] : aDefaultValue;
380   },
382   QueryInterface : XPCOMUtils.generateQI([Ci.extISessionStorage])
386 //=================================================
387 // Extension constructor
388 function Extension(aItem) {
389   this._item = aItem;
390   this._firstRun = false;
391   this._prefs = new PreferenceBranch("extensions." + this._item.id + ".");
392   this._storage = new SessionStorage();
393   this._events = new Events();
395   var installPref = "install-event-fired";
396   if (!this._prefs.has(installPref)) {
397     this._prefs.setValue(installPref, true);
398     this._firstRun = true;
399   }
401   this._enabled = false;
402   const PREFIX_ITEM_URI = "urn:mozilla:item:";
403   const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
404   var rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
405   var itemResource = rdf.GetResource(PREFIX_ITEM_URI + this._item.id);
406   if (itemResource) {
407     var extmgr = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
408     var ds = extmgr.datasource;
409     var target = ds.GetTarget(itemResource, rdf.GetResource(PREFIX_NS_EM + "isDisabled"), true);
410     if (target && target instanceof Ci.nsIRDFLiteral)
411       this._enabled = (target.Value != "true");
412   }
414   var os = Components.classes["@mozilla.org/observer-service;1"]
415                      .getService(Ci.nsIObserverService);
416   os.addObserver(this, "em-action-requested", false);
418   var self = this;
419   gShutdown.push(function(){ self._shutdown(); });
422 //=================================================
423 // Extension implementation
424 Extension.prototype = {
425   // cleanup observer so we don't leak
426   _shutdown: function ext_shutdown() {
427     var os = Components.classes["@mozilla.org/observer-service;1"]
428                        .getService(Ci.nsIObserverService);
429     os.removeObserver(this, "em-action-requested");
431     this._prefs = null;
432     this._storage = null;
433     this._events = null;
434   },
436   // for nsIObserver
437   observe: function ext_observe(aSubject, aTopic, aData)
438   {
439     if ((aSubject instanceof Ci.nsIUpdateItem) && (aSubject.id == this._item.id))
440     {
441       if (aData == "item-uninstalled")
442         this._events.dispatch("uninstall", this._item.id);
443       else if (aData == "item-disabled")
444         this._events.dispatch("disable", this._item.id);
445       else if (aData == "item-enabled")
446         this._events.dispatch("enable", this._item.id);
447       else if (aData == "item-cancel-action")
448         this._events.dispatch("cancel", this._item.id);
449       else if (aData == "item-upgraded")
450         this._events.dispatch("upgrade", this._item.id);
451     }
452   },
454   get id() {
455     return this._item.id;
456   },
458   get name() {
459     return this._item.name;
460   },
462   get enabled() {
463     return this._enabled;
464   },
466   get version() {
467     return this._item.version;
468   },
470   get firstRun() {
471     return this._firstRun;
472   },
474   get storage() {
475     return this._storage;
476   },
478   get prefs() {
479     return this._prefs;
480   },
482   get events() {
483     return this._events;
484   },
486   QueryInterface : XPCOMUtils.generateQI([Ci.extIExtension])
490 //=================================================
491 // Extensions constructor
492 function Extensions() {
493   this._extmgr = Components.classes["@mozilla.org/extensions/manager;1"]
494                            .getService(Ci.nsIExtensionManager);
496   this._cache = {};
498   var self = this;
499   gShutdown.push(function() { self._shutdown(); });
502 //=================================================
503 // Extensions implementation
504 Extensions.prototype = {
505   _shutdown : function exts_shutdown() {
506     this._extmgr = null;
507     this._cache = null;
508   },
510   /*
511    * Helper method to check cache before creating a new extension
512    */
513   _get : function exts_get(aId) {
514     if (this._cache.hasOwnProperty(aId))
515       return this._cache[aId];
517     var newExt = new Extension(this._extmgr.getItemForID(aId));
518     this._cache[aId] = newExt;
519     return newExt;
520   },
522   get all() {
523     return this.find({});
524   },
526   // XXX: Disabled until we can figure out the wrapped object issues
527   // id: "some@id" or /id/
528   // name: "name" or /name/
529   // version: "1.0.1"
530   // minVersion: "1.0"
531   // maxVersion: "2.0"
532   find : function exts_find(aOptions) {
533     var retVal = [];
534     var items = this._extmgr.getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION, {});
536     for (var i = 0; i < items.length; i++) {
537       retVal.push(this._get(items[i].id));
538     }
540     return retVal;
541   },
543   has : function exts_has(aId) {
544     return this._extmgr.getItemForID(aId) != null;
545   },
547   get : function exts_get(aId) {
548     return this.has(aId) ? this._get(aId) : null;
549   },
551   QueryInterface : XPCOMUtils.generateQI([Ci.extIExtensions])
554 //=================================================
555 // extApplication constructor
556 function extApplication() {
559 //=================================================
560 // extApplication implementation
561 extApplication.prototype = {
562   initToolkitHelpers: function extApp_initToolkitHelpers() {
563     this._console = null;
564     this._storage = null;
565     this._prefs = null;
566     this._extensions = null;
567     this._events = null;
569     this._info = Components.classes["@mozilla.org/xre/app-info;1"]
570                            .getService(Ci.nsIXULAppInfo);
572     var os = Components.classes["@mozilla.org/observer-service;1"]
573                        .getService(Ci.nsIObserverService);
575     os.addObserver(this, "final-ui-startup", false);
576     os.addObserver(this, "quit-application-requested", false);
577     os.addObserver(this, "xpcom-shutdown", false);
578   },
580   // get this contractID registered for certain categories via XPCOMUtils
581   _xpcom_categories: [
582     // make Application a startup observer
583     { category: "app-startup", service: true },
585     // add Application as a global property for easy access
586     { category: "JavaScript global privileged property" }
587   ],
589   // for nsIClassInfo
590   flags : Ci.nsIClassInfo.SINGLETON,
591   implementationLanguage : Ci.nsIProgrammingLanguage.JAVASCRIPT,
593   getInterfaces : function app_gi(aCount) {
594     var interfaces = [Ci.extIApplication, Ci.nsIObserver, Ci.nsIClassInfo];
595     aCount.value = interfaces.length;
596     return interfaces;
597   },
599   getHelperForLanguage : function app_ghfl(aCount) {
600     return null;
601   },
603   // extIApplication
604   get id() {
605     return this._info.ID;
606   },
608   get name() {
609     return this._info.name;
610   },
612   get version() {
613     return this._info.version;
614   },
616   // for nsIObserver
617   observe: function app_observe(aSubject, aTopic, aData) {
618     if (aTopic == "app-startup") {
619       this.events.dispatch("load", "application");
620     }
621     else if (aTopic == "final-ui-startup") {
622       this.events.dispatch("ready", "application");
623     }
624     else if (aTopic == "quit-application-requested") {
625       // we can stop the quit by checking the return value
626       if (this.events.dispatch("quit", "application") == false)
627         aSubject.data = true;
628     }
629     else if (aTopic == "xpcom-shutdown") {
631       this.events.dispatch("unload", "application");
633       // call the cleanup functions and empty the array
634       while (gShutdown.length) {
635         gShutdown.shift()();
636       }
638       // release our observers
639       var os = Components.classes["@mozilla.org/observer-service;1"]
640                          .getService(Ci.nsIObserverService);
642       os.removeObserver(this, "final-ui-startup");
643       os.removeObserver(this, "quit-application-requested");
644       os.removeObserver(this, "xpcom-shutdown");
646       this._info = null;
647       this._console = null;
648       this._prefs = null;
649       this._storage = null;
650       this._events = null;
651       this._extensions = null;
652     }
653   },
655   get console() {
656     if (this._console == null)
657         this._console = new Console();
659     return this._console;
660   },
662   get storage() {
663     if (this._storage == null)
664         this._storage = new SessionStorage();
666     return this._storage;
667   },
669   get prefs() {
670     if (this._prefs == null)
671         this._prefs = new PreferenceBranch("");
673     return this._prefs;
674   },
676   get extensions() {
677     if (this._extensions == null)
678       this._extensions = new Extensions();
680     return this._extensions;
681   },
683   get events() {
684     if (this._events == null)
685         this._events = new Events();
687     return this._events;
688   },
690   // helper method for correct quitting/restarting
691   _quitWithFlags: function app__quitWithFlags(aFlags) {
692     let os = Components.classes["@mozilla.org/observer-service;1"]
693                        .getService(Components.interfaces.nsIObserverService);
694     let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
695                                .createInstance(Components.interfaces.nsISupportsPRBool);
696     os.notifyObservers(cancelQuit, "quit-application-requested", null);
697     if (cancelQuit.data)
698       return false; // somebody canceled our quit request
699     
700     let appStartup = Components.classes['@mozilla.org/toolkit/app-startup;1']
701                                .getService(Components.interfaces.nsIAppStartup);
702     appStartup.quit(aFlags);
703     return true;
704   },
706   quit: function app_quit() {
707     return this._quitWithFlags(Components.interfaces.nsIAppStartup.eAttemptQuit);
708   },
710   restart: function app_restart() {
711     return this._quitWithFlags(Components.interfaces.nsIAppStartup.eAttemptQuit |
712                                Components.interfaces.nsIAppStartup.eRestart);
713   },
715   QueryInterface : XPCOMUtils.generateQI([Ci.extIApplication, Ci.nsISupportsWeakReference])