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");
14 var res = yield before_quit_hook.run();
17 var appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
18 .getService(Ci.nsIAppStartup);
19 appStartup.quit(appStartup.eAttemptQuit);
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");
75 * Given a callback func and an interactive context I, call func, passing
76 * either a focused field, or the minibuffer's input element if the
77 * minibuffer is active. Afterward, call `scroll_selection_into_view' on
78 * the field. See `paste_x_primary_selection' and `open_line' for
81 function call_on_focused_field (I, func) {
82 var m = I.window.minibuffer;
83 var s = m.current_state;
84 if (m._input_mode_enabled) {
85 m._restore_normal_state();
86 var e = m.input_element;
88 var e = I.buffer.focused_element;
90 scroll_selection_into_view(e);
91 if (s && s.handle_input)
97 * Replace the current region with modifier(selection). Deactivates region and
98 * sets point to the end of the inserted text, unless keep_point is true, in
99 * which case the point will be left at the beginning of the inserted text.
101 function modify_region (field, modifier, keep_point) {
102 if (field.getAttribute("contenteditable") == 'true') {
104 var doc = field.ownerDocument;
105 var win = doc.defaultView;
106 doc.execCommand("insertHTML", false,
107 html_escape(modifier(win.getSelection().toString()))
108 .replace(/\n/g, '<br>')
109 .replace(/ /g, ' '));
113 modifier(field.value.substring(field.selectionStart, field.selectionEnd+1));
114 var point = field.selectionStart;
116 field.value.substr(0, field.selectionStart) + replacement +
117 field.value.substr(field.selectionEnd);
118 if (!keep_point) point += replacement.length;
119 field.setSelectionRange(point, point);
124 function paste_x_primary_selection (field) {
125 modify_region(field, function (str) read_from_x_primary_selection());
127 interactive("paste-x-primary-selection",
128 "Insert the contents of the X primary selection into the selected field or "+
129 "minibuffer. Deactivates the region if it is active, and leaves the point "+
130 "after the inserted text.",
131 function (I) call_on_focused_field(I, paste_x_primary_selection));
134 function open_line (field) {
135 modify_region(field, function() "\n", true);
137 interactive("open-line",
138 "If there is an active region, replace is with a newline, otherwise just "+
139 "insert a newline. In both cases leave point before the inserted newline.",
140 function (I) call_on_focused_field(I, open_line));
143 function transpose_chars (field) {
144 var value = field.value;
145 var caret = field.selectionStart; // Caret position.
146 var length = value.length;
148 // If we have less than two character in the field or if we are at the
149 // beginning of the field, do nothing.
150 if (length < 2 || caret == 0)
153 // If we are at the end of the field, switch places on the two last
154 // characters. TODO: This should happen at the end of every line, not only
155 // at the end of the field.
159 // Do the transposing.
160 field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
162 // Increment the caret position. If this is not done, the caret is left at
163 // the end of the field as a result of the replacing of contents.
164 field.selectionStart = caret + 1;
165 field.selectionEnd = caret + 1;
167 interactive("transpose-chars",
168 "Interchange characters around point, moving forward one character.",
169 function (I) call_on_focused_field(I, transpose_chars));
172 interactive("execute-extended-command",
173 "Call a command specified in the minibuffer.",
176 var boc = I.browser_object;
179 prompt = I.key_sequence.join(" ");
181 prompt += ' ['+boc.name+']';
182 if (prefix !== null && prefix !== undefined) {
183 if (typeof prefix == "object")
184 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
186 prompt += " "+prefix;
188 var command = yield I.minibuffer.read_command($prompt = prompt);
189 call_after_timeout(function () {
190 input_handle_command.call(I.window, new command_event(command));
196 /// built in commands
197 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
199 // Performs a command on a browser buffer content area
202 define_builtin_commands(
204 function (I, command) {
205 var buffer = I.buffer;
207 buffer.do_command(command);
209 /* Ignore exceptions */
213 I.buffer.mark_active = !I.buffer.mark_active;
215 function (I) I.buffer.mark_active,
219 define_builtin_commands(
221 function (I, command) {
222 var buffer = I.buffer;
224 buffer.do_command(command);
226 /* Ignore exceptions */
230 I.buffer.mark_active = !I.buffer.mark_active;
232 function (I) I.buffer.mark_active,
235 function get_link_text () {
236 var e = document.commandDispatcher.focusedElement;
237 if (e && e.getAttribute("href")) {
238 return e.getAttribute("href");
245 function copy_email_address (loc)
247 // Copy the comma-separated list of email addresses only.
248 // There are other ways of embedding email addresses in a mailto:
249 // link, but such complex parsing is beyond us.
250 var qmark = loc.indexOf( "?" );
253 if ( qmark > 7 ) { // 7 == length of "mailto:"
254 addresses = loc.substring( 7, qmark );
256 addresses = loc.substr( 7 );
259 //XXX: the original code, which we got from firefox, unescapes the string
260 // using the current character set. To do this in conkeror, we
261 // *should* use an interactive method that gives us the character set,
262 // rather than fetching it by side-effect.
264 // // Let's try to unescape it using a character set
265 // // in case the address is not ASCII.
267 // var characterSet = this.target.ownerDocument.characterSet;
268 // const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
269 // .getService(Components.interfaces.nsITextToSubURI);
270 // addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
276 writeToClipboard(addresses);
277 message("Copied '" + addresses + "'");
279 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
282 /* FIXME: fix this command */
284 interactive("source",
285 "Load a JavaScript file.",
286 function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
288 function reinit (window) {
292 window.minibuffer.message("Loaded: " + path);
294 window.minibuffer.message("Failed to load: "+path);
298 interactive("reinit",
299 "Reload the Conkeror rc file.",
300 function (I) { reinit(I.window); });
302 interactive("help-page", "Open the Conkeror help page.",
303 "find-url-new-buffer",
304 $browser_object = "chrome://conkeror-help/content/help.html");
306 interactive("tutorial", "Open the Conkeror tutorial.",
307 "find-url-new-buffer",
308 $browser_object = "chrome://conkeror-help/content/tutorial.html");
310 function univ_arg_to_number (prefix, default_value) {
311 if (prefix == null) {
312 if (default_value == null)
315 return default_value;
317 if (typeof prefix == "object")
322 function eval_expression (window, s) {
323 // eval in the global scope.
325 // In addition, the following variables are available:
327 var buffer = window.buffers.current;
328 var result = eval(s);
329 if (result !== undefined) {
330 window.minibuffer.message(String(result));
333 interactive("eval-expression",
334 "Evaluate JavaScript statements.",
338 (yield I.minibuffer.read($prompt = "Eval:",
339 $history = "eval-expression",
340 $completer = javascript_completer(I.buffer))));
344 function show_extension_manager () {
345 return conkeror.window_watcher.openWindow(
347 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
349 "resizable=yes,dialog=no",
352 interactive("extensions",
353 "Open the extensions manager in a new window.",
354 show_extension_manager);
356 function print_buffer (buffer) {
357 buffer.top_frame.print();
359 interactive("print-buffer",
360 "Print the currently loaded page.",
361 function (I) { print_buffer(I.buffer); });
363 function view_partial_source (window, charset, selection) {
365 charset = "charset=" + charset;
366 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
367 "_blank", "scrollbars,resizable,chrome,dialog=no",
368 null, charset, selection, 'selection');
370 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
373 function view_mathml_source (window, charset, target) {
375 charset = "charset=" + charset;
376 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
377 "_blank", "scrollbars,resizable,chrome,dialog=no",
378 null, charset, target, 'mathml');
382 function send_key_as_event (window, element, combo) {
383 var split = unformat_key_combo(combo);
384 var event = window.document.createEvent("KeyboardEvent");
397 return element.dispatchEvent(event);
399 return window.dispatchEvent(event);
404 function ensure_content_focused (buffer) {
405 var foc = buffer.focused_frame_or_null;
407 buffer.top_frame.focus();
409 interactive("ensure-content-focused", "Ensure that the content document has focus.",
410 function (I) { ensure_content_focused(I.buffer); });
413 function network_set_online_status (status) {
414 const io_service = Cc["@mozilla.org/network/io-service;1"]
415 .getService(Ci.nsIIOService2);
417 io_service.manageOfflineStatus = false;
418 io_service.offline = status;
420 interactive("network-go-online", "Work online.",
421 function (I) { network_set_online_status(true); });
422 interactive("network-go-offline", "Work offline.",
423 function (I) { network_set_online_status(false); });
426 interactive("submit-form",
427 "Submit the form to which the focused element belongs.",
429 var el = I.buffer.focused_element.parentNode;
430 while (el && el.tagName != "FORM")
433 var inputs = el.getElementsByTagName("input");
434 for (var i = 0, ilen = inputs.length; i < ilen; i++) {
435 if (inputs[i].getAttribute("type") == "submit")
436 return browser_object_follow(I.buffer, FOLLOW_DEFAULT,
445 * Browser Object Commands
447 interactive("follow", null,
448 alternates(follow, follow_new_buffer, follow_new_window),
449 $browser_object = browser_object_links);
451 interactive("follow-top", null,
452 alternates(follow_current_buffer, follow_current_frame),
453 $browser_object = browser_object_frames,
456 interactive("follow-new-buffer",
457 "Follow a link in a new buffer",
458 alternates(follow_new_buffer, follow_new_window),
459 $browser_object = browser_object_links,
462 interactive("follow-new-buffer-background",
463 "Follow a link in a new buffer in the background",
464 alternates(follow_new_buffer_background, follow_new_window),
465 $browser_object = browser_object_links,
468 interactive("follow-new-window",
469 "Follow a link in a new window",
471 $browser_object = browser_object_links,
474 interactive("find-url", "Open a URL in the current buffer",
475 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
476 $browser_object = browser_object_url);
478 interactive("find-url-new-buffer",
479 "Open a URL in a new buffer",
480 alternates(follow_new_buffer, follow_new_window),
481 $browser_object = browser_object_url,
482 $prompt = "Find url");
484 interactive("find-url-new-window", "Open a URL in a new window",
486 $browser_object = browser_object_url,
487 $prompt = "Find url");
489 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
492 define_browser_object_class("alternate-url", null,
493 function (I, prompt) {
494 check_buffer(I.buffer, content_buffer);
495 var result = yield I.buffer.window.minibuffer.read_url(
497 $initial_value = I.buffer.display_uri_string);
498 yield co_return(result);
500 $prompt = "Find url");
503 interactive("up", "Go to the parent directory of the current URL",
505 $browser_object = browser_object_up_url);
508 "Go to the homepage in the current buffer.", "follow",
509 $browser_object = function () { return homepage; });
511 interactive("make-window",
512 "Make a new window with the homepage.",
514 $browser_object = function () { return homepage; });
516 interactive("focus", null,
518 var element = yield read_browser_object(I);
519 browser_element_focus(I.buffer, element);
521 $browser_object = browser_object_frames);
525 "Save a browser object.",
527 var element = yield read_browser_object(I);
528 var spec = load_spec(element);
530 panel = create_info_panel(I.window, "download-panel",
532 element_get_operation_label(element, "Saving"),
533 load_spec_uri_string(spec)],
534 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
536 var file = yield I.minibuffer.read_file_check_overwrite(
537 $prompt = "Save as:",
538 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
547 $browser_object = browser_object_links);
550 interactive("copy", null,
552 var element = yield read_browser_object(I);
553 browser_element_copy(I.buffer, element);
555 $browser_object = browser_object_links);
557 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
558 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
559 $browser_object = browser_object_paste_url);
561 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
562 alternates(follow_new_buffer, follow_new_window),
563 $browser_object = browser_object_paste_url);
565 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
567 $browser_object = browser_object_paste_url);
569 interactive("view-source", null,
570 alternates(view_source, view_source_new_buffer, view_source_new_window),
571 $browser_object = browser_object_frames);
574 interactive("shell-command-on-url",
575 "Run a shell command on the url of a browser object.",
577 var cwd = I.local.cwd;
578 var element = yield read_browser_object(I);
579 var spec = load_spec(element);
580 var uri = load_spec_uri_string(spec);
582 panel = create_info_panel(I.window, "download-panel",
584 element_get_operation_label(element, "Running on", "URI"),
585 load_spec_uri_string(spec)],
586 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
588 var cmd = yield I.minibuffer.read_shell_command(
590 $initial_value = load_spec_default_shell_command(spec));
594 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
596 $browser_object = browser_object_url,
597 $prompt = "Shell command");
600 interactive("shell-command-on-file",
601 "Download a document to a temporary file and run a shell command on it.",
603 var cwd = I.local.cwd;
604 var element = yield read_browser_object(I);
605 var spec = load_spec(element);
606 var uri = load_spec_uri_string(spec);
608 panel = create_info_panel(I.window, "download-panel",
610 element_get_operation_label(element, "Running on"),
611 load_spec_uri_string(spec)],
612 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
614 var cmd = yield I.minibuffer.read_shell_command(
616 $initial_value = load_spec_default_shell_command(spec));
620 yield browser_element_shell_command(I.buffer, element, cmd, cwd);
622 $browser_object = browser_object_links,
623 $prompt = "Shell command");
626 interactive("bookmark",
627 "Create a bookmark.",
629 var element = yield read_browser_object(I);
630 var spec = load_spec(element);
631 var uri_string = load_spec_uri_string(spec);
633 panel = create_info_panel(I.window, "bookmark-panel",
635 element_get_operation_label(element, "Bookmarking"),
638 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
642 add_bookmark(uri_string, title);
643 I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
645 $browser_object = browser_object_frames);
648 interactive("save-page",
649 "Save a document, not including any embedded documents such as images "+
652 check_buffer(I.buffer, content_buffer);
653 var element = yield read_browser_object(I);
654 var spec = load_spec(element);
655 if (!load_spec_document(spec))
656 throw interactive_error("Element is not associated with a document.");
657 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
660 panel = create_info_panel(I.window, "download-panel",
662 element_get_operation_label(element, "Saving"),
663 load_spec_uri_string(spec)],
664 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
667 var file = yield I.minibuffer.read_file_check_overwrite(
668 $prompt = "Save page as:",
670 $initial_value = suggested_path);
675 save_uri(spec, file, $buffer = I.buffer);
677 $browser_object = browser_object_frames);
680 interactive("save-page-as-text",
681 "Save a page as plain text.",
683 check_buffer(I.buffer, content_buffer);
684 var element = yield read_browser_object(I);
685 var spec = load_spec(element);
687 if (!(doc = load_spec_document(spec)))
688 throw interactive_error("Element is not associated with a document.");
689 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
692 panel = create_info_panel(I.window, "download-panel",
694 element_get_operation_label(element, "Saving", "as text"),
695 load_spec_uri_string(spec)],
696 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
699 var file = yield I.minibuffer.read_file_check_overwrite(
700 $prompt = "Save page as text:",
702 $initial_value = suggested_path);
707 save_document_as_text(doc, file, $buffer = I.buffer);
709 $browser_object = browser_object_frames);
712 interactive("save-page-complete",
713 "Save a page and all supporting documents, including images, css, "+
714 "and child frame documents.",
716 check_buffer(I.buffer, content_buffer);
717 var element = yield read_browser_object(I);
718 var spec = load_spec(element);
720 if (!(doc = load_spec_document(spec)))
721 throw interactive_error("Element is not associated with a document.");
722 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
725 panel = create_info_panel(I.window, "download-panel",
727 element_get_operation_label(element, "Saving complete"),
728 load_spec_uri_string(spec)],
729 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
732 var file = yield I.minibuffer.read_file_check_overwrite(
733 $prompt = "Save page complete:",
735 $initial_value = suggested_path);
736 // FIXME: use proper read function
737 var dir = yield I.minibuffer.read_file(
738 $prompt = "Data Directory:",
740 $initial_value = file.path + ".support");
745 save_document_complete(doc, file, dir, $buffer = I.buffer);
747 $browser_object = browser_object_frames);
750 function view_as_mime_type (I, target) {
752 var element = yield read_browser_object(I);
753 var spec = load_spec(element);
756 target = FOLLOW_CURRENT_FRAME;
758 if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
759 throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
763 var mime_type = load_spec_mime_type(spec);
764 panel = create_info_panel(I.window, "download-panel",
766 element_get_operation_label(element, "View in browser"),
767 load_spec_uri_string(spec)],
768 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
772 let suggested_type = mime_type;
773 if (viewable_mime_type_list.indexOf(suggested_type) == -1)
774 suggested_type = "text/plain";
775 mime_type = yield I.minibuffer.read_viewable_mime_type(
776 $prompt = "View internally as",
777 $initial_value = suggested_type,
779 override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
780 browser_object_follow(I.buffer, target, spec);
785 function view_as_mime_type_new_buffer (I) {
786 yield view_as_mime_type(I, OPEN_NEW_BUFFER);
788 function view_as_mime_type_new_window (I) {
789 yield view_as_mime_type(I, OPEN_NEW_WINDOW);
791 interactive("view-as-mime-type",
792 "Display a browser object in the browser using the specified MIME type.",
793 alternates(view_as_mime_type,
794 view_as_mime_type_new_buffer,
795 view_as_mime_type_new_window),
796 $browser_object = browser_object_frames);
799 interactive("charset-prefix",
800 "A prefix command that prompts for a charset to use in a "+
801 "subsequent navigation command.",
803 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
804 .getService(Ci.nsICharsetConverterManager);
805 var decoders = ccman.getDecoderList()
807 while (decoders.hasMore())
808 charsets.push(decoders.getNext());
809 I.forced_charset = yield I.minibuffer.read(
810 $prompt = "Charset:",
811 $completer = prefix_completer(
812 $completions = charsets,
813 $get_string = function (x) x.toLowerCase()),
819 interactive("reload-with-charset",
820 "Prompt for a charset, and reload the current page, forcing use "+
823 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
824 .getService(Ci.nsICharsetConverterManager);
825 var decoders = ccman.getDecoderList()
827 while (decoders.hasMore())
828 charsets.push(decoders.getNext());
829 var forced_charset = yield I.minibuffer.read(
830 $prompt = "Charset:",
831 $completer = prefix_completer(
832 $completions = charsets,
833 $get_string = function (x) x.toLowerCase()),
835 reload(I.buffer, false, null, forced_charset);
840 "Paste the contents of the clipboard",
842 I.buffer.mark_active = false;
843 I.buffer.do_command("cmd_paste");
846 interactive("kill-region",
847 "Kill (\"cut\") the selected text.",
849 I.buffer.mark_active = false;
850 I.buffer.do_command("cmd_cut");
853 interactive("kill-ring-save",
854 "Save the region as if killed, but don't kill it.",
856 I.buffer.mark_active = false;
857 I.buffer.do_command("cmd_copy");