2 * (C) Copyright 2008 Jeremy Maitin-Shepard
3 * (C) Copyright 2009-2010 John Foerch
5 * Use, modification, and distribution are subject to the terms specified in the
9 require("mime-type-override.js");
11 define_mime_type_table("content_handlers", {},
12 "A mime type table mapping mime types to content handlers. This table "+
13 "will be checked for a match when a navigation event causes Conkeror "+
14 "to encounter a mime type which is not supported by Mozilla. If no "+
15 "appropriate content handler is found in this table, the user will "+
16 "be prompted to choose from among common actions.");
19 * content_handler_context is a datatype for storing contextual information
20 * that can be used by content handlers, such as window, buffer, frame, and
21 * the download launcher object. It will also contain an abort method to
22 * abort the download. Download helper actions are passed an object of this
25 function content_handler_context (launcher, context) {
26 var xulrunner_version = Cc['@mozilla.org/xre/app-info;1']
27 .getService(Ci.nsIXULAppInfo)
29 var vc = Cc["@mozilla.org/xpcom/version-comparator;1"]
30 .getService(Ci.nsIVersionComparator);
31 this.launcher = launcher;
33 this.frame = context.QueryInterface(Ci.nsIInterfaceRequestor)
34 .getInterface((vc.compare(xulrunner_version, "8.0") >= 0) ?
36 Ci.nsIDOMWindowInternal)
37 this.window = get_window_from_frame(this.frame);
38 this.buffer = get_buffer_from_frame(this.window, this.frame);
40 dumpln("Error: Failed to find buffer corresponding to content handler context");
42 this.window = get_recent_conkeror_window();
44 this.buffer = this.window.buffers.current;
45 this.frame = this.buffer.focused_frame; //doesn't necessarily exist for all buffer types
49 content_handler_context.prototype = {
50 constructor: content_handler_context,
56 const NS_BINDING_ABORTED = 0x804b0002;
57 this.launcher.cancel(NS_BINDING_ABORTED);
63 * The content_handler_* functions below are all things that conkeror
64 * is able to do when Mozilla has invoked download_helper because of an
65 * attempt to navigate to a document of an unsupported mime type.
67 function content_handler_save (ctx) {
68 var suggested_path = suggest_save_path_from_file_name(
69 ctx.launcher.suggestedFileName, ctx.buffer);
70 var file = yield ctx.window.minibuffer.read_file_check_overwrite(
71 $prompt = "Save to file:",
72 $initial_value = suggested_path,
74 register_download(ctx.buffer, ctx.launcher.source);
75 ctx.launcher.saveToDisk(file, false);
78 function content_handler_save_in (path, inhibit_prompt) {
79 path = make_file(path);
80 return function (ctx) {
82 var suggested_path = path.clone();
83 suggested_path.append(ctx.launcher.suggestedFileName);
85 file = suggested_path;
87 file = yield ctx.window.minibuffer.read_file_check_overwrite(
88 $prompt = "Save to file:",
89 $initial_value = suggested_path.path,
92 register_download(ctx.buffer, ctx.launcher.source);
93 ctx.launcher.saveToDisk(file, false);
97 function content_handler_open (ctx) {
98 var cwd = with_current_buffer(ctx.buffer, function (I) I.local.cwd);
99 var mime_type = ctx.launcher.MIMEInfo.MIMEType;
100 var suggested_action = external_content_handlers.get(mime_type);
101 var command = yield ctx.window.minibuffer.read_shell_command(
102 $initial_value = suggested_action,
104 var file = get_temporary_file(ctx.launcher.suggestedFileName);
105 var info = register_download(ctx.buffer, ctx.launcher.source);
106 info.temporary_status = DOWNLOAD_TEMPORARY_FOR_COMMAND;
107 info.set_shell_command(command, cwd);
108 ctx.launcher.saveToDisk(file, false);
111 function content_handler_open_default_viewer (ctx) {
112 var cwd = with_current_buffer(ctx.buffer, function (I) I.local.cwd);
113 var mime_type = ctx.launcher.MIMEInfo.MIMEType;
114 var command = external_content_handlers.get(mime_type);
116 command = yield ctx.window.minibuffer.read_shell_command(
117 $initial_value = command,
119 var file = get_temporary_file(ctx.launcher.suggestedFileName);
120 var info = register_download(ctx.buffer, ctx.launcher.source);
121 info.temporary_status = DOWNLOAD_TEMPORARY_FOR_COMMAND;
122 info.set_shell_command(command, cwd);
123 ctx.launcher.saveToDisk(file, false);
126 function content_handler_open_url (ctx) {
127 ctx.abort(); // abort download
128 let mime_type = ctx.launcher.MIMEInfo.MIMEType;
129 let cwd = with_current_buffer(ctx.buffer, function (I) I.local.cwd);
130 let cmd = yield ctx.window.minibuffer.read_shell_command(
132 $initial_value = external_content_handlers.get(mime_type));
133 shell_command_with_argument_blind(cmd, ctx.launcher.source.spec, $cwd = cwd);
136 function content_handler_copy_url (ctx) {
137 ctx.abort(); // abort download
138 let uri = ctx.launcher.source.spec;
139 writeToClipboard(uri);
140 ctx.window.minibuffer.message("Copied: " + uri);
143 function content_handler_view_internally (ctx) {
144 var suggested_type = ctx.launcher.MIMEInfo.MIMEType;
145 if (viewable_mime_type_list.indexOf(suggested_type) == -1)
146 suggested_type = "text/plain";
147 var mime_type = yield ctx.window.minibuffer.read_viewable_mime_type(
148 $prompt = "View internally as",
149 $initial_value = suggested_type,
151 ctx.abort(); // abort before reloading
152 yield override_mime_type_for_next_load(ctx.launcher.source, mime_type);
153 ctx.frame.location = ctx.launcher.source.spec; // reload
156 function content_handler_view_as_text (ctx) {
157 ctx.abort(); // abort before reloading
158 yield override_mime_type_for_next_load(ctx.launcher.source, "text/plain");
159 ctx.frame.location = ctx.launcher.source.spec; // reload
162 function content_handler_prompt (ctx) {
163 var action_chosen = false;
164 var can_view_internally = ctx.frame != null &&
165 can_override_mime_type_for_uri(ctx.launcher.source);
168 panel = create_info_panel(ctx.window, "download-panel",
169 [["downloading", "Downloading:", ctx.launcher.source.spec],
170 ["mime-type", "Mime type:", ctx.launcher.MIMEInfo.MIMEType]]);
171 var action = yield ctx.window.minibuffer.read_single_character_option(
172 $prompt = "Action to perform: (s: save; o: open; O: open URL; c: copy URL; " +
173 (can_view_internally ? "i: view internally; t: view as text)" : ")"),
174 $options = (can_view_internally ? ["s", "o", "O", "c", "i", "t"] : ["s", "o", "O", "c"]));
177 yield content_handler_save(ctx);
178 action_chosen = true;
181 yield content_handler_open(ctx);
182 action_chosen = true;
185 yield content_handler_open_url(ctx);
186 action_chosen = true;
189 yield content_handler_copy_url(ctx);
190 action_chosen = true;
193 yield content_handler_view_internally(ctx);
194 action_chosen = true;
197 yield content_handler_view_as_text(ctx);
198 action_chosen = true;
202 handle_interactive_error(ctx.window, e);
213 * download_helper implements nsIHelperAppLauncherDialog.
215 * Sometimes, like when following a link, the content type of the document
216 * cannot be displayed by Mozilla. When this happens, Mozilla calls the
217 * `show' method of our download_helper with a handle for the started
218 * download and other contextual information.
220 function download_helper () {}
221 download_helper.prototype = {
222 constructor: download_helper,
223 QueryInterface: generate_QI(Ci.nsIHelperAppLauncherDialog,
224 Ci.nsIWebProgressListener2),
225 show: function (launcher, context, reason) {
226 var ctx = new content_handler_context(launcher, context);
228 dumpln("Warning: Aborting download because of lack of window");
229 ctx.abort(); //XXX: impolite; need better solution.
233 // is there anything in content_handlers for this object?
234 var mime_type = launcher.MIMEInfo.MIMEType;
235 var action = content_handlers.get(mime_type) ||
236 content_handler_prompt;
239 handle_interactive_error(ctx.window, e);
242 promptForSaveToFile: function (launcher, context, default_file, suggested_file_extension) {
247 provide("content-handler");