Factor out mime.js matching logic to predicate_alist_match
[conkeror.git] / modules / utils.js
blobff293ab1d50994dbd9ed5da9b4b1e5c3b51ac57b
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
12 License.
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.
33  *
34  */
37 function string_hashset() {
40 string_hashset.prototype = {
41     constructor : string_hashset,
43     add : function(s) {
44         this["-" + s] = true;
45     },
46     
47     contains : function(s) {
48         return (("-" + s) in this);
49     },
51     remove : function (s) {
52         delete this["-" + s];
53     },
55     for_each : function (f) {
56         for (var i in this) {
57             if (i[0] == "-")
58                 f(i.slice(1));
59         }
60     }
63 function string_hashmap() {
66 string_hashmap.prototype = {
67     constructor : string_hashmap,
69     put : function(s,value) {
70         this["-" + s] = value;
71     },
72     
73     contains : function(s) {
74         return (("-" + s) in this);
75     },
77     get : function(s, default_value) {
78         if (this.contains(s))
79             return this["-" + s];
80         return default_value;
81     },
83     get_put_default : function(s, default_value) {
84         if (this.contains(s))
85             return this["-" + s];
86         return (this["-" + s] = default_value);
87     },
89     remove : function (s) {
90         delete this["-" + s];
91     },
93     for_each : function (f) {
94         for (var i in this) {
95             if (i[0] == "-")
96                 f(i.slice(1), this[i]);
97         }
98     },
100     for_each_value : function (f) {
101         for (var i in this) {
102             if (i[0] == "-")
103                 f(this[i]);
104         }
105     }
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".
114  */
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";
121     else
122         return "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",
138              function (buffer) {
139                  set_window_title(buffer.window);
140              });
141     add_hook("select_buffer_hook", function (buffer) { set_window_title(buffer.window); }, true);
142     add_hook("current_buffer_title_change_hook",
143              function (buffer) {
144                  set_window_title(buffer.window);
145              });
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)
161     // Construct nsIURL.
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);
175     }
176     return null;
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)
189         return uri;
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;
205     try {
206         content_disposition =
207             document_o.defaultView
208             .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
209             .getInterface(Components.interfaces.nsIDOMWindowUtils)
210             .getDocumentMetadata("content-disposition");
211     } catch (e) { }
212     return content_disposition;
216 function set_focus_no_scroll(window, element)
218     window.document.commandDispatcher.suppressFocusScroll = true;
219     element.focus();
220     window.document.commandDispatcher.suppressFocusScroll = false;
223 function do_repeatedly_positive(func, n) {
224     var args = Array.prototype.slice.call(arguments, 2);
225     while (n-- > 0)
226         func.apply(null, args);
229 function do_repeatedly(func, n, positive_args, negative_args) {
230     if (n < 0)
231         do func.apply(null, negative_args); while (++n < 0);
232     else
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)
245     var orig = node;
246     var pt = {};
247     try {
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]);
255         }
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;
264         }
265     } catch(e) {
266 //      node = orig;
267 //      while (node.tagName != "BODY") {
268 //          alert("okay: " + node + " " + node.tagName + " " + pt.x + " " + pt.y);
269 //          node = node.offsetParent;
270 //      }
271     }
272     return pt;
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);
279 function get_os ()
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);
291     return null;
294 function set_default_directory(directory_s) {
295     if (! directory_s)
296     {
297         if ( get_os() == "WINNT")
298         {
299             directory_s = getenv ('USERPROFILE') ||
300                 getenv ('HOMEDRIVE') + getenv ('HOMEPATH');
301         }
302         else {
303             directory_s = getenv ('HOME');
304         }
305     }
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")
327         return XHTML_NS;
328     if (prefix == "m")
329         return MATHML_NS;
330     if (prefix == "xul")
331         return XUL_NS;
332     return null;
335 function method_caller(obj, func) {
336     return function () {
337         func.apply(obj, arguments);
338     }
341 function shell_quote(str) {
342     var s = str.replace("\"", "\\\"", "g");
343     s = s.replace("$", "\$", "g");
344     return s;
347 function get_window_from_frame(frame) {
348     try {
349         var window = frame.QueryInterface(Ci.nsIInterfaceRequestor)
350             .getInterface(Ci.nsIWebNavigation)
351             .QueryInterface(Ci.nsIDocShellTreeItem)
352             .rootTreeItem
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 */
358         return window;
359     } catch (e) {
360         return null;
361     }
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)
369             return b;
370     }
371     return null;
374 var file_locator = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
376 function get_shortdoc_string(doc) {
377     var shortdoc = null;
378     if (doc != null) {
379         var idx = doc.indexOf("\n");
380         if (idx >= 0)
381             shortdoc = doc.substring(0,idx);
382         else
383             shortdoc = doc;
384     }
385     return shortdoc;
388 var conkeror_source_code_path = null;
390 function source_code_reference(uri, line_number) {
391     this.uri = uri;
392     this.line_number = line_number;
394 source_code_reference.prototype = {
395     get module_name () {
396         if (this.uri.indexOf(module_uri_prefix) == 0)
397             return this.uri.substring(module_uri_prefix.length);
398         return null;
399     },
401     get file_name () {
402         var file_uri_prefix = "file://";
403         if (this.uri.indexOf(file_uri_prefix) == 0)
404             return this.uri.substring(file_uri_prefix.length);
405         return null;
406     },
408     get best_uri () {
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;
413         }
414         return this.uri;
415     },
417     open_in_editor : function() {
418         yield open_with_external_editor(this.best_uri, $line = this.line_number);
419     }
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) {
433             --frames_to_skip;
434             continue;
435         }
436         if (get_caller_source_code_reference_ignored_functions[f.name])
437             continue;
438         return new source_code_reference(f.filename, f.lineNumber);
439     }
441     return null;
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;
452     this.ns = ns;
454 dom_generator.prototype = {
455     element : function(tag, parent) {
456         var node = this.document.createElementNS(this.ns, tag);
457         var i = 1;
458         if (parent != null && (parent instanceof Ci.nsIDOMNode)) {
459             parent.appendChild(node);
460             i = 2;
461         }
462         for (; i < arguments.length; i += 2)
463             node.setAttribute(arguments[i], arguments[i+1]);
464         return node;
465     },
467     text : function(str, parent) {
468         var node = this.document.createTextNode(str);
469         if (parent)
470             parent.appendChild(node);
471         return node;
472     },
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);
480         if (parent)
481             parent.appendChild(node);
482         return node;
483     },
486     add_stylesheet : function (url) {
487         var head = this.document.documentElement.firstChild;
488         this.stylesheet_link(url, head);
489     }
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.
498  */
499 function generate_QI() {
500     var args = Array.prototype.slice.call(arguments).map(String).concat(["nsISupports"]);
501     var fstr = "if(" +
502         Array.prototype.map.call(args,
503                                  function (x)
504                                      "iid.equals(Components.interfaces." + x + ")")
505         .join("||") +
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);
517     }
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 pref_is_locked(name, value) {
531     var branch = preferences.getBranch(null);
532     return branch.prefIsLocked(name);
535 function lock_pref(name, value) {
536     var branch = preferences.getBranch(null);
537     if (branch.prefIsLocked(name))
538         branch.unlockPref(name);
539     default_pref(name, value);
540     branch.lockPref(name);
543 function unlock_pref(name) {
544     var branch = preferences.getBranch(null);
545     branch.unlockPref(name);
548 function get_branch_pref(branch, name) {
549     switch (branch.getPrefType(name)) {
550     case branch.PREF_STRING:
551         return branch.getCharPref(name);
552     case branch.PREF_INT:
553         return branch.getIntPref(name);
554     case branch.PREF_BOOL:
555         return branch.getBoolPref(name);
556     default:
557         return null;
558     }
561 function get_pref(name) {
562     var branch = preferences.getBranch(null);
563     return get_branch_pref(branch, name);
566 function get_default_pref(name) {
567     var branch = preferences.getDefaultBranch(null);
568     return get_branch_pref(branch, name);
571 function clear_pref(name) {
572     var branch = preferences.getBranch(null);
573     return branch.clearUserPref(name);
576 function clear_default_pref(name) {
577     var branch = preferences.getDefaultBranch(null);
578     return branch.clearUserPref(name);
581 function pref_has_user_value(name) {
582     var branch = preferences.getBranch(null);
583     return branch.prefHasUserValue(name);
586 function pref_has_default_value(name) {
587     var branch = preferences.getDefaultBranch(null);
588     return branch.prefHasUserValue(name);
591 function session_pref (name, value) {
592     try { clear_pref (name); }
593     catch (e) {}
594     return default_pref (name, value);
597 const USER_AGENT_OVERRIDE_PREF = "general.useragent.override";
599 function set_user_agent(str) {
600     lock_pref(USER_AGENT_OVERRIDE_PREF, str);
603 function define_builtin_commands(prefix, do_command_function, toggle_mark, mark_active_predicate) {
605     // Specify a docstring
606     function D(cmd, docstring) {
607         var o = new String(cmd);
608         o.doc = docstring;
609         return o;
610     }
612     // Specify a forward/reverse pair
613     function R(a, b) {
614         var o = [a,b];
615         o.is_reverse_pair = true;
616         return o;
617     }
619     // Specify a movement/select command pair.
620     function S(command, movement, select) {
621         var o = [movement, select];
622         o.command = command;
623         o.is_move_select_pair = true;
624         return o;
625     }
627     var builtin_commands = [
628         S(D("beginning-of-line", "Move or extend the selection to the beginning of the current line."),
629           D("cmd_beginLine", "Move point to the beginning of the current line."),
630           D("cmd_selectBeginLine", "Extend selection to the beginning of the current line.")),
631         S(D("end-of-line", "Move or extend the selection to the end of the current line."),
632           D("cmd_endLine", "Move point to the end of the current line."),
633           D("cmd_selectEndLine", "Extend selection to the end of the current line.")),
634         D("cmd_copy", "Copy the selection into the clipboard."),
635         "cmd_copyOrDelete",
636         D("cmd_cut", "Cut the selection into the clipboard."),
637         "cmd_cutOrDelete",
638         D("cmd_deleteToBeginningOfLine", "Delete to the beginning of the current line."),
639         D("cmd_deleteToEndOfLine", "Delete to the end of the current line."),
640         S(D("beginning-of-first-line", "Move or extend the selection to the beginning of the first line."),
641           D("cmd_moveTop", "Move point to the beginning of the first line."),
642           D("cmd_selectTop", "Extend selection to the beginning of the first line.")),
643         S(D("end-of-last-line", "Move or extend the selection to the end of the last line."),
644           D("cmd_moveBottom", "Move point to the end of the last line."),
645           D("cmd_selectBottom", "Extend selection to the end of the last line.")),
646         D("cmd_selectAll", "Select all."),
647         "cmd_scrollBeginLine",
648         "cmd_scrollEndLine",
649         D("cmd_scrollTop", "Scroll to the top of the buffer."),
650         D("cmd_scrollBottom", "Scroll to the bottom of the buffer.")];
652     var builtin_commands_with_count = [
653         R(S(D("forward-char", "Move or extend the selection forward one character."),
654             D("cmd_charNext", "Move point forward one character."),
655             D("cmd_selectCharNext", "Extend selection forward one character.")),
656           S(D("backward-char", "Move or extend the selection backward one character."),
657             D("cmd_charPrevious", "Move point backward one character."),
658             D("cmd_selectCharPrevious", "Extend selection backward one character."))),
659         R(D("cmd_deleteCharForward", "Delete the following character."),
660           D("cmd_deleteCharBackward", "Delete the previous character.")),
661         R(D("cmd_deleteWordForward", "Delete the following word."),
662           D("cmd_deleteWordBackward", "Delete the previous word.")),
663         R(S(D("forward-line", "Move or extend the selection forward one line."),
664             D("cmd_lineNext", "Move point forward one line."),
665             "cmd_selectLineNext", "Extend selection forward one line."),
666           S(D("backward-line", "Move or extend the selection backward one line."),
667             D("cmd_linePrevious", "Move point backward one line."),
668             D("cmd_selectLinePrevious", "Extend selection backward one line."))),
669         R(S(D("forward-page", "Move or extend the selection forward one page."),
670             D("cmd_movePageDown", "Move point forward one page."),
671             D("cmd_selectPageDown", "Extend selection forward one page.")),
672           S(D("backward-page", "Move or extend the selection backward one page."),
673             D("cmd_movePageUp", "Move point backward one page."),
674             D("cmd_selectPageUp", "Extend selection backward one page."))),
675         R(D("cmd_undo", "Undo last editing action."),
676           D("cmd_redo", "Redo last editing action.")),
677         R(S(D("forward-word", "Move or extend the selection forward one word."),
678             D("cmd_wordNext", "Move point forward one word."),
679             D("cmd_selectWordNext", "Extend selection forward one word.")),
680           S(D("backward-word", "Move or extend the selection backward one word."),
681             D("cmd_wordPrevious", "Move point backward one word."),
682             D("cmd_selectWordPrevious", "Extend selection backward one word."))),
683         R(D("cmd_scrollPageUp", "Scroll up one page."),
684           D("cmd_scrollPageDown", "Scroll down one page.")),
685         R(D("cmd_scrollLineUp", "Scroll up one line."),
686           D("cmd_scrollLineDown", "Scroll down one line.")),
687         R(D("cmd_scrollLeft", "Scroll left."),
688           D("cmd_scrollRight", "Scroll right.")),
689         D("cmd_paste", "Insert the contents of the clipboard.")];
691     interactive(prefix + "set-mark",
692                 "Toggle whether the mark is active.\n" +
693                 "When the mark is active, movement commands affect the selection.",
694                 toggle_mark);
696     function doc_for_builtin(c) {
697         var s = "";
698         if (c.doc != null)
699             s += c.doc + "\n";
700         return s + "Run the built-in command " + c + ".";
701     }
703     function define_simple_command(c) {
704         interactive(prefix + c, doc_for_builtin(c), function (I) { do_command_function(I, c); });
705     }
707     function get_move_select_doc_string(c) {
708         return c.command.doc +
709             "\nSpecifically, if the mark is inactive, runs `" + prefix + c[0] + "'.  " +
710             "Otherwise, runs `" + prefix + c[1] + "'.\n" +
711             "To toggle whether the mark is active, use `" + prefix + "set-mark'.";
712     }
714     for each (let c_temp in builtin_commands)  {
715         let c = c_temp;
716         if (c.is_move_select_pair) {
717             interactive(prefix + c.command, get_move_select_doc_string(c), function (I) {
718                 do_command_function(I, mark_active_predicate(I) ? c[1] : c[0]);
719             });
720             define_simple_command(c[0]);
721             define_simple_command(c[1]);
722         }
723         else
724             define_simple_command(c);
725     }
727     function get_reverse_pair_doc_string(main_doc, alt_command) {
728         return main_doc + "\n" +
729             "The prefix argument specifies a repeat count for this command.  " +
730             "If the count is negative, `" + prefix + alt_command + "' is performed instead with " +
731             "a corresponding positive repeat count.";
732     }
734     function define_simple_reverse_pair(a, b) {
735         interactive(prefix + a, get_reverse_pair_doc_string(doc_for_builtin(a), b),
736                     function (I) {
737                         do_repeatedly(do_command_function, I.p, [I, a], [I, b]);
738                     });
739         interactive(prefix + b, get_reverse_pair_doc_string(doc_for_builtin(b), a),
740                     function (I) {
741                         do_repeatedly(do_command_function, I.p, [I, b], [I, a]);
742                     });
743     }
745     for each (let c_temp in builtin_commands_with_count)
746     {
747         let c = c_temp;
748         if (c.is_reverse_pair) {
749             if (c[0].is_move_select_pair) {
750                 interactive(prefix + c[0].command, get_reverse_pair_doc_string(get_move_select_doc_string(c[0]),
751                                                                                c[1].command),
752                             function (I) {
753                                 var idx = mark_active_predicate(I) ? 1 : 0;
754                                 do_repeatedly(do_command_function, I.p, [I, c[0][idx]], [I, c[1][idx]]);
755                             });
756                 interactive(prefix + c[1].command, get_reverse_pair_doc_string(get_move_select_doc_string(c[1]),
757                                                                                c[0].command),
758                             function (I) {
759                                 var idx = mark_active_predicate(I) ? 1 : 0;
760                                 do_repeatedly(do_command_function, I.p, [I, c[1][idx]], [I, c[0][idx]]);
761                             });
762                 define_simple_reverse_pair(c[0][0], c[1][0]);
763                 define_simple_reverse_pair(c[0][1], c[1][1]);
764             } else
765                 define_simple_reverse_pair(c[0], c[1]);
766         } else {
767             let doc = doc_for_builtin(c) +
768                 "\nThe prefix argument specifies a positive repeat count for this command.";
769             interactive(prefix + c, doc, function (I) {
770                 do_repeatedly_positive(do_command_function, I.p, I, c);
771             });
772         }
773     }
776 var observer_service = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
778 function abort(str) {
779     var e = new Error(str);
780     e.__proto__ = abort.prototype;
781     return e;
783 abort.prototype.__proto__ = Error.prototype;
786 function get_temporary_file(name) {
787     if (name == null)
788         name = "temp.txt";
789     var file = file_locator.get("TmpD", Ci.nsIFile);
790     file.append(name);
791     // Create the file now to ensure that no exploits are possible
792     file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
793     return file;
797 /* FIXME: This should be moved somewhere else, perhaps. */
798 function create_info_panel(window, panel_class, row_arr) {
799     /* Show information panel above minibuffer */
801     var g = new dom_generator(window.document, XUL_NS);
803     var p = g.element("vbox", "class", "panel " + panel_class, "flex", "0");
804     var grid = g.element("grid", p);
805     var cols = g.element("columns", grid);
806     g.element("column", cols, "flex", "0");
807     g.element("column", cols, "flex", "1");
809     var rows = g.element("rows", grid);
810     var row;
812     for each (let [row_class, row_label, row_value] in row_arr) {
813         row = g.element("row", rows, "class", row_class);
814         g.element("label", row,
815                   "value", row_label,
816                   "class", "panel-row-label");
817         g.element("label", row,
818                   "value", row_value,
819                   "class", "panel-row-value");
820     }
821     window.minibuffer.insert_before(p);
823     p.destroy = function () {
824         this.parentNode.removeChild(this);
825     };
827     return p;
831 // read_from_x_primary_selection favors the X PRIMARY SELECTION, when
832 // it exists.  The builtin cmd_paste always uses X CLIPBOARD.  So this
833 // is an auxiliary utility, in case you need to work with the primary
834 // selection.
836 function read_from_x_primary_selection ()
838     // Get clipboard.
839     var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
840         .getService(Components.interfaces.nsIClipboard);
842     // Create tranferable that will transfer the text.
843     var trans = Components.classes["@mozilla.org/widget/transferable;1"]
844         .createInstance(Components.interfaces.nsITransferable);
846     trans.addDataFlavor("text/unicode");
847     // If available, use selection clipboard, otherwise global one
848     if (clipboard.supportsSelectionClipboard())
849         clipboard.getData(trans, clipboard.kSelectionClipboard);
850     else
851         clipboard.getData(trans, clipboard.kGlobalClipboard);
853     var data = {};
854     var dataLen = {};
855     trans.getTransferData("text/unicode", data, dataLen);
857     if (data) {
858         data = data.value.QueryInterface(Components.interfaces.nsISupportsString);
859         return data.data.substring(0, dataLen.value / 2);
860     } else {
861         return "";
862     }
865 var user_variables = new string_hashmap();
867 function define_variable(name, default_value, doc) {
868     conkeror[name] = default_value;
869     user_variables.put(name, {
870         default_value: default_value,
871         doc: doc,
872         shortdoc: get_shortdoc_string(doc),
873         source_code_reference: get_caller_source_code_reference() });
877 function register_user_stylesheet(url)
879     var uri = makeURL(url);
880     var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
881     sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
884 function unregister_user_stylesheet(url)
886     var uri = makeURL(url);
887     var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
888     if (sss.sheetRegistered(uri, sss.USER_SHEET))
889         ss.unregisterSheet(uri, sss.USER_SHEET);
892 function predicate_alist_match(alist, key) {
893     for each (let i in alist) {
894         if (i[0](key))
895             return i[1];
896     }
897     return undefined;