Make dependency on JSON optional
[conkeror.git] / modules / utils.js
blob7b9dd057473fca81ee6f1e682bdcb73e12e8d8da
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() {}
39 string_hashset.prototype = {
40     constructor : string_hashset,
42     add : function(s) {
43         this["-" + s] = true;
44     },
46     contains : function(s) {
47         return (("-" + s) in this);
48     },
50     remove : function (s) {
51         delete this["-" + s];
52     },
54     for_each : function (f) {
55         for (var i in this) {
56             if (i[0] == "-")
57                 f(i.slice(1));
58         }
59     }
62 function string_hashmap() {
65 string_hashmap.prototype = {
66     constructor : string_hashmap,
68     put : function(s,value) {
69         this["-" + s] = value;
70     },
72     contains : function(s) {
73         return (("-" + s) in this);
74     },
76     get : function(s, default_value) {
77         if (this.contains(s))
78             return this["-" + s];
79         return default_value;
80     },
82     get_put_default : function(s, default_value) {
83         if (this.contains(s))
84             return this["-" + s];
85         return (this["-" + s] = default_value);
86     },
88     remove : function (s) {
89         delete this["-" + s];
90     },
92     for_each : function (f) {
93         for (var i in this) {
94             if (i[0] == "-")
95                 f(i.slice(1), this[i]);
96         }
97     },
99     for_each_value : function (f) {
100         for (var i in this) {
101             if (i[0] == "-")
102                 f(this[i]);
103         }
104     },
106     iterator: function (only_keys) {
107         if (only_keys) {
108             for (let k in Iterator(this, true)) {
109                 if (k[0] == "-")
110                     yield k.slice(1);
111             }
112         } else {
113             for (let [k,v] in Iterator(this, false)) {
114                 if (k[0] == "-")
115                     yield [k.slice(1),v];
116             }
117         }
118     }
121 /// Window title formatting
124  * Default tile formatter.  The page url is ignored.  If there is a
125  * page_title, returns: "Page title - Conkeror".  Otherwise, it
126  * returns just: "Conkeror".
127  */
128 function default_title_formatter (window)
130     var page_title = window.buffers.current.title;
132     if (page_title && page_title.length > 0)
133         return page_title + " - Conkeror";
134     else
135         return "Conkeror";
138 var title_format_fn = null;
140 function set_window_title (window)
142     window.document.title = title_format_fn(window);
145 function init_window_title ()
147     title_format_fn = default_title_formatter;
149     add_hook("window_initialize_late_hook", set_window_title);
150     add_hook("current_content_buffer_location_change_hook",
151              function (buffer) {
152                  set_window_title(buffer.window);
153              });
154     add_hook("select_buffer_hook", function (buffer) { set_window_title(buffer.window); }, true);
155     add_hook("current_buffer_title_change_hook",
156              function (buffer) {
157                  set_window_title(buffer.window);
158              });
161 init_window_title ();
166 // Put the string on the clipboard
167 function writeToClipboard(str)
169     const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
170         .getService(Components.interfaces.nsIClipboardHelper);
171     gClipboardHelper.copyString(str);
175 function makeURLAbsolute (base, url)
177     // Construct nsIURL.
178     var ioService = Components.classes["@mozilla.org/network/io-service;1"]
179         .getService(Components.interfaces.nsIIOService);
180     var baseURI  = ioService.newURI(base, null, null);
182     return ioService.newURI (baseURI.resolve (url), null, null).spec;
186 function get_link_location (element)
188     if (element && element.getAttribute("href")) {
189         var loc = element.getAttribute("href");
190         return makeURLAbsolute(element.baseURI, loc);
191     }
192     return null;
196 var io_service = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
198 function make_uri(uri, charset, base_uri) {
199     if (uri instanceof Ci.nsIURI)
200         return uri;
201     return io_service.newURI(uri, charset, base_uri);
204 var makeURL = make_uri; // until all callers are fixed
206 function makeFileURL(aFile)
208     return io_service.newFileURI(aFile).QueryInterface(Ci.nsIURL);
212 function get_document_content_disposition (document_o)
214     var content_disposition = null;
215     try {
216         content_disposition =
217             document_o.defaultView
218             .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
219             .getInterface(Components.interfaces.nsIDOMWindowUtils)
220             .getDocumentMetadata("content-disposition");
221     } catch (e) { }
222     return content_disposition;
226 function set_focus_no_scroll(window, element)
228     window.document.commandDispatcher.suppressFocusScroll = true;
229     element.focus();
230     window.document.commandDispatcher.suppressFocusScroll = false;
233 function do_repeatedly_positive(func, n) {
234     var args = Array.prototype.slice.call(arguments, 2);
235     while (n-- > 0)
236         func.apply(null, args);
239 function do_repeatedly(func, n, positive_args, negative_args) {
240     if (n < 0)
241         do func.apply(null, negative_args); while (++n < 0);
242     else
243         while (n-- > 0) func.apply(null, positive_args);
246 // remove whitespace from the beginning and end
247 function trim_whitespace (str)
249     var tmp = new String (str);
250     return tmp.replace (/^\s+/, "").replace (/\s+$/, "");
253 function abs_point (node)
255     var orig = node;
256     var pt = {};
257     try {
258         pt.x = node.offsetLeft;
259         pt.y = node.offsetTop;
260         // find imagemap's coordinates
261         if (node.tagName == "AREA") {
262             var coords = node.getAttribute("coords").split(",");
263             pt.x += Number(coords[0]);
264             pt.y += Number(coords[1]);
265         }
267         node = node.offsetParent;
268         // Sometimes this fails, so just return what we got.
270         while (node.tagName != "BODY") {
271             pt.x += node.offsetLeft;
272             pt.y += node.offsetTop;
273             node = node.offsetParent;
274         }
275     } catch(e) {
276 //      node = orig;
277 //      while (node.tagName != "BODY") {
278 //          alert("okay: " + node + " " + node.tagName + " " + pt.x + " " + pt.y);
279 //          node = node.offsetParent;
280 //      }
281     }
282     return pt;
285 var xul_app_info = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
286 var xul_runtime = Cc['@mozilla.org/xre/app-info;1'].getService(Ci.nsIXULRuntime);
289 function get_os ()
291     // possible return values: 'Darwin', 'Linux', 'WINNT', ...
292     return xul_runtime.OS;
295 var default_directory = null;
297 var env = Cc['@mozilla.org/process/environment;1'].getService(Ci.nsIEnvironment);
298 function getenv (variable) {
299     if (env.exists (variable))
300         return env.get(variable);
301     return null;
304 function set_default_directory(directory_s) {
305     if (! directory_s)
306     {
307         if ( get_os() == "WINNT")
308         {
309             directory_s = getenv ('USERPROFILE') ||
310                 getenv ('HOMEDRIVE') + getenv ('HOMEPATH');
311         }
312         else {
313             directory_s = getenv ('HOME');
314         }
315     }
317     default_directory = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
318     default_directory.initWithPath (directory_s);
321 set_default_directory();
323 const XHTML_NS = "http://www.w3.org/1999/xhtml";
324 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
325 const MATHML_NS = "http://www.w3.org/1998/Math/MathML";
326 const XLINK_NS = "http://www.w3.org/1999/xlink";
328 function create_XUL(window, tag_name)
330     return window.document.createElementNS(XUL_NS, tag_name);
334 /* Used in calls to XPath evaluate */
335 function xpath_lookup_namespace(prefix) {
336     if (prefix == "xhtml")
337         return XHTML_NS;
338     if (prefix == "m")
339         return MATHML_NS;
340     if (prefix == "xul")
341         return XUL_NS;
342     return null;
345 function method_caller(obj, func) {
346     return function () {
347         func.apply(obj, arguments);
348     }
351 function shell_quote(str) {
352     var s = str.replace("\"", "\\\"", "g");
353     s = s.replace("$", "\$", "g");
354     return s;
357 function get_window_from_frame(frame) {
358     try {
359         var window = frame.QueryInterface(Ci.nsIInterfaceRequestor)
360             .getInterface(Ci.nsIWebNavigation)
361             .QueryInterface(Ci.nsIDocShellTreeItem)
362             .rootTreeItem
363             .QueryInterface(Ci.nsIInterfaceRequestor)
364             .getInterface(Ci.nsIDOMWindow).wrappedJSObject;
365         /* window is now an XPCSafeJSObjectWrapper */
366         window.escape_wrapper(function (w) { window = w; });
367         /* window is now completely unwrapped */
368         return window;
369     } catch (e) {
370         return null;
371     }
374 function get_buffer_from_frame(window, frame) {
375     var count = window.buffers.count;
376     for (var i = 0; i < count; ++i) {
377         var b = window.buffers.get_buffer(i);
378         if (b.top_frame == frame)
379             return b;
380     }
381     return null;
384 var file_locator = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
386 function get_shortdoc_string(doc) {
387     var shortdoc = null;
388     if (doc != null) {
389         var idx = doc.indexOf("\n");
390         if (idx >= 0)
391             shortdoc = doc.substring(0,idx);
392         else
393             shortdoc = doc;
394     }
395     return shortdoc;
398 var conkeror_source_code_path = null;
400 function source_code_reference(uri, line_number) {
401     this.uri = uri;
402     this.line_number = line_number;
404 source_code_reference.prototype = {
405     get module_name () {
406         if (this.uri.indexOf(module_uri_prefix) == 0)
407             return this.uri.substring(module_uri_prefix.length);
408         return null;
409     },
411     get file_name () {
412         var file_uri_prefix = "file://";
413         if (this.uri.indexOf(file_uri_prefix) == 0)
414             return this.uri.substring(file_uri_prefix.length);
415         return null;
416     },
418     get best_uri () {
419         if (conkeror_source_code_path != null) {
420             var module_name = this.module_name;
421             if (module_name != null)
422                 return "file://" + conkeror_source_code_path + "/modules/" + module_name;
423         }
424         return this.uri;
425     },
427     open_in_editor : function() {
428         yield open_with_external_editor(this.best_uri, $line = this.line_number);
429     }
432 var get_caller_source_code_reference_ignored_functions = {};
434 function get_caller_source_code_reference(extra_frames_back) {
435     /* Skip at least this function itself and whoever called it (and
436      * more if the caller wants to be skipped). */
437     var frames_to_skip = 2;
438     if (extra_frames_back != null)
439         frames_to_skip += extra_frames_back;
441     for (let f = Components.stack; f != null; f = f.caller) {
442         if (frames_to_skip > 0) {
443             --frames_to_skip;
444             continue;
445         }
446         if (get_caller_source_code_reference_ignored_functions[f.name])
447             continue;
448         return new source_code_reference(f.filename, f.lineNumber);
449     }
451     return null;
454 function ignore_function_for_get_caller_source_code_reference(func_name) {
455     get_caller_source_code_reference_ignored_functions[func_name] = 1;
458 require_later("external-editor.js");
460 function dom_generator(document, ns) {
461     this.document = document;
462     this.ns = ns;
464 dom_generator.prototype = {
465     element : function(tag, parent) {
466         var node = this.document.createElementNS(this.ns, tag);
467         var i = 1;
468         if (parent != null && (parent instanceof Ci.nsIDOMNode)) {
469             parent.appendChild(node);
470             i = 2;
471         }
472         for (; i < arguments.length; i += 2)
473             node.setAttribute(arguments[i], arguments[i+1]);
474         return node;
475     },
477     text : function(str, parent) {
478         var node = this.document.createTextNode(str);
479         if (parent)
480             parent.appendChild(node);
481         return node;
482     },
485     stylesheet_link : function(href, parent) {
486         var node = this.element("link");
487         node.setAttribute("rel", "stylesheet");
488         node.setAttribute("type", "text/css");
489         node.setAttribute("href", href);
490         if (parent)
491             parent.appendChild(node);
492         return node;
493     },
496     add_stylesheet : function (url) {
497         var head = this.document.documentElement.firstChild;
498         this.stylesheet_link(url, head);
499     }
503  * Generates a QueryInterface function suitable for an implemenation
504  * of an XPCOM interface.  Unlike XPCOMUtils, this uses the Function
505  * constructor to generate a slightly more efficient version.  The
506  * arguments can be either Strings or elements of
507  * Components.interfaces.
508  */
509 function generate_QI() {
510     var args = Array.prototype.slice.call(arguments).map(String).concat(["nsISupports"]);
511     var fstr = "if(" +
512         Array.prototype.map.call(args,
513                                  function (x)
514                                      "iid.equals(Components.interfaces." + x + ")")
515         .join("||") +
516         ") return this; throw Components.results.NS_ERROR_NO_INTERFACE;";
517     return new Function("iid", fstr);
520 function set_branch_pref(branch, name, value) {
521     if (typeof(value) == "string") {
522         branch.setCharPref(name, value);
523     } else if (typeof(value) == "number") {
524         branch.setIntPref(name, value);
525     } else if (typeof(value) == "boolean") {
526         branch.setBoolPref(name, value);
527     }
530 function default_pref(name, value) {
531     var branch = preferences.getDefaultBranch(null);
532     set_branch_pref(branch, name, value);
535 function user_pref(name, value) {
536     var branch = preferences.getBranch(null);
537     set_branch_pref(branch, name, value);
540 function get_branch_pref(branch, name) {
541     switch (branch.getPrefType(name)) {
542     case branch.PREF_STRING:
543         return branch.getCharPref(name);
544     case branch.PREF_INT:
545         return branch.getIntPref(name);
546     case branch.PREF_BOOL:
547         return branch.getBoolPref(name);
548     default:
549         return null;
550     }
553 function get_localized_pref(name) {
554     try {
555         return preferences.getBranch(null).getComplexValue(name, Ci.nsIPrefLocalizedString).data;
556     } catch (e) {
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 pref_has_user_value(name) {
577     var branch = preferences.getBranch(null);
578     return branch.prefHasUserValue(name);
581 function pref_has_default_value(name) {
582     var branch = preferences.getDefaultBranch(null);
583     return branch.prefHasUserValue(name);
586 function session_pref (name, value) {
587     try { clear_pref (name); }
588     catch (e) {}
589     return default_pref (name, value);
592 const LOCALE_PREF = "general.useragent.locale";
594 function get_locale() {
595     return get_localized_pref(LOCALE_PREF) || get_pref(LOCALE_PREF);
598 const USER_AGENT_OVERRIDE_PREF = "general.useragent.override";
600 function set_user_agent(str) {
601     session_pref(USER_AGENT_OVERRIDE_PREF, str);
604 function define_builtin_commands(prefix, do_command_function, toggle_mark, mark_active_predicate) {
606     // Specify a docstring
607     function D(cmd, docstring) {
608         var o = new String(cmd);
609         o.doc = docstring;
610         return o;
611     }
613     // Specify a forward/reverse pair
614     function R(a, b) {
615         var o = [a,b];
616         o.is_reverse_pair = true;
617         return o;
618     }
620     // Specify a movement/select command pair.
621     function S(command, movement, select) {
622         var o = [movement, select];
623         o.command = command;
624         o.is_move_select_pair = true;
625         return o;
626     }
628     var builtin_commands = [
629         S(D("beginning-of-line", "Move or extend the selection to the beginning of the current line."),
630           D("cmd_beginLine", "Move point to the beginning of the current line."),
631           D("cmd_selectBeginLine", "Extend selection to the beginning of the current line.")),
632         S(D("end-of-line", "Move or extend the selection to the end of the current line."),
633           D("cmd_endLine", "Move point to the end of the current line."),
634           D("cmd_selectEndLine", "Extend selection to the end of the current line.")),
635         D("cmd_copy", "Copy the selection into the clipboard."),
636         "cmd_copyOrDelete",
637         D("cmd_cut", "Cut the selection into the clipboard."),
638         "cmd_cutOrDelete",
639         D("cmd_deleteToBeginningOfLine", "Delete to the beginning of the current line."),
640         D("cmd_deleteToEndOfLine", "Delete to the end of the current line."),
641         S(D("beginning-of-first-line", "Move or extend the selection to the beginning of the first line."),
642           D("cmd_moveTop", "Move point to the beginning of the first line."),
643           D("cmd_selectTop", "Extend selection to the beginning of the first line.")),
644         S(D("end-of-last-line", "Move or extend the selection to the end of the last line."),
645           D("cmd_moveBottom", "Move point to the end of the last line."),
646           D("cmd_selectBottom", "Extend selection to the end of the last line.")),
647         D("cmd_selectAll", "Select all."),
648         "cmd_scrollBeginLine",
649         "cmd_scrollEndLine",
650         D("cmd_scrollTop", "Scroll to the top of the buffer."),
651         D("cmd_scrollBottom", "Scroll to the bottom of the buffer.")];
653     var builtin_commands_with_count = [
654         R(S(D("forward-char", "Move or extend the selection forward one character."),
655             D("cmd_charNext", "Move point forward one character."),
656             D("cmd_selectCharNext", "Extend selection forward one character.")),
657           S(D("backward-char", "Move or extend the selection backward one character."),
658             D("cmd_charPrevious", "Move point backward one character."),
659             D("cmd_selectCharPrevious", "Extend selection backward one character."))),
660         R(D("cmd_deleteCharForward", "Delete the following character."),
661           D("cmd_deleteCharBackward", "Delete the previous character.")),
662         R(D("cmd_deleteWordForward", "Delete the following word."),
663           D("cmd_deleteWordBackward", "Delete the previous word.")),
664         R(S(D("forward-line", "Move or extend the selection forward one line."),
665             D("cmd_lineNext", "Move point forward one line."),
666             "cmd_selectLineNext", "Extend selection forward one line."),
667           S(D("backward-line", "Move or extend the selection backward one line."),
668             D("cmd_linePrevious", "Move point backward one line."),
669             D("cmd_selectLinePrevious", "Extend selection backward one line."))),
670         R(S(D("forward-page", "Move or extend the selection forward one page."),
671             D("cmd_movePageDown", "Move point forward one page."),
672             D("cmd_selectPageDown", "Extend selection forward one page.")),
673           S(D("backward-page", "Move or extend the selection backward one page."),
674             D("cmd_movePageUp", "Move point backward one page."),
675             D("cmd_selectPageUp", "Extend selection backward one page."))),
676         R(D("cmd_undo", "Undo last editing action."),
677           D("cmd_redo", "Redo last editing action.")),
678         R(S(D("forward-word", "Move or extend the selection forward one word."),
679             D("cmd_wordNext", "Move point forward one word."),
680             D("cmd_selectWordNext", "Extend selection forward one word.")),
681           S(D("backward-word", "Move or extend the selection backward one word."),
682             D("cmd_wordPrevious", "Move point backward one word."),
683             D("cmd_selectWordPrevious", "Extend selection backward one word."))),
684         R(D("cmd_scrollPageUp", "Scroll up one page."),
685           D("cmd_scrollPageDown", "Scroll down one page.")),
686         R(D("cmd_scrollLineUp", "Scroll up one line."),
687           D("cmd_scrollLineDown", "Scroll down one line.")),
688         R(D("cmd_scrollLeft", "Scroll left."),
689           D("cmd_scrollRight", "Scroll right.")),
690         D("cmd_paste", "Insert the contents of the clipboard.")];
692     interactive(prefix + "set-mark",
693                 "Toggle whether the mark is active.\n" +
694                 "When the mark is active, movement commands affect the selection.",
695                 toggle_mark);
697     function doc_for_builtin(c) {
698         var s = "";
699         if (c.doc != null)
700             s += c.doc + "\n";
701         return s + "Run the built-in command " + c + ".";
702     }
704     function define_simple_command(c) {
705         interactive(prefix + c, doc_for_builtin(c), function (I) { do_command_function(I, c); });
706     }
708     function get_move_select_doc_string(c) {
709         return c.command.doc +
710             "\nSpecifically, if the mark is inactive, runs `" + prefix + c[0] + "'.  " +
711             "Otherwise, runs `" + prefix + c[1] + "'.\n" +
712             "To toggle whether the mark is active, use `" + prefix + "set-mark'.";
713     }
715     for each (let c_temp in builtin_commands)  {
716         let c = c_temp;
717         if (c.is_move_select_pair) {
718             interactive(prefix + c.command, get_move_select_doc_string(c), function (I) {
719                 do_command_function(I, mark_active_predicate(I) ? c[1] : c[0]);
720             });
721             define_simple_command(c[0]);
722             define_simple_command(c[1]);
723         }
724         else
725             define_simple_command(c);
726     }
728     function get_reverse_pair_doc_string(main_doc, alt_command) {
729         return main_doc + "\n" +
730             "The prefix argument specifies a repeat count for this command.  " +
731             "If the count is negative, `" + prefix + alt_command + "' is performed instead with " +
732             "a corresponding positive repeat count.";
733     }
735     function define_simple_reverse_pair(a, b) {
736         interactive(prefix + a, get_reverse_pair_doc_string(doc_for_builtin(a), b),
737                     function (I) {
738                         do_repeatedly(do_command_function, I.p, [I, a], [I, b]);
739                     });
740         interactive(prefix + b, get_reverse_pair_doc_string(doc_for_builtin(b), a),
741                     function (I) {
742                         do_repeatedly(do_command_function, I.p, [I, b], [I, a]);
743                     });
744     }
746     for each (let c_temp in builtin_commands_with_count)
747     {
748         let c = c_temp;
749         if (c.is_reverse_pair) {
750             if (c[0].is_move_select_pair) {
751                 interactive(prefix + c[0].command, get_reverse_pair_doc_string(get_move_select_doc_string(c[0]),
752                                                                                c[1].command),
753                             function (I) {
754                                 var idx = mark_active_predicate(I) ? 1 : 0;
755                                 do_repeatedly(do_command_function, I.p, [I, c[0][idx]], [I, c[1][idx]]);
756                             });
757                 interactive(prefix + c[1].command, get_reverse_pair_doc_string(get_move_select_doc_string(c[1]),
758                                                                                c[0].command),
759                             function (I) {
760                                 var idx = mark_active_predicate(I) ? 1 : 0;
761                                 do_repeatedly(do_command_function, I.p, [I, c[1][idx]], [I, c[0][idx]]);
762                             });
763                 define_simple_reverse_pair(c[0][0], c[1][0]);
764                 define_simple_reverse_pair(c[0][1], c[1][1]);
765             } else
766                 define_simple_reverse_pair(c[0], c[1]);
767         } else {
768             let doc = doc_for_builtin(c) +
769                 "\nThe prefix argument specifies a positive repeat count for this command.";
770             interactive(prefix + c, doc, function (I) {
771                 do_repeatedly_positive(do_command_function, I.p, I, c);
772             });
773         }
774     }
777 var observer_service = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
779 function abort(str) {
780     var e = new Error(str);
781     e.__proto__ = abort.prototype;
782     return e;
784 abort.prototype.__proto__ = Error.prototype;
787 function get_temporary_file(name) {
788     if (name == null)
789         name = "temp.txt";
790     var file = file_locator.get("TmpD", Ci.nsIFile);
791     file.append(name);
792     // Create the file now to ensure that no exploits are possible
793     file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
794     return file;
798 /* FIXME: This should be moved somewhere else, perhaps. */
799 function create_info_panel(window, panel_class, row_arr) {
800     /* Show information panel above minibuffer */
802     var g = new dom_generator(window.document, XUL_NS);
804     var p = g.element("vbox", "class", "panel " + panel_class, "flex", "0");
805     var grid = g.element("grid", p);
806     var cols = g.element("columns", grid);
807     g.element("column", cols, "flex", "0");
808     g.element("column", cols, "flex", "1");
810     var rows = g.element("rows", grid);
811     var row;
813     for each (let [row_class, row_label, row_value] in row_arr) {
814         row = g.element("row", rows, "class", row_class);
815         g.element("label", row,
816                   "value", row_label,
817                   "class", "panel-row-label");
818         g.element("label", row,
819                   "value", row_value,
820                   "class", "panel-row-value");
821     }
822     window.minibuffer.insert_before(p);
824     p.destroy = function () {
825         this.parentNode.removeChild(this);
826     };
828     return p;
832 // read_from_x_primary_selection favors the X PRIMARY SELECTION, when
833 // it exists.  The builtin cmd_paste always uses X CLIPBOARD.  So this
834 // is an auxiliary utility, in case you need to work with the primary
835 // selection.
837 function read_from_x_primary_selection ()
839     // Get clipboard.
840     var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
841         .getService(Components.interfaces.nsIClipboard);
843     // Create tranferable that will transfer the text.
844     var trans = Components.classes["@mozilla.org/widget/transferable;1"]
845         .createInstance(Components.interfaces.nsITransferable);
847     trans.addDataFlavor("text/unicode");
848     // If available, use selection clipboard, otherwise global one
849     if (clipboard.supportsSelectionClipboard())
850         clipboard.getData(trans, clipboard.kSelectionClipboard);
851     else
852         clipboard.getData(trans, clipboard.kGlobalClipboard);
854     var data = {};
855     var dataLen = {};
856     trans.getTransferData("text/unicode", data, dataLen);
858     if (data) {
859         data = data.value.QueryInterface(Components.interfaces.nsISupportsString);
860         return data.data.substring(0, dataLen.value / 2);
861     } else {
862         return "";
863     }
866 var user_variables = new string_hashmap();
868 function define_variable(name, default_value, doc) {
869     conkeror[name] = default_value;
870     user_variables.put(name, {
871         default_value: default_value,
872         doc: doc,
873         shortdoc: get_shortdoc_string(doc),
874         source_code_reference: get_caller_source_code_reference() });
878 function register_user_stylesheet(url)
880     var uri = makeURL(url);
881     var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
882     sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
885 function unregister_user_stylesheet(url)
887     var uri = makeURL(url);
888     var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
889     if (sss.sheetRegistered(uri, sss.USER_SHEET))
890         ss.unregisterSheet(uri, sss.USER_SHEET);
893 function predicate_alist_match(alist, key) {
894     for each (let i in alist) {
895         if (i[0](key))
896             return i[1];
897     }
898     return undefined;
902 function get_meta_title(doc) {
903     var title = doc.evaluate("//meta[@name='title']/@content", doc, xpath_lookup_namespace,
904                              Ci.nsIDOMXPathResult.STRING_TYPE , null);
905     if (title && title.stringValue)
906         return title.stringValue;
907     return null;
910 var rdf_service = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
912 const PREFIX_ITEM_URI     = "urn:mozilla:item:";
913 const PREFIX_NS_EM        = "http://www.mozilla.org/2004/em-rdf#";
915 var extension_manager = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
917 function get_extension_rdf_property(id, name, type) {
918     var value = extension_manager.datasource.GetTarget(
919         rdf_service.GetResource(PREFIX_ITEM_URI + id),
920         rdf_service.GetResource(PREFIX_NS_EM + name),
921         true);
922     if (value == null)
923         return null;
924     return value.QueryInterface(type || Ci.nsIRDFLiteral).Value;
927 function get_extension_update_item(id) {
928     return extension_manager.getItemForID(id);
931 function extension_info(id) {
932     this.id = id;
934 extension_info.prototype = {
935     // Returns the nsIUpdateItem object associated with this extension
936     get update_item () { return get_extension_update_item(this.id); },
938     get_rdf_property : function (name, type) {
939         return get_extension_rdf_property(this.id, name, type);
940     },
942     // RDF properties
943     get isDisabled () { return this.get_rdf_property("isDisabled"); },
944     get aboutURL () { return this.get_rdf_property("aboutURL"); },
945     get addonID () { return this.get_rdf_property("addonID"); },
946     get availableUpdateURL () { return this.get_rdf_property("availableUpdateURL"); },
947     get availableUpdateVersion () { return this.get_rdf_property("availableUpdateVersion"); },
948     get blocklisted () { return this.get_rdf_property("blocklisted"); },
949     get compatible () { return this.get_rdf_property("compatible"); },
950     get description () { return this.get_rdf_property("description"); },
951     get downloadURL () { return this.get_rdf_property("downloadURL"); },
952     get isDisabled () { return this.get_rdf_property("isDisabled"); },
953     get hidden () { return this.get_rdf_property("hidden"); },
954     get homepageURL () { return this.get_rdf_property("homepageURL"); },
955     get iconURL () { return this.get_rdf_property("iconURL"); },
956     get internalName () { return this.get_rdf_property("internalName"); },
957     get locked () { return this.get_rdf_property("locked"); },
958     get name () { return this.get_rdf_property("name"); },
959     get optionsURL () { return this.get_rdf_property("optionsURL"); },
960     get opType () { return this.get_rdf_property("opType"); },
961     get plugin () { return this.get_rdf_property("plugin"); },
962     get previewImage () { return this.get_rdf_property("previewImage"); },
963     get satisfiesDependencies () { return this.get_rdf_property("satisfiesDependencies"); },
964     get providesUpdatesSecurely () { return this.get_rdf_property("providesUpdatesSecurely"); },
965     get type () { return this.get_rdf_property("type", Ci.nsIRDFInt); },
966     get updateable () { return this.get_rdf_property("updateable"); },
967     get updateURL () { return this.get_rdf_property("updateURL"); },
968     get version () { return this.get_rdf_property("version"); }
971 function extension_is_enabled(id) {
972     var info = new extension_info(id);
973     return info.update_item && (info.isDisabled == "false");
976 function for_each_frame(root_frame, callback, start_with) {
977     if (start_with)
978         callback(start_with);
979     function helper(f) {
980         if (f == start_with)
981             return;
982         callback(f);
983         for (let i = 0; i < f.frames.length; ++i) {
984             helper(f.frames[i]);
985         }
986     }
987     helper(root_frame);
990 // Co-routine version of for_each_frame
991 function co_for_each_frame(root_frame, callback, start_with) {
992     if (start_with)
993         yield callback(start_with);
994     function helper(f) {
995         if (f == start_with)
996             return;
997         yield callback(f);
998         for (let i = 0; i < f.frames.length; ++i) {
999             yield helper(f.frames[i]);
1000         }
1001     }
1002     yield helper(root_frame);
1005 function xml_http_request() {
1006     return Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest).QueryInterface(Ci.nsIJSXMLHttpRequest).QueryInterface(Ci.nsIDOMEventTarget);
1009 var xml_http_request_load_listener = {
1010   // nsIBadCertListener2
1011   notifyCertProblem: function SSLL_certProblem(socketInfo, status, targetSite) {
1012     return true;
1013   },
1015   // nsISSLErrorListener
1016   notifySSLError: function SSLL_SSLError(socketInfo, error, targetSite) {
1017     return true;
1018   },
1020   // nsIInterfaceRequestor
1021   getInterface: function SSLL_getInterface(iid) {
1022     return this.QueryInterface(iid);
1023   },
1025   // nsISupports
1026   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBadCertListener2,
1027                                          Ci.nsISSLErrorListener,
1028                                          Ci.nsIInterfaceRequestor])
1032 define_keywords("$user", "$password", "$override_mime_type", "$headers");
1033 function send_http_request(lspec) {
1034     keywords(arguments, $user = undefined, $password = undefined,
1035              $override_mime_type = undefined, $headers = undefined);
1036     var req = xml_http_request();
1037     var cc = yield CONTINUATION;
1038     req.onreadystatechange = function () {
1039         if (req.readyState != 4)
1040             return;
1041         cc();
1042     };
1044     if (arguments.$override_mime_type)
1045         req.overrideMimeType(arguments.$override_mime_type);
1047     var post_data = load_spec_raw_post_data(lspec);
1049     var method = post_data ? "POST" : "GET";
1051     req.open(method, load_spec_uri_string(lspec), true, arguments.$user, arguments.$password);
1052     req.channel.notificationCallbacks = xml_http_request_load_listener;
1054     for each (let [name,value] in arguments.$headers) {
1055         req.setRequestHeader(name, value);
1056     }
1058     if (post_data) {
1059         req.setRequestHeader("Content-Type", load_spec_request_mime_type(lspec));
1060         req.send(post_data);
1061     } else
1062         req.send(null);
1064     try {
1065         yield SUSPEND;
1066     } catch (e) {
1067         req.abort();
1068         throw e;
1069     }
1071     // Let the caller access the status and reponse data
1072     yield co_return(req);
1076 var JSON = ("@mozilla.org/dom/json;1" in Cc) && Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);