download-manager.js: fix bug resulting in un-killable special buffers
[conkeror.git] / modules / save.js
blobd648a7671b0dca1d7c884727a179280f03c904bb
1 /**
2  * (C) Copyright 2004-2007 Shawn Betts
3  * (C) Copyright 2007 John J. Foerch
4  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
5  *
6  * Use, modification, and distribution are subject to the terms specified in the
7  * COPYING file.
8 **/
10 require("content-buffer.js");
11 require("load-spec.js");
12 require("suggest-file-name.js");
14 /* buffer is used only to associate with the download */
15 define_keywords("$use_cache", "$buffer", "$prepare_download");
16 function save_uri(lspec, output_file) {
17     keywords(arguments, $use_cache = false);
19     var use_cache = arguments.$use_cache;
21     var buffer = arguments.$buffer;
23     var prepare_download = arguments.$prepare_download;
25     var cache_key = null;
26     var uri = load_spec_uri(lspec);
27     var referrer_uri = load_spec_referrer(lspec);
28     var post_data = load_spec_post_data(lspec);
29     if (use_cache)
30         cache_key = load_spec_cache_key(lspec);
32     var file_uri = makeFileURL(output_file);
34     var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
36     persist.persistFlags =
37         Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
38         Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION |
39         Ci.nsIWebBrowserPersist.PERSIST_FLAGS_CLEANUP_ON_FAILURE;
41     if (use_cache)
42         persist.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE;
43     else
44         persist.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE;
46     var info = register_download(buffer, uri);
47     if (prepare_download)
48         prepare_download(info);
50     var tr = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer);
51     tr.init(uri, file_uri, output_file.leafName,
52             load_spec_mime_info(lspec),
53             null /* start time */,
54             null /* temp file */,
55             persist);
56     persist.progressListener = tr;
57     persist.saveURI(uri, cache_key, referrer_uri, post_data, null /* no extra headers */, file_uri);
59     return info;
63 // We have no DOM, and can only save the URL as is.
64 const SAVEMODE_FILEONLY      = 0x00;
65 // We have a DOM and can save as complete.
66 const SAVEMODE_COMPLETE_DOM  = 0x01;
67 // We have a DOM which we can serialize as text.
68 const SAVEMODE_COMPLETE_TEXT = 0x02;
70 function get_save_mode_from_content_type(content_type) {
71     var mode = SAVEMODE_FILEONLY;
72     switch (content_type) {
73     case "text/html":
74     case "application/xhtml+xml":
75         mode |= SAVEMODE_COMPLETE_TEXT;
76         // Fall through
77     case "text/xml":
78     case "application/xml":
79         mode |= SAVEMODE_COMPLETE_DOM;
80         break;
81     }
82     return mode;
85 define_keywords("$use_cache", "$buffer", "$wrap_column", "$prepare_download");
86 function save_document_as_text(document, output_file)
88     keywords(arguments, $use_cache = true, $wrap_column = 80);
90     var mode = get_save_mode_from_content_type(document.contentType);
91     if (!(mode & SAVEMODE_COMPLETE_TEXT))
92         throw interactive_error("Document cannot be saved as text.");
94     var use_cache = arguments.$use_cache;
96     var prepare_download = arguments.$prepare_download;
98     var buffer = arguments.$buffer;
100     var uri = document.documentURIObject;
102     var file_uri = makeFileURL(output_file);
104     var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
106     persist.persistFlags =
107         Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
108         Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION |
109         Ci.nsIWebBrowserPersist.PERSIST_FLAGS_CLEANUP_ON_FAILURE;
111     var encoding_flags =
112         Ci.nsIWebBrowserPersist.ENCODE_FLAGS_FORMATTED |
113         Ci.nsIWebBrowserPersist.ENCODE_FLAGS_ABSOLUTE_LINKS |
114         Ci.nsIWebBrowserPersist.ENCODE_FLAGS_NOFRAMES_CONTENT;
117     if (use_cache)
118         persist.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE;
119     else
120         persist.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE;
124     var info = register_download(buffer, uri);
125     if (prepare_download)
126         prepare_download(info);
128     var tr = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer);
129     tr.init(uri, file_uri, output_file.leafName,
130             mime_info_from_mime_type("text/plain"),
131             null /* start time */,
132             null /* temp file */,
133             persist);
134     persist.progressListener = tr;
135     persist.saveDocument(document, file_uri, null /* data path */,
136                          "text/plain", encoding_flags,
137                          arguments.$wrap_column);
138     return info;
141 define_keywords("$use_cache", "$buffer", "$prepare_download");
142 function save_document_complete(document, output_file, output_dir) {
144     var mime_type = document.contentType;
146     var mode = get_save_mode_from_content_type(mime_type);
147     if (!(mode & SAVEMODE_COMPLETE_DOM))
148         throw interactive_error("Complete document cannot be saved.");
150     var use_cache = arguments.$use_cache;
152     var buffer = arguments.$buffer;
154     var prepare_download = arguments.$prepare_download;
156     var uri = document.documentURIObject;
158     var file_uri = makeFileURL(output_file);
160     var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
162     persist.persistFlags =
163         Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
164         Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION |
165         Ci.nsIWebBrowserPersist.PERSIST_FLAGS_CLEANUP_ON_FAILURE;
167     var encoding_flags =
168         Ci.nsIWebBrowserPersist.ENCODE_FLAGS_BASIC_ENTITIES;
170     if (use_cache)
171         persist.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE;
172     else
173         persist.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE;
176     var info = register_download(buffer, uri);
177     if (prepare_download)
178         prepare_download(info);
180     var tr = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer);
181     tr.init(uri, file_uri, output_file.leafName,
182             mime_info_from_mime_type(mime_type),
183             null /* start time */,
184             null /* temp file */,
185             persist);
186     persist.progressListener = tr;
187     persist.saveDocument(document, file_uri, output_dir /* data path */,
188                          mime_type, encoding_flags,
189                          0);
190     return info;
193 function download_failed_error() {
194     var e = new Error("Download failed");
195     e.__proto__ = download_failed_error.prototype;
196     return e;
198 download_failed_error.prototype.__proto__ = Error.prototype;
200 /* Returns an array of two elements: the first element is the
201  * nsILocalFile object, the second element is a boolean indicating
202  * whether the file is temporary and should be deleted when the caller is
203  * done with it.
204  */
205 define_keywords("$action", "$shell_command", "$shell_command_cwd", "$buffer", "$use_cache");
206 function download_as_temporary(lspec) {
207     keywords(arguments, $use_cache = true);
209     var action_description = arguments.$action;
210     var shell_command = arguments.$shell_command;
211     var shell_command_cwd = arguments.$shell_command_cwd;
213     var uri = load_spec_uri(lspec);
214     // If it is local file, there is no need to download it
215     if (uri.scheme == "file")
216     {
217         let file = uri.QueryInterface(Ci.nsIFileURL).file;
219         yield co_return([file, false /* not temporary */]);
220     }
223     var file = get_temporary_file(suggest_file_name(lspec));
225     var cc = yield CONTINUATION;
227     function handle_state_change(info) {
228         var state = info.state;
229         switch (state) {
230         case DOWNLOAD_CANCELED:
231         case DOWNLOAD_FAILED:
232             info.remove(); // ensure that the download cannot be retried later
233             try {
234                 // Delete the temporary file
235                 file.remove(false /*non-recursive*/);
236             } catch (e) {}
237             cc.throw(download_failed_error());
238             break;
239         case DOWNLOAD_FINISHED:
240             cc([file, true /* temporary */]);
241             break;
242         }
243     }
245     save_uri(lspec, file,
246              $use_cache = arguments.$use_cache,
247              $buffer = arguments.$buffer,
248              $prepare_download = function (info) {
249                  if (action_description != null) {
250                      info.action_description = action_description;
251                      info.temporary_status = DOWNLOAD_TEMPORARY_FOR_ACTION;
252                  } else if (shell_command != null) {
253                      info.set_shell_command(shell_command, shell_command_cwd);
254                      info.temporary_status = DOWNLOAD_TEMPORARY_FOR_COMMAND;
255                  }
256                  add_hook.call(info, "download_state_change_hook", handle_state_change);
257              });
259     var result = yield SUSPEND;
260     yield co_return(result);