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);
148 init_window_title ();
153 // Put the string on the clipboard
154 function writeToClipboard(str)
156 const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
157 .getService(Components.interfaces.nsIClipboardHelper);
158 gClipboardHelper.copyString(str);
162 function makeURLAbsolute (base, url)
165 var ioService = Components.classes["@mozilla.org/network/io-service;1"]
166 .getService(Components.interfaces.nsIIOService);
167 var baseURI = ioService.newURI(base, null, null);
169 return ioService.newURI (baseURI.resolve (url), null, null).spec;
173 function get_link_location (element)
175 if (element && element.getAttribute("href")) {
176 var loc = element.getAttribute("href");
177 return makeURLAbsolute(element.baseURI, loc);
183 function makeURL(aURL)
185 var ioService = Cc["@mozilla.org/network/io-service;1"]
186 .getService(Ci.nsIIOService);
187 return ioService.newURI(aURL, null, null);
190 function make_uri(uri) {
191 if (uri instanceof Ci.nsIURI)
193 var io_service = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
194 return io_service.newURI(uri, null, null);
197 function makeFileURL(aFile)
199 var ioService = Cc["@mozilla.org/network/io-service;1"]
200 .getService(Ci.nsIIOService);
201 return ioService.newFileURI(aFile);
205 function get_document_content_disposition (document_o)
207 var content_disposition = null;
209 content_disposition =
210 document_o.defaultView
211 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
212 .getInterface(Components.interfaces.nsIDOMWindowUtils)
213 .getDocumentMetadata("content-disposition");
215 return content_disposition;
219 function set_focus_no_scroll(window, element)
221 window.document.commandDispatcher.suppressFocusScroll = true;
223 window.document.commandDispatcher.suppressFocusScroll = false;
226 function do_repeatedly_positive(func, n) {
227 var args = Array.prototype.slice.call(arguments, 2);
229 func.apply(null, args);
232 function do_repeatedly(func, n, positive_args, negative_args) {
234 do func.apply(null, negative_args); while (++n < 0);
236 while (n-- > 0) func.apply(null, positive_args);
239 // remove whitespace from the beginning and end
240 function trim_whitespace (str)
242 var tmp = new String (str);
243 return tmp.replace (/^\s+/, "").replace (/\s+$/, "");
246 function abs_point (node)
251 pt.x = node.offsetLeft;
252 pt.y = node.offsetTop;
253 // find imagemap's coordinates
254 if (node.tagName == "AREA") {
255 var coords = node.getAttribute("coords").split(",");
256 pt.x += Number(coords[0]);
257 pt.y += Number(coords[1]);
260 node = node.offsetParent;
261 // Sometimes this fails, so just return what we got.
263 while (node.tagName != "BODY") {
264 pt.x += node.offsetLeft;
265 pt.y += node.offsetTop;
266 node = node.offsetParent;
270 // while (node.tagName != "BODY") {
271 // alert("okay: " + node + " " + node.tagName + " " + pt.x + " " + pt.y);
272 // node = node.offsetParent;
278 var xul_app_info = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
279 var xul_runtime = Cc['@mozilla.org/xre/app-info;1'].getService(Ci.nsIXULRuntime);
284 // possible return values: 'Darwin', 'Linux', 'WINNT', ...
285 return xul_runtime.OS;
288 var default_directory = null;
290 var env = Cc['@mozilla.org/process/environment;1'].getService(Ci.nsIEnvironment);
291 function getenv (variable) {
292 if (env.exists (variable))
293 return env.get(variable);
297 function set_default_directory(directory_s) {
300 if ( get_os() == "WINNT")
302 directory_s = getenv ('USERPROFILE') ||
303 getenv ('HOMEDRIVE') + getenv ('HOMEPATH');
306 directory_s = getenv ('HOME');
310 default_directory = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
311 default_directory.initWithPath (directory_s);
314 set_default_directory();
316 const XHTML_NS = "http://www.w3.org/1999/xhtml";
317 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
318 const MATHML_NS = "http://www.w3.org/1998/Math/MathML";
319 const XLINK_NS = "http://www.w3.org/1999/xlink";
321 function create_XUL(window, tag_name)
323 return window.document.createElementNS(XUL_NS, tag_name);
327 /* Used in calls to XPath evaluate */
328 function xpath_lookup_namespace(prefix) {
329 if (prefix == "xhtml")
338 function method_caller(obj, func) {
340 func.apply(obj, arguments);
344 function shell_quote(str) {
345 var s = str.replace("\"", "\\\"", "g");
346 s = s.replace("$", "\$", "g");
350 function get_window_from_frame(frame) {
352 var window = frame.QueryInterface(Ci.nsIInterfaceRequestor)
353 .getInterface(Ci.nsIWebNavigation)
354 .QueryInterface(Ci.nsIDocShellTreeItem)
356 .QueryInterface(Ci.nsIInterfaceRequestor)
357 .getInterface(Ci.nsIDOMWindow).wrappedJSObject;
358 /* window is now an XPCSafeJSObjectWrapper */
359 window.escape_wrapper(function (w) { window = w; });
360 /* window is now completely unwrapped */
367 function get_buffer_from_frame(window, frame) {
368 var count = window.buffers.count;
369 for (var i = 0; i < count; ++i) {
370 var b = window.buffers.get_buffer(i);
371 if (b.top_frame == frame)
377 var file_locator = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
379 function get_shortdoc_string(doc) {
382 var idx = doc.indexOf("\n");
384 shortdoc = doc.substring(0,idx);
391 var conkeror_source_code_path = null;
393 function source_code_reference(uri, line_number) {
395 this.line_number = line_number;
397 source_code_reference.prototype = {
399 if (this.uri.indexOf(module_uri_prefix) == 0)
400 return this.uri.substring(module_uri_prefix.length);
405 var file_uri_prefix = "file://";
406 if (this.uri.indexOf(file_uri_prefix) == 0)
407 return this.uri.substring(file_uri_prefix.length);
412 if (conkeror_source_code_path != null) {
413 var module_name = this.module_name;
414 if (module_name != null)
415 return "file://" + conkeror_source_code_path + "/modules/" + module_name;
420 open_in_editor : function() {
421 yield open_with_external_editor(this.best_uri, $line = this.line_number);
425 var get_caller_source_code_reference_ignored_functions = {};
427 function get_caller_source_code_reference(extra_frames_back) {
428 /* Skip at least this function itself and whoever called it (and
429 * more if the caller wants to be skipped). */
430 var frames_to_skip = 2;
431 if (extra_frames_back != null)
432 frames_to_skip += extra_frames_back;
434 for (let f = Components.stack; f != null; f = f.caller) {
435 if (frames_to_skip > 0) {
439 if (get_caller_source_code_reference_ignored_functions[f.name])
441 return new source_code_reference(f.filename, f.lineNumber);
447 function ignore_function_for_get_caller_source_code_reference(func_name) {
448 get_caller_source_code_reference_ignored_functions[func_name] = 1;
451 require_later("external-editor.js");
453 function dom_generator(document, ns) {
454 this.document = document;
457 dom_generator.prototype = {
458 element : function(tag, parent) {
459 var node = this.document.createElementNS(this.ns, tag);
461 if (parent != null && (parent instanceof Ci.nsIDOMNode)) {
462 parent.appendChild(node);
465 for (; i < arguments.length; i += 2)
466 node.setAttribute(arguments[i], arguments[i+1]);
470 text : function(str, parent) {
471 var node = this.document.createTextNode(str);
473 parent.appendChild(node);
478 stylesheet_link : function(href, parent) {
479 var node = this.element("link");
480 node.setAttribute("rel", "stylesheet");
481 node.setAttribute("type", "text/css");
482 node.setAttribute("href", href);
484 parent.appendChild(node);
489 add_stylesheet : function (url) {
490 var head = this.document.documentElement.firstChild;
491 this.stylesheet_link(url, head);
496 * Generates a QueryInterface function suitable for an implemenation
497 * of an XPCOM interface. Unlike XPCOMUtils, this uses the Function
498 * constructor to generate a slightly more efficient version. The
499 * arguments can be either Strings or elements of
500 * Components.interfaces.
502 function generate_QI() {
503 var args = Array.prototype.slice.call(arguments).map(String).concat(["nsISupports"]);
505 Array.prototype.map.call(args,
507 "iid.equals(Components.interfaces." + x + ")")
509 ") return this; throw Components.results.NS_ERROR_NO_INTERFACE;";
510 return new Function("iid", fstr);
513 function set_branch_pref(branch, name, value) {
514 if (typeof(value) == "string") {
515 branch.setCharPref(name, value);
516 } else if (typeof(value) == "number") {
517 branch.setIntPref(name, value);
518 } else if (typeof(value) == "boolean") {
519 branch.setBoolPref(name, value);
523 function default_pref(name, value) {
524 var branch = preferences.getDefaultBranch(null);
525 set_branch_pref(branch, name, value);
528 function user_pref(name, value) {
529 var branch = preferences.getBranch(null);
530 set_branch_pref(branch, name, value);
533 function get_branch_pref(branch, name) {
534 switch (branch.getPrefType(name)) {
535 case branch.PREF_STRING:
536 return branch.getCharPref(name);
537 case branch.PREF_INT:
538 return branch.getIntPref(name);
539 case branch.PREF_BOOL:
540 return branch.getBoolPref(name);
546 function get_pref(name) {
547 var branch = preferences.getBranch(null);
548 return get_branch_pref(branch, name);
551 function get_default_pref(name) {
552 var branch = preferences.getDefaultBranch(null);
553 return get_branch_pref(branch, name);
556 function clear_pref(name) {
557 var branch = preferences.getBranch(null);
558 return branch.clearUserPref(name);
561 function clear_default_pref(name) {
562 var branch = preferences.getDefaultBranch(null);
563 return branch.clearUserPref(name);
566 function pref_has_user_value(name) {
567 var branch = preferences.getBranch(null);
568 return branch.prefHasUserValue(name);
571 function pref_has_default_value(name) {
572 var branch = preferences.getDefaultBranch(null);
573 return branch.prefHasUserValue(name);
576 function session_pref (name, value) {
577 try { clear_pref (name); }
579 return default_pref (name, value);
582 const USER_AGENT_OVERRIDE_PREF = "general.useragent.override";
584 function set_user_agent(str) {
585 session_pref(USER_AGENT_OVERRIDE_PREF, str);
588 function define_builtin_commands(prefix, do_command_function, toggle_mark, mark_active_predicate) {
590 // Specify a docstring
591 function D(cmd, docstring) {
592 var o = new String(cmd);
597 // Specify a forward/reverse pair
600 o.is_reverse_pair = true;
604 // Specify a movement/select command pair.
605 function S(command, movement, select) {
606 var o = [movement, select];
608 o.is_move_select_pair = true;
612 var builtin_commands = [
613 S(D("beginning-of-line", "Move or extend the selection to the beginning of the current line."),
614 D("cmd_beginLine", "Move point to the beginning of the current line."),
615 D("cmd_selectBeginLine", "Extend selection to the beginning of the current line.")),
616 S(D("end-of-line", "Move or extend the selection to the end of the current line."),
617 D("cmd_endLine", "Move point to the end of the current line."),
618 D("cmd_selectEndLine", "Extend selection to the end of the current line.")),
619 D("cmd_copy", "Copy the selection into the clipboard."),
621 D("cmd_cut", "Cut the selection into the clipboard."),
623 D("cmd_deleteToBeginningOfLine", "Delete to the beginning of the current line."),
624 D("cmd_deleteToEndOfLine", "Delete to the end of the current line."),
625 S(D("beginning-of-first-line", "Move or extend the selection to the beginning of the first line."),
626 D("cmd_moveTop", "Move point to the beginning of the first line."),
627 D("cmd_selectTop", "Extend selection to the beginning of the first line.")),
628 S(D("end-of-last-line", "Move or extend the selection to the end of the last line."),
629 D("cmd_moveBottom", "Move point to the end of the last line."),
630 D("cmd_selectBottom", "Extend selection to the end of the last line.")),
631 D("cmd_selectAll", "Select all."),
632 "cmd_scrollBeginLine",
634 D("cmd_scrollTop", "Scroll to the top of the buffer."),
635 D("cmd_scrollBottom", "Scroll to the bottom of the buffer.")];
637 var builtin_commands_with_count = [
638 R(S(D("forward-char", "Move or extend the selection forward one character."),
639 D("cmd_charNext", "Move point forward one character."),
640 D("cmd_selectCharNext", "Extend selection forward one character.")),
641 S(D("backward-char", "Move or extend the selection backward one character."),
642 D("cmd_charPrevious", "Move point backward one character."),
643 D("cmd_selectCharPrevious", "Extend selection backward one character."))),
644 R(D("cmd_deleteCharForward", "Delete the following character."),
645 D("cmd_deleteCharBackward", "Delete the previous character.")),
646 R(D("cmd_deleteWordForward", "Delete the following word."),
647 D("cmd_deleteWordBackward", "Delete the previous word.")),
648 R(S(D("forward-line", "Move or extend the selection forward one line."),
649 D("cmd_lineNext", "Move point forward one line."),
650 "cmd_selectLineNext", "Extend selection forward one line."),
651 S(D("backward-line", "Move or extend the selection backward one line."),
652 D("cmd_linePrevious", "Move point backward one line."),
653 D("cmd_selectLinePrevious", "Extend selection backward one line."))),
654 R(S(D("forward-page", "Move or extend the selection forward one page."),
655 D("cmd_movePageDown", "Move point forward one page."),
656 D("cmd_selectPageDown", "Extend selection forward one page.")),
657 S(D("backward-page", "Move or extend the selection backward one page."),
658 D("cmd_movePageUp", "Move point backward one page."),
659 D("cmd_selectPageUp", "Extend selection backward one page."))),
660 R(D("cmd_undo", "Undo last editing action."),
661 D("cmd_redo", "Redo last editing action.")),
662 R(S(D("forward-word", "Move or extend the selection forward one word."),
663 D("cmd_wordNext", "Move point forward one word."),
664 D("cmd_selectWordNext", "Extend selection forward one word.")),
665 S(D("backward-word", "Move or extend the selection backward one word."),
666 D("cmd_wordPrevious", "Move point backward one word."),
667 D("cmd_selectWordPrevious", "Extend selection backward one word."))),
668 R(D("cmd_scrollPageUp", "Scroll up one page."),
669 D("cmd_scrollPageDown", "Scroll down one page.")),
670 R(D("cmd_scrollLineUp", "Scroll up one line."),
671 D("cmd_scrollLineDown", "Scroll down one line.")),
672 R(D("cmd_scrollLeft", "Scroll left."),
673 D("cmd_scrollRight", "Scroll right.")),
674 D("cmd_paste", "Insert the contents of the clipboard.")];
676 interactive(prefix + "set-mark",
677 "Toggle whether the mark is active.\n" +
678 "When the mark is active, movement commands affect the selection.",
681 function doc_for_builtin(c) {
685 return s + "Run the built-in command " + c + ".";
688 function define_simple_command(c) {
689 interactive(prefix + c, doc_for_builtin(c), function (I) { do_command_function(I, c); });
692 function get_move_select_doc_string(c) {
693 return c.command.doc +
694 "\nSpecifically, if the mark is inactive, runs `" + prefix + c[0] + "'. " +
695 "Otherwise, runs `" + prefix + c[1] + "'.\n" +
696 "To toggle whether the mark is active, use `" + prefix + "set-mark'.";
699 for each (let c_temp in builtin_commands) {
701 if (c.is_move_select_pair) {
702 interactive(prefix + c.command, get_move_select_doc_string(c), function (I) {
703 do_command_function(I, mark_active_predicate(I) ? c[1] : c[0]);
705 define_simple_command(c[0]);
706 define_simple_command(c[1]);
709 define_simple_command(c);
712 function get_reverse_pair_doc_string(main_doc, alt_command) {
713 return main_doc + "\n" +
714 "The prefix argument specifies a repeat count for this command. " +
715 "If the count is negative, `" + prefix + alt_command + "' is performed instead with " +
716 "a corresponding positive repeat count.";
719 function define_simple_reverse_pair(a, b) {
720 interactive(prefix + a, get_reverse_pair_doc_string(doc_for_builtin(a), b),
722 do_repeatedly(do_command_function, I.p, [I, a], [I, b]);
724 interactive(prefix + b, get_reverse_pair_doc_string(doc_for_builtin(b), a),
726 do_repeatedly(do_command_function, I.p, [I, b], [I, a]);
730 for each (let c_temp in builtin_commands_with_count)
733 if (c.is_reverse_pair) {
734 if (c[0].is_move_select_pair) {
735 interactive(prefix + c[0].command, get_reverse_pair_doc_string(get_move_select_doc_string(c[0]),
738 var idx = mark_active_predicate(I) ? 1 : 0;
739 do_repeatedly(do_command_function, I.p, [I, c[0][idx]], [I, c[1][idx]]);
741 interactive(prefix + c[1].command, get_reverse_pair_doc_string(get_move_select_doc_string(c[1]),
744 var idx = mark_active_predicate(I) ? 1 : 0;
745 do_repeatedly(do_command_function, I.p, [I, c[1][idx]], [I, c[0][idx]]);
747 define_simple_reverse_pair(c[0][0], c[1][0]);
748 define_simple_reverse_pair(c[0][1], c[1][1]);
750 define_simple_reverse_pair(c[0], c[1]);
752 let doc = doc_for_builtin(c) +
753 "\nThe prefix argument specifies a positive repeat count for this command.";
754 interactive(prefix + c, doc, function (I) {
755 do_repeatedly_positive(do_command_function, I.p, I, c);
761 var observer_service = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
763 function abort(str) {
764 var e = new Error(str);
765 e.__proto__ = abort.prototype;
768 abort.prototype.__proto__ = Error.prototype;
771 function get_temporary_file(name) {
774 var file = file_locator.get("TmpD", Ci.nsIFile);
776 // Create the file now to ensure that no exploits are possible
777 file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
782 /* FIXME: This should be moved somewhere else, perhaps. */
783 function create_info_panel(window, panel_class, row_arr) {
784 /* Show information panel above minibuffer */
786 var g = new dom_generator(window.document, XUL_NS);
788 var p = g.element("vbox", "class", "panel " + panel_class, "flex", "0");
789 var grid = g.element("grid", p);
790 var cols = g.element("columns", grid);
791 g.element("column", cols, "flex", "0");
792 g.element("column", cols, "flex", "1");
794 var rows = g.element("rows", grid);
797 for each (let [row_class, row_label, row_value] in row_arr) {
798 row = g.element("row", rows, "class", row_class);
799 g.element("label", row,
801 "class", "panel-row-label");
802 g.element("label", row,
804 "class", "panel-row-value");
806 window.minibuffer.insert_before(p);
808 p.destroy = function () {
809 this.parentNode.removeChild(this);
816 // read_from_x_primary_selection favors the X PRIMARY SELECTION, when
817 // it exists. The builtin cmd_paste always uses X CLIPBOARD. So this
818 // is an auxiliary utility, in case you need to work with the primary
821 function read_from_x_primary_selection ()
824 var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
825 .getService(Components.interfaces.nsIClipboard);
827 // Create tranferable that will transfer the text.
828 var trans = Components.classes["@mozilla.org/widget/transferable;1"]
829 .createInstance(Components.interfaces.nsITransferable);
831 trans.addDataFlavor("text/unicode");
832 // If available, use selection clipboard, otherwise global one
833 if (clipboard.supportsSelectionClipboard())
834 clipboard.getData(trans, clipboard.kSelectionClipboard);
836 clipboard.getData(trans, clipboard.kGlobalClipboard);
840 trans.getTransferData("text/unicode", data, dataLen);
843 data = data.value.QueryInterface(Components.interfaces.nsISupportsString);
844 return data.data.substring(0, dataLen.value / 2);
850 var user_variables = new string_hashmap();
852 function define_variable(name, default_value, doc) {
853 conkeror[name] = default_value;
854 user_variables.put(name, {
855 default_value: default_value,
857 shortdoc: get_shortdoc_string(doc),
858 source_code_reference: get_caller_source_code_reference() });
862 function register_user_stylesheet(url)
864 var uri = makeURL(url);
865 var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
866 sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
869 function unregister_user_stylesheet(url)
871 var uri = makeURL(url);
872 var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
873 if (sss.sheetRegistered(uri, sss.USER_SHEET))
874 ss.unregisterSheet(uri, sss.USER_SHEET);
877 function predicate_alist_match(alist, key) {
878 for each (let i in alist) {