remove in_module mechanism
[conkeror.git] / components / application.js
blob7e88914e3fa243e6d54591f8e088749f469b117a
1 /**
2  * (C) Copyright 2007,2010 John J. Foerch
3  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
4  *
5  * Use, modification, and distribution are subject to the terms specified in the
6  * COPYING file.
7 **/
9 const Cc = Components.classes;
10 const Ci = Components.interfaces;
11 const Cr = Components.results;
12 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
14 function application () {
15     Components.utils.import("resource://gre/modules/XPCOMUtils.jsm", this);
17     this.wrappedJSObject = this;
18     this.conkeror = this;
20     this.load_url = this.subscript_loader.loadSubScript;
21     this.loading_urls = [];
22     this.loading_paths = [];
23     this.loading_features = [];
24     this.features = {};
25     this.load_paths = [this.module_uri_prefix,
26                        this.module_uri_prefix+'extensions',
27                        this.module_uri_prefix+'page-modes'];
28     this.after_load_functions = {};
29     this.pending_loads = [];
31     // clear the startup-cache so that modules and the user's rc are
32     // loaded from disk, not from a cache.  this problem is a
33     // characteristic of using mozIJSSubScriptLoader.loadSubScript as our
34     // primary means of loading, since XULRunner 8.0.
35     var obs = Cc["@mozilla.org/observer-service;1"]
36         .getService(Ci.nsIObserverService);
37     obs.notifyObservers(null, "startupcache-invalidate", null);
39     try {
40         this.require("conkeror.js");
41     } catch (e) {
42         this.dumpln("Error initializing.");
43         this.dump_error(e);
44     }
46 application.prototype = {
47     constructor: application,
48     Cc: Cc,
49     Ci: Ci,
50     Cr: Cr,
51     /* Note: resource://app currently doesn't result in xpcnativewrappers=yes */
52     module_uri_prefix: "chrome://conkeror/content/",
53     subscript_loader: Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader),
54     preferences: Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService),
55     get version () {
56         var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
57             .getService(Ci.nsIURLFormatter);
58         return formatter.formatURL("%VERSION%");
59     },
60     dumpln: function (str) {
61         dump(str);
62         dump("\n");
63     },
64     dump_error: function (e) {
65         if (e instanceof Error) {
66             this.dumpln(e.name + ": " + e.message);
67             this.dumpln(e.fileName + ":" + e.lineNumber);
68             dump(e.stack);
69         } else if (e instanceof Ci.nsIException) {
70             this.dumpln(e.name + ": " + e.message);
71             var stack_frame = e.location;
72             while (stack_frame) {
73                 this.dumpln(stack_frame.name + "()@" + stack_frame.filename + ":" + stack_frame.lineNumber);
74                 stack_frame = stack_frame.caller;
75             }
76         } else {
77             this.dumpln("Error: " + e);
78         }
79     },
81     make_uri: function (uri, charset, base_uri) {
82         const io_service = Cc["@mozilla.org/network/io-service;1"]
83             .getService(Ci.nsIIOService2);
84         if (uri instanceof Ci.nsIURI)
85             return uri;
86         if (uri instanceof Ci.nsIFile)
87             return io_service.newFileURI(uri);
88         return io_service.newURI(uri, charset, base_uri);
89     },
90     load: function (module) {
91         function load1 (url, path) {
92             try {
93                 this.loading_paths.unshift(path);
94                 this.loading_urls.unshift(url);
95                 this.loading_features.unshift({});
96                 if (this.loading_urls.indexOf(url, 1) != -1)
97                     throw new Error("Circular module dependency detected: "+
98                                     this.loading_urls.join(",\n"));
99                 this.load_url(url, this);
100                 var success = true;
101                 // call-after-load callbacks
102                 for (let f in this.loading_features[0]) {
103                     this.features[f] = true;
104                     this.run_after_load_functions(f);
105                 }
106             } finally {
107                 this.loading_paths.shift();
108                 this.loading_urls.shift();
109                 this.loading_features.shift();
110             }
111             // do pending loads
112             if (success && this.loading_urls[0] === undefined) {
113                 let pending = this.pending_loads;
114                 this.pending_loads = [];
115                 for (let i = 0, m; m = pending[i]; ++i) {
116                     this.require(m);
117                 }
118             }
119         }
120         if (module instanceof Ci.nsIURI)
121             var path = module.spec.substr(0, module.spec.lastIndexOf('/')+1);
122         else if (module instanceof Ci.nsIFile)
123             path = module.parent.path;
124         if (path !== undefined) {
125             var url = this.make_uri(module).spec;
126             load1.call(this, url, path);
127         } else {
128             // module name or relative path
129             var autoext = module.substr(-3) != '.js';
130             var suffix = false;
131             var i = -1;
132             var tried = {};
133             path = this.loading_paths[0];
134             if (path === undefined)
135                 path = this.load_paths[++i];
136             while (path !== undefined) {
137                 let truepath = path;
138                 try {
139                     let sep = path.substr(-1) == '/' ? '' : '/';
140                     url = path + sep + module + (suffix ? '.js' : '');
141                     let si = module.lastIndexOf('/');
142                     if (si > -1)
143                         truepath += module.substr(0, si);
144                     if (! tried[url]) {
145                         tried[url] = true;
146                         load1.call(this, url, truepath);
147                         return;
148                     }
149                 } catch (e if (typeof e == 'string' &&
150                                {"ContentLength not available (not a local URL?)":true,
151                                 "Error creating channel (invalid URL scheme?)":true,
152                                 "Error opening input stream (invalid filename?)":true}
153                                [e])) {
154                     // null op. (suppress error, try next path)
155                 }
156                 if (autoext)
157                     suffix = !suffix;
158                 if (! suffix)
159                     path = this.load_paths[++i];
160             }
161             throw new Error("Module not found ("+module+")");
162         }
163     },
164     provide: function (symbol) {
165         if (! symbol)
166             throw new Error("Cannot provide null feature");
167         if (this.loading_urls[0] === undefined) {
168             this.features[symbol] = true;
169             this.run_after_load_functions(symbol);
170         } else
171             this.loading_features[0][symbol] = true;
172     },
173     featurep: function (symbol) {
174         return this.features[symbol] || false;
175     },
176     call_after_load: function (feature, func) {
177         if (this.featurep(feature))
178             func();
179         else {
180             var funcs = this.after_load_functions[feature];
181             if (! funcs)
182                 funcs = this.after_load_functions[feature] = [];
183             funcs.push(func);
184         }
185     },
186     run_after_load_functions: function (symbol) {
187         var funcs = this.after_load_functions[symbol];
188         if (funcs) {
189             delete this.after_load_functions[symbol];
190             for (var i = 0; funcs[i]; ++i) {
191                 try {
192                     funcs[i]();
193                 } catch (e) {
194                     dump_error(e);
195                 }
196             }
197         }
198     },
199     require: function (module) {
200         if (module instanceof Ci.nsIURI)
201             var feature = module.spec.substr(module.spec.lastIndexOf('/')+1);
202         else if (module instanceof Ci.nsIFile)
203             feature = module.leafName;
204         else
205             feature = module.substr(module.lastIndexOf('/')+1);
206         var dot = feature.indexOf('.');
207         if (dot == 0)
208             return false;
209         if (dot > 0)
210             feature = feature.substr(0, dot);
211         feature = feature.replace('_', '-', 'g');
212         if (this.featurep(feature))
213             return true;
214         try {
215             // ensure current path is not searched for 'require'
216             this.loading_paths.unshift(undefined);
217             this.load(module);
218         } finally {
219             this.loading_paths.shift();
220         }
221         return true;
222     },
223     require_later: function (module) {
224         this.pending_loads.push(module);
225     },
227     /* nsISupports */
228     QueryInterface: XPCOMUtils.generateQI([]),
230     /* XPCOM registration */
231     classDescription: "Conkeror global object",
232     classID: Components.ID("{72a7eea7-a894-47ec-93a9-a7bc172cf1ac}"),
233     contractID: "@conkeror.mozdev.org/application;1"
236 if (XPCOMUtils.generateNSGetFactory)
237     var NSGetFactory = XPCOMUtils.generateNSGetFactory([application]); //XULRunner 2.0
238 else
239     var NSGetModule = XPCOMUtils.generateNSGetModule([application]);