browser-next-form-field: skip non-visible elements
[conkeror.git] / modules / suggest-file-name.js
blob267cbd398cbf3713b0541ec56a5f1a03ab8e1982
1 /**
2  * (C) Copyright 2007 John J. Foerch
3  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard.
4  *
5  * Portions of this file were derived from Mozilla,
6  * (C) Copyright 1998-2007 Mozilla Foundation.
7  *
8  * Use, modification, and distribution are subject to the terms specified in the
9  * COPYING file.
10 **/
12 /* maybe_get_filename_extension
13  *
14  * file_name_s: string filename, may be null.
15  *
16  * returns null or extension part of file_name_s.
17  */
18 function maybe_get_filename_extension (file_name_s) {
19     var url = Components.classes["@mozilla.org/network/standard-url;1"]
20         .createInstance(Components.interfaces.nsIURL);
21     url.filePath = file_name_s;
22     if (url.fileExtension == '')
23         return null;
24     return url.fileExtension;
28 function maybe_get_url_extension (url_o) {
29     try {
30         var url = url_o.QueryInterface(Components.interfaces.nsIURL);
31         if (url.fileExtension == '')
32             return null;
33         return url.fileExtension;
34     } catch (e) {
35         return null;
36     }
39 /* maybe_get_preferred_filename_extension
40  *
41  * file_name_s: string filename, may be null.
42  *
43  * content_type: string content type, may be null.
44  *
45  * returns null, the default extension for the given
46  * content type, or the extension part of file_name_s.
47  */
48 function maybe_get_preferred_filename_extension (file_name_s, content_type)
50     var ext = maybe_get_filename_extension (file_name_s);
51     var mimeInfo = null;
52     var primary = null;
53     if (content_type) {
54         try {
55             // throws if content_type is an empty string
56             mimeInfo = Components.classes["@mozilla.org/mime;1"]
57                 .getService(Components.interfaces.nsIMIMEService)
58                 .getFromTypeAndExtension(content_type, ext);
59             primary = mimeInfo.primaryExtension;
60         } catch (e) { }
61     }
62     if (ext && mimeInfo && mimeInfo.extensionExists (ext))
63         return ext;
64     else if (primary)
65         return primary;
66     else
67         return ext;
71 function maybe_get_preferred_url_extension (url_o, content_type) {
72     var ext = maybe_get_url_extension (url_o);
73     var mimeInfo = null;
74     var primary = null;
75     if (content_type) {
76         try {
77             mimeInfo = Components.classes["@mozilla.org/mime;1"]
78                 .getService(Components.interfaces.nsIMIMEService)
79                 .getFromTypeAndExtension(content_type, null);
80             primary = mimeInfo.primaryExtension;
81         } catch (e) { }
82     }
83     if (ext && mimeInfo && mimeInfo.extensionExists (ext))
84         return ext;
85     else if (primary)
86         return primary;
87     else
88         return ext;
91 function get_default_extension (file_name_s, url_o, content_type) {
92     if (content_type == "text/plain" ||
93         content_type == "application/octet-stream" ||
94         url_o.scheme == "ftp")
95     {
96         return "";
97     }
98     return (maybe_get_preferred_filename_extension (file_name_s, content_type) ||
99             maybe_get_preferred_url_extension (url_o, content_type));
102 function get_charset_for_save(aDocument)
104     if (aDocument)
105         return aDocument.characterSet;
106     return null;
110 function maybe_filename_from_content_disposition (aContentDisposition, charset) {
111     if (aContentDisposition) {
112         const mhp = Components.classes["@mozilla.org/network/mime-hdrparam;1"]
113             .getService(Components.interfaces.nsIMIMEHeaderParam);
114         var dummy = { value: null };  // Need an out param...
116         var fileName = null;
117         try {
118             fileName = mhp.getParameter(aContentDisposition, "filename", charset, true, dummy);
119         } catch (e) {
120             try {
121                 fileName = mhp.getParameter(aContentDisposition, "name", charset, true, dummy);
122             } catch (e) { }
123         }
124         if (fileName)
125             return fileName;
126         else
127             return null;
128     }
129     return null;
132 function maybe_filename_from_uri(uri) {
133     try {
134         var url = uri.QueryInterface(Components.interfaces.nsIURL);
135         if (url.fileName != "") {
136             // 2) Use the actual file name, if present
137             var textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].getService(Ci.nsITextToSubURI);
138             return textToSubURI.unEscapeURIForUI(url.originCharset || "UTF-8", url.fileName);
139         }
140     } catch (e) {
141         // This is something like a data: and so forth URI... no filename here.
142     }
143     return null;
146 function maybe_filename_from_title(title) {
147     if (title) {
148         var docTitle = title.replace(/^\s+|\s+$/g, "");
149         if (docTitle && docTitle.length > 0) {
150             // 3) Use the document title
151             return docTitle;
152         }
153     }
154     return null;
157 function maybe_filename_from_url_last_directory (aURI) {
158     // 5) If this is a directory, use the last directory name
159     try {
160     var path = aURI.path.match(/\/([^\/]+)\/$/);
161     if (path && path.length > 1)
162         return path[1];
163     return null;
164     } catch (e) {
165         return null;
166     }
169 function maybe_filename_from_url_host (aURI) {
170     if (aURI && 'host' in aURI)
171         return aURI.host;
172     return null;
175 function maybe_filename_from_localization_default () {
176     try {
177         return getStringBundle().GetStringFromName("DefaultSaveFileName");
178     } catch (e) {
179         return null;
180     }
183 function generate_filename_safely_default (filename) {
184     return filename.replace (/[\/]+/g, '_');
187 function generate_filename_safely_darwin (filename) {
188     return filename.replace(/[\:\/]+/g, '_');
191 function generate_filename_safely_winnt (filename)
193     filename = filename.replace (/[\"]+/g,     "'");
194     filename = filename.replace (/[\*\:\?]+/g, ' ');
195     filename = filename.replace (/[\<]+/g,     '(');
196     filename = filename.replace (/[\>]+/g,     ')');
197     filename = filename.replace (/[\\\/\|]+/g, '_');
198     return filename;
202 var generate_filename_safely_fn = null;
204 switch (get_os()) {
205 case 'Darwin':
206     generate_filename_safely_fn = generate_filename_safely_darwin;
207     break;
208 case 'WINNT':
209     generate_filename_safely_fn = generate_filename_safely_winnt;
210     break;
211 default:
212     generate_filename_safely_fn = generate_filename_safely_default;
213     break;
217  * spec may be a string (URI), a load spec, or an nsIDOMDocument
219  * extension may be null, in which case an extension is suggested as well
220  */
221 function suggest_file_name(spec, extension) {
222     var document;
223     var uri;
224     var content_type;
226     if (spec instanceof Ci.nsIDOMDocument)
227         spec = {document: spec};
229     var file_name = load_spec_filename(spec);
231     document = load_spec_document(spec);
232     uri = load_spec_uri(spec);
233     content_type = load_spec_mime_type(spec);
235     if (!file_name) {
236         file_name = generate_filename_safely_fn(
237             maybe_filename_from_content_disposition(document && get_document_content_disposition(document),
238                                                     get_charset_for_save(document)) ||
239             ((spec.suggest_filename_from_uri != false) && maybe_filename_from_uri(uri)) ||
240             maybe_filename_from_title(load_spec_title(spec)) ||
241             maybe_filename_from_url_last_directory(uri) ||
242             maybe_filename_from_url_host(uri) ||
243             maybe_filename_from_localization_default() ||
244             "index");
246     }
247     var base_name = file_name.replace(/\.[^.]*$/, "");
249     var file_ext = extension || load_spec_filename_extension(spec);
251     if (!file_ext) {
252         file_ext = get_default_extension(file_name, uri, content_type);
253         if (file_ext == "") {
254             let x = file_name.lastIndexOf(".");
255             if (x == -1)
256                 file_ext = null;
257             else
258                 file_ext = file_name.substring(x+1);
259         }
260         if (!file_ext && (/^http(s?)/i).test(uri.scheme) && !content_type ||
261             content_type == "application/octet-stream")
262             file_ext = "html";
263     }
265     if (file_ext != null && file_ext.length > 0)
266         return base_name + "." + file_ext;
267     else
268         return base_name;
271 function suggest_save_path_from_file_name(file_name, buffer) {
272     var cwd = (buffer && buffer.cwd) || default_directory.path;
273     var file = get_file(cwd);
274     file.append(file_name);
275     return file.path;