1 /***** BEGIN LICENSE BLOCK *****
2 Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 The contents of this file are subject to the Mozilla Public License Version
5 1.1 (the "License"); you may not use this file except in compliance with
6 the License. You may obtain a copy of the License at
7 http://www.mozilla.org/MPL/
9 Software distributed under the License is distributed on an "AS IS" basis,
10 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 for the specific language governing rights and limitations under the
14 The Initial Developer of the Original Code is Shawn Betts.
15 Portions created by the Initial Developer are Copyright (C) 2004,2005
16 by the Initial Developer. All Rights Reserved.
18 Alternatively, the contents of this file may be used under the terms of
19 either the GNU General Public License Version 2 or later (the "GPL"), or
20 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
21 in which case the provisions of the GPL or the LGPL are applicable instead
22 of those above. If you wish to allow use of your version of this file only
23 under the terms of either the GPL or the LGPL, and not to allow others to
24 use your version of this file under the terms of the MPL, indicate your
25 decision by deleting the provisions above and replace them with the notice
26 and other provisions required by the GPL or the LGPL. If you do not delete
27 the provisions above, a recipient may use your version of this file under
28 the terms of any one of the MPL, the GPL or the LGPL.
29 ***** END LICENSE BLOCK *****/
32 * Utility functions for application scope.
37 function string_hashset() {
40 string_hashset.prototype = {
41 constructor : string_hashset,
47 contains : function(s) {
48 return (("-" + s) in this);
51 remove : function (s) {
55 for_each : function (f) {
63 function string_hashmap() {
66 string_hashmap.prototype = {
67 constructor : string_hashmap,
69 put : function(s,value) {
70 this["-" + s] = value;
73 contains : function(s) {
74 return (("-" + s) in this);
77 get : function(s, default_value) {
83 get_put_default : function(s, default_value) {
86 return (this["-" + s] = default_value);
89 remove : function (s) {
93 for_each : function (f) {
96 f(i.slice(1), this[i]);
100 for_each_value : function (f) {
101 for (var i in this) {
108 /// Window title formatting
111 * Default tile formatter. The page url is ignored. If there is a
112 * page_title, returns: "Page title - Conkeror". Otherwise, it
113 * returns just: "Conkeror".
115 function default_title_formatter (window)
117 var page_title = window.buffers.current.title;
119 if (page_title && page_title.length > 0)
120 return page_title + " - Conkeror";
125 var title_format_fn = null;
127 function set_window_title (window)
129 window.document.title = title_format_fn(window);
132 function init_window_title ()
134 title_format_fn = default_title_formatter;
136 add_hook("window_initialize_late_hook", set_window_title);
137 add_hook("current_content_buffer_location_change_hook",
139 set_window_title(buffer.window);
141 add_hook("select_buffer_hook", function (buffer) { set_window_title(buffer.window); }, true);
142 add_hook("current_buffer_title_change_hook",
144 set_window_title(buffer.window);
150 // Put the string on the clipboard
151 function writeToClipboard(str)
153 const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
154 .getService(Components.interfaces.nsIClipboardHelper);
155 gClipboardHelper.copyString(str);
159 function makeURLAbsolute (base, url)
162 var ioService = Components.classes["@mozilla.org/network/io-service;1"]
163 .getService(Components.interfaces.nsIIOService);
164 var baseURI = ioService.newURI(base, null, null);
166 return ioService.newURI (baseURI.resolve (url), null, null).spec;
170 function get_link_location (element)
172 if (element && element.getAttribute("href")) {
173 var loc = element.getAttribute("href");
174 return makeURLAbsolute(element.baseURI, loc);
180 function makeURL(aURL)
182 var ioService = Cc["@mozilla.org/network/io-service;1"]
183 .getService(Ci.nsIIOService);
184 return ioService.newURI(aURL, null, null);
187 function make_uri(uri) {
188 if (uri instanceof Ci.nsIURI)
190 var io_service = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
191 return io_service.newURI(uri, null, null);
194 function makeFileURL(aFile)
196 var ioService = Cc["@mozilla.org/network/io-service;1"]
197 .getService(Ci.nsIIOService);
198 return ioService.newFileURI(aFile);
202 function get_document_content_disposition (document_o)
204 var content_disposition = null;
206 content_disposition =
207 document_o.defaultView
208 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
209 .getInterface(Components.interfaces.nsIDOMWindowUtils)
210 .getDocumentMetadata("content-disposition");
212 return content_disposition;
216 function set_focus_no_scroll(window, element)
218 window.document.commandDispatcher.suppressFocusScroll = true;
220 window.document.commandDispatcher.suppressFocusScroll = false;
223 function do_repeatedly_positive(func, n) {
224 var args = Array.prototype.slice.call(arguments, 2);
226 func.apply(null, args);
229 function do_repeatedly(func, n, positive_args, negative_args) {
231 do func.apply(null, negative_args); while (++n < 0);
233 while (n-- > 0) func.apply(null, positive_args);
236 // remove whitespace from the beginning and end
237 function trim_whitespace (str)
239 var tmp = new String (str);
240 return tmp.replace (/^\s+/, "").replace (/\s+$/, "");
243 function abs_point (node)
248 pt.x = node.offsetLeft;
249 pt.y = node.offsetTop;
250 // find imagemap's coordinates
251 if (node.tagName == "AREA") {
252 var coords = node.getAttribute("coords").split(",");
253 pt.x += Number(coords[0]);
254 pt.y += Number(coords[1]);
257 node = node.offsetParent;
258 // Sometimes this fails, so just return what we got.
260 while (node.tagName != "BODY") {
261 pt.x += node.offsetLeft;
262 pt.y += node.offsetTop;
263 node = node.offsetParent;
267 // while (node.tagName != "BODY") {
268 // alert("okay: " + node + " " + node.tagName + " " + pt.x + " " + pt.y);
269 // node = node.offsetParent;
275 var xul_app_info = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
276 var xul_runtime = Cc['@mozilla.org/xre/app-info;1'].getService(Ci.nsIXULRuntime);
281 // possible return values: 'Darwin', 'Linux', 'WINNT', ...
282 return xul_runtime.OS;
285 var default_directory = null;
287 var env = Cc['@mozilla.org/process/environment;1'].getService(Ci.nsIEnvironment);
288 function getenv (variable) {
289 if (env.exists (variable))
290 return env.get(variable);
294 function set_default_directory(directory_s) {
297 if ( get_os() == "WINNT")
299 directory_s = getenv ('USERPROFILE') ||
300 getenv ('HOMEDRIVE') + getenv ('HOMEPATH');
303 directory_s = getenv ('HOME');
307 default_directory = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
308 default_directory.initWithPath (directory_s);
311 set_default_directory();
313 const XHTML_NS = "http://www.w3.org/1999/xhtml";
314 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
315 const MATHML_NS = "http://www.w3.org/1998/Math/MathML";
316 const XLINK_NS = "http://www.w3.org/1999/xlink";
318 function create_XUL(window, tag_name)
320 return window.document.createElementNS(XUL_NS, tag_name);
324 /* Used in calls to XPath evaluate */
325 function xpath_lookup_namespace(prefix) {
326 if (prefix == "xhtml")
335 function method_caller(obj, func) {
337 func.apply(obj, arguments);
341 function shell_quote(str) {
342 var s = str.replace("\"", "\\\"", "g");
343 s = s.replace("$", "\$", "g");
347 function get_window_from_frame(frame) {
349 var window = frame.QueryInterface(Ci.nsIInterfaceRequestor)
350 .getInterface(Ci.nsIWebNavigation)
351 .QueryInterface(Ci.nsIDocShellTreeItem)
353 .QueryInterface(Ci.nsIInterfaceRequestor)
354 .getInterface(Ci.nsIDOMWindow).wrappedJSObject;
355 /* window is now an XPCSafeJSObjectWrapper */
356 window.escape_wrapper(function (w) { window = w; });
357 /* window is now completely unwrapped */
364 function get_buffer_from_frame(window, frame) {
365 var count = window.buffers.count;
366 for (var i = 0; i < count; ++i) {
367 var b = window.buffers.get_buffer(i);
368 if (b.top_frame == frame)
374 var file_locator = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
376 function get_shortdoc_string(doc) {
379 var idx = doc.indexOf("\n");
381 shortdoc = doc.substring(0,idx);
388 var conkeror_source_code_path = null;
390 function source_code_reference(uri, line_number) {
392 this.line_number = line_number;
394 source_code_reference.prototype = {
396 if (this.uri.indexOf(module_uri_prefix) == 0)
397 return this.uri.substring(module_uri_prefix.length);
402 var file_uri_prefix = "file://";
403 if (this.uri.indexOf(file_uri_prefix) == 0)
404 return this.uri.substring(file_uri_prefix.length);
409 if (conkeror_source_code_path != null) {
410 var module_name = this.module_name;
411 if (module_name != null)
412 return "file://" + conkeror_source_code_path + "/modules/" + module_name;
417 open_in_editor : function() {
418 yield open_with_external_editor(this.best_uri, $line = this.line_number);
422 var get_caller_source_code_reference_ignored_functions = {};
424 function get_caller_source_code_reference(extra_frames_back) {
425 /* Skip at least this function itself and whoever called it (and
426 * more if the caller wants to be skipped). */
427 var frames_to_skip = 2;
428 if (extra_frames_back != null)
429 frames_to_skip += extra_frames_back;
431 for (let f = Components.stack; f != null; f = f.caller) {
432 if (frames_to_skip > 0) {
436 if (get_caller_source_code_reference_ignored_functions[f.name])
438 return new source_code_reference(f.filename, f.lineNumber);
444 function ignore_function_for_get_caller_source_code_reference(func_name) {
445 get_caller_source_code_reference_ignored_functions[func_name] = 1;
448 require_later("external-editor.js");
450 function dom_generator(document, ns) {
451 this.document = document;
454 dom_generator.prototype = {
455 element : function(tag, parent) {
456 var node = this.document.createElementNS(this.ns, tag);
458 if (parent != null && (parent instanceof Ci.nsIDOMNode)) {
459 parent.appendChild(node);
462 for (; i < arguments.length; i += 2)
463 node.setAttribute(arguments[i], arguments[i+1]);
467 text : function(str, parent) {
468 var node = this.document.createTextNode(str);
470 parent.appendChild(node);
475 stylesheet_link : function(href, parent) {
476 var node = this.element("link");
477 node.setAttribute("rel", "stylesheet");
478 node.setAttribute("type", "text/css");
479 node.setAttribute("href", href);
481 parent.appendChild(node);
486 add_stylesheet : function (url) {
487 var head = this.document.documentElement.firstChild;
488 this.stylesheet_link(url, head);
493 * Generates a QueryInterface function suitable for an implemenation
494 * of an XPCOM interface. Unlike XPCOMUtils, this uses the Function
495 * constructor to generate a slightly more efficient version. The
496 * arguments can be either Strings or elements of
497 * Components.interfaces.
499 function generate_QI() {
500 var args = Array.prototype.slice.call(arguments).map(String).concat(["nsISupports"]);
502 Array.prototype.map.call(args,
504 "iid.equals(Components.interfaces." + x + ")")
506 ") return this; throw Components.results.NS_ERROR_NO_INTERFACE;";
507 return new Function("iid", fstr);
510 function set_branch_pref(branch, name, value) {
511 if (typeof(value) == "string") {
512 branch.setCharPref(name, value);
513 } else if (typeof(value) == "number") {
514 branch.setIntPref(name, value);
515 } else if (typeof(value) == "boolean") {
516 branch.setBoolPref(name, value);
520 function default_pref(name, value) {
521 var branch = preferences.getDefaultBranch(null);
522 set_branch_pref(branch, name, value);
525 function user_pref(name, value) {
526 var branch = preferences.getBranch(null);
527 set_branch_pref(branch, name, value);
530 function get_branch_pref(branch, name) {
531 switch (branch.getPrefType(name)) {
532 case branch.PREF_STRING:
533 return branch.getCharPref(name);
534 case branch.PREF_INT:
535 return branch.getIntPref(name);
536 case branch.PREF_BOOL:
537 return branch.getBoolPref(name);
543 function get_pref(name) {
544 var branch = preferences.getBranch(null);
545 return get_branch_pref(branch, name);
548 function get_default_pref(name) {
549 var branch = preferences.getDefaultBranch(null);
550 return get_branch_pref(branch, name);
553 function clear_pref(name) {
554 var branch = preferences.getBranch(null);
555 return branch.clearUserPref(name);
558 function clear_default_pref(name) {
559 var branch = preferences.getDefaultBranch(null);
560 return branch.clearUserPref(name);
563 function pref_has_user_value(name) {
564 var branch = preferences.getBranch(null);
565 return branch.prefHasUserValue(name);
568 function pref_has_default_value(name) {
569 var branch = preferences.getDefaultBranch(null);
570 return branch.prefHasUserValue(name);
573 function session_pref (name, value) {
574 try { clear_pref (name); }
576 return default_pref (name, value);
579 const USER_AGENT_OVERRIDE_PREF = "general.useragent.override";
581 function set_user_agent(str) {
582 session_pref(USER_AGENT_OVERRIDE_PREF, str);
585 function define_builtin_commands(prefix, do_command_function, toggle_mark, mark_active_predicate) {
587 // Specify a docstring
588 function D(cmd, docstring) {
589 var o = new String(cmd);
594 // Specify a forward/reverse pair
597 o.is_reverse_pair = true;
601 // Specify a movement/select command pair.
602 function S(command, movement, select) {
603 var o = [movement, select];
605 o.is_move_select_pair = true;
609 var builtin_commands = [
610 S(D("beginning-of-line", "Move or extend the selection to the beginning of the current line."),
611 D("cmd_beginLine", "Move point to the beginning of the current line."),
612 D("cmd_selectBeginLine", "Extend selection to the beginning of the current line.")),
613 S(D("end-of-line", "Move or extend the selection to the end of the current line."),
614 D("cmd_endLine", "Move point to the end of the current line."),
615 D("cmd_selectEndLine", "Extend selection to the end of the current line.")),
616 D("cmd_copy", "Copy the selection into the clipboard."),
618 D("cmd_cut", "Cut the selection into the clipboard."),
620 D("cmd_deleteToBeginningOfLine", "Delete to the beginning of the current line."),
621 D("cmd_deleteToEndOfLine", "Delete to the end of the current line."),
622 S(D("beginning-of-first-line", "Move or extend the selection to the beginning of the first line."),
623 D("cmd_moveTop", "Move point to the beginning of the first line."),
624 D("cmd_selectTop", "Extend selection to the beginning of the first line.")),
625 S(D("end-of-last-line", "Move or extend the selection to the end of the last line."),
626 D("cmd_moveBottom", "Move point to the end of the last line."),
627 D("cmd_selectBottom", "Extend selection to the end of the last line.")),
628 D("cmd_selectAll", "Select all."),
629 "cmd_scrollBeginLine",
631 D("cmd_scrollTop", "Scroll to the top of the buffer."),
632 D("cmd_scrollBottom", "Scroll to the bottom of the buffer.")];
634 var builtin_commands_with_count = [
635 R(S(D("forward-char", "Move or extend the selection forward one character."),
636 D("cmd_charNext", "Move point forward one character."),
637 D("cmd_selectCharNext", "Extend selection forward one character.")),
638 S(D("backward-char", "Move or extend the selection backward one character."),
639 D("cmd_charPrevious", "Move point backward one character."),
640 D("cmd_selectCharPrevious", "Extend selection backward one character."))),
641 R(D("cmd_deleteCharForward", "Delete the following character."),
642 D("cmd_deleteCharBackward", "Delete the previous character.")),
643 R(D("cmd_deleteWordForward", "Delete the following word."),
644 D("cmd_deleteWordBackward", "Delete the previous word.")),
645 R(S(D("forward-line", "Move or extend the selection forward one line."),
646 D("cmd_lineNext", "Move point forward one line."),
647 "cmd_selectLineNext", "Extend selection forward one line."),
648 S(D("backward-line", "Move or extend the selection backward one line."),
649 D("cmd_linePrevious", "Move point backward one line."),
650 D("cmd_selectLinePrevious", "Extend selection backward one line."))),
651 R(S(D("forward-page", "Move or extend the selection forward one page."),
652 D("cmd_movePageDown", "Move point forward one page."),
653 D("cmd_selectPageDown", "Extend selection forward one page.")),
654 S(D("backward-page", "Move or extend the selection backward one page."),
655 D("cmd_movePageUp", "Move point backward one page."),
656 D("cmd_selectPageUp", "Extend selection backward one page."))),
657 R(D("cmd_undo", "Undo last editing action."),
658 D("cmd_redo", "Redo last editing action.")),
659 R(S(D("forward-word", "Move or extend the selection forward one word."),
660 D("cmd_wordNext", "Move point forward one word."),
661 D("cmd_selectWordNext", "Extend selection forward one word.")),
662 S(D("backward-word", "Move or extend the selection backward one word."),
663 D("cmd_wordPrevious", "Move point backward one word."),
664 D("cmd_selectWordPrevious", "Extend selection backward one word."))),
665 R(D("cmd_scrollPageUp", "Scroll up one page."),
666 D("cmd_scrollPageDown", "Scroll down one page.")),
667 R(D("cmd_scrollLineUp", "Scroll up one line."),
668 D("cmd_scrollLineDown", "Scroll down one line.")),
669 R(D("cmd_scrollLeft", "Scroll left."),
670 D("cmd_scrollRight", "Scroll right.")),
671 D("cmd_paste", "Insert the contents of the clipboard.")];
673 interactive(prefix + "set-mark",
674 "Toggle whether the mark is active.\n" +
675 "When the mark is active, movement commands affect the selection.",
678 function doc_for_builtin(c) {
682 return s + "Run the built-in command " + c + ".";
685 function define_simple_command(c) {
686 interactive(prefix + c, doc_for_builtin(c), function (I) { do_command_function(I, c); });
689 function get_move_select_doc_string(c) {
690 return c.command.doc +
691 "\nSpecifically, if the mark is inactive, runs `" + prefix + c[0] + "'. " +
692 "Otherwise, runs `" + prefix + c[1] + "'.\n" +
693 "To toggle whether the mark is active, use `" + prefix + "set-mark'.";
696 for each (let c_temp in builtin_commands) {
698 if (c.is_move_select_pair) {
699 interactive(prefix + c.command, get_move_select_doc_string(c), function (I) {
700 do_command_function(I, mark_active_predicate(I) ? c[1] : c[0]);
702 define_simple_command(c[0]);
703 define_simple_command(c[1]);
706 define_simple_command(c);
709 function get_reverse_pair_doc_string(main_doc, alt_command) {
710 return main_doc + "\n" +
711 "The prefix argument specifies a repeat count for this command. " +
712 "If the count is negative, `" + prefix + alt_command + "' is performed instead with " +
713 "a corresponding positive repeat count.";
716 function define_simple_reverse_pair(a, b) {
717 interactive(prefix + a, get_reverse_pair_doc_string(doc_for_builtin(a), b),
719 do_repeatedly(do_command_function, I.p, [I, a], [I, b]);
721 interactive(prefix + b, get_reverse_pair_doc_string(doc_for_builtin(b), a),
723 do_repeatedly(do_command_function, I.p, [I, b], [I, a]);
727 for each (let c_temp in builtin_commands_with_count)
730 if (c.is_reverse_pair) {
731 if (c[0].is_move_select_pair) {
732 interactive(prefix + c[0].command, get_reverse_pair_doc_string(get_move_select_doc_string(c[0]),
735 var idx = mark_active_predicate(I) ? 1 : 0;
736 do_repeatedly(do_command_function, I.p, [I, c[0][idx]], [I, c[1][idx]]);
738 interactive(prefix + c[1].command, get_reverse_pair_doc_string(get_move_select_doc_string(c[1]),
741 var idx = mark_active_predicate(I) ? 1 : 0;
742 do_repeatedly(do_command_function, I.p, [I, c[1][idx]], [I, c[0][idx]]);
744 define_simple_reverse_pair(c[0][0], c[1][0]);
745 define_simple_reverse_pair(c[0][1], c[1][1]);
747 define_simple_reverse_pair(c[0], c[1]);
749 let doc = doc_for_builtin(c) +
750 "\nThe prefix argument specifies a positive repeat count for this command.";
751 interactive(prefix + c, doc, function (I) {
752 do_repeatedly_positive(do_command_function, I.p, I, c);
758 var observer_service = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
760 function abort(str) {
761 var e = new Error(str);
762 e.__proto__ = abort.prototype;
765 abort.prototype.__proto__ = Error.prototype;
768 function get_temporary_file(name) {
771 var file = file_locator.get("TmpD", Ci.nsIFile);
773 // Create the file now to ensure that no exploits are possible
774 file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
779 /* FIXME: This should be moved somewhere else, perhaps. */
780 function create_info_panel(window, panel_class, row_arr) {
781 /* Show information panel above minibuffer */
783 var g = new dom_generator(window.document, XUL_NS);
785 var p = g.element("vbox", "class", "panel " + panel_class, "flex", "0");
786 var grid = g.element("grid", p);
787 var cols = g.element("columns", grid);
788 g.element("column", cols, "flex", "0");
789 g.element("column", cols, "flex", "1");
791 var rows = g.element("rows", grid);
794 for each (let [row_class, row_label, row_value] in row_arr) {
795 row = g.element("row", rows, "class", row_class);
796 g.element("label", row,
798 "class", "panel-row-label");
799 g.element("label", row,
801 "class", "panel-row-value");
803 window.minibuffer.insert_before(p);
805 p.destroy = function () {
806 this.parentNode.removeChild(this);
813 // read_from_x_primary_selection favors the X PRIMARY SELECTION, when
814 // it exists. The builtin cmd_paste always uses X CLIPBOARD. So this
815 // is an auxiliary utility, in case you need to work with the primary
818 function read_from_x_primary_selection ()
821 var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
822 .getService(Components.interfaces.nsIClipboard);
824 // Create tranferable that will transfer the text.
825 var trans = Components.classes["@mozilla.org/widget/transferable;1"]
826 .createInstance(Components.interfaces.nsITransferable);
828 trans.addDataFlavor("text/unicode");
829 // If available, use selection clipboard, otherwise global one
830 if (clipboard.supportsSelectionClipboard())
831 clipboard.getData(trans, clipboard.kSelectionClipboard);
833 clipboard.getData(trans, clipboard.kGlobalClipboard);
837 trans.getTransferData("text/unicode", data, dataLen);
840 data = data.value.QueryInterface(Components.interfaces.nsISupportsString);
841 return data.data.substring(0, dataLen.value / 2);
847 var user_variables = new string_hashmap();
849 function define_variable(name, default_value, doc) {
850 conkeror[name] = default_value;
851 user_variables.put(name, {
852 default_value: default_value,
854 shortdoc: get_shortdoc_string(doc),
855 source_code_reference: get_caller_source_code_reference() });
859 function register_user_stylesheet(url)
861 var uri = makeURL(url);
862 var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
863 sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
866 function unregister_user_stylesheet(url)
868 var uri = makeURL(url);
869 var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
870 if (sss.sheetRegistered(uri, sss.USER_SHEET))
871 ss.unregisterSheet(uri, sss.USER_SHEET);
874 function predicate_alist_match(alist, key) {
875 for each (let i in alist) {