3 var search_engines = new string_hashmap();
5 function search_engine_parse_error(msg) {
6 var e = new Error(msg);
7 e.__proto__ = search_engine_parse_error.prototype;
10 search_engine_parse_error.prototype.__proto__ = Error.prototype;
12 function search_engine() {
13 this.urls = new string_hashmap();
16 function search_engine_url(type, method, template) {
17 if (!method || !type || !template)
18 throw search_engine_parse_error("Missing method, type, or template for search engine URL");
19 method = method.toUpperCase();
20 type = type.toUpperCase();
21 if (method != "GET" && method != "POST")
22 throw search_engine_parse_error("Invalid method");
23 var template_uri = make_uri(template);
24 switch (template_uri.scheme) {
29 throw search_engine_parse_error("URL template has invalid scheme.");
34 this.template = template;
37 search_engine_url.prototype.add_param = function search_engine_url__add_param(name, value) {
38 this.params.push({name: name, value: value});
41 function load_search_engines_in_directory(dir) {
44 files = dir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
46 while (files.hasMoreElements()) {
47 var file = files.nextFile;
53 load_search_engine_from_file(file);
55 dumpln("WARNING: Failed to load search engine from file: " + file.path);
60 // FIXME: maybe have a better error message
68 function load_search_engine_from_file(file) {
69 var file_istream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
70 file_istream.init(file, MODE_RDONLY, 0644, false);
71 var dom_parser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
72 var doc = dom_parser.parseFromStream(file_istream, "UTF-8", file.fileSize, "text/xml");
74 var eng = parse_search_engine_from_dom_node(doc.documentElement);
75 search_engines.put(file.leafName, eng);
78 // Supported OpenSearch parameters
79 // http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_URL_template_syntax
80 const OPENSEARCH_PARAM_USER_DEFINED = /\{searchTerms\??\}/g;
81 const OPENSEARCH_PARAM_INPUT_ENCODING = /\{inputEncoding\??\}/g;
82 const OPENSEARCH_PARAM_LANGUAGE = /\{language\??\}/g;
83 const OPENSEARCH_PARAM_OUTPUT_ENCODING = /\{outputEncoding\??\}/g;
86 const OPENSEARCH_PARAM_LANGUAGE_DEF = "*";
87 const OPENSEARCH_PARAM_OUTPUT_ENCODING_DEF = "UTF-8";
88 const OPENSEARCH_PARAM_INPUT_ENCODING_DEF = "UTF-8";
90 // "Unsupported" OpenSearch parameters. For example, we don't support
91 // page-based results, so if the engine requires that we send the "page index"
92 // parameter, we'll always send "1".
93 const OPENSEARCH_PARAM_COUNT = /\{count\??\}/g;
94 const OPENSEARCH_PARAM_START_INDEX = /\{startIndex\??\}/g;
95 const OPENSEARCH_PARAM_START_PAGE = /\{startPage\??\}/g;
98 const OPENSEARCH_PARAM_COUNT_DEF = "20"; // 20 results
99 const OPENSEARCH_PARAM_START_INDEX_DEF = "1"; // start at 1st result
100 const OPENSEARCH_PARAM_START_PAGE_DEF = "1"; // 1st page
102 // Optional parameter
103 const OPENSEARCH_PARAM_OPTIONAL = /\{(?:\w+:)?\w+\?\}/g;
105 // A array of arrays containing parameters that we don't fully support, and
106 // their default values. We will only send values for these parameters if
107 // required, since our values are just really arbitrary "guesses" that should
108 // give us the output we want.
109 var OPENSEARCH_UNSUPPORTED_PARAMS = [
110 [OPENSEARCH_PARAM_COUNT, OPENSEARCH_PARAM_COUNT_DEF],
111 [OPENSEARCH_PARAM_START_INDEX, OPENSEARCH_PARAM_START_INDEX_DEF],
112 [OPENSEARCH_PARAM_START_PAGE, OPENSEARCH_PARAM_START_PAGE_DEF],
116 function parse_search_engine_from_dom_node(node) {
117 var eng = new search_engine();
118 eng.query_charset = OPENSEARCH_PARAM_INPUT_ENCODING_DEF;
120 for each (let child in node.childNodes) {
121 switch (child.localName) {
123 eng.name = child.textContent;
126 eng.description = child.textContent;
130 let type = child.getAttribute("type");
131 let method = child.getAttribute("method") || "GET";
132 let template = child.getAttribute("template");
134 let engine_url = new search_engine_url(type, method, template);
135 for each (let p in child.childNodes) {
136 if (p.localName == "Param") {
137 let name = p.getAttribute("name");
138 let value = p.getAttribute("value");
140 engine_url.add_param(name, value);
143 eng.urls.put(type, engine_url);
145 // Skip this element if parsing fails
148 case "InputEncoding":
149 eng.query_charset = child.textContent.toUpperCase();
157 * Returns null if the result mime_type isn't supported. The string
158 * search_terms will be escaped by this function.
160 search_engine.prototype.get_query_load_spec = function search_engine__get_query_load_spec(search_terms, type) {
163 var url = this.urls.get(type);
166 search_terms = encodeURIComponent(search_terms);
169 function substitute(value) {
170 // Insert the OpenSearch parameters we're confident about
171 value = value.replace(OPENSEARCH_PARAM_USER_DEFINED, search_terms);
172 value = value.replace(OPENSEARCH_PARAM_INPUT_ENCODING, eng.query_charset);
173 value = value.replace(OPENSEARCH_PARAM_LANGUAGE,
174 get_locale() || OPENSEARCH_PARAM_LANGUAGE_DEF);
175 value = value.replace(OPENSEARCH_PARAM_OUTPUT_ENCODING,
176 OPENSEARCH_PARAM_OUTPUT_ENCODING_DEF);
178 // Replace any optional parameters
179 value = value.replace(OPENSEARCH_PARAM_OPTIONAL, "");
181 // Insert any remaining required params with our default values
182 for (let i = 0; i < OPENSEARCH_UNSUPPORTED_PARAMS.length; ++i) {
183 value = value.replace(OPENSEARCH_UNSUPPORTED_PARAMS[i][0],
184 OPENSEARCH_UNSUPPORTED_PARAMS[i][1]);
190 var url_string = substitute(url.template);
192 var data = url.params.map(function (p) (p.name + "=" + substitute(p.value))).join("&");
194 if (url.method == "GET") {
195 if (data.length > 0) {
196 if (url_string.indexOf("?") == -1)
202 return load_spec({uri: uri_string});
204 var string_stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
205 string_stream.data = data;
206 return load_spec({uri: uri_string, raw_post_data: data,
207 request_mime_type: "application/x-www-form-urlencoded"});
211 // Load search engines from default directories
213 let dir = file_locator.get("CurProcD", Ci.nsIFile);
214 dir.append("search-engines");
215 if (dir.exists() && dir.isDirectory())
216 load_search_engines_in_directory(dir);
218 dir = file_locator.get("ProfD", Ci.nsIFile);
219 dir.append("search-engines");
220 if (dir.exists() && dir.isDirectory())
221 load_search_engines_in_directory(dir);