suggest_file_name: refactoring and rewriting a bit
[conkeror.git] / modules / suggest-file-name.js
blobe320ce290602e2ad2bfba8cf57f952b3a253f939
1 /**
2  * (C) Copyright 2007,2010 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 in_module(null);
14 /**
15  * maybe_get_filename_extension
16  *
17  * file_name_s: string filename, may be null.
18  *
19  * returns null or extension part of file_name_s.
20  */
21 function maybe_get_filename_extension (file_name_s) {
22     var url = Cc["@mozilla.org/network/standard-url;1"]
23         .createInstance(Ci.nsIURL);
24     url.filePath = file_name_s;
25     if (url.fileExtension == '')
26         return null;
27     return url.fileExtension;
31 function maybe_get_url_extension (url_o) {
32     try {
33         var url = url_o.QueryInterface(Ci.nsIURL);
34         if (url.fileExtension == '')
35             return null;
36         return url.fileExtension;
37     } catch (e) {
38         return null;
39     }
42 /**
43  * maybe_get_preferred_filename_extension
44  *
45  * file_name_s: string filename, may be null.
46  *
47  * content_type: string content type, may be null.
48  *
49  * returns null, the default extension for the given
50  * content type, or the extension part of file_name_s.
51  */
52 function maybe_get_preferred_filename_extension (file_name_s, content_type) {
53     var ext = maybe_get_filename_extension (file_name_s);
54     var mimeInfo = null;
55     var primary = null;
56     if (content_type) {
57         try {
58             // throws if content_type is an empty string
59             mimeInfo = Cc["@mozilla.org/mime;1"]
60                 .getService(Ci.nsIMIMEService)
61                 .getFromTypeAndExtension(content_type, ext);
62             primary = mimeInfo.primaryExtension;
63         } catch (e) { }
64     }
65     if (ext && mimeInfo && mimeInfo.extensionExists(ext))
66         return ext;
67     else if (primary)
68         return primary;
69     else
70         return ext;
74 function maybe_get_preferred_url_extension (url_o, content_type) {
75     var ext = maybe_get_url_extension(url_o);
76     var mimeInfo = null;
77     var primary = null;
78     if (content_type) {
79         try {
80             mimeInfo = Cc["@mozilla.org/mime;1"]
81                 .getService(Ci.nsIMIMEService)
82                 .getFromTypeAndExtension(content_type, null);
83             primary = mimeInfo.primaryExtension;
84         } catch (e) { }
85     }
86     if (ext && mimeInfo && mimeInfo.extensionExists(ext))
87         return ext;
88     else if (primary)
89         return primary;
90     else
91         return ext;
94 function get_default_extension (file_name_s, url_o, content_type) {
95     if (content_type == "text/plain" ||
96         content_type == "application/octet-stream" ||
97         url_o.scheme == "ftp")
98     {
99         return "";
100     }
101     return (maybe_get_preferred_filename_extension(file_name_s, content_type) ||
102             maybe_get_preferred_url_extension(url_o, content_type));
105 function get_charset_for_save (doc) {
106     if (doc)
107         return doc.characterSet;
108     return null;
111 function maybe_filename_from_content_disposition (spec) {
112     var document = load_spec_document(spec);
113     var content_disposition = (document && get_document_content_disposition(document));
114     var charset = get_charset_for_save(document);
115     if (content_disposition) {
116         const mhp = Cc["@mozilla.org/network/mime-hdrparam;1"]
117             .getService(Ci.nsIMIMEHeaderParam);
118         var dummy = { value: null };  // Need an out param...
120         var filename = null;
121         try {
122             filename = mhp.getParameter(content_disposition, "filename", charset, true, dummy);
123         } catch (e) {
124             try {
125                 filename = mhp.getParameter(content_disposition, "name", charset, true, dummy);
126             } catch (e) { }
127         }
128         if (filename)
129             return filename;
130         else
131             return null;
132     }
133     return null;
136 function maybe_filename_from_uri (spec) {
137     var uri = load_spec_uri(spec);
138     try {
139         var url = uri.QueryInterface(Ci.nsIURL);
140         if (url.fileName != "") {
141             // 2) Use the actual file name, if present
142             var text_to_sub_uri = Cc["@mozilla.org/intl/texttosuburi;1"].
143                 getService(Ci.nsITextToSubURI);
144             return text_to_sub_uri.unEscapeURIForUI(url.originCharset ||
145                                                     "UTF-8", url.fileName);
146         }
147     } catch (e) {
148         // This is something like a data: and so forth URI... no filename here.
149     }
150     return null;
153 function maybe_filename_from_title_if_no_document (spec) {
154     if (load_spec_document(spec))
155         return null;
156     var title = load_spec_title(spec);
157     if (title) {
158         title = trim_whitespace(title);
159         if (title)
160             return title + ".";
161     }
162     return null;
165 function maybe_filename_from_url_last_directory (spec) {
166     var uri = load_spec_uri(spec);
167     try {
168         var path = uri.path.match(/\/([^\/]+)\/$/);
169         if (path && path.length > 1)
170             return path[1] + ".";
171         return null;
172     } catch (e) {
173         return null;
174     }
177 function maybe_filename_from_url_host (spec) {
178     var uri = load_spec_uri(spec);
179     if (uri && 'host' in uri) {
180         try {
181             return uri.host + ".";
182         } catch (e) {}
183     }
184     return null;
187 function maybe_filename_from_localization_default () {
188     try {
189         return getStringBundle().GetStringFromName("DefaultSaveFileName");
190     } catch (e) {
191         return null;
192     }
197  * Sanitize filename for various platforms.
198  */
200 function generate_filename_safely_default (filename) {
201     return filename.replace(/[\/]+/g, '_');
204 function generate_filename_safely_darwin (filename) {
205     return filename.replace(/[\:\/]+/g, '_');
208 function generate_filename_safely_winnt (filename) {
209     filename = filename.replace(/[\"]+/g,     "'");
210     filename = filename.replace(/[\*\:\?]+/g, ' ');
211     filename = filename.replace(/[\<]+/g,     '(');
212     filename = filename.replace(/[\>]+/g,     ')');
213     filename = filename.replace(/[\\\/\|]+/g, '_');
214     return filename;
218 define_variable("generate_filename_safely_fn",
219     ({ Darwin: generate_filename_safely_darwin,
220        WINNT: generate_filename_safely_winnt }[get_os()]
221      || generate_filename_safely_default),
222     "Function to sanitize the filenames generated by suggest_file_name "+
223     "for the current platform.");
227  * spec may be a string (URI), a load spec, or an nsIDOMDocument
229  * extension may be null, in which case an extension is suggested as well
230  */
231 function suggest_file_name (spec, extension) {
232     if (typeof spec == "string" || spec instanceof Ci.nsIDOMDocument)
233         spec = load_spec(spec);
235     var file_name = load_spec_filename(spec);
236     var uri = load_spec_uri(spec);
237     var content_type = load_spec_mime_type(spec);
239     if (! file_name) {
240         file_name = generate_filename_safely_fn(
241             maybe_filename_from_content_disposition(spec) ||
242             maybe_filename_from_title_if_no_document(spec) ||
243             maybe_filename_from_uri(spec) ||
244             maybe_filename_from_url_last_directory(spec) ||
245             maybe_filename_from_url_host(spec) ||
246             maybe_filename_from_localization_default() ||
247             "index");
248     }
249     var base_name = file_name.replace(/\.[^.]*$/, "");
251     var file_ext = extension || load_spec_filename_extension(spec);
253     if (! file_ext) {
254         file_ext = get_default_extension(file_name, uri, content_type);
255         if (file_ext == "") {
256             let x = file_name.lastIndexOf(".");
257             if (x == -1)
258                 file_ext = null;
259             else
260                 file_ext = file_name.substring(x+1);
261         }
262         if (!file_ext && (/^http(s?)/i).test(uri.scheme) && !content_type ||
263             content_type == "application/octet-stream")
264         {
265             file_ext = "html";
266         }
267     }
269     if (file_ext != null && file_ext.length > 0)
270         return base_name + "." + file_ext;
271     else
272         return base_name;
275 function suggest_save_path_from_file_name (file_name, buffer) {
276     var cwd = with_current_buffer(buffer, function (I) I.local.cwd);
277     var file = cwd.clone();
278     file.append(file_name);
279     return file.path;
282 provide("suggest-file-name");