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);
22 interactive("quit", "Quit Conkeror", quit);
25 function show_conkeror_version (window) {
26 var xulrunner_version = Cc['@mozilla.org/xre/app-info;1']
27 .getService(Ci.nsIXULAppInfo)
29 window.minibuffer.message("Conkeror "+conkeror.version+
30 " (XULRunner "+xulrunner_version+
33 interactive("conkeror-version",
34 "Show version information for Conkeror.",
35 function (I) { show_conkeror_version(I.window); });
36 interactive("version",
37 "Show version information for Conkeror.",
40 /* FIXME: maybe this should be supported for non-browser buffers */
41 function scroll_horiz_complete (buffer, n) {
42 var w = buffer.focused_frame;
43 w.scrollTo (n > 0 ? w.scrollMaxX : 0, w.scrollY);
45 interactive("scroll-beginning-of-line",
46 "Scroll the current frame all the way to the left.",
47 function (I) { scroll_horiz_complete(I.buffer, -1); });
49 interactive("scroll-end-of-line",
50 "Scroll the current frame all the way to the right.",
51 function (I) { scroll_horiz_complete(I.buffer, 1); });
53 interactive("scroll-top-left",
54 "Scroll the current frame all the way to the top left",
55 function (I) { I.buffer.do_command("cmd_scrollTop");
56 scroll_horiz_complete(I.buffer, -1); });
59 function delete_window (window) {
60 window.window.close();
62 interactive("delete-window",
63 "Delete the current window.",
64 function (I) { delete_window(I.window); });
66 interactive("jsconsole",
67 "Open the JavaScript console.",
68 "find-url-new-buffer",
69 $browser_object = "chrome://global/content/console.xul");
72 function paste_x_primary_selection (field) {
73 modify_region(field, function (str) read_from_x_primary_selection());
75 interactive("paste-x-primary-selection",
76 "Insert the contents of the X primary selection into the selected field or "+
77 "minibuffer. Deactivates the region if it is active, and leaves the point "+
78 "after the inserted text.",
79 function (I) call_on_focused_field(I, paste_x_primary_selection, true));
82 function open_line (field) {
83 modify_region(field, function() ["\n", 0]);
85 interactive("open-line",
86 "If there is an active region, replace is with a newline, otherwise just "+
87 "insert a newline. In both cases leave point before the inserted newline.",
88 function (I) call_on_focused_field(I, open_line, true));
91 interactive("insert-parentheses",
92 "Insert a pair of parentheses, or surround the currently selected text "+
93 "with a pair of parentheses.",
95 call_on_focused_field(I, function (field) {
98 return ["("+str+")", (str ? str.length+2 : 1)];
104 function transpose_chars (field) {
105 var value = field.value;
106 var caret = field.selectionStart; // Caret position.
107 var length = value.length;
109 // If we have less than two character in the field or if we are at the
110 // beginning of the field, do nothing.
111 if (length < 2 || caret == 0)
114 // If we are at the end of the field, switch places on the two last
115 // characters. TODO: This should happen at the end of every line, not only
116 // at the end of the field.
120 // Do the transposing.
121 field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
123 // Increment the caret position. If this is not done, the caret is left at
124 // the end of the field as a result of the replacing of contents.
125 field.selectionStart = caret + 1;
126 field.selectionEnd = caret + 1;
128 interactive("transpose-chars",
129 "Interchange characters around point, moving forward one character.",
130 function (I) call_on_focused_field(I, transpose_chars, true));
133 interactive("execute-extended-command",
134 "Call a command specified in the minibuffer.",
137 var boc = I.browser_object;
140 prompt = I.key_sequence.join(" ");
142 prompt += ' ['+boc.name+']';
143 if (prefix !== null && prefix !== undefined) {
144 if (typeof prefix == "object")
145 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
147 prompt += " "+prefix;
149 var command = yield I.minibuffer.read_command($prompt = prompt);
150 call_after_timeout(function () {
151 input_handle_command.call(I.window, new command_event(command));
157 /// built in commands
158 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
160 // Performs a command on a browser buffer content area
163 define_builtin_commands(
165 function (I, command) {
166 call_builtin_command(I.window, command);
171 define_builtin_commands(
173 function (I, command) {
174 var buffer = I.buffer;
176 buffer.do_command(command);
178 /* Ignore exceptions */
183 function get_link_text () {
184 var e = document.commandDispatcher.focusedElement;
185 if (e && e.getAttribute("href")) {
186 return e.getAttribute("href");
193 function copy_email_address (loc)
195 // Copy the comma-separated list of email addresses only.
196 // There are other ways of embedding email addresses in a mailto:
197 // link, but such complex parsing is beyond us.
198 var qmark = loc.indexOf( "?" );
201 if ( qmark > 7 ) { // 7 == length of "mailto:"
202 addresses = loc.substring( 7, qmark );
204 addresses = loc.substr( 7 );
207 //XXX: the original code, which we got from firefox, unescapes the string
208 // using the current character set. To do this in conkeror, we
209 // *should* use an interactive method that gives us the character set,
210 // rather than fetching it by side-effect.
212 // // Let's try to unescape it using a character set
213 // // in case the address is not ASCII.
215 // var characterSet = this.target.ownerDocument.characterSet;
216 // const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
217 // .getService(Components.interfaces.nsITextToSubURI);
218 // addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
224 writeToClipboard(addresses);
225 message("Copied '" + addresses + "'");
227 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
230 /* FIXME: fix this command */
232 interactive("source",
233 "Load a JavaScript file.",
234 function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
236 function reinit (window) {
240 window.minibuffer.message("Loaded: " + path);
242 window.minibuffer.message("Failed to load: "+path);
246 interactive("reinit",
247 "Reload the Conkeror rc file.",
248 function (I) { reinit(I.window); });
250 interactive("help-page", "Open the Conkeror help page.",
251 "find-url-new-buffer",
252 $browser_object = "chrome://conkeror-help/content/help.html");
254 interactive("tutorial", "Open the Conkeror tutorial.",
255 "find-url-new-buffer",
256 $browser_object = "chrome://conkeror-help/content/tutorial.html");
258 function univ_arg_to_number (prefix, default_value) {
259 if (prefix == null) {
260 if (default_value == null)
263 return default_value;
265 if (typeof prefix == "object")
271 interactive("eval-expression",
272 "Evaluate JavaScript statements.",
274 var s = yield I.minibuffer.read(
276 $history = "eval-expression",
277 $completer = javascript_completer(I.buffer));
278 var result = evaluate(s);
279 if (result !== undefined)
280 I.window.minibuffer.message(String(result));
284 function show_extension_manager () {
285 return conkeror.window_watcher.openWindow(
287 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
289 "resizable=yes,dialog=no",
292 interactive("extensions",
293 "Open the extensions manager in a new window.",
294 show_extension_manager);
296 function print_buffer (buffer) {
297 buffer.top_frame.print();
300 interactive("print-buffer",
301 "Print the currently loaded page.",
302 function (I) { print_buffer(I.buffer); });
304 function view_partial_source (window, charset, selection) {
306 charset = "charset=" + charset;
307 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
308 "_blank", "scrollbars,resizable,chrome,dialog=no",
309 null, charset, selection, 'selection');
311 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
314 function view_mathml_source (window, charset, target) {
316 charset = "charset=" + charset;
317 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
318 "_blank", "scrollbars,resizable,chrome,dialog=no",
319 null, charset, target, 'mathml');
323 function send_key_as_event (window, element, combo) {
324 var split = unformat_key_combo(combo);
325 var event = window.document.createEvent("KeyboardEvent");
338 return element.dispatchEvent(event);
340 return window.dispatchEvent(event);
345 function ensure_content_focused (buffer) {
346 var foc = buffer.focused_frame_or_null;
348 buffer.top_frame.focus();
350 interactive("ensure-content-focused", "Ensure that the content document has focus.",
351 function (I) { ensure_content_focused(I.buffer); });
354 function network_set_online_status (status) {
355 const io_service = Cc["@mozilla.org/network/io-service;1"]
356 .getService(Ci.nsIIOService2);
358 io_service.manageOfflineStatus = false;
359 io_service.offline = status;
361 interactive("network-go-online", "Work online.",
362 function (I) { network_set_online_status(true); });
364 interactive("network-go-offline", "Work offline.",
365 function (I) { network_set_online_status(false); });
368 interactive("submit-form",
369 "Submit the form to which the focused element belongs.",
371 var el = I.buffer.focused_element.parentNode;
372 while (el && el.tagName != "FORM")
375 var inputs = el.getElementsByTagName("input");
376 for (var i = 0, ilen = inputs.length; i < ilen; i++) {
377 if (inputs[i].getAttribute("type") == "submit")
378 return browser_object_follow(I.buffer, FOLLOW_DEFAULT,
387 * Browser Object Commands
389 interactive("follow", null,
390 alternates(follow, follow_new_buffer, follow_new_window),
391 $browser_object = browser_object_links);
393 interactive("follow-top", null,
394 alternates(follow_current_buffer, follow_current_frame),
395 $browser_object = browser_object_frames,
398 interactive("follow-new-buffer",
399 "Follow a link in a new buffer",
400 alternates(follow_new_buffer, follow_new_window),
401 $browser_object = browser_object_links,
404 interactive("follow-new-buffer-background",
405 "Follow a link in a new buffer in the background",
406 alternates(follow_new_buffer_background, follow_new_window),
407 $browser_object = browser_object_links,
410 interactive("follow-new-window",
411 "Follow a link in a new window",
413 $browser_object = browser_object_links,
416 interactive("find-url", "Open a URL in the current buffer",
417 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
418 $browser_object = browser_object_url);
420 interactive("find-url-new-buffer",
421 "Open a URL in a new buffer",
422 alternates(follow_new_buffer, follow_new_window),
423 $browser_object = browser_object_url,
424 $prompt = "Find url");
426 interactive("find-url-new-window", "Open a URL in a new window",
428 $browser_object = browser_object_url,
429 $prompt = "Find url");
431 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
434 define_browser_object_class("alternate-url", null,
435 function (I, prompt) {
436 check_buffer(I.buffer, content_buffer);
437 var result = yield I.buffer.window.minibuffer.read_url(
439 $initial_value = I.buffer.display_uri_string);
440 yield co_return(result);
442 $prompt = "Find url");
445 interactive("up", "Go to the parent directory of the current URL",
447 $browser_object = browser_object_up_url);
450 "Go to the homepage in the current buffer.", "follow",
451 $browser_object = function () { return homepage; });
453 interactive("make-window",
454 "Make a new window with the homepage.",
456 $browser_object = function () { return homepage; });
458 interactive("focus", null,
460 var element = yield read_browser_object(I);
461 browser_element_focus(I.buffer, element);
463 $browser_object = browser_object_frames);
466 "Save a browser object.",
468 var element = yield read_browser_object(I);
469 var spec = load_spec(element);
471 panel = create_info_panel(I.window, "download-panel",
473 element_get_operation_label(element, "Saving"),
474 load_spec_uri_string(spec)],
475 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
477 var file = yield I.minibuffer.read_file_check_overwrite(
478 $prompt = "Save as:",
479 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
488 $browser_object = browser_object_links);
491 interactive("copy", null,
492 alternates(copy_text, copy_text_append),
493 $browser_object = browser_object_links);
495 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
496 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
497 $browser_object = browser_object_paste_url);
499 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
500 alternates(follow_new_buffer, follow_new_window),
501 $browser_object = browser_object_paste_url);
503 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
505 $browser_object = browser_object_paste_url);
507 interactive("view-source",
508 "Toggle between source and rendered views of a URL.",
509 alternates(view_source, view_source_new_buffer, view_source_new_window),
510 $browser_object = browser_object_frames);
513 interactive("shell-command-on-url",
514 "Run a shell command on the url of a browser object.",
516 var cwd = I.local.cwd;
517 var element = yield read_browser_object(I);
518 var spec = load_spec(element);
519 var uri = load_spec_uri_string(spec);
521 panel = create_info_panel(I.window, "download-panel",
523 element_get_operation_label(element, "Running on", "URI"),
524 load_spec_uri_string(spec)],
525 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
527 var cmd = yield I.minibuffer.read_shell_command(
529 $initial_value = load_spec_default_shell_command(spec));
533 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
535 $browser_object = browser_object_url,
536 $prompt = "Shell command");
539 interactive("shell-command-on-file",
540 "Download a document to a temporary file and run a shell command on it.",
542 var cwd = I.local.cwd;
543 var element = yield read_browser_object(I);
544 var spec = load_spec(element);
545 var uri = load_spec_uri_string(spec);
547 panel = create_info_panel(I.window, "download-panel",
549 element_get_operation_label(element, "Running on"),
550 load_spec_uri_string(spec)],
551 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
553 var cmd = yield I.minibuffer.read_shell_command(
555 $initial_value = load_spec_default_shell_command(spec));
559 yield browser_element_shell_command(I.buffer, element, cmd, cwd);
561 $browser_object = browser_object_links,
562 $prompt = "Shell command");
565 interactive("save-page",
566 "Save a document, not including any embedded documents such as images "+
569 check_buffer(I.buffer, content_buffer);
570 var element = yield read_browser_object(I);
571 var spec = load_spec(element);
572 if (!load_spec_document(spec))
573 throw interactive_error("Element is not associated with a document.");
574 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
577 panel = create_info_panel(I.window, "download-panel",
579 element_get_operation_label(element, "Saving"),
580 load_spec_uri_string(spec)],
581 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
584 var file = yield I.minibuffer.read_file_check_overwrite(
585 $prompt = "Save page as:",
587 $initial_value = suggested_path);
592 save_uri(spec, file, $buffer = I.buffer);
594 $browser_object = browser_object_frames);
597 interactive("save-page-as-text",
598 "Save a page as plain text.",
600 check_buffer(I.buffer, content_buffer);
601 var element = yield read_browser_object(I);
602 var spec = load_spec(element);
604 if (!(doc = load_spec_document(spec)))
605 throw interactive_error("Element is not associated with a document.");
606 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
609 panel = create_info_panel(I.window, "download-panel",
611 element_get_operation_label(element, "Saving", "as text"),
612 load_spec_uri_string(spec)],
613 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
616 var file = yield I.minibuffer.read_file_check_overwrite(
617 $prompt = "Save page as text:",
619 $initial_value = suggested_path);
624 save_document_as_text(doc, file, $buffer = I.buffer);
626 $browser_object = browser_object_frames);
629 interactive("save-page-complete",
630 "Save a page and all supporting documents, including images, css, "+
631 "and child frame documents.",
633 check_buffer(I.buffer, content_buffer);
634 var element = yield read_browser_object(I);
635 var spec = load_spec(element);
637 if (!(doc = load_spec_document(spec)))
638 throw interactive_error("Element is not associated with a document.");
639 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
642 panel = create_info_panel(I.window, "download-panel",
644 element_get_operation_label(element, "Saving complete"),
645 load_spec_uri_string(spec)],
646 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
649 var file = yield I.minibuffer.read_file_check_overwrite(
650 $prompt = "Save page complete:",
652 $initial_value = suggested_path);
653 // FIXME: use proper read function
654 var dir = yield I.minibuffer.read_file(
655 $prompt = "Data Directory:",
657 $initial_value = file.path + ".support");
662 save_document_complete(doc, file, dir, $buffer = I.buffer);
664 $browser_object = browser_object_frames);
667 function view_as_mime_type (I, target) {
669 var element = yield read_browser_object(I);
670 var spec = load_spec(element);
673 target = FOLLOW_CURRENT_FRAME;
675 if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
676 throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
680 var mime_type = load_spec_mime_type(spec);
681 panel = create_info_panel(I.window, "download-panel",
683 element_get_operation_label(element, "View in browser"),
684 load_spec_uri_string(spec)],
685 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
689 let suggested_type = mime_type;
690 if (viewable_mime_type_list.indexOf(suggested_type) == -1)
691 suggested_type = "text/plain";
692 mime_type = yield I.minibuffer.read_viewable_mime_type(
693 $prompt = "View internally as",
694 $initial_value = suggested_type,
696 override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
697 browser_object_follow(I.buffer, target, spec);
703 function view_as_mime_type_new_buffer (I) {
704 yield view_as_mime_type(I, OPEN_NEW_BUFFER);
707 function view_as_mime_type_new_window (I) {
708 yield view_as_mime_type(I, OPEN_NEW_WINDOW);
711 interactive("view-as-mime-type",
712 "Display a browser object in the browser using the specified MIME type.",
713 alternates(view_as_mime_type,
714 view_as_mime_type_new_buffer,
715 view_as_mime_type_new_window),
716 $browser_object = browser_object_frames);
719 interactive("delete",
720 "Delete a DOM node, given as a browser object.",
722 var elem = yield read_browser_object(I);
723 if (! (elem instanceof Ci.nsIDOMNode))
724 throw interactive_error("Cannot delete item");
725 elem.parentNode.removeChild(elem);
727 $browser_object = browser_object_dom_node);
730 interactive("charset-prefix",
731 "A prefix command that prompts for a charset to use in a "+
732 "subsequent navigation command.",
734 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
735 .getService(Ci.nsICharsetConverterManager);
736 var decoders = ccman.getDecoderList()
738 while (decoders.hasMore())
739 charsets.push(decoders.getNext());
740 I.forced_charset = yield I.minibuffer.read(
741 $prompt = "Charset:",
742 $completer = prefix_completer(
743 $completions = charsets,
744 $get_string = function (x) x.toLowerCase()),
751 interactive("reload-with-charset",
752 "Prompt for a charset, and reload the current page, forcing use "+
755 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
756 .getService(Ci.nsICharsetConverterManager);
757 var decoders = ccman.getDecoderList()
759 while (decoders.hasMore())
760 charsets.push(decoders.getNext());
761 var forced_charset = yield I.minibuffer.read(
762 $prompt = "Charset:",
763 $completer = prefix_completer(
764 $completions = charsets,
765 $get_string = function (x) x.toLowerCase()),
768 reload(I.buffer, false, null, forced_charset);
773 "Paste the contents of the clipboard",
775 call_builtin_command(I.window, "cmd_paste", true);
778 interactive("kill-region",
779 "Kill (\"cut\") the selected text.",
781 call_builtin_command(I.window, "cmd_cut", true);
784 interactive("kill-ring-save",
785 "Save the region as if killed, but don't kill it.",
787 call_builtin_command(I.window, "cmd_copy", true);
790 interactive("password-manager",
791 "Open the password manager.",
792 "find-url-new-buffer",
793 $browser_object = "chrome://passwordmgr/content/passwordManager.xul");
796 interactive("toggle-full-screen",
797 "Toggle full screen mode for the current window.",
799 window_set_full_screen(I.window);
800 if (I.window.fullScreen)
801 I.minibuffer.message("Fullscreen mode on");
803 I.minibuffer.message("Fullscreen mode off");
807 interactive("image-toggle-zoom-to-fit",
808 "Toggle zoom-to-fit (viewport) on an image document.",
811 var doc = I.buffer.document
812 .QueryInterface(Ci.nsIImageDocument);
813 doc.toggleImageSize();
814 zoom_hook.run(I.buffer);
816 I.minibuffer.message("Not an image document");