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
10 define_coroutine_hook("before_quit_hook", RUN_HOOK_UNTIL_FAILURE);
11 define_hook("quit_hook");
13 function quit (I, restart) {
14 var res = yield before_quit_hook.run();
17 var appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
18 .getService(Ci.nsIAppStartup);
20 appStartup.quit(appStartup.eRestart | appStartup.eAttemptQuit);
22 appStartup.quit(appStartup.eAttemptQuit);
26 interactive("quit", "Quit Conkeror", quit);
28 interactive("restart",
34 function show_conkeror_version (window) {
35 var xulrunner_version = Cc['@mozilla.org/xre/app-info;1']
36 .getService(Ci.nsIXULAppInfo)
38 window.minibuffer.message("Conkeror "+conkeror.version+
39 " (XULRunner "+xulrunner_version+
42 interactive("conkeror-version",
43 "Show version information for Conkeror.",
44 function (I) { show_conkeror_version(I.window); });
45 interactive("version",
46 "Show version information for Conkeror.",
49 /* FIXME: maybe this should be supported for non-browser buffers */
50 function scroll_horiz_complete (buffer, n) {
51 var w = buffer.focused_frame;
52 w.scrollTo (n > 0 ? w.scrollMaxX : 0, w.scrollY);
54 interactive("scroll-beginning-of-line",
55 "Scroll the current frame all the way to the left.",
56 function (I) { scroll_horiz_complete(I.buffer, -1); });
58 interactive("scroll-end-of-line",
59 "Scroll the current frame all the way to the right.",
60 function (I) { scroll_horiz_complete(I.buffer, 1); });
62 interactive("scroll-top-left",
63 "Scroll the current frame all the way to the top left",
65 scroll_horiz_complete(I.buffer, -1);
66 I.buffer.do_command("cmd_scrollTop");
70 function delete_window (window) {
71 window.window.close();
73 interactive("delete-window",
74 "Delete the current window.",
75 function (I) { delete_window(I.window); });
77 interactive("jsconsole",
78 "Open the JavaScript console.",
79 "find-url-new-buffer",
80 $browser_object = "chrome://global/content/console.xul");
83 function paste_x_primary_selection (field) {
84 modify_region(field, function (str) read_from_x_primary_selection());
86 interactive("paste-x-primary-selection",
87 "Insert the contents of the X primary selection into the selected field or "+
88 "minibuffer. Deactivates the region if it is active, and leaves the point "+
89 "after the inserted text.",
90 function (I) call_on_focused_field(I, paste_x_primary_selection, true));
93 function open_line (field) {
94 modify_region(field, function() ["\n", 0]);
96 interactive("open-line",
97 "If there is an active region, replace is with a newline, otherwise just "+
98 "insert a newline. In both cases leave point before the inserted newline.",
99 function (I) call_on_focused_field(I, open_line, true));
102 interactive("insert-parentheses",
103 "Insert a pair of parentheses, or surround the currently selected text "+
104 "with a pair of parentheses.",
106 call_on_focused_field(I, function (field) {
109 return ["("+str+")", (str ? str.length+2 : 1)];
115 function transpose_chars (field) {
116 var value = field.value;
117 var caret = field.selectionStart; // Caret position.
118 var length = value.length;
120 // If we have less than two character in the field or if we are at the
121 // beginning of the field, do nothing.
122 if (length < 2 || caret == 0)
125 // If we are at the end of the field, switch places on the two last
126 // characters. TODO: This should happen at the end of every line, not only
127 // at the end of the field.
131 // Do the transposing.
132 field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
134 // Increment the caret position. If this is not done, the caret is left at
135 // the end of the field as a result of the replacing of contents.
136 field.selectionStart = caret + 1;
137 field.selectionEnd = caret + 1;
139 interactive("transpose-chars",
140 "Interchange characters around point, moving forward one character.",
141 function (I) call_on_focused_field(I, transpose_chars, true));
144 interactive("execute-extended-command",
145 "Call a command specified in the minibuffer.",
148 var boc = I.browser_object;
151 prompt = I.key_sequence.join(" ");
153 prompt += ' ['+boc.name+']';
154 if (prefix !== null && prefix !== undefined) {
155 if (typeof prefix == "object")
156 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
158 prompt += " "+prefix;
160 var command = yield I.minibuffer.read_command($prompt = prompt);
161 call_after_timeout(function () {
162 input_handle_command.call(I.window, new command_event(command));
168 /// built in commands
169 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
171 // Performs a command on a browser buffer content area
174 define_builtin_commands("",
175 function (I, command) {
176 call_builtin_command(I.window, command);
180 define_builtin_commands("caret-",
181 function (I, command) {
182 var buffer = I.buffer;
184 buffer.do_command(command);
186 /* Ignore exceptions */
191 function get_link_text () {
192 var e = document.commandDispatcher.focusedElement;
193 if (e && e.getAttribute("href")) {
194 return e.getAttribute("href");
201 function copy_email_address (loc)
203 // Copy the comma-separated list of email addresses only.
204 // There are other ways of embedding email addresses in a mailto:
205 // link, but such complex parsing is beyond us.
206 var qmark = loc.indexOf( "?" );
209 if ( qmark > 7 ) { // 7 == length of "mailto:"
210 addresses = loc.substring( 7, qmark );
212 addresses = loc.substr( 7 );
215 //XXX: the original code, which we got from firefox, unescapes the string
216 // using the current character set. To do this in conkeror, we
217 // *should* use an interactive method that gives us the character set,
218 // rather than fetching it by side-effect.
220 // // Let's try to unescape it using a character set
221 // // in case the address is not ASCII.
223 // var characterSet = this.target.ownerDocument.characterSet;
224 // const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
225 // .getService(Components.interfaces.nsITextToSubURI);
226 // addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
232 writeToClipboard(addresses);
233 message("Copied '" + addresses + "'");
235 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
238 /* FIXME: fix this command */
240 interactive("source",
241 "Load a JavaScript file.",
242 function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
244 function reinit (window) {
246 var obs = Cc["@mozilla.org/observer-service;1"]
247 .getService(Ci.nsIObserverService);
248 obs.notifyObservers(null, "startupcache-invalidate", null);
249 var path = load_rc();
250 window.minibuffer.message("Loaded: " + path);
252 window.minibuffer.message("Failed to load: "+path);
256 interactive("reinit",
257 "Reload the Conkeror rc file.",
258 function (I) { reinit(I.window); });
260 interactive("help-page",
261 "Open the Conkeror help page.",
262 "find-url-new-buffer",
263 $browser_object = "chrome://conkeror-help/content/help.html");
265 interactive("tutorial",
266 "Open the Conkeror tutorial.",
267 "find-url-new-buffer",
268 $browser_object = "chrome://conkeror-help/content/tutorial.html");
270 function univ_arg_to_number (prefix, default_value) {
271 if (prefix == null) {
272 if (default_value == null)
275 return default_value;
277 if (typeof prefix == "object")
283 interactive("eval-expression",
284 "Evaluate JavaScript statements.",
286 var s = yield I.minibuffer.read(
288 $history = "eval-expression",
289 $completer = new javascript_completer(conkeror));
290 var result = evaluate(s);
291 if (result !== undefined)
292 I.window.minibuffer.message(String(result));
296 function show_extension_manager () {
297 return conkeror.window_watcher.openWindow(
299 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
301 "resizable=yes,dialog=no",
304 interactive("extensions",
305 "Open the extensions manager in a new window.",
306 show_extension_manager);
308 function print_buffer (buffer) {
309 buffer.top_frame.print();
312 interactive("print-buffer",
313 "Print the currently loaded page.",
314 function (I) { print_buffer(I.buffer); });
316 function view_partial_source (window, charset, selection) {
318 charset = "charset=" + charset;
319 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
320 "_blank", "scrollbars,resizable,chrome,dialog=no",
321 null, charset, selection, 'selection');
323 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
326 function view_mathml_source (window, charset, target) {
328 charset = "charset=" + charset;
329 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
330 "_blank", "scrollbars,resizable,chrome,dialog=no",
331 null, charset, target, 'mathml');
335 function send_key_as_event (window, element, combo) {
336 var split = unformat_key_combo(combo);
337 var event = window.document.createEvent("KeyboardEvent");
350 return element.dispatchEvent(event);
352 return window.dispatchEvent(event);
356 function ensure_content_focused (buffer) {
357 var foc = buffer.focused_frame_or_null;
359 buffer.top_frame.focus();
361 interactive("ensure-content-focused", "Ensure that the content document has focus.",
362 function (I) { ensure_content_focused(I.buffer); });
365 function network_set_online_status (status) {
366 const io_service = Cc["@mozilla.org/network/io-service;1"]
367 .getService(Ci.nsIIOService2);
369 io_service.manageOfflineStatus = false;
370 io_service.offline = status;
372 interactive("network-go-online", "Work online.",
373 function (I) { network_set_online_status(true); });
375 interactive("network-go-offline", "Work offline.",
376 function (I) { network_set_online_status(false); });
379 interactive("submit-form",
380 "Submit the form to which the focused element belongs.",
382 var el = I.buffer.focused_element.parentNode;
383 while (el && el.tagName != "FORM")
386 var inputs = el.getElementsByTagName("input");
387 for (var i = 0, ilen = inputs.length; i < ilen; i++) {
388 if (inputs[i].getAttribute("type") == "submit")
389 return browser_object_follow(I.buffer, FOLLOW_DEFAULT,
398 * Browser Object Commands
400 interactive("follow", null,
401 alternates(follow, follow_new_buffer, follow_new_window),
402 $browser_object = browser_object_links);
404 interactive("follow-top", null,
405 alternates(follow_current_buffer, follow_current_frame),
406 $browser_object = browser_object_frames,
409 interactive("follow-new-buffer",
410 "Follow a link in a new buffer",
411 alternates(follow_new_buffer, follow_new_window),
412 $browser_object = browser_object_links,
415 interactive("follow-new-buffer-background",
416 "Follow a link in a new buffer in the background",
417 alternates(follow_new_buffer_background, follow_new_window),
418 $browser_object = browser_object_links,
421 interactive("follow-new-window",
422 "Follow a link in a new window",
424 $browser_object = browser_object_links,
427 interactive("find-url", "Open a URL in the current buffer",
428 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
429 $browser_object = browser_object_url);
431 interactive("find-url-new-buffer",
432 "Open a URL in a new buffer",
433 alternates(follow_new_buffer, follow_new_window),
434 $browser_object = browser_object_url,
435 $prompt = "Find url");
437 interactive("find-url-new-window", "Open a URL in a new window",
439 $browser_object = browser_object_url,
440 $prompt = "Find url");
442 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
445 define_browser_object_class("alternate-url", null,
446 function (I, prompt) {
447 check_buffer(I.buffer, content_buffer);
448 var result = yield I.buffer.window.minibuffer.read_url(
450 $initial_value = I.buffer.display_uri_string);
451 yield co_return(result);
453 $prompt = "Find url");
456 interactive("up", "Go to the parent directory of the current URL",
458 $browser_object = browser_object_up_url);
461 "Go to the homepage in the current buffer.", "follow",
462 $browser_object = function () { return homepage; });
464 interactive("make-window",
465 "Make a new window with the homepage.",
467 $browser_object = function () { return homepage; });
469 interactive("focus", null,
471 var element = yield read_browser_object(I);
472 browser_element_focus(I.buffer, element);
474 $browser_object = browser_object_frames);
477 "Save a browser object.",
479 var element = yield read_browser_object(I);
480 var spec = load_spec(element);
482 panel = create_info_panel(I.window, "download-panel",
484 element_get_operation_label(element, "Saving"),
485 load_spec_uri_string(spec)],
486 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
488 var file = yield I.minibuffer.read_file_check_overwrite(
489 $prompt = "Save as:",
490 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
499 $browser_object = browser_object_links);
502 interactive("copy", null,
503 alternates(copy_text, copy_text_append),
504 $browser_object = browser_object_links);
506 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
507 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
508 $browser_object = browser_object_paste_url);
510 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
511 alternates(follow_new_buffer, follow_new_window),
512 $browser_object = browser_object_paste_url);
514 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
516 $browser_object = browser_object_paste_url);
518 interactive("view-source",
519 "Toggle between source and rendered views of a URL.",
520 alternates(view_source, view_source_new_buffer, view_source_new_window),
521 $browser_object = browser_object_frames);
524 interactive("shell-command-on-url",
525 "Run a shell command on the url of a browser object.\n\n"+
526 "If the given shell command contains the string '{}', the "+
527 "url will be substituted in its place, otherwise the url "+
528 "will be added to the end of the command.",
530 var cwd = I.local.cwd;
531 var element = yield read_browser_object(I);
532 var spec = load_spec(element);
533 var uri = load_spec_uri_string(spec);
534 var panel = create_info_panel(I.window, "download-panel",
536 element_get_operation_label(element, "Running on", "URI"),
537 load_spec_uri_string(spec)],
538 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
540 var cmd = yield I.minibuffer.read_shell_command(
542 $initial_value = load_spec_default_shell_command(spec));
546 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
548 $browser_object = browser_object_url,
549 $prompt = "Shell command");
552 interactive("shell-command-on-file",
553 "Download a document to a temporary file and run a shell command on it.",
555 var cwd = I.local.cwd;
556 var element = yield read_browser_object(I);
557 var spec = load_spec(element);
558 var uri = load_spec_uri_string(spec);
559 var panel = create_info_panel(I.window, "download-panel",
561 element_get_operation_label(element, "Running on"),
562 load_spec_uri_string(spec)],
563 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
565 var cmd = yield I.minibuffer.read_shell_command(
567 $initial_value = load_spec_default_shell_command(spec));
571 yield browser_element_shell_command(I.buffer, element, cmd, cwd);
573 $browser_object = browser_object_links,
574 $prompt = "Shell command");
577 interactive("bookmark",
578 "Create a bookmark.",
580 var element = yield read_browser_object(I);
581 var spec = load_spec(element);
582 var uri_string = load_spec_uri_string(spec);
583 var panel = create_info_panel(I.window, "bookmark-panel",
585 element_get_operation_label(element, "Bookmarking"),
588 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
592 add_bookmark(uri_string, title);
593 I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
595 $browser_object = browser_object_frames);
598 interactive("save-page",
599 "Save a document, not including any embedded documents such as images "+
602 check_buffer(I.buffer, content_buffer);
603 var element = yield read_browser_object(I);
604 var spec = load_spec(element);
605 if (!load_spec_document(spec))
606 throw interactive_error("Element is not associated with a document.");
607 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
608 var panel = create_info_panel(I.window, "download-panel",
610 element_get_operation_label(element, "Saving"),
611 load_spec_uri_string(spec)],
612 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
614 var file = yield I.minibuffer.read_file_check_overwrite(
615 $prompt = "Save page as:",
617 $initial_value = suggested_path);
621 save_uri(spec, file, $buffer = I.buffer);
623 $browser_object = browser_object_frames);
626 interactive("save-page-as-text",
627 "Save a page as plain text.",
629 check_buffer(I.buffer, content_buffer);
630 var element = yield read_browser_object(I);
631 var spec = load_spec(element);
633 if (!(doc = load_spec_document(spec)))
634 throw interactive_error("Element is not associated with a document.");
635 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
636 var 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)]]);
642 var file = yield I.minibuffer.read_file_check_overwrite(
643 $prompt = "Save page as text:",
645 $initial_value = suggested_path);
649 save_document_as_text(doc, file, $buffer = I.buffer);
651 $browser_object = browser_object_frames);
654 interactive("save-page-complete",
655 "Save a page and all supporting documents, including images, css, "+
656 "and child frame documents.",
658 check_buffer(I.buffer, content_buffer);
659 var element = yield read_browser_object(I);
660 var spec = load_spec(element);
662 if (!(doc = load_spec_document(spec)))
663 throw interactive_error("Element is not associated with a document.");
664 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
665 var panel = create_info_panel(I.window, "download-panel",
667 element_get_operation_label(element, "Saving complete"),
668 load_spec_uri_string(spec)],
669 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
671 var file = yield I.minibuffer.read_file_check_overwrite(
672 $prompt = "Save page complete:",
674 $initial_value = suggested_path);
675 // FIXME: use proper read function
676 var dir = yield I.minibuffer.read_file(
677 $prompt = "Data Directory:",
679 $initial_value = file.path + ".support");
683 save_document_complete(doc, file, dir, $buffer = I.buffer);
685 $browser_object = browser_object_frames);
688 function view_as_mime_type (I, target) {
690 var element = yield read_browser_object(I);
691 var spec = load_spec(element);
694 target = FOLLOW_CURRENT_FRAME;
696 if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
697 throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
699 var mime_type = load_spec_mime_type(spec);
700 var panel = create_info_panel(I.window, "download-panel",
702 element_get_operation_label(element, "View in browser"),
703 load_spec_uri_string(spec)],
704 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
706 let suggested_type = mime_type;
707 if (viewable_mime_type_list.indexOf(suggested_type) == -1)
708 suggested_type = "text/plain";
709 mime_type = yield I.minibuffer.read_viewable_mime_type(
710 $prompt = "View internally as",
711 $initial_value = suggested_type,
713 yield override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
714 browser_object_follow(I.buffer, target, spec);
720 function view_as_mime_type_new_buffer (I) {
721 yield view_as_mime_type(I, OPEN_NEW_BUFFER);
724 function view_as_mime_type_new_window (I) {
725 yield view_as_mime_type(I, OPEN_NEW_WINDOW);
728 interactive("view-as-mime-type",
729 "Display a browser object in the browser using the specified MIME type.",
730 alternates(view_as_mime_type,
731 view_as_mime_type_new_buffer,
732 view_as_mime_type_new_window),
733 $browser_object = browser_object_frames);
736 interactive("delete",
737 "Delete a DOM node, given as a browser object.",
739 var elem = yield read_browser_object(I);
740 if (! (elem instanceof Ci.nsIDOMNode))
741 throw interactive_error("Cannot delete item");
742 elem.parentNode.removeChild(elem);
744 $browser_object = browser_object_dom_node);
747 interactive("charset-prefix",
748 "A prefix command that prompts for a charset to use in a "+
749 "subsequent navigation command.",
751 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
752 .getService(Ci.nsICharsetConverterManager);
753 var decoders = ccman.getDecoderList()
755 while (decoders.hasMore())
756 charsets.push(decoders.getNext());
757 I.forced_charset = yield I.minibuffer.read(
758 $prompt = "Charset:",
759 $completer = new prefix_completer(
760 $completions = charsets,
761 $get_string = function (x) x.toLowerCase()),
768 interactive("reload-with-charset",
769 "Prompt for a charset, and reload the current page, forcing use "+
772 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
773 .getService(Ci.nsICharsetConverterManager);
774 var decoders = ccman.getDecoderList()
776 while (decoders.hasMore())
777 charsets.push(decoders.getNext());
778 var forced_charset = yield I.minibuffer.read(
779 $prompt = "Charset:",
780 $completer = new prefix_completer(
781 $completions = charsets,
782 $get_string = function (x) x.toLowerCase()),
785 reload(I.buffer, false, null, forced_charset);
790 "Paste the contents of the clipboard",
792 call_builtin_command(I.window, "cmd_paste", true);
795 interactive("kill-region",
796 "Kill (\"cut\") the selected text.",
798 call_builtin_command(I.window, "cmd_cut", true);
801 interactive("kill-ring-save",
802 "Save the region as if killed, but don't kill it.",
804 call_builtin_command(I.window, "cmd_copy", true);
807 interactive("password-manager",
808 "Open the password manager.",
809 "find-url-new-buffer",
810 $browser_object = "chrome://passwordmgr/content/passwordManager.xul");
813 interactive("toggle-full-screen",
814 "Toggle full screen mode for the current window.",
816 window_set_full_screen(I.window);
817 if (I.window.fullScreen)
818 I.minibuffer.message("Fullscreen mode on");
820 I.minibuffer.message("Fullscreen mode off");
824 interactive("image-toggle-zoom-to-fit",
825 "Toggle zoom-to-fit (viewport) on an image document.",
828 var doc = I.buffer.document
829 .QueryInterface(Ci.nsIImageDocument);
830 doc.toggleImageSize();
831 zoom_hook.run(I.buffer);
833 I.minibuffer.message("Not an image document");