2 * (C) Copyright 2009 Nicholas A. Zigarovich
4 * Use, modification, and distribution are subject to the terms specified in the
12 * - A session-load-window-replace command which is like
13 * window-current-replace, but which also recycles existing windows.
14 * - A session-load-window-current-flatten command which loads all buffers in a
15 * multi-window section in the current window.
16 * - Session files should be more human readable.
17 * - Ability to store arbitrary session data, for example, a buffer's history.
18 * - The previous two features depend on a parser/generator for retroj's
19 * structured plaintext format.
23 * - This module does not work correctly with daemon mode.
27 * - Inhibit loading of the homepage in windows' initial buffers when auto-
29 * - Auto-save the session when the last conkeror window is closed by a
30 * window manager event. Currently no session is saved.
31 * - Consider how and which errors should be handled. Too often we silently
32 * fail and return without telling the user why we are doing so.
36 //// Manual sessions. ////
39 let _session_dir_default = Cc["@mozilla.org/file/directory_service;1"]
40 .getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
41 _session_dir_default.append("sessions");
42 if (! _session_dir_default.exists())
43 _session_dir_default.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
45 define_variable("session_dir", _session_dir_default,
46 "Default directory for save/load interactive commands.");
49 * session_token is instantiated for the $opener property of buffers
50 * created by the session. This allows the possibility of knowing,
51 * during buffer init, whether the buffer was created by a session or
54 function session_token () {}
55 session_token.prototype = {
56 constructor: session_token
60 * session_get generates and returns a structure containing session
61 * data for the current group of open windows.
63 function session_get () {
66 for_each_window(function (w) {
68 w.buffers.for_each(function (b) {
69 if (b instanceof content_buffer)
70 buffers.push(b.display_uri_string);
72 if (buffers.length == 0)
81 * session_load loads the given session (given as a data structure),
82 * with optional window as the first window to load the session into,
83 * with optional buffer_idx as the buffer index at which to begin
84 * overwriting existing buffers.
86 function session_load (session, window, buffer_idx) {
87 if (! (session[0] && session[0][0]))
88 throw new Error("Invalid 'session' argument.");
90 var opener = new session_token();
92 let bi = buffer_idx != undefined ?
93 buffer_idx : window.buffers.count;
95 // first kill special buffers slated for recycling.
96 let (b, i = (bi == 0 ? 1 : bi),
99 while ((b = window.buffers.get_buffer(i))) {
100 if (b instanceof content_buffer) {
104 kill_buffer(b, true);
107 (b = window.buffers.get_buffer(0)) &&
108 !(b instanceof content_buffer))
111 create_buffer(window,
112 buffer_creator(content_buffer,
114 OPEN_NEW_BUFFER_BACKGROUND);
115 kill_buffer(b, true);
119 // it is now safe to recycle the remaining buffers.
120 for (let i = 0; session[s][i] != undefined; ++i, ++bi) {
121 let b = window.buffers.get_buffer(bi);
124 let history = b.web_navigation.sessionHistory;
125 history.PurgeHistory(history.count);
127 b.load(session[s][i]);
129 let c = buffer_creator(content_buffer,
130 $load = session[s][i],
132 $position = buffer_position_end);
133 create_buffer(window, c, OPEN_NEW_BUFFER_BACKGROUND);
136 for (let b = window.buffers.get_buffer(bi); b;
137 b = window.buffers.get_buffer(bi))
139 kill_buffer(b, true);
144 function make_init_hook (session) {
145 function init_hook (window) {
146 for (let i = 1; session[i] != undefined; ++i) {
147 let c = buffer_creator(content_buffer,
150 $position = buffer_position_end);
151 create_buffer(window, c, OPEN_NEW_BUFFER_BACKGROUND);
157 for (; session[s] != undefined; ++s) {
158 let w = make_window(buffer_creator(content_buffer,
159 $load = session[s][0],
161 add_hook.call(w, "window_initialize_late_hook",
162 make_init_hook(session[s]));
167 * session_load_window_new loads the given session into new windows.
169 function session_load_window_new (session) {
170 session_load(session);
174 * session_load_window_current loads the given session, with the
175 * session's first window being appended to window. No existing
176 * buffers will be overwritten.
178 function session_load_window_current (session, window) {
179 let w = window ? window : get_recent_conkeror_window();
180 session_load(session, w);
184 * session_load_window_current loads the given session, with the
185 * session's first window replacing the given window. All buffers in
186 * the given window will be overwritten.
188 function session_load_window_current_replace (session, window) {
189 let w = window ? window : get_recent_conkeror_window();
190 session_load(session, w, 0);
194 * session_write writes the given session to the file given by path.
196 function session_write (path, session) {
197 if (! (path instanceof Ci.nsIFile))
198 path = make_file(path);
200 session = session_get();
201 write_text_file(path, JSON.stringify(session));
205 * session_read reads session data from the given file path,
206 * and returns a decoded session structure.
208 function session_read (path) {
209 if (! (path instanceof Ci.nsIFile))
210 path = make_file(path);
211 return JSON.parse(read_text_file(path));
215 * session_remove deletes the given session file.
217 function session_remove (path) {
218 if (! (path instanceof Ci.nsIFile))
219 path = make_file(path);
223 let _session_prompt_file = function (I) {
225 yield I.minibuffer.read_file_path(
226 $prompt = "Session file:",
227 $initial_value = session_dir.path,
233 let _session_file_not_found = function (I, file) {
234 let mb = I ? I.minibuffer : get_recent_conkeror_window().minibuffer;
235 let msg = "Session file not found: " + file.path;
240 interactive("session-save",
241 "Save the current session.",
243 session_write(make_file(yield _session_prompt_file(I)),
247 interactive("session-load-window-new",
248 "Load a session in a new window.",
250 let file = make_file(yield _session_prompt_file(I));
252 _session_file_not_found(I, file);
254 session_load_window_new(session_read(file));
257 interactive("session-load-window-current",
258 "Load a session in new buffers in the current window.",
260 let file = make_file(yield _session_prompt_file(I));
262 _session_file_not_found(I, file);
264 session_load_window_current(session_read(file), I.window);
267 interactive("session-load-window-current-replace",
268 "Replace all buffers in the current window with buffers "+
269 "in the saved session.",
271 let file = make_file(yield _session_prompt_file(I));
273 _session_file_not_found(I, file);
275 session_load_window_current_replace(session_read(file),
279 interactive("session-remove",
280 "Remove a session file.",
282 let file = make_file(yield _session_prompt_file(I));
284 _session_file_not_found(I, file);
286 session_remove(file);
290 //// Auto-save sessions. ////
293 define_variable("session_auto_save_file", "auto-save",
294 "Default filename for the auto-save session.");
296 define_variable("session_auto_save_auto_load", false,
297 'Whether to load the auto-saved session when the browser is started. '+
298 'May be true, false, or "prompt".');
300 function session_auto_save_load_window_new () {
301 session_load_window_new(_session_auto_save_cached);
304 function session_auto_save_load_window_current (window) {
305 session_load_window_current(_session_auto_save_cached, window);
308 function session_auto_save_load_window_current_replace (window) {
309 session_load_window_current_replace(_session_auto_save_cached, window);
312 define_variable("session_auto_save_auto_load_fn",
314 "Function to be called to load the auto-saved session at start-up " +
315 "when URLs are given on the command-line. May be " +
316 "session_auto_save_load_window_new, " +
317 "session_auto_save_load_window_current, or null. If null, the" +
318 "session will not be auto-loaded when URLs are given.");
321 // undefined - we have not tried to cache the auto-save.
322 // null - we have tried to cache the auto-save, but it didn't exist.
323 // object - the cached session object for the auto-save.
324 let _session_auto_save_cached = undefined;
326 let _session_auto_save_file_get = function () {
327 if (session_auto_save_file instanceof Ci.nsIFile)
328 return session_auto_save_file;
329 let f = session_dir.clone();
330 f.append(session_auto_save_file);
334 function session_auto_save_save () {
335 let f = _session_auto_save_file_get();
336 let s = session_get();
343 function session_auto_save_remove () {
344 let f = _session_auto_save_file_get();
349 let _session_auto_save_auto_load = function (user_gave_urls) {
350 if (! session_auto_save_auto_load)
352 if (! _session_auto_save_cached) {
353 _session_file_not_found(null, _session_auto_save_file_get());
357 let window = get_recent_conkeror_window();
358 if (session_auto_save_auto_load == true)
360 else if (session_auto_save_auto_load == "prompt" && !user_gave_urls) {
361 do_load = (yield window.minibuffer.read_single_character_option(
362 $prompt = "Load auto-saved session? (y/n)",
363 $options = ["y", "n"]
366 throw new Error("Invalid value for session_auto_save_auto_load: " +
367 session_auto_save_auto_load);
370 if (user_gave_urls) {
371 if (session_auto_save_auto_load_fn)
372 session_auto_save_auto_load_fn(window);
374 session_auto_save_load_window_current_replace(window);
377 interactive("session-auto-save-load-window-new",
378 "Load the auto-save session in a new window.",
380 if (_session_auto_save_cached == null)
381 _session_file_not_found(I, _session_auto_save_file_get());
383 session_auto_save_load_window_new();
386 interactive("session-auto-save-load-window-current",
387 "Load the auto-save session in new buffers in the current window.",
389 if (_session_auto_save_cached == null)
390 _session_file_not_found(I, _session_auto_save_file_get());
392 session_auto_save_load_window_current(I.window);
395 interactive("session-auto-save-load-window-current-replace",
396 "Replace all buffers in the current window with buffers in the "+
397 "auto-saved session.",
399 if (_session_auto_save_cached == null)
400 _session_file_not_found(I, _session_auto_save_file_get());
402 session_auto_save_load_window_current_replace(I.window);
405 interactive("session-auto-save-remove",
406 "Remove the auto-save session",
407 session_auto_save_remove);
410 //// auto-save-session-mode ////
413 let _session_auto_save_mode_bootstrap = function (b) {
414 remove_hook("window_initialize_late_hook", _session_auto_save_mode_bootstrap);
415 add_hook("create_buffer_hook", session_auto_save_save);
416 add_hook("kill_buffer_hook", session_auto_save_save);
417 add_hook("move_buffer_hook", session_auto_save_save);
418 add_hook("content_buffer_location_change_hook", session_auto_save_save);
419 let user_gave_urls = false;
420 for (let i = 0; i < command_line.length; ++i) {
421 if (command_line[i][0] != '-') {
422 user_gave_urls = true;
426 co_call(_session_auto_save_auto_load(user_gave_urls));
429 let _session_auto_save_mode_enable = function () {
430 if (_session_auto_save_cached == undefined) {
431 let f = _session_auto_save_file_get();
432 _session_auto_save_cached = f.exists() ? session_read(f) : null;
434 if (conkeror_started) {
435 add_hook("create_buffer_hook", session_auto_save_save);
436 add_hook("kill_buffer_hook", session_auto_save_save);
437 add_hook("move_buffer_hook", session_auto_save_save);
438 add_hook("content_buffer_location_change_hook", session_auto_save_save);
440 add_hook("window_initialize_late_hook", _session_auto_save_mode_bootstrap);
443 let _session_auto_save_mode_disable = function () {
444 remove_hook("create_buffer_hook", session_auto_save_save);
445 remove_hook("kill_buffer_hook", session_auto_save_save);
446 remove_hook("move_buffer_hook", session_auto_save_save);
447 remove_hook("content_buffer_location_change_hook", session_auto_save_save);
449 remove_hook("window_initialize_late_hook", _session_auto_save_mode_bootstrap);
452 define_global_mode("session_auto_save_mode",
453 _session_auto_save_mode_enable,
454 _session_auto_save_mode_disable);
456 session_auto_save_mode(true);