kludge issue 513
[conkeror.git] / modules / window.js
blobc20f2035bea029053b1843ff742d56c6588c69c1
1 /**
2 * (C) Copyright 2007 John J. Foerch
3 * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
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;
38 var re_result = re.exec(w.tag);
39 if (re_result)
40 existing.push(re_result[1]);
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;
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);
95 spawn(attempt_close());
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;
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);
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;
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.
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;
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 gbrowser(win) {
185 this.win=win;
188 gbrowser.prototype = {
189 constructor: gbrowser,
190 toString: function () "#<gbrowser>",
191 getBrowserForContentWindow: function (win) {
192 return this.win.buffers.current.browser;
196 function window_initialize (window) {
197 window.conkeror = conkeror;
199 // Used by get_window_from_frame to get an unwrapped window reference
200 window.escape_wrapper = function (x) { x(window); };
202 window_setup_args(window);
204 // Set tag
205 var tag = null;
206 if ('tag' in window.args)
207 tag = window.args.tag;
208 window.tag = generate_new_window_tag(tag);
210 // Add a getBrowser() and content to help certain extensions designed
211 // for Firefox work with conkeror
212 window.getBrowser = window_get_this_browser;
213 window.gBrowser = new gbrowser(window);
215 window.__defineGetter__('content',
216 function () {
217 return this.buffers.current.browser.contentWindow;
220 // Tell Mozilla the window_initialize_late_hook to run after a timeout
222 // Do this here, before doing the rest of the window
223 // initialization, so that window_initialize_late_hook will be run
224 // prior to any other "late" hooks set up by other modules in a
225 // function registered in window_initialize{_early,}_hook.
226 window.setTimeout(function () {
227 window_initialize_late_hook.run(window);
228 delete window.window_initialize_late_hook; // used only once
229 delete window.args; // get rid of args
230 }, 0);
232 window_initialize_early_hook.run(window);
233 delete window.window_initialize_early_hook; // used only once
235 window_initialize_hook.run(window);
236 delete window.window_initialize_hook; // used only once
238 window.addEventListener("close",
239 function (event) {
240 event.preventDefault();
241 event.stopPropagation();
242 this.close();
244 true /* capture */);
247 define_window_local_coroutine_hook("window_before_close_hook",
248 RUN_HOOK_UNTIL_FAILURE);
249 define_window_local_hook("window_close_hook", RUN_HOOK);
252 * Window Modes
255 function define_global_window_mode (name, hook_name) {
256 keywords(arguments);
257 function install (window) {
258 if (name in window)
259 throw new Error(name + " already initialized for window");
260 window[name] = new conkeror[name](window);
262 function uninstall (window) {
263 if (!window[name])
264 throw new Error(name + " not initialized for window");
265 window[name].uninstall();
266 delete window[name];
268 define_global_mode(name + "_mode",
269 function () { // enable
270 add_hook(hook_name, install);
271 for_each_window(install);
273 function () { // disable
274 remove_hook(hook_name, install);
275 for_each_window(uninstall);
277 forward_keywords(arguments));
279 ignore_function_for_get_caller_source_code_reference("define_global_window_mode");
284 * Window Title
288 * Default tile formatter. The page url is ignored. If there is a
289 * page_title, returns: "Page title - Conkeror". Otherwise, it
290 * returns just: "Conkeror".
292 function default_title_formatter (window) {
293 var page_title = window.buffers.current.title;
295 if (page_title && page_title.length > 0)
296 return page_title + " - Conkeror";
297 else
298 return "Conkeror";
301 var title_format_fn = null;
303 function set_window_title (window) {
304 window.document.title = title_format_fn(window);
307 function init_window_title () {
308 title_format_fn = default_title_formatter;
310 add_hook("window_initialize_late_hook", set_window_title);
311 add_hook("current_content_buffer_location_change_hook",
312 function (buffer) {
313 set_window_title(buffer.window);
315 add_hook("select_buffer_hook",
316 function (buffer) {
317 set_window_title(buffer.window);
318 }, true);
319 add_hook("current_buffer_title_change_hook",
320 function (buffer) {
321 set_window_title(buffer.window);
325 init_window_title();
328 function call_builtin_command (window, command, clear_mark) {
329 var m = window.minibuffer;
330 if (m.active && m._input_mode_enabled) {
331 m._restore_normal_state();
332 var e = m.input_element;
333 var c = e.controllers.getControllerForCommand(command);
334 try {
335 if (c && c.isCommandEnabled(command))
336 c.doCommand(command);
337 } catch (e) {
338 // ignore errors
340 if (clear_mark)
341 m.current_state.mark_active = false;
342 } else {
343 var attempt_command = function attempt_command (element) {
344 var c;
345 if (element.controllers
346 && (c = element.controllers.getControllerForCommand(command)) != null
347 && c.isCommandEnabled(command))
349 try {
350 c.doCommand(command);
351 } catch (e) {
352 // ignore errors
354 if (clear_mark)
355 window.buffers.current.mark_active = false;
356 return true;
358 return false;
360 var element = window.buffers.current.focused_element;
361 if (element && attempt_command(element, command))
362 return;
363 var win = window.buffers.current.focused_frame;
364 while (true) {
365 if (attempt_command(win, command))
366 return;
367 if (!win.parent || win == win.parent)
368 break;
369 win = win.parent;
376 * window_set_full_screen sets or toggles the fullScreen and hideChrome
377 * properties of the given window. When fullscreen is a boolean, it sets
378 * it to that value. When it is null or not given, it toggles the current
379 * value.
381 function window_set_full_screen (window, fullscreen) {
382 if (fullscreen === true || fullscreen === false) {
383 window.fullScreen = fullscreen;
384 window.hideChrome = fullscreen;
385 } else {
386 window.fullScreen = ! window.fullScreen;
387 window.hideChrome = window.fullScreen;
392 provide("window");