contrib/art/conker.svg, outline.svg: better 'string'
[conkeror.git] / modules / window.js
blob77fcbcb5e3f32be56e5f4e22976ad4b09b8916e6
1 /**
2  * (C) Copyright 2007 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 require("mode.js");
11 var define_window_local_hook = simple_local_hook_definer();
12 var define_window_local_coroutine_hook = simple_local_coroutine_hook_definer();
15 define_hook("make_window_hook");
17 var window_watcher = Cc["@mozilla.org/embedcomp/window-watcher;1"]
18     .getService(Ci.nsIWindowWatcher);
20 function generate_new_window_tag (tag) {
21     var existing = [];
22     var exact_match = false;
23     var en = window_watcher.getWindowEnumerator();
24     if (tag == '')
25         tag = null;
26     var re;
27     if (tag)
28         re = new RegExp ("^" + tag + "<(\\d+)>$");
29     else
30         re = new RegExp ("^(\\d+)$");
31     while (en.hasMoreElements()) {
32         var w = en.getNext().QueryInterface(Ci.nsIDOMWindow);
33         if ('tag' in w)  {
34             if (tag && w.tag == tag) {
35                 exact_match = true;
36                 continue;
37             }
38             var re_result = re.exec(w.tag);
39             if (re_result)
40                 existing.push(re_result[1]);
41         }
42     }
43     if (tag && ! exact_match)
44         return tag;
46     existing.sort(function (a, b) { return a - b; });
48     var n = 1;
49     for (var i = 0; i < existing.length; i++) {
50         if (existing[i] < n) continue;
51         if (existing[i] == n) { n++; continue; }
52         break;
53     }
54     if (tag)
55         return tag + "<" + n + ">";
56     else
57         return n;
60 function make_chrome_window (chrome_uri, args) {
61     return window_watcher.openWindow(null, chrome_uri, "_blank",
62                                      "chrome,dialog=no,all,resizable", args);
65 var conkeror_chrome_uri = "chrome://conkeror-gui/content/conkeror.xul";
67 /**
68  * This is called by Conkeror to explicitly create a conkeror.xul window.
69  * However, windows are sometimes created internally be Mozilla, for example due
70  * to the logic in content-buffer.js:browser_dom_window.openURI.  In that case,
71  * make_window is not called and Conkeror sees the window for the first time in
72  * window_initialize, which needs to take care of all of the initialization that
73  * would normally happen in make_window.
74  **/
75 function make_window (initial_buffer_creator, tag) {
76     var args = { tag: tag,
77                  initial_buffer_creator: initial_buffer_creator };
78     var result = make_chrome_window(conkeror_chrome_uri, null);
79     result.args = args;
80     make_window_hook.run(result);
81     window_install_close_intercept(result);
82     return result;
85 function window_install_close_intercept (window) {
86     var close = window.close;
87     window.close = function () {
88         function attempt_close () {
89             var res = yield window_before_close_hook.run(window);
90             if (res) {
91                 window_close_hook.run(window);
92                 close.call(window);
93             }
94         }
95         co_call(attempt_close());
96     };
99 function get_window_by_tag (tag) {
100     var en = window_watcher.getWindowEnumerator();
101     while (en.hasMoreElements()) {
102         var w = en.getNext().QueryInterface(Ci.nsIDOMWindow);
103         if ('tag' in w && w.tag == tag)
104             return w;
105     }
106     return null;
109 /* FIXME: decide if this should include not-fully-initialized windows  */
110 function for_each_window (func) {
111     var en = window_watcher.getWindowEnumerator();
112     while (en.hasMoreElements()) {
113         var w = en.getNext().QueryInterface(Ci.nsIDOMWindow);
114         if ('conkeror' in w)
115             func(w);
116     }
119 function get_recent_conkeror_window () {
120     var wm = Cc['@mozilla.org/appshell/window-mediator;1']
121        .getService(Ci.nsIWindowMediator);
122     var window = wm.getMostRecentWindow("navigator:browser");
123     if (window && ("conkeror" in window))
124         return window;
125     var en = window_watcher.getWindowEnumerator();
126     while (en.hasMoreElements()) {
127         window = en.getNext().QueryInterface(Ci.nsIDOMWindow);
128         if ('conkeror' in window)
129             return window;
130     }
131     return null;
134 define_window_local_hook("window_initialize_early_hook");
135 define_window_local_hook("window_initialize_hook");
136 define_window_local_hook("window_initialize_late_hook");
138 var window_extra_argument_list = [];
140 define_variable("window_extra_argument_max_delay", 100);
143  * Called by window_initialize.  If the window was created using make_window (as
144  * indicated by window.args having been set), nothing needs to be done.
145  * Otherwise, we need to perform the setup that would otherwise occur in
146  * make_window.
147  **/
148 function window_setup_args (window) {
149     if (window.args != null)
150         return;
152     window_install_close_intercept(window);
155     var cur_time = Date.now();
156     var i;
157     var result = null;
158     for (i = 0; i < window_extra_argument_list.length; ++i) {
159         var a = window_extra_argument_list[i];
160         if (a.time > cur_time - window_extra_argument_max_delay) {
161             delete a.time;
162             result = a;
163             i++;
164             break;
165         }
166     }
167     window_extra_argument_list = window_extra_argument_list.slice(i);
169     if (result == null)
170         window.args = {};
171     else
172         window.args = result;
175 function window_set_extra_arguments (args) {
176     args.time = Date.now();
177     window_extra_argument_list.push(args);
180 function window_get_this_browser () {
181     return this.buffers.current.browser;
184 function window_initialize (window) {
185     window.conkeror = conkeror;
187     // Used by get_window_from_frame to get an unwrapped window reference
188     window.escape_wrapper = function (x) { x(window); };
190     window_setup_args(window);
192     // Set tag
193     var tag = null;
194     if ('tag' in window.args)
195         tag = window.args.tag;
196     window.tag = generate_new_window_tag(tag);
198     // Add a getBrowser() and content to help certain extensions designed
199     // for Firefox work with conkeror
200     window.getBrowser = window_get_this_browser;
201     window.__defineGetter__('content',
202                             function () {
203                                 return this.buffers.current.browser.contentWindow;
204                             });
206     // Tell Mozilla the window_initialize_late_hook to run after a timeout
208     // Do this here, before doing the rest of the window
209     // initialization, so that window_initialize_late_hook will be run
210     // prior to any other "late" hooks set up by other modules in a
211     // function registered in window_initialize{_early,}_hook.
212     window.setTimeout(function () {
213             window_initialize_late_hook.run(window);
214             delete window.window_initialize_late_hook; // used only once
215             delete window.args; // get rid of args
216         }, 0);
218     window_initialize_early_hook.run(window);
219     delete window.window_initialize_early_hook; // used only once
221     window_initialize_hook.run(window);
222     delete window.window_initialize_hook; // used only once
224     window.addEventListener("close",
225                             function (event) {
226                                 event.preventDefault();
227                                 event.stopPropagation();
228                                 this.close();
229                             },
230                             true /* capture */);
233 define_window_local_coroutine_hook("window_before_close_hook",
234                                    RUN_HOOK_UNTIL_FAILURE);
235 define_window_local_hook("window_close_hook", RUN_HOOK);
238  * Window Modes
239  */
241 function define_global_window_mode (name, hook_name) {
242     keywords(arguments);
243     function install (window) {
244         if (name in window)
245             throw new Error(name + " already initialized for window");
246         window[name] = new conkeror[name](window);
247     }
248     function uninstall (window) {
249         if (!window[name])
250             throw new Error(name + " not initialized for window");
251         window[name].uninstall();
252         delete window[name];
253     }
254     define_global_mode(name + "_mode",
255                        function () { // enable
256                            add_hook(hook_name, install);
257                            for_each_window(install);
258                        },
259                        function () { // disable
260                            remove_hook(hook_name, install);
261                            for_each_window(uninstall);
262                        },
263                        forward_keywords(arguments));
265 ignore_function_for_get_caller_source_code_reference("define_global_window_mode");
270  * Window Title
271  */
274  * Default tile formatter.  The page url is ignored.  If there is a
275  * page_title, returns: "Page title - Conkeror".  Otherwise, it
276  * returns just: "Conkeror".
277  */
278 function default_title_formatter (window) {
279     var page_title = window.buffers.current.title;
281     if (page_title && page_title.length > 0)
282         return page_title + " - Conkeror";
283     else
284         return "Conkeror";
287 var title_format_fn = null;
289 function set_window_title (window) {
290     window.document.title = title_format_fn(window);
293 function init_window_title () {
294     title_format_fn = default_title_formatter;
296     add_hook("window_initialize_late_hook", set_window_title);
297     add_hook("current_content_buffer_location_change_hook",
298              function (buffer) {
299                  set_window_title(buffer.window);
300              });
301     add_hook("select_buffer_hook",
302              function (buffer) {
303                  set_window_title(buffer.window);
304              }, true);
305     add_hook("current_buffer_title_change_hook",
306              function (buffer) {
307                  set_window_title(buffer.window);
308              });
311 init_window_title();
314 function call_builtin_command (window, command, clear_mark) {
315     var m = window.minibuffer;
316     if (m.active && m._input_mode_enabled) {
317         m._restore_normal_state();
318         var e = m.input_element;
319         var c = e.controllers.getControllerForCommand(command);
320         try {
321             if (c && c.isCommandEnabled(command))
322                 c.doCommand(command);
323         } catch (e) {
324             // ignore errors
325         }
326         if (clear_mark)
327             m.current_state.mark_active = false;
328     } else {
329         function attempt_command (element) {
330             var c;
331             if (element.controllers
332                 && (c = element.controllers.getControllerForCommand(command)) != null
333                 && c.isCommandEnabled(command))
334             {
335                 try {
336                     c.doCommand(command);
337                 } catch (e) {
338                     // ignore errors
339                 }
340                 if (clear_mark)
341                     window.buffers.current.mark_active = false;
342                 return true;
343             }
344             return false;
345         }
346         var element = window.buffers.current.focused_element;
347         if (element && attempt_command(element, command))
348             return;
349         var win = window.buffers.current.focused_frame;
350         while (true) {
351             if (attempt_command(win, command))
352                 return;
353             if (!win.parent || win == win.parent)
354                 break;
355             win = win.parent;
356         }
357     }
362  * window_set_full_screen sets or toggles the fullScreen and hideChrome
363  * properties of the given window.  When fullscreen is a boolean, it sets
364  * it to that value.  When it is null or not given, it toggles the current
365  * value.
366  */
367 function window_set_full_screen (window, fullscreen) {
368     if (fullscreen === true || fullscreen === false) {
369         window.fullScreen = fullscreen;
370         window.hideChrome = fullscreen;
371     } else {
372         window.fullScreen = ! window.fullScreen;
373         window.hideChrome = window.fullScreen;
374     }
378 provide("window");