view-as-mime-type: fix bug in interactive declaration
[conkeror.git] / modules / suggest-file-name.js
blob987a2fb4471e744a918ec4ae67cc85b4b98ea88b
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) {
49     var ext = maybe_get_filename_extension (file_name_s);
50     var mimeInfo = null;
51     var primary = null;
52     if (content_type) {
53         try {
54             // throws if content_type is an empty string
55             mimeInfo = Components.classes["@mozilla.org/mime;1"]
56                 .getService(Components.interfaces.nsIMIMEService)
57                 .getFromTypeAndExtension(content_type, ext);
58             primary = mimeInfo.primaryExtension;
59         } catch (e) { }
60     }
61     if (ext && mimeInfo && mimeInfo.extensionExists(ext))
62         return ext;
63     else if (primary)
64         return primary;
65     else
66         return ext;
70 function maybe_get_preferred_url_extension (url_o, content_type) {
71     var ext = maybe_get_url_extension(url_o);
72     var mimeInfo = null;
73     var primary = null;
74     if (content_type) {
75         try {
76             mimeInfo = Components.classes["@mozilla.org/mime;1"]
77                 .getService(Components.interfaces.nsIMIMEService)
78                 .getFromTypeAndExtension(content_type, null);
79             primary = mimeInfo.primaryExtension;
80         } catch (e) { }
81     }
82     if (ext && mimeInfo && mimeInfo.extensionExists(ext))
83         return ext;
84     else if (primary)
85         return primary;
86     else
87         return ext;
90 function get_default_extension (file_name_s, url_o, content_type) {
91     if (content_type == "text/plain" ||
92         content_type == "application/octet-stream" ||
93         url_o.scheme == "ftp")
94     {
95         return "";
96     }
97     return (maybe_get_preferred_filename_extension(file_name_s, content_type) ||
98             maybe_get_preferred_url_extension(url_o, content_type));
101 function get_charset_for_save (doc) {
102     if (doc)
103         return doc.characterSet;
104     return null;
108 function maybe_filename_from_content_disposition (content_disposition, charset) {
109     if (content_disposition) {
110         const mhp = Components.classes["@mozilla.org/network/mime-hdrparam;1"]
111             .getService(Components.interfaces.nsIMIMEHeaderParam);
112         var dummy = { value: null };  // Need an out param...
114         var filename = null;
115         try {
116             filename = mhp.getParameter(content_disposition, "filename", charset, true, dummy);
117         } catch (e) {
118             try {
119                 filename = mhp.getParameter(content_disposition, "name", charset, true, dummy);
120             } catch (e) { }
121         }
122         if (filename)
123             return filename;
124         else
125             return null;
126     }
127     return null;
130 function maybe_filename_from_uri (uri) {
131     try {
132         var url = uri.QueryInterface(Components.interfaces.nsIURL);
133         if (url.fileName != "") {
134             // 2) Use the actual file name, if present
135             var text_to_sub_uri = Cc["@mozilla.org/intl/texttosuburi;1"].
136                 getService(Ci.nsITextToSubURI);
137             return text_to_sub_uri.unEscapeURIForUI(url.originCharset ||
138                                                     "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         title = title.replace(/^\s+|\s+$/g, "");
149         if (title && title.length > 0) {
150             // 3) Use the document title
151             return title;
152         }
153     }
154     return null;
157 function maybe_filename_from_url_last_directory (uri) {
158     // 5) If this is a directory, use the last directory name
159     try {
160         var path = uri.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 (uri) {
170     if (uri && 'host' in uri)
171         return uri.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) {
192     filename = filename.replace(/[\"]+/g,     "'");
193     filename = filename.replace(/[\*\:\?]+/g, ' ');
194     filename = filename.replace(/[\<]+/g,     '(');
195     filename = filename.replace(/[\>]+/g,     ')');
196     filename = filename.replace(/[\\\/\|]+/g, '_');
197     return filename;
201 var generate_filename_safely_fn = null;
203 switch (get_os()) {
204 case 'Darwin':
205     generate_filename_safely_fn = generate_filename_safely_darwin;
206     break;
207 case 'WINNT':
208     generate_filename_safely_fn = generate_filename_safely_winnt;
209     break;
210 default:
211     generate_filename_safely_fn = generate_filename_safely_default;
212     break;
216  * spec may be a string (URI), a load spec, or an nsIDOMDocument
218  * extension may be null, in which case an extension is suggested as well
219  */
220 function suggest_file_name (spec, extension) {
221     var document;
222     var uri;
223     var content_type;
225     if (typeof(spec) == "string" || spec instanceof Ci.nsIDOMDocument)
226         spec = load_spec(spec);
228     var file_name = load_spec_filename(spec);
230     document = load_spec_document(spec);
231     uri = load_spec_uri(spec);
232     content_type = load_spec_mime_type(spec);
234     if (!file_name) {
235         file_name = generate_filename_safely_fn(
236             maybe_filename_from_content_disposition(
237                 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 = with_current_buffer(buffer, function (I) I.local.cwd);
273     var file = make_file(cwd).clone();
274     file.append(file_name);
275     return file.path;