buffer.focused_selection_controller: update for new focus system
[conkeror.git] / components / application.js
blob82b0133341a8cb4a9cc7474622a5b58588a8a069
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_modules = [];
24     this.loading_features = [];
25     this.features = {};
26     this.load_paths = [this.module_uri_prefix,
27                        this.module_uri_prefix+'extensions',
28                        this.module_uri_prefix+'page-modes'];
29     this.after_load_functions = {};
30     this.pending_loads = [];
32     this.module_assert_conflict_error.prototype = Error.prototype;
34     try {
35         this.require("conkeror.js", null);
36     } catch (e) {
37         this.dumpln("Error initializing.");
38         this.dump_error(e);
39     }
41 application.prototype = {
42     constructor: application,
43     Cc: Cc,
44     Ci: Ci,
45     Cr: Cr,
46     /* Note: resource://app currently doesn't result in xpcnativewrappers=yes */
47     module_uri_prefix: "chrome://conkeror/content/",
48     subscript_loader: Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader),
49     preferences: Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService),
50     get version () {
51         var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
52             .getService(Ci.nsIURLFormatter);
53         return formatter.formatURL("%VERSION%");
54     },
55     dumpln: function (str) {
56         dump(str);
57         dump("\n");
58     },
59     dump_error: function (e) {
60         if (e instanceof Error) {
61             this.dumpln(e.name + ": " + e.message);
62             this.dumpln(e.fileName + ":" + e.lineNumber);
63             dump(e.stack);
64         } else if (e instanceof Ci.nsIException) {
65             this.dumpln(e.name + ": " + e.message);
66             var stack_frame = e.location;
67             while (stack_frame) {
68                 this.dumpln(stack_frame.name + "()@" + stack_frame.filename + ":" + stack_frame.lineNumber);
69                 stack_frame = stack_frame.caller;
70             }
71         } else {
72             this.dumpln("Error: " + e);
73         }
74     },
76     make_uri: function (uri, charset, base_uri) {
77         const io_service = Cc["@mozilla.org/network/io-service;1"]
78             .getService(Ci.nsIIOService2);
79         if (uri instanceof Ci.nsIURI)
80             return uri;
81         if (uri instanceof Ci.nsIFile)
82             return io_service.newFileURI(uri);
83         return io_service.newURI(uri, charset, base_uri);
84     },
85     skip_module_load: {},
86     load: function (module, as) {
87         var conkeror = this;
88         function module_scope () {
89             this.__proto__ = conkeror;
90             this.conkeror = conkeror;
91         }
92         function load1 (url, scope, path, as) {
93             var success;
94             try {
95                 this.loading_paths.unshift(path);
96                 this.loading_urls.unshift(url);
97                 this.loading_modules.unshift(as);
98                 this.loading_features.unshift({});
99                 if (this.loading_urls.indexOf(url, 1) != -1)
100                     throw new Error("Circular module dependency detected: "+
101                                     this.loading_urls.join(",\n"));
102                 this.load_url(url, scope);
103                 success = true;
104                 if (as)
105                     this[as] = scope;
106                 // call-after-load callbacks
107                 for (let f in this.loading_features[0]) {
108                     this.features[f] = true;
109                     this.run_after_load_functions(f);
110                 }
111             } finally {
112                 this.loading_paths.shift();
113                 this.loading_urls.shift();
114                 this.loading_modules.shift();
115                 this.loading_features.shift();
116             }
117             // do pending loads
118             if (success && this.loading_urls[0] === undefined) {
119                 let pending = this.pending_loads;
120                 this.pending_loads = [];
121                 for (let i = 0, m; m = pending[i]; ++i) {
122                     this.require(m[0], m[1]);
123                 }
124             }
125         }
126         var scope = as;
127         if (as == null)
128             scope = this;
129         else if (typeof as == 'string')
130             scope = new module_scope();
131         else
132             as = null;
133         var path, url;
134         if (module instanceof Ci.nsIURI)
135             path = module.path.substr(0, module.path.lastIndexOf('/')+1);
136         else if (module instanceof Ci.nsIFile)
137             path = module.parent.path;
138         var restarted = false;
139         if (path !== undefined) {
140             url = this.make_uri(module).spec;
141             do {
142                 try {
143                     load1.call(this, url, scope, path, as);
144                     return;
145                 } catch (e if e instanceof this.module_assert_error) {
146                     if (restarted)
147                         throw new this.module_assert_conflict_error(url);
148                     as = e.module;
149                     if (e.module)
150                         scope = new module_scope();
151                     else
152                         scope = this;
153                     restarted = true;
154                 } catch (e if e == this.skip_module_load) {
155                     return;
156                 }
157             } while (restarted);
158         } else {
159             // module name or relative path
160             var autoext = module.substr(-3) != '.js';
161             var suffix = false;
162             var i = -1;
163             var tried = {};
164             path = this.loading_paths[0];
165             if (path === undefined)
166                 path = this.load_paths[++i];
167             while (path !== undefined) {
168                 let opath = path;
169                 try {
170                     let sep = path[path.length-1] == '/' ? '' : '/';
171                     url = path + sep + module + (suffix ? '.js' : '');
172                     let si = module.lastIndexOf('/');
173                     if (si > -1)
174                         path += module.substr(0, si);
175                     if (! tried[url] || tried[url] !== scope) {
176                         tried[url] = scope;
177                         load1.call(this, url, scope, path, as);
178                         return;
179                     }
180                 } catch (e if e instanceof this.module_assert_error) {
181                     if (restarted)
182                         throw new this.module_assert_conflict_error(url);
183                     as = e.module;
184                     if (e.module)
185                         scope = new module_scope();
186                     else
187                         scope = this;
188                     path = opath;
189                     restarted = true;
190                     continue;
191                 } catch (e if (typeof e == 'string' &&
192                                {"ContentLength not available (not a local URL?)":true,
193                                 "Error creating channel (invalid URL scheme?)":true,
194                                 "Error opening input stream (invalid filename?)":true}
195                                [e])) {
196                     // null op. (suppress error, try next path)
197                 } catch (e if e == this.skip_module_load) {
198                     return;
199                 }
200                 if (autoext)
201                     suffix = !suffix;
202                 if (! suffix)
203                     path = this.load_paths[++i];
204             }
205             throw new Error("Module not found");
206         }
207     },
208     module_assert_error: function (module) {
209         this.module = module;
210     },
211     module_assert_conflict_error: function (url) { //subclass of Error
212         this.name = "module_assert_conflict_error";
213         this.message = "Conflicting in_module calls";
214         this.fileName = url;
215         this.lineNumber = '';
216     },
217     in_module: function (module) {
218         if (module != this.loading_modules[0])
219             throw new this.module_assert_error(module);
220     },
221     provide: function (symbol) {
222         if (! symbol)
223             throw new Error("Cannot provide null feature");
224         if (this.loading_urls[0] === undefined) {
225             this.features[symbol] = true;
226             this.run_after_load_functions(symbol);
227         } else
228             this.loading_features[0][symbol] = true;
229     },
230     featurep: function (symbol) {
231         return this.features[symbol] || false;
232     },
233     call_after_load: function (feature, func) {
234         if (this.featurep(feature))
235             func();
236         else {
237             var funcs = this.after_load_functions[feature];
238             if (! funcs)
239                 funcs = this.after_load_functions[feature] = [];
240             funcs.push(func);
241         }
242     },
243     run_after_load_functions: function (symbol) {
244         var funcs = this.after_load_functions[symbol];
245         if (funcs) {
246             delete this.after_load_functions[symbol];
247             for (var i = 0; funcs[i]; ++i) {
248                 try {
249                     funcs[i]();
250                 } catch (e) {
251                     dump_error(e);
252                 }
253             }
254         }
255     },
256     require: function (module, as) {
257         var feature = as;
258         if (! feature) {
259             if (module instanceof Ci.nsIURI)
260                 feature = module.spec.substr(module.spec.lastIndexOf('/')+1);
261             else if (module instanceof Ci.nsIFile)
262                 feature = module.leafName;
263             else
264                 feature = module.substr(module.lastIndexOf('/')+1);
265             let dot = feature.indexOf('.');
266             if (dot == 0)
267                 return false;
268             if (dot > 0)
269                 feature = feature.substr(0, dot);
270             feature = feature.replace('_', '-', 'g');
271         }
272         if (this.featurep(feature))
273             return true;
274         if (as === undefined)
275             as = feature.replace('-', '_', 'g');
276         try {
277             // ensure current path is not searched for 'require'
278             this.loading_paths.unshift(undefined);
279             this.load(module, as);
280         } finally {
281             this.loading_paths.shift();
282         }
283         return true;
284     },
285     require_later: function (module, as) {
286         this.pending_loads.push([module, as]);
287     },
289     /* nsISupports */
290     QueryInterface: XPCOMUtils.generateQI([]),
292     /* XPCOM registration */
293     classDescription: "Conkeror global object",
294     classID: Components.ID("{72a7eea7-a894-47ec-93a9-a7bc172cf1ac}"),
295     contractID: "@conkeror.mozdev.org/application;1"
298 function NSGetModule (compMgr, fileSpec) {
299     return XPCOMUtils.generateModule([application]);