2 * (C) Copyright 2004-2007 Shawn Betts
3 * (C) Copyright 2007-2010 John J. Foerch
4 * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
6 * Use, modification, and distribution are subject to the terms specified in the
12 define_coroutine_hook("before_quit_hook", RUN_HOOK_UNTIL_FAILURE);
13 define_hook("quit_hook");
16 var res = yield before_quit_hook.run();
19 var appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
20 .getService(Ci.nsIAppStartup);
21 appStartup.quit(appStartup.eAttemptQuit);
24 interactive("quit", "Quit Conkeror", quit);
27 function show_conkeror_version (window) {
28 var xulrunner_version = Cc['@mozilla.org/xre/app-info;1']
29 .getService(Ci.nsIXULAppInfo)
31 window.minibuffer.message("Conkeror "+conkeror.version+
32 " (XULRunner "+xulrunner_version+
35 interactive("conkeror-version",
36 "Show version information for Conkeror.",
37 function (I) { show_conkeror_version(I.window); });
38 interactive("version",
39 "Show version information for Conkeror.",
42 /* FIXME: maybe this should be supported for non-browser buffers */
43 function scroll_horiz_complete (buffer, n) {
44 var w = buffer.focused_frame;
45 w.scrollTo (n > 0 ? w.scrollMaxX : 0, w.scrollY);
47 interactive("scroll-beginning-of-line",
48 "Scroll the current frame all the way to the left.",
49 function (I) { scroll_horiz_complete(I.buffer, -1); });
51 interactive("scroll-end-of-line",
52 "Scroll the current frame all the way to the right.",
53 function (I) { scroll_horiz_complete(I.buffer, 1); });
55 interactive("scroll-top-left",
56 "Scroll the current frame all the way to the top left",
57 function (I) { I.buffer.do_command("cmd_scrollTop");
58 scroll_horiz_complete(I.buffer, -1); });
61 function delete_window (window) {
62 window.window.close();
64 interactive("delete-window",
65 "Delete the current window.",
66 function (I) { delete_window(I.window); });
68 interactive("jsconsole",
69 "Open the JavaScript console.",
70 "find-url-new-buffer",
71 $browser_object = "chrome://global/content/console.xul");
74 function paste_x_primary_selection (field) {
75 modify_region(field, function (str) read_from_x_primary_selection());
77 interactive("paste-x-primary-selection",
78 "Insert the contents of the X primary selection into the selected field or "+
79 "minibuffer. Deactivates the region if it is active, and leaves the point "+
80 "after the inserted text.",
81 function (I) call_on_focused_field(I, paste_x_primary_selection, true));
84 function open_line (field) {
85 modify_region(field, function() ["\n", 0]);
87 interactive("open-line",
88 "If there is an active region, replace is with a newline, otherwise just "+
89 "insert a newline. In both cases leave point before the inserted newline.",
90 function (I) call_on_focused_field(I, open_line, true));
93 interactive("insert-parentheses",
94 "Insert a pair of parentheses, or surround the currently selected text "+
95 "with a pair of parentheses.",
97 call_on_focused_field(I, function (field) {
100 return ["("+str+")", (str ? str.length+2 : 1)];
106 function transpose_chars (field) {
107 var value = field.value;
108 var caret = field.selectionStart; // Caret position.
109 var length = value.length;
111 // If we have less than two character in the field or if we are at the
112 // beginning of the field, do nothing.
113 if (length < 2 || caret == 0)
116 // If we are at the end of the field, switch places on the two last
117 // characters. TODO: This should happen at the end of every line, not only
118 // at the end of the field.
122 // Do the transposing.
123 field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
125 // Increment the caret position. If this is not done, the caret is left at
126 // the end of the field as a result of the replacing of contents.
127 field.selectionStart = caret + 1;
128 field.selectionEnd = caret + 1;
130 interactive("transpose-chars",
131 "Interchange characters around point, moving forward one character.",
132 function (I) call_on_focused_field(I, transpose_chars, true));
135 interactive("execute-extended-command",
136 "Call a command specified in the minibuffer.",
139 var boc = I.browser_object;
142 prompt = I.key_sequence.join(" ");
144 prompt += ' ['+boc.name+']';
145 if (prefix !== null && prefix !== undefined) {
146 if (typeof prefix == "object")
147 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
149 prompt += " "+prefix;
151 var command = yield I.minibuffer.read_command($prompt = prompt);
152 call_after_timeout(function () {
153 input_handle_command.call(I.window, new command_event(command));
159 /// built in commands
160 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
162 // Performs a command on a browser buffer content area
165 define_builtin_commands(
167 function (I, command) {
168 call_builtin_command(I.window, command);
173 define_builtin_commands(
175 function (I, command) {
176 var buffer = I.buffer;
178 buffer.do_command(command);
180 /* Ignore exceptions */
185 function get_link_text () {
186 var e = document.commandDispatcher.focusedElement;
187 if (e && e.getAttribute("href")) {
188 return e.getAttribute("href");
195 function copy_email_address (loc)
197 // Copy the comma-separated list of email addresses only.
198 // There are other ways of embedding email addresses in a mailto:
199 // link, but such complex parsing is beyond us.
200 var qmark = loc.indexOf( "?" );
203 if ( qmark > 7 ) { // 7 == length of "mailto:"
204 addresses = loc.substring( 7, qmark );
206 addresses = loc.substr( 7 );
209 //XXX: the original code, which we got from firefox, unescapes the string
210 // using the current character set. To do this in conkeror, we
211 // *should* use an interactive method that gives us the character set,
212 // rather than fetching it by side-effect.
214 // // Let's try to unescape it using a character set
215 // // in case the address is not ASCII.
217 // var characterSet = this.target.ownerDocument.characterSet;
218 // const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
219 // .getService(Components.interfaces.nsITextToSubURI);
220 // addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
226 writeToClipboard(addresses);
227 message("Copied '" + addresses + "'");
229 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
232 /* FIXME: fix this command */
234 interactive("source",
235 "Load a JavaScript file.",
236 function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
238 function reinit (window) {
242 window.minibuffer.message("Loaded: " + path);
244 window.minibuffer.message("Failed to load: "+path);
248 interactive("reinit",
249 "Reload the Conkeror rc file.",
250 function (I) { reinit(I.window); });
252 interactive("help-page", "Open the Conkeror help page.",
253 "find-url-new-buffer",
254 $browser_object = "chrome://conkeror-help/content/help.html");
256 interactive("tutorial", "Open the Conkeror tutorial.",
257 "find-url-new-buffer",
258 $browser_object = "chrome://conkeror-help/content/tutorial.html");
260 function univ_arg_to_number (prefix, default_value) {
261 if (prefix == null) {
262 if (default_value == null)
265 return default_value;
267 if (typeof prefix == "object")
273 interactive("eval-expression",
274 "Evaluate JavaScript statements.",
276 var s = yield I.minibuffer.read(
278 $history = "eval-expression",
279 $completer = javascript_completer(I.buffer));
280 var result = evaluate(s);
281 if (result !== undefined)
282 I.window.minibuffer.message(String(result));
286 function show_extension_manager () {
287 return conkeror.window_watcher.openWindow(
289 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
291 "resizable=yes,dialog=no",
294 interactive("extensions",
295 "Open the extensions manager in a new window.",
296 show_extension_manager);
298 function print_buffer (buffer) {
299 buffer.top_frame.print();
302 interactive("print-buffer",
303 "Print the currently loaded page.",
304 function (I) { print_buffer(I.buffer); });
306 function view_partial_source (window, charset, selection) {
308 charset = "charset=" + charset;
309 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
310 "_blank", "scrollbars,resizable,chrome,dialog=no",
311 null, charset, selection, 'selection');
313 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
316 function view_mathml_source (window, charset, target) {
318 charset = "charset=" + charset;
319 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
320 "_blank", "scrollbars,resizable,chrome,dialog=no",
321 null, charset, target, 'mathml');
325 function send_key_as_event (window, element, combo) {
326 var split = unformat_key_combo(combo);
327 var event = window.document.createEvent("KeyboardEvent");
340 return element.dispatchEvent(event);
342 return window.dispatchEvent(event);
347 function ensure_content_focused (buffer) {
348 var foc = buffer.focused_frame_or_null;
350 buffer.top_frame.focus();
352 interactive("ensure-content-focused", "Ensure that the content document has focus.",
353 function (I) { ensure_content_focused(I.buffer); });
356 function network_set_online_status (status) {
357 const io_service = Cc["@mozilla.org/network/io-service;1"]
358 .getService(Ci.nsIIOService2);
360 io_service.manageOfflineStatus = false;
361 io_service.offline = status;
363 interactive("network-go-online", "Work online.",
364 function (I) { network_set_online_status(true); });
366 interactive("network-go-offline", "Work offline.",
367 function (I) { network_set_online_status(false); });
370 interactive("submit-form",
371 "Submit the form to which the focused element belongs.",
373 var el = I.buffer.focused_element.parentNode;
374 while (el && el.tagName != "FORM")
377 var inputs = el.getElementsByTagName("input");
378 for (var i = 0, ilen = inputs.length; i < ilen; i++) {
379 if (inputs[i].getAttribute("type") == "submit")
380 return browser_object_follow(I.buffer, FOLLOW_DEFAULT,
389 * Browser Object Commands
391 interactive("follow", null,
392 alternates(follow, follow_new_buffer, follow_new_window),
393 $browser_object = browser_object_links);
395 interactive("follow-top", null,
396 alternates(follow_current_buffer, follow_current_frame),
397 $browser_object = browser_object_frames,
400 interactive("follow-new-buffer",
401 "Follow a link in a new buffer",
402 alternates(follow_new_buffer, follow_new_window),
403 $browser_object = browser_object_links,
406 interactive("follow-new-buffer-background",
407 "Follow a link in a new buffer in the background",
408 alternates(follow_new_buffer_background, follow_new_window),
409 $browser_object = browser_object_links,
412 interactive("follow-new-window",
413 "Follow a link in a new window",
415 $browser_object = browser_object_links,
418 interactive("find-url", "Open a URL in the current buffer",
419 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
420 $browser_object = browser_object_url);
422 interactive("find-url-new-buffer",
423 "Open a URL in a new buffer",
424 alternates(follow_new_buffer, follow_new_window),
425 $browser_object = browser_object_url,
426 $prompt = "Find url");
428 interactive("find-url-new-window", "Open a URL in a new window",
430 $browser_object = browser_object_url,
431 $prompt = "Find url");
433 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
436 define_browser_object_class("alternate-url", null,
437 function (I, prompt) {
438 check_buffer(I.buffer, content_buffer);
439 var result = yield I.buffer.window.minibuffer.read_url(
441 $initial_value = I.buffer.display_uri_string);
442 yield co_return(result);
444 $prompt = "Find url");
447 interactive("up", "Go to the parent directory of the current URL",
449 $browser_object = browser_object_up_url);
452 "Go to the homepage in the current buffer.", "follow",
453 $browser_object = function () { return homepage; });
455 interactive("make-window",
456 "Make a new window with the homepage.",
458 $browser_object = function () { return homepage; });
460 interactive("focus", null,
462 var element = yield read_browser_object(I);
463 browser_element_focus(I.buffer, element);
465 $browser_object = browser_object_frames);
468 "Save a browser object.",
470 var element = yield read_browser_object(I);
471 var spec = load_spec(element);
473 panel = create_info_panel(I.window, "download-panel",
475 element_get_operation_label(element, "Saving"),
476 load_spec_uri_string(spec)],
477 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
479 var file = yield I.minibuffer.read_file_check_overwrite(
480 $prompt = "Save as:",
481 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
490 $browser_object = browser_object_links);
493 interactive("copy", null,
495 var element = yield read_browser_object(I);
496 browser_element_copy(I.buffer, element);
498 $browser_object = browser_object_links);
500 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
501 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
502 $browser_object = browser_object_paste_url);
504 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
505 alternates(follow_new_buffer, follow_new_window),
506 $browser_object = browser_object_paste_url);
508 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
510 $browser_object = browser_object_paste_url);
512 interactive("view-source",
513 "Toggle between source and rendered views of a URL.",
514 alternates(view_source, view_source_new_buffer, view_source_new_window),
515 $browser_object = browser_object_frames);
518 interactive("shell-command-on-url",
519 "Run a shell command on the url of a browser object.",
521 var cwd = I.local.cwd;
522 var element = yield read_browser_object(I);
523 var spec = load_spec(element);
524 var uri = load_spec_uri_string(spec);
526 panel = create_info_panel(I.window, "download-panel",
528 element_get_operation_label(element, "Running on", "URI"),
529 load_spec_uri_string(spec)],
530 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
532 var cmd = yield I.minibuffer.read_shell_command(
534 $initial_value = load_spec_default_shell_command(spec));
538 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
540 $browser_object = browser_object_url,
541 $prompt = "Shell command");
544 interactive("shell-command-on-file",
545 "Download a document to a temporary file and run a shell command on it.",
547 var cwd = I.local.cwd;
548 var element = yield read_browser_object(I);
549 var spec = load_spec(element);
550 var uri = load_spec_uri_string(spec);
552 panel = create_info_panel(I.window, "download-panel",
554 element_get_operation_label(element, "Running on"),
555 load_spec_uri_string(spec)],
556 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
558 var cmd = yield I.minibuffer.read_shell_command(
560 $initial_value = load_spec_default_shell_command(spec));
564 yield browser_element_shell_command(I.buffer, element, cmd, cwd);
566 $browser_object = browser_object_links,
567 $prompt = "Shell command");
570 interactive("bookmark",
571 "Create a bookmark.",
573 var element = yield read_browser_object(I);
574 var spec = load_spec(element);
575 var uri_string = load_spec_uri_string(spec);
577 panel = create_info_panel(I.window, "bookmark-panel",
579 element_get_operation_label(element, "Bookmarking"),
582 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
586 add_bookmark(uri_string, title);
587 I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
589 $browser_object = browser_object_frames);
592 interactive("save-page",
593 "Save a document, not including any embedded documents such as images "+
596 check_buffer(I.buffer, content_buffer);
597 var element = yield read_browser_object(I);
598 var spec = load_spec(element);
599 if (!load_spec_document(spec))
600 throw interactive_error("Element is not associated with a document.");
601 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
604 panel = create_info_panel(I.window, "download-panel",
606 element_get_operation_label(element, "Saving"),
607 load_spec_uri_string(spec)],
608 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
611 var file = yield I.minibuffer.read_file_check_overwrite(
612 $prompt = "Save page as:",
614 $initial_value = suggested_path);
619 save_uri(spec, file, $buffer = I.buffer);
621 $browser_object = browser_object_frames);
624 interactive("save-page-as-text",
625 "Save a page as plain text.",
627 check_buffer(I.buffer, content_buffer);
628 var element = yield read_browser_object(I);
629 var spec = load_spec(element);
631 if (!(doc = load_spec_document(spec)))
632 throw interactive_error("Element is not associated with a document.");
633 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
636 panel = create_info_panel(I.window, "download-panel",
638 element_get_operation_label(element, "Saving", "as text"),
639 load_spec_uri_string(spec)],
640 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
643 var file = yield I.minibuffer.read_file_check_overwrite(
644 $prompt = "Save page as text:",
646 $initial_value = suggested_path);
651 save_document_as_text(doc, file, $buffer = I.buffer);
653 $browser_object = browser_object_frames);
656 interactive("save-page-complete",
657 "Save a page and all supporting documents, including images, css, "+
658 "and child frame documents.",
660 check_buffer(I.buffer, content_buffer);
661 var element = yield read_browser_object(I);
662 var spec = load_spec(element);
664 if (!(doc = load_spec_document(spec)))
665 throw interactive_error("Element is not associated with a document.");
666 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
669 panel = create_info_panel(I.window, "download-panel",
671 element_get_operation_label(element, "Saving complete"),
672 load_spec_uri_string(spec)],
673 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
676 var file = yield I.minibuffer.read_file_check_overwrite(
677 $prompt = "Save page complete:",
679 $initial_value = suggested_path);
680 // FIXME: use proper read function
681 var dir = yield I.minibuffer.read_file(
682 $prompt = "Data Directory:",
684 $initial_value = file.path + ".support");
689 save_document_complete(doc, file, dir, $buffer = I.buffer);
691 $browser_object = browser_object_frames);
694 function view_as_mime_type (I, target) {
696 var element = yield read_browser_object(I);
697 var spec = load_spec(element);
700 target = FOLLOW_CURRENT_FRAME;
702 if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
703 throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
707 var mime_type = load_spec_mime_type(spec);
708 panel = create_info_panel(I.window, "download-panel",
710 element_get_operation_label(element, "View in browser"),
711 load_spec_uri_string(spec)],
712 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
716 let suggested_type = mime_type;
717 if (viewable_mime_type_list.indexOf(suggested_type) == -1)
718 suggested_type = "text/plain";
719 mime_type = yield I.minibuffer.read_viewable_mime_type(
720 $prompt = "View internally as",
721 $initial_value = suggested_type,
723 override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
724 browser_object_follow(I.buffer, target, spec);
730 function view_as_mime_type_new_buffer (I) {
731 yield view_as_mime_type(I, OPEN_NEW_BUFFER);
734 function view_as_mime_type_new_window (I) {
735 yield view_as_mime_type(I, OPEN_NEW_WINDOW);
738 interactive("view-as-mime-type",
739 "Display a browser object in the browser using the specified MIME type.",
740 alternates(view_as_mime_type,
741 view_as_mime_type_new_buffer,
742 view_as_mime_type_new_window),
743 $browser_object = browser_object_frames);
746 interactive("delete",
747 "Delete a DOM node, given as a browser object.",
749 var elem = yield read_browser_object(I);
750 if (! (elem instanceof Ci.nsIDOMNode))
751 throw interactive_error("Cannot delete item");
752 elem.parentNode.removeChild(elem);
754 $browser_object = browser_object_dom_node);
757 interactive("charset-prefix",
758 "A prefix command that prompts for a charset to use in a "+
759 "subsequent navigation command.",
761 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
762 .getService(Ci.nsICharsetConverterManager);
763 var decoders = ccman.getDecoderList()
765 while (decoders.hasMore())
766 charsets.push(decoders.getNext());
767 I.forced_charset = yield I.minibuffer.read(
768 $prompt = "Charset:",
769 $completer = prefix_completer(
770 $completions = charsets,
771 $get_string = function (x) x.toLowerCase()),
778 interactive("reload-with-charset",
779 "Prompt for a charset, and reload the current page, forcing use "+
782 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
783 .getService(Ci.nsICharsetConverterManager);
784 var decoders = ccman.getDecoderList()
786 while (decoders.hasMore())
787 charsets.push(decoders.getNext());
788 var forced_charset = yield I.minibuffer.read(
789 $prompt = "Charset:",
790 $completer = prefix_completer(
791 $completions = charsets,
792 $get_string = function (x) x.toLowerCase()),
795 reload(I.buffer, false, null, forced_charset);
800 "Paste the contents of the clipboard",
802 call_builtin_command(I.window, "cmd_paste", true);
805 interactive("kill-region",
806 "Kill (\"cut\") the selected text.",
808 call_builtin_command(I.window, "cmd_cut", true);
811 interactive("kill-ring-save",
812 "Save the region as if killed, but don't kill it.",
814 call_builtin_command(I.window, "cmd_copy", true);
817 interactive("password-manager",
818 "Open the password manager.",
819 "find-url-new-buffer",
820 $browser_object = "chrome://passwordmgr/content/passwordManager.xul");
823 interactive("toggle-full-screen",
824 "Toggle full screen mode for the current window.",
826 I.window.fullScreen = ! I.window.fullScreen;
827 I.window.hideChrome = I.window.fullScreen;
828 if (I.window.fullScreen)
829 I.minibuffer.message("Fullscreen mode on");
831 I.minibuffer.message("Fullscreen mode off");