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);
29 function show_conkeror_version (window) {
30 var xulrunner_version = Cc['@mozilla.org/xre/app-info;1']
31 .getService(Ci.nsIXULAppInfo)
33 window.minibuffer.message("Conkeror "+conkeror.version+
34 " (XULRunner "+xulrunner_version+
37 interactive("conkeror-version",
38 "Show version information for Conkeror.",
39 function (I) { show_conkeror_version(I.window); });
40 interactive("version",
41 "Show version information for Conkeror.",
44 /* FIXME: maybe this should be supported for non-browser buffers */
45 function scroll_horiz_complete (buffer, n) {
46 var w = buffer.focused_frame;
47 w.scrollTo (n > 0 ? w.scrollMaxX : 0, w.scrollY);
49 interactive("scroll-beginning-of-line",
50 "Scroll the current frame all the way to the left.",
51 function (I) { scroll_horiz_complete(I.buffer, -1); });
53 interactive("scroll-end-of-line",
54 "Scroll the current frame all the way to the right.",
55 function (I) { scroll_horiz_complete(I.buffer, 1); });
57 interactive("scroll-top-left",
58 "Scroll the current frame all the way to the top left",
59 function (I) { I.buffer.do_command("cmd_scrollTop");
60 scroll_horiz_complete(I.buffer, -1); });
63 function delete_window (window) {
64 window.window.close();
66 interactive("delete-window",
67 "Delete the current window.",
68 function (I) { delete_window(I.window); });
70 interactive("jsconsole",
71 "Open the JavaScript console.",
72 "find-url-new-buffer",
73 $browser_object = "chrome://global/content/console.xul");
76 function paste_x_primary_selection (field) {
77 modify_region(field, function (str) read_from_x_primary_selection());
79 interactive("paste-x-primary-selection",
80 "Insert the contents of the X primary selection into the selected field or "+
81 "minibuffer. Deactivates the region if it is active, and leaves the point "+
82 "after the inserted text.",
83 function (I) call_on_focused_field(I, paste_x_primary_selection, true));
86 function open_line (field) {
87 modify_region(field, function() ["\n", 0]);
89 interactive("open-line",
90 "If there is an active region, replace is with a newline, otherwise just "+
91 "insert a newline. In both cases leave point before the inserted newline.",
92 function (I) call_on_focused_field(I, open_line, true));
95 interactive("insert-parentheses",
96 "Insert a pair of parentheses, or surround the currently selected text "+
97 "with a pair of parentheses.",
99 call_on_focused_field(I, function (field) {
102 return ["("+str+")", (str ? str.length+2 : 1)];
108 function transpose_chars (field) {
109 var value = field.value;
110 var caret = field.selectionStart; // Caret position.
111 var length = value.length;
113 // If we have less than two character in the field or if we are at the
114 // beginning of the field, do nothing.
115 if (length < 2 || caret == 0)
118 // If we are at the end of the field, switch places on the two last
119 // characters. TODO: This should happen at the end of every line, not only
120 // at the end of the field.
124 // Do the transposing.
125 field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
127 // Increment the caret position. If this is not done, the caret is left at
128 // the end of the field as a result of the replacing of contents.
129 field.selectionStart = caret + 1;
130 field.selectionEnd = caret + 1;
132 interactive("transpose-chars",
133 "Interchange characters around point, moving forward one character.",
134 function (I) call_on_focused_field(I, transpose_chars, true));
137 interactive("execute-extended-command",
138 "Call a command specified in the minibuffer.",
141 var boc = I.browser_object;
144 prompt = I.key_sequence.join(" ");
146 prompt += ' ['+boc.name+']';
147 if (prefix !== null && prefix !== undefined) {
148 if (typeof prefix == "object")
149 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
151 prompt += " "+prefix;
153 var command = yield I.minibuffer.read_command($prompt = prompt);
154 call_after_timeout(function () {
155 input_handle_command.call(I.window, new command_event(command));
161 /// built in commands
162 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
164 // Performs a command on a browser buffer content area
167 define_builtin_commands(
169 function (I, command) {
170 var buffer = I.buffer;
172 buffer.do_command(command);
174 /* Ignore exceptions */
178 I.buffer.mark_active = !I.buffer.mark_active;
180 function (I) I.buffer.mark_active,
184 define_builtin_commands(
186 function (I, command) {
187 var buffer = I.buffer;
189 buffer.do_command(command);
191 /* Ignore exceptions */
195 I.buffer.mark_active = !I.buffer.mark_active;
197 function (I) I.buffer.mark_active,
200 function get_link_text () {
201 var e = document.commandDispatcher.focusedElement;
202 if (e && e.getAttribute("href")) {
203 return e.getAttribute("href");
210 function copy_email_address (loc)
212 // Copy the comma-separated list of email addresses only.
213 // There are other ways of embedding email addresses in a mailto:
214 // link, but such complex parsing is beyond us.
215 var qmark = loc.indexOf( "?" );
218 if ( qmark > 7 ) { // 7 == length of "mailto:"
219 addresses = loc.substring( 7, qmark );
221 addresses = loc.substr( 7 );
224 //XXX: the original code, which we got from firefox, unescapes the string
225 // using the current character set. To do this in conkeror, we
226 // *should* use an interactive method that gives us the character set,
227 // rather than fetching it by side-effect.
229 // // Let's try to unescape it using a character set
230 // // in case the address is not ASCII.
232 // var characterSet = this.target.ownerDocument.characterSet;
233 // const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
234 // .getService(Components.interfaces.nsITextToSubURI);
235 // addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
241 writeToClipboard(addresses);
242 message("Copied '" + addresses + "'");
244 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
247 /* FIXME: fix this command */
249 interactive("source",
250 "Load a JavaScript file.",
251 function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
253 function reinit (window) {
257 window.minibuffer.message("Loaded: " + path);
259 window.minibuffer.message("Failed to load: "+path);
263 interactive("reinit",
264 "Reload the Conkeror rc file.",
265 function (I) { reinit(I.window); });
267 interactive("help-page", "Open the Conkeror help page.",
268 "find-url-new-buffer",
269 $browser_object = "chrome://conkeror-help/content/help.html");
271 interactive("tutorial", "Open the Conkeror tutorial.",
272 "find-url-new-buffer",
273 $browser_object = "chrome://conkeror-help/content/tutorial.html");
275 function univ_arg_to_number (prefix, default_value) {
276 if (prefix == null) {
277 if (default_value == null)
280 return default_value;
282 if (typeof prefix == "object")
287 function eval_expression (window, s) {
288 // eval in the global scope.
290 // In addition, the following variables are available:
292 var buffer = window.buffers.current;
293 var result = eval(s);
294 if (result !== undefined) {
295 window.minibuffer.message(String(result));
298 interactive("eval-expression",
299 "Evaluate JavaScript statements.",
303 (yield I.minibuffer.read($prompt = "Eval:",
304 $history = "eval-expression",
305 $completer = javascript_completer(I.buffer))));
308 function show_extension_manager () {
309 return conkeror.window_watcher.openWindow(
311 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
313 "resizable=yes,dialog=no",
316 interactive("extensions",
317 "Open the extensions manager in a new window.",
318 show_extension_manager);
320 function print_buffer (buffer) {
321 buffer.top_frame.print();
324 interactive("print-buffer",
325 "Print the currently loaded page.",
326 function (I) { print_buffer(I.buffer); });
328 function view_partial_source (window, charset, selection) {
330 charset = "charset=" + charset;
331 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
332 "_blank", "scrollbars,resizable,chrome,dialog=no",
333 null, charset, selection, 'selection');
335 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
338 function view_mathml_source (window, charset, target) {
340 charset = "charset=" + charset;
341 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
342 "_blank", "scrollbars,resizable,chrome,dialog=no",
343 null, charset, target, 'mathml');
347 function send_key_as_event (window, element, combo) {
348 var split = unformat_key_combo(combo);
349 var event = window.document.createEvent("KeyboardEvent");
362 return element.dispatchEvent(event);
364 return window.dispatchEvent(event);
369 function ensure_content_focused (buffer) {
370 var foc = buffer.focused_frame_or_null;
372 buffer.top_frame.focus();
374 interactive("ensure-content-focused", "Ensure that the content document has focus.",
375 function (I) { ensure_content_focused(I.buffer); });
378 function network_set_online_status (status) {
379 const io_service = Cc["@mozilla.org/network/io-service;1"]
380 .getService(Ci.nsIIOService2);
382 io_service.manageOfflineStatus = false;
383 io_service.offline = status;
385 interactive("network-go-online", "Work online.",
386 function (I) { network_set_online_status(true); });
388 interactive("network-go-offline", "Work offline.",
389 function (I) { network_set_online_status(false); });
392 interactive("submit-form",
393 "Submit the form to which the focused element belongs.",
395 var el = I.buffer.focused_element.parentNode;
396 while (el && el.tagName != "FORM")
399 var inputs = el.getElementsByTagName("input");
400 for (var i = 0, ilen = inputs.length; i < ilen; i++) {
401 if (inputs[i].getAttribute("type") == "submit")
402 return browser_object_follow(I.buffer, FOLLOW_DEFAULT,
411 * Browser Object Commands
413 interactive("follow", null,
414 alternates(follow, follow_new_buffer, follow_new_window),
415 $browser_object = browser_object_links);
417 interactive("follow-top", null,
418 alternates(follow_current_buffer, follow_current_frame),
419 $browser_object = browser_object_frames,
422 interactive("follow-new-buffer",
423 "Follow a link in a new buffer",
424 alternates(follow_new_buffer, follow_new_window),
425 $browser_object = browser_object_links,
428 interactive("follow-new-buffer-background",
429 "Follow a link in a new buffer in the background",
430 alternates(follow_new_buffer_background, follow_new_window),
431 $browser_object = browser_object_links,
434 interactive("follow-new-window",
435 "Follow a link in a new window",
437 $browser_object = browser_object_links,
440 interactive("find-url", "Open a URL in the current buffer",
441 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
442 $browser_object = browser_object_url);
444 interactive("find-url-new-buffer",
445 "Open a URL in a new buffer",
446 alternates(follow_new_buffer, follow_new_window),
447 $browser_object = browser_object_url,
448 $prompt = "Find url");
450 interactive("find-url-new-window", "Open a URL in a new window",
452 $browser_object = browser_object_url,
453 $prompt = "Find url");
455 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
458 define_browser_object_class("alternate-url", null,
459 function (I, prompt) {
460 check_buffer(I.buffer, content_buffer);
461 var result = yield I.buffer.window.minibuffer.read_url(
463 $initial_value = I.buffer.display_uri_string);
464 yield co_return(result);
466 $prompt = "Find url");
469 interactive("up", "Go to the parent directory of the current URL",
471 $browser_object = browser_object_up_url);
474 "Go to the homepage in the current buffer.", "follow",
475 $browser_object = function () { return homepage; });
477 interactive("make-window",
478 "Make a new window with the homepage.",
480 $browser_object = function () { return homepage; });
482 interactive("focus", null,
484 var element = yield read_browser_object(I);
485 browser_element_focus(I.buffer, element);
487 $browser_object = browser_object_frames);
490 "Save a browser object.",
492 var element = yield read_browser_object(I);
493 var spec = load_spec(element);
495 panel = create_info_panel(I.window, "download-panel",
497 element_get_operation_label(element, "Saving"),
498 load_spec_uri_string(spec)],
499 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
501 var file = yield I.minibuffer.read_file_check_overwrite(
502 $prompt = "Save as:",
503 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
512 $browser_object = browser_object_links);
515 interactive("copy", null,
517 var element = yield read_browser_object(I);
518 browser_element_copy(I.buffer, element);
520 $browser_object = browser_object_links);
522 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
523 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
524 $browser_object = browser_object_paste_url);
526 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
527 alternates(follow_new_buffer, follow_new_window),
528 $browser_object = browser_object_paste_url);
530 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
532 $browser_object = browser_object_paste_url);
534 interactive("view-source",
535 "Toggle between source and rendered views of a URL.",
536 alternates(view_source, view_source_new_buffer, view_source_new_window),
537 $browser_object = browser_object_frames);
540 interactive("shell-command-on-url",
541 "Run a shell command on the url of a browser object.",
543 var cwd = I.local.cwd;
544 var element = yield read_browser_object(I);
545 var spec = load_spec(element);
546 var uri = load_spec_uri_string(spec);
548 panel = create_info_panel(I.window, "download-panel",
550 element_get_operation_label(element, "Running on", "URI"),
551 load_spec_uri_string(spec)],
552 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
554 var cmd = yield I.minibuffer.read_shell_command(
556 $initial_value = load_spec_default_shell_command(spec));
560 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
562 $browser_object = browser_object_url,
563 $prompt = "Shell command");
566 interactive("shell-command-on-file",
567 "Download a document to a temporary file and run a shell command on it.",
569 var cwd = I.local.cwd;
570 var element = yield read_browser_object(I);
571 var spec = load_spec(element);
572 var uri = load_spec_uri_string(spec);
574 panel = create_info_panel(I.window, "download-panel",
576 element_get_operation_label(element, "Running on"),
577 load_spec_uri_string(spec)],
578 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
580 var cmd = yield I.minibuffer.read_shell_command(
582 $initial_value = load_spec_default_shell_command(spec));
586 yield browser_element_shell_command(I.buffer, element, cmd, cwd);
588 $browser_object = browser_object_links,
589 $prompt = "Shell command");
592 interactive("bookmark",
593 "Create a bookmark.",
595 var element = yield read_browser_object(I);
596 var spec = load_spec(element);
597 var uri_string = load_spec_uri_string(spec);
599 panel = create_info_panel(I.window, "bookmark-panel",
601 element_get_operation_label(element, "Bookmarking"),
604 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
608 add_bookmark(uri_string, title);
609 I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
611 $browser_object = browser_object_frames);
614 interactive("save-page",
615 "Save a document, not including any embedded documents such as images "+
618 check_buffer(I.buffer, content_buffer);
619 var element = yield read_browser_object(I);
620 var spec = load_spec(element);
621 if (!load_spec_document(spec))
622 throw interactive_error("Element is not associated with a document.");
623 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
626 panel = create_info_panel(I.window, "download-panel",
628 element_get_operation_label(element, "Saving"),
629 load_spec_uri_string(spec)],
630 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
633 var file = yield I.minibuffer.read_file_check_overwrite(
634 $prompt = "Save page as:",
636 $initial_value = suggested_path);
641 save_uri(spec, file, $buffer = I.buffer);
643 $browser_object = browser_object_frames);
646 interactive("save-page-as-text",
647 "Save a page as plain text.",
649 check_buffer(I.buffer, content_buffer);
650 var element = yield read_browser_object(I);
651 var spec = load_spec(element);
653 if (!(doc = load_spec_document(spec)))
654 throw interactive_error("Element is not associated with a document.");
655 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
658 panel = create_info_panel(I.window, "download-panel",
660 element_get_operation_label(element, "Saving", "as text"),
661 load_spec_uri_string(spec)],
662 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
665 var file = yield I.minibuffer.read_file_check_overwrite(
666 $prompt = "Save page as text:",
668 $initial_value = suggested_path);
673 save_document_as_text(doc, file, $buffer = I.buffer);
675 $browser_object = browser_object_frames);
678 interactive("save-page-complete",
679 "Save a page and all supporting documents, including images, css, "+
680 "and child frame documents.",
682 check_buffer(I.buffer, content_buffer);
683 var element = yield read_browser_object(I);
684 var spec = load_spec(element);
686 if (!(doc = load_spec_document(spec)))
687 throw interactive_error("Element is not associated with a document.");
688 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
691 panel = create_info_panel(I.window, "download-panel",
693 element_get_operation_label(element, "Saving complete"),
694 load_spec_uri_string(spec)],
695 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
698 var file = yield I.minibuffer.read_file_check_overwrite(
699 $prompt = "Save page complete:",
701 $initial_value = suggested_path);
702 // FIXME: use proper read function
703 var dir = yield I.minibuffer.read_file(
704 $prompt = "Data Directory:",
706 $initial_value = file.path + ".support");
711 save_document_complete(doc, file, dir, $buffer = I.buffer);
713 $browser_object = browser_object_frames);
716 function view_as_mime_type (I, target) {
718 var element = yield read_browser_object(I);
719 var spec = load_spec(element);
722 target = FOLLOW_CURRENT_FRAME;
724 if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
725 throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
729 var mime_type = load_spec_mime_type(spec);
730 panel = create_info_panel(I.window, "download-panel",
732 element_get_operation_label(element, "View in browser"),
733 load_spec_uri_string(spec)],
734 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
738 let suggested_type = mime_type;
739 if (viewable_mime_type_list.indexOf(suggested_type) == -1)
740 suggested_type = "text/plain";
741 mime_type = yield I.minibuffer.read_viewable_mime_type(
742 $prompt = "View internally as",
743 $initial_value = suggested_type,
745 override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
746 browser_object_follow(I.buffer, target, spec);
752 function view_as_mime_type_new_buffer (I) {
753 yield view_as_mime_type(I, OPEN_NEW_BUFFER);
756 function view_as_mime_type_new_window (I) {
757 yield view_as_mime_type(I, OPEN_NEW_WINDOW);
760 interactive("view-as-mime-type",
761 "Display a browser object in the browser using the specified MIME type.",
762 alternates(view_as_mime_type,
763 view_as_mime_type_new_buffer,
764 view_as_mime_type_new_window),
765 $browser_object = browser_object_frames);
768 interactive("delete",
769 "Delete a DOM node, given as a browser object.",
771 var elem = yield read_browser_object(I);
772 elem.parentNode.removeChild(elem);
774 $browser_object = browser_object_dom_node);
777 interactive("charset-prefix",
778 "A prefix command that prompts for a charset to use in a "+
779 "subsequent navigation command.",
781 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
782 .getService(Ci.nsICharsetConverterManager);
783 var decoders = ccman.getDecoderList()
785 while (decoders.hasMore())
786 charsets.push(decoders.getNext());
787 I.forced_charset = yield I.minibuffer.read(
788 $prompt = "Charset:",
789 $completer = prefix_completer(
790 $completions = charsets,
791 $get_string = function (x) x.toLowerCase()),
797 interactive("reload-with-charset",
798 "Prompt for a charset, and reload the current page, forcing use "+
801 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
802 .getService(Ci.nsICharsetConverterManager);
803 var decoders = ccman.getDecoderList()
805 while (decoders.hasMore())
806 charsets.push(decoders.getNext());
807 var forced_charset = yield I.minibuffer.read(
808 $prompt = "Charset:",
809 $completer = prefix_completer(
810 $completions = charsets,
811 $get_string = function (x) x.toLowerCase()),
813 reload(I.buffer, false, null, forced_charset);
818 "Paste the contents of the clipboard",
820 I.buffer.mark_active = false;
821 I.buffer.do_command("cmd_paste");
824 interactive("kill-region",
825 "Kill (\"cut\") the selected text.",
827 I.buffer.mark_active = false;
828 I.buffer.do_command("cmd_cut");
831 interactive("kill-ring-save",
832 "Save the region as if killed, but don't kill it.",
834 I.buffer.mark_active = false;
835 I.buffer.do_command("cmd_copy");