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");
77 * Given a callback func and an interactive context I, call func, passing
78 * either a focused field, or the minibuffer's input element if the
79 * minibuffer is active. Afterward, call `scroll_selection_into_view' on
80 * the field. See `paste_x_primary_selection' and `open_line' for
83 function call_on_focused_field (I, func) {
84 var m = I.window.minibuffer;
85 var s = m.current_state;
86 if (m._input_mode_enabled) {
87 m._restore_normal_state();
88 var e = m.input_element;
90 var e = I.buffer.focused_element;
92 scroll_selection_into_view(e);
93 if (s && s.handle_input)
99 * Replace the current region with modifier(selection). Deactivates region and
100 * sets point to the end of the inserted text, unless keep_point is true, in
101 * which case the point will be left at the beginning of the inserted text.
103 function modify_region (field, modifier, keep_point) {
104 if (field.getAttribute("contenteditable") == 'true') {
106 var doc = field.ownerDocument;
107 var win = doc.defaultView;
108 doc.execCommand("insertHTML", false,
109 html_escape(modifier(win.getSelection().toString()))
110 .replace(/\n/g, '<br>')
111 .replace(/ /g, ' '));
115 modifier(field.value.substring(field.selectionStart, field.selectionEnd+1));
116 var point = field.selectionStart;
118 field.value.substr(0, field.selectionStart) + replacement +
119 field.value.substr(field.selectionEnd);
120 if (!keep_point) point += replacement.length;
121 field.setSelectionRange(point, point);
126 function paste_x_primary_selection (field) {
127 modify_region(field, function (str) read_from_x_primary_selection());
129 interactive("paste-x-primary-selection",
130 "Insert the contents of the X primary selection into the selected field or "+
131 "minibuffer. Deactivates the region if it is active, and leaves the point "+
132 "after the inserted text.",
133 function (I) call_on_focused_field(I, paste_x_primary_selection));
136 function open_line (field) {
137 modify_region(field, function() "\n", true);
139 interactive("open-line",
140 "If there is an active region, replace is with a newline, otherwise just "+
141 "insert a newline. In both cases leave point before the inserted newline.",
142 function (I) call_on_focused_field(I, open_line));
145 function transpose_chars (field) {
146 var value = field.value;
147 var caret = field.selectionStart; // Caret position.
148 var length = value.length;
150 // If we have less than two character in the field or if we are at the
151 // beginning of the field, do nothing.
152 if (length < 2 || caret == 0)
155 // If we are at the end of the field, switch places on the two last
156 // characters. TODO: This should happen at the end of every line, not only
157 // at the end of the field.
161 // Do the transposing.
162 field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
164 // Increment the caret position. If this is not done, the caret is left at
165 // the end of the field as a result of the replacing of contents.
166 field.selectionStart = caret + 1;
167 field.selectionEnd = caret + 1;
169 interactive("transpose-chars",
170 "Interchange characters around point, moving forward one character.",
171 function (I) call_on_focused_field(I, transpose_chars));
174 interactive("execute-extended-command",
175 "Call a command specified in the minibuffer.",
178 var boc = I.browser_object;
181 prompt = I.key_sequence.join(" ");
183 prompt += ' ['+boc.name+']';
184 if (prefix !== null && prefix !== undefined) {
185 if (typeof prefix == "object")
186 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
188 prompt += " "+prefix;
190 var command = yield I.minibuffer.read_command($prompt = prompt);
191 call_after_timeout(function () {
192 input_handle_command.call(I.window, new command_event(command));
198 /// built in commands
199 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
201 // Performs a command on a browser buffer content area
204 define_builtin_commands(
206 function (I, command) {
207 var buffer = I.buffer;
209 buffer.do_command(command);
211 /* Ignore exceptions */
215 I.buffer.mark_active = !I.buffer.mark_active;
217 function (I) I.buffer.mark_active,
221 define_builtin_commands(
223 function (I, command) {
224 var buffer = I.buffer;
226 buffer.do_command(command);
228 /* Ignore exceptions */
232 I.buffer.mark_active = !I.buffer.mark_active;
234 function (I) I.buffer.mark_active,
237 function get_link_text () {
238 var e = document.commandDispatcher.focusedElement;
239 if (e && e.getAttribute("href")) {
240 return e.getAttribute("href");
247 function copy_email_address (loc)
249 // Copy the comma-separated list of email addresses only.
250 // There are other ways of embedding email addresses in a mailto:
251 // link, but such complex parsing is beyond us.
252 var qmark = loc.indexOf( "?" );
255 if ( qmark > 7 ) { // 7 == length of "mailto:"
256 addresses = loc.substring( 7, qmark );
258 addresses = loc.substr( 7 );
261 //XXX: the original code, which we got from firefox, unescapes the string
262 // using the current character set. To do this in conkeror, we
263 // *should* use an interactive method that gives us the character set,
264 // rather than fetching it by side-effect.
266 // // Let's try to unescape it using a character set
267 // // in case the address is not ASCII.
269 // var characterSet = this.target.ownerDocument.characterSet;
270 // const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
271 // .getService(Components.interfaces.nsITextToSubURI);
272 // addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
278 writeToClipboard(addresses);
279 message("Copied '" + addresses + "'");
281 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
284 /* FIXME: fix this command */
286 interactive("source",
287 "Load a JavaScript file.",
288 function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
290 function reinit (window) {
294 window.minibuffer.message("Loaded: " + path);
296 window.minibuffer.message("Failed to load: "+path);
300 interactive("reinit",
301 "Reload the Conkeror rc file.",
302 function (I) { reinit(I.window); });
304 interactive("help-page", "Open the Conkeror help page.",
305 "find-url-new-buffer",
306 $browser_object = "chrome://conkeror-help/content/help.html");
308 interactive("tutorial", "Open the Conkeror tutorial.",
309 "find-url-new-buffer",
310 $browser_object = "chrome://conkeror-help/content/tutorial.html");
312 function univ_arg_to_number (prefix, default_value) {
313 if (prefix == null) {
314 if (default_value == null)
317 return default_value;
319 if (typeof prefix == "object")
324 function eval_expression (window, s) {
325 // eval in the global scope.
327 // In addition, the following variables are available:
329 var buffer = window.buffers.current;
330 var result = eval(s);
331 if (result !== undefined) {
332 window.minibuffer.message(String(result));
335 interactive("eval-expression",
336 "Evaluate JavaScript statements.",
340 (yield I.minibuffer.read($prompt = "Eval:",
341 $history = "eval-expression",
342 $completer = javascript_completer(I.buffer))));
345 function show_extension_manager () {
346 return conkeror.window_watcher.openWindow(
348 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
350 "resizable=yes,dialog=no",
353 interactive("extensions",
354 "Open the extensions manager in a new window.",
355 show_extension_manager);
357 function print_buffer (buffer) {
358 buffer.top_frame.print();
361 interactive("print-buffer",
362 "Print the currently loaded page.",
363 function (I) { print_buffer(I.buffer); });
365 function view_partial_source (window, charset, selection) {
367 charset = "charset=" + charset;
368 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
369 "_blank", "scrollbars,resizable,chrome,dialog=no",
370 null, charset, selection, 'selection');
372 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
375 function view_mathml_source (window, charset, target) {
377 charset = "charset=" + charset;
378 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
379 "_blank", "scrollbars,resizable,chrome,dialog=no",
380 null, charset, target, 'mathml');
384 function send_key_as_event (window, element, combo) {
385 var split = unformat_key_combo(combo);
386 var event = window.document.createEvent("KeyboardEvent");
399 return element.dispatchEvent(event);
401 return window.dispatchEvent(event);
406 function ensure_content_focused (buffer) {
407 var foc = buffer.focused_frame_or_null;
409 buffer.top_frame.focus();
411 interactive("ensure-content-focused", "Ensure that the content document has focus.",
412 function (I) { ensure_content_focused(I.buffer); });
415 function network_set_online_status (status) {
416 const io_service = Cc["@mozilla.org/network/io-service;1"]
417 .getService(Ci.nsIIOService2);
419 io_service.manageOfflineStatus = false;
420 io_service.offline = status;
422 interactive("network-go-online", "Work online.",
423 function (I) { network_set_online_status(true); });
425 interactive("network-go-offline", "Work offline.",
426 function (I) { network_set_online_status(false); });
429 interactive("submit-form",
430 "Submit the form to which the focused element belongs.",
432 var el = I.buffer.focused_element.parentNode;
433 while (el && el.tagName != "FORM")
436 var inputs = el.getElementsByTagName("input");
437 for (var i = 0, ilen = inputs.length; i < ilen; i++) {
438 if (inputs[i].getAttribute("type") == "submit")
439 return browser_object_follow(I.buffer, FOLLOW_DEFAULT,
448 * Browser Object Commands
450 interactive("follow", null,
451 alternates(follow, follow_new_buffer, follow_new_window),
452 $browser_object = browser_object_links);
454 interactive("follow-top", null,
455 alternates(follow_current_buffer, follow_current_frame),
456 $browser_object = browser_object_frames,
459 interactive("follow-new-buffer",
460 "Follow a link in a new buffer",
461 alternates(follow_new_buffer, follow_new_window),
462 $browser_object = browser_object_links,
465 interactive("follow-new-buffer-background",
466 "Follow a link in a new buffer in the background",
467 alternates(follow_new_buffer_background, follow_new_window),
468 $browser_object = browser_object_links,
471 interactive("follow-new-window",
472 "Follow a link in a new window",
474 $browser_object = browser_object_links,
477 interactive("find-url", "Open a URL in the current buffer",
478 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
479 $browser_object = browser_object_url);
481 interactive("find-url-new-buffer",
482 "Open a URL in a new buffer",
483 alternates(follow_new_buffer, follow_new_window),
484 $browser_object = browser_object_url,
485 $prompt = "Find url");
487 interactive("find-url-new-window", "Open a URL in a new window",
489 $browser_object = browser_object_url,
490 $prompt = "Find url");
492 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
495 define_browser_object_class("alternate-url", null,
496 function (I, prompt) {
497 check_buffer(I.buffer, content_buffer);
498 var result = yield I.buffer.window.minibuffer.read_url(
500 $initial_value = I.buffer.display_uri_string);
501 yield co_return(result);
503 $prompt = "Find url");
506 interactive("up", "Go to the parent directory of the current URL",
508 $browser_object = browser_object_up_url);
511 "Go to the homepage in the current buffer.", "follow",
512 $browser_object = function () { return homepage; });
514 interactive("make-window",
515 "Make a new window with the homepage.",
517 $browser_object = function () { return homepage; });
519 interactive("focus", null,
521 var element = yield read_browser_object(I);
522 browser_element_focus(I.buffer, element);
524 $browser_object = browser_object_frames);
527 "Save a browser object.",
529 var element = yield read_browser_object(I);
530 var spec = load_spec(element);
532 panel = create_info_panel(I.window, "download-panel",
534 element_get_operation_label(element, "Saving"),
535 load_spec_uri_string(spec)],
536 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
538 var file = yield I.minibuffer.read_file_check_overwrite(
539 $prompt = "Save as:",
540 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
549 $browser_object = browser_object_links);
552 interactive("copy", null,
554 var element = yield read_browser_object(I);
555 browser_element_copy(I.buffer, element);
557 $browser_object = browser_object_links);
559 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
560 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
561 $browser_object = browser_object_paste_url);
563 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
564 alternates(follow_new_buffer, follow_new_window),
565 $browser_object = browser_object_paste_url);
567 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
569 $browser_object = browser_object_paste_url);
571 interactive("view-source", null,
572 alternates(view_source, view_source_new_buffer, view_source_new_window),
573 $browser_object = browser_object_frames);
576 interactive("shell-command-on-url",
577 "Run a shell command on the url of a browser object.",
579 var cwd = I.local.cwd;
580 var element = yield read_browser_object(I);
581 var spec = load_spec(element);
582 var uri = load_spec_uri_string(spec);
584 panel = create_info_panel(I.window, "download-panel",
586 element_get_operation_label(element, "Running on", "URI"),
587 load_spec_uri_string(spec)],
588 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
590 var cmd = yield I.minibuffer.read_shell_command(
592 $initial_value = load_spec_default_shell_command(spec));
596 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
598 $browser_object = browser_object_url,
599 $prompt = "Shell command");
602 interactive("shell-command-on-file",
603 "Download a document to a temporary file and run a shell command on it.",
605 var cwd = I.local.cwd;
606 var element = yield read_browser_object(I);
607 var spec = load_spec(element);
608 var uri = load_spec_uri_string(spec);
610 panel = create_info_panel(I.window, "download-panel",
612 element_get_operation_label(element, "Running on"),
613 load_spec_uri_string(spec)],
614 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
616 var cmd = yield I.minibuffer.read_shell_command(
618 $initial_value = load_spec_default_shell_command(spec));
622 yield browser_element_shell_command(I.buffer, element, cmd, cwd);
624 $browser_object = browser_object_links,
625 $prompt = "Shell command");
628 interactive("bookmark",
629 "Create a bookmark.",
631 var element = yield read_browser_object(I);
632 var spec = load_spec(element);
633 var uri_string = load_spec_uri_string(spec);
635 panel = create_info_panel(I.window, "bookmark-panel",
637 element_get_operation_label(element, "Bookmarking"),
640 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
644 add_bookmark(uri_string, title);
645 I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
647 $browser_object = browser_object_frames);
650 interactive("save-page",
651 "Save a document, not including any embedded documents such as images "+
654 check_buffer(I.buffer, content_buffer);
655 var element = yield read_browser_object(I);
656 var spec = load_spec(element);
657 if (!load_spec_document(spec))
658 throw interactive_error("Element is not associated with a document.");
659 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
662 panel = create_info_panel(I.window, "download-panel",
664 element_get_operation_label(element, "Saving"),
665 load_spec_uri_string(spec)],
666 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
669 var file = yield I.minibuffer.read_file_check_overwrite(
670 $prompt = "Save page as:",
672 $initial_value = suggested_path);
677 save_uri(spec, file, $buffer = I.buffer);
679 $browser_object = browser_object_frames);
682 interactive("save-page-as-text",
683 "Save a page as plain text.",
685 check_buffer(I.buffer, content_buffer);
686 var element = yield read_browser_object(I);
687 var spec = load_spec(element);
689 if (!(doc = load_spec_document(spec)))
690 throw interactive_error("Element is not associated with a document.");
691 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
694 panel = create_info_panel(I.window, "download-panel",
696 element_get_operation_label(element, "Saving", "as text"),
697 load_spec_uri_string(spec)],
698 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
701 var file = yield I.minibuffer.read_file_check_overwrite(
702 $prompt = "Save page as text:",
704 $initial_value = suggested_path);
709 save_document_as_text(doc, file, $buffer = I.buffer);
711 $browser_object = browser_object_frames);
714 interactive("save-page-complete",
715 "Save a page and all supporting documents, including images, css, "+
716 "and child frame documents.",
718 check_buffer(I.buffer, content_buffer);
719 var element = yield read_browser_object(I);
720 var spec = load_spec(element);
722 if (!(doc = load_spec_document(spec)))
723 throw interactive_error("Element is not associated with a document.");
724 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
727 panel = create_info_panel(I.window, "download-panel",
729 element_get_operation_label(element, "Saving complete"),
730 load_spec_uri_string(spec)],
731 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
734 var file = yield I.minibuffer.read_file_check_overwrite(
735 $prompt = "Save page complete:",
737 $initial_value = suggested_path);
738 // FIXME: use proper read function
739 var dir = yield I.minibuffer.read_file(
740 $prompt = "Data Directory:",
742 $initial_value = file.path + ".support");
747 save_document_complete(doc, file, dir, $buffer = I.buffer);
749 $browser_object = browser_object_frames);
752 function view_as_mime_type (I, target) {
754 var element = yield read_browser_object(I);
755 var spec = load_spec(element);
758 target = FOLLOW_CURRENT_FRAME;
760 if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
761 throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
765 var mime_type = load_spec_mime_type(spec);
766 panel = create_info_panel(I.window, "download-panel",
768 element_get_operation_label(element, "View in browser"),
769 load_spec_uri_string(spec)],
770 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
774 let suggested_type = mime_type;
775 if (viewable_mime_type_list.indexOf(suggested_type) == -1)
776 suggested_type = "text/plain";
777 mime_type = yield I.minibuffer.read_viewable_mime_type(
778 $prompt = "View internally as",
779 $initial_value = suggested_type,
781 override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
782 browser_object_follow(I.buffer, target, spec);
788 function view_as_mime_type_new_buffer (I) {
789 yield view_as_mime_type(I, OPEN_NEW_BUFFER);
792 function view_as_mime_type_new_window (I) {
793 yield view_as_mime_type(I, OPEN_NEW_WINDOW);
796 interactive("view-as-mime-type",
797 "Display a browser object in the browser using the specified MIME type.",
798 alternates(view_as_mime_type,
799 view_as_mime_type_new_buffer,
800 view_as_mime_type_new_window),
801 $browser_object = browser_object_frames);
804 interactive("delete",
805 "Delete a DOM node, given as a browser object.",
807 var elem = yield read_browser_object(I);
808 elem.parentNode.removeChild(elem);
810 $browser_object = browser_object_dom_node);
813 interactive("charset-prefix",
814 "A prefix command that prompts for a charset to use in a "+
815 "subsequent navigation command.",
817 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
818 .getService(Ci.nsICharsetConverterManager);
819 var decoders = ccman.getDecoderList()
821 while (decoders.hasMore())
822 charsets.push(decoders.getNext());
823 I.forced_charset = yield I.minibuffer.read(
824 $prompt = "Charset:",
825 $completer = prefix_completer(
826 $completions = charsets,
827 $get_string = function (x) x.toLowerCase()),
833 interactive("reload-with-charset",
834 "Prompt for a charset, and reload the current page, forcing use "+
837 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
838 .getService(Ci.nsICharsetConverterManager);
839 var decoders = ccman.getDecoderList()
841 while (decoders.hasMore())
842 charsets.push(decoders.getNext());
843 var forced_charset = yield I.minibuffer.read(
844 $prompt = "Charset:",
845 $completer = prefix_completer(
846 $completions = charsets,
847 $get_string = function (x) x.toLowerCase()),
849 reload(I.buffer, false, null, forced_charset);
854 "Paste the contents of the clipboard",
856 I.buffer.mark_active = false;
857 I.buffer.do_command("cmd_paste");
860 interactive("kill-region",
861 "Kill (\"cut\") the selected text.",
863 I.buffer.mark_active = false;
864 I.buffer.do_command("cmd_cut");
867 interactive("kill-ring-save",
868 "Save the region as if killed, but don't kill it.",
870 I.buffer.mark_active = false;
871 I.buffer.do_command("cmd_copy");