4 function is_dom_node_or_window(elem) {
5 if (elem instanceof Ci.nsIDOMNode)
7 if (elem instanceof Ci.nsIDOMWindow)
13 * This is a simple wrapper function that sets focus to elem, and
14 * bypasses the automatic focus prevention system, which might
15 * otherwise prevent this from happening.
17 function browser_set_element_focus(buffer, elem, prevent_scroll) {
18 if (!is_dom_node_or_window(elem))
21 buffer.last_user_input_received = Date.now();
23 set_focus_no_scroll(buffer.window, elem);
28 function browser_element_focus(buffer, elem)
30 if (!is_dom_node_or_window(elem))
33 if (elem instanceof Ci.nsIDOMXULTextBoxElement) {
34 // Focus the input field instead
35 elem = elem.wrappedJSObject.inputField;
38 browser_set_element_focus(buffer, elem);
39 if (elem instanceof Ci.nsIDOMWindow) {
42 // If it is not a window, it must be an HTML element
45 if (elem instanceof Ci.nsIDOMHTMLFrameElement || elem instanceof Ci.nsIDOMHTMLIFrameElement) {
46 elem.contentWindow.focus();
49 if (elem instanceof Ci.nsIDOMHTMLAreaElement) {
50 var coords = elem.getAttribute("coords").split(",");
51 x = Number(coords[0]);
52 y = Number(coords[1]);
55 var doc = elem.ownerDocument;
56 var evt = doc.createEvent("MouseEvents");
57 var doc = elem.ownerDocument;
59 evt.initMouseEvent("mouseover", true, true, doc.defaultView, 1, x, y, 0, 0, 0, 0, 0, 0, 0, null);
60 elem.dispatchEvent(evt);
63 function browser_element_follow(buffer, target, elem)
65 browser_set_element_focus(buffer, elem, true /* no scroll */);
68 var current_frame = null;
70 if (elem instanceof media_spec) {
72 current_frame = elem.frame;
73 } else if (elem instanceof Ci.nsIDOMWindow) {
76 } else if (elem instanceof Ci.nsIDOMHTMLFrameElement ||
77 elem instanceof Ci.nsIDOMHTMLIFrameElement ||
78 elem instanceof Ci.nsIDOMHTMLLinkElement) {
80 } else if (elem instanceof Ci.nsIDOMHTMLImageElement) {
81 if (!elem.hasAttribute("onmousedown") && !elem.hasAttribute("onclick"))
85 if (target == FOLLOW_DEFAULT && !no_click) {
87 if (elem instanceof Ci.nsIDOMHTMLAreaElement) {
88 var coords = elem.getAttribute("coords").split(",");
89 if (coords.length >= 2) {
90 x = Number(coords[0]) + 1;
91 y = Number(coords[1]) + 1;
94 browser_follow_link_with_click(buffer, elem, x, y);
98 var load_spec = element_get_load_spec(elem);
99 if (load_spec == null) {
100 throw interactive_error("Element has no associated URL");
104 if (load_spec.url.match(/^\s*javascript:/)) {
105 // This URL won't work
106 throw interactive_error("Can't load javascript URL");
109 var target_obj = null;
111 case FOLLOW_CURRENT_FRAME:
112 if (current_frame == null) {
113 if (elem.ownerDocument)
114 current_frame = elem.ownerDocument.defaultView;
116 current_frame = buffer.top_frame;
118 if (current_frame != buffer.top_frame) {
119 target_obj = get_web_navigation_for_window(current_frame);
120 apply_load_spec(target_obj, load_spec);
124 case FOLLOW_TOP_FRAME:
125 case OPEN_CURRENT_BUFFER:
126 buffer.load(load_spec);
128 case OPEN_NEW_WINDOW:
129 case OPEN_NEW_BUFFER:
130 case OPEN_NEW_BUFFER_BACKGROUND:
131 create_buffer(buffer.window,
132 buffer_creator(content_buffer,
134 $configuration = buffer.configuration),
140 * Follow a link-like element by generating fake mouse events.
142 function browser_follow_link_with_click(buffer, elem, x, y) {
143 var doc = elem.ownerDocument;
144 var view = doc.defaultView;
146 var evt = doc.createEvent("MouseEvents");
147 evt.initMouseEvent("mousedown", true, true, view, 1, x, y, 0, 0, /*ctrl*/ 0, /*event.altKey*/0,
148 /*event.shiftKey*/ 0, /*event.metaKey*/ 0, 0, null);
149 elem.dispatchEvent(evt);
151 evt.initMouseEvent("click", true, true, view, 1, x, y, 0, 0, /*ctrl*/ 0, /*event.altKey*/0,
152 /*event.shiftKey*/ 0, /*event.metaKey*/ 0, 0, null);
153 elem.dispatchEvent(evt);
156 function element_get_url(elem) {
159 if (elem instanceof media_spec) {
160 if (elem instanceof media_spec_simple_uri)
164 else if (elem instanceof Ci.nsIDOMWindow)
165 url = elem.location.href;
167 else if (elem instanceof Ci.nsIDOMHTMLFrameElement ||
168 elem instanceof Ci.nsIDOMHTMLIFrameElement)
169 url = elem.contentWindow.location.href;
171 else if (elem instanceof Ci.nsIDOMHTMLImageElement)
174 else if (elem instanceof Ci.nsIDOMHTMLAnchorElement ||
175 elem instanceof Ci.nsIDOMHTMLAreaElement ||
176 elem instanceof Ci.nsIDOMHTMLLinkElement) {
177 if (elem.hasAttribute("href"))
180 return null; // nothing can be done
183 while (node && !(node instanceof Ci.nsIDOMHTMLAnchorElement))
184 node = node.parentNode;
185 if (node && !node.hasAttribute("href"))
191 if (node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
192 url = linkNode.getAttributeNS(XLINK_NS, "href");
195 node = node.parentNode;
198 url = makeURLAbsolute(node.baseURI, url);
202 if (url && url.length == 0)
207 function element_get_load_spec(elem) {
208 var load_spec = null;
210 if (elem instanceof media_spec) {
211 if (elem instanceof media_spec_simple_uri) {
212 load_spec = {url: elem.uri, referrer: make_uri(elem.frame.location.href),
213 filename: elem.filename,
214 mime_type: elem.mime_type };
217 else if (elem instanceof Ci.nsIDOMWindow)
218 load_spec = document_load_spec(elem.document);
220 else if (elem instanceof Ci.nsIDOMHTMLFrameElement ||
221 elem instanceof Ci.nsIDOMHTMLIFrameElement)
222 load_spec = document_load_spec(elem.contentDocument);
227 if (elem instanceof Ci.nsIDOMHTMLAnchorElement ||
228 elem instanceof Ci.nsIDOMHTMLAreaElement ||
229 elem instanceof Ci.nsIDOMHTMLLinkElement) {
230 if (!elem.hasAttribute("href"))
231 return null; // nothing can be done, as no nesting within these elements is allowed
234 else if (elem instanceof Ci.nsIDOMHTMLImageElement) {
239 while (node && !(node instanceof Ci.nsIDOMHTMLAnchorElement))
240 node = node.parentNode;
241 if (node && !node.hasAttribute("href"))
249 if (node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
250 url = linkNode.getAttributeNS(XLINK_NS, "href");
253 node = node.parentNode;
256 url = makeURLAbsolute(node.baseURI, url);
259 if (url && url.length > 0) {
262 referrer = makeURL(elem.ownerDocument.location.href);
264 load_spec = {url: url, referrer: referrer};
271 "hints_default_object_classes",
274 follow_top: "frames",
278 view_source: "frames",
281 save_page_complete: "top",
282 save_page_as_text: "frames",
285 "Specifies the default object class for each operation.\n" +
286 "This variable should be an object literal with string-valued properties that specify one of the supported object classes (\"links\", \"frames\", \"top\", or \"images\") defined in `hints_xpath_expression'. If a property named after the operation is not present, the \"def\" property is consulted instead.");
288 interactive_context.prototype.hints_object_class = function (action_name) {
290 this._hints_object_class ||
291 this.get("hints_default_object_classes")[action_name] ||
292 this.get("hints_default_object_classes")["def"];
296 function resolve_hints_xpath_expression(buffer, object_class, action_name) {
297 var db = buffer.get("hints_xpath_expressions")[object_class];
298 return db[action_name] || db["def"];
301 interactive_context.prototype.hints_xpath_expression = function (action_name) {
302 return resolve_hints_xpath_expression(this.buffer, this.hints_object_class(action_name), action_name);
305 function hints_object_class_selector(name) {
306 return function (ctx, active_keymap, overlay_keymap) {
307 ctx._hints_object_class = name;
308 ctx.overlay_keymap = overlay_keymap || active_keymap;
312 interactive_context.prototype.read_hinted_element_with_prompt = function(action, action_name, default_class, target)
314 var object_class = this.hints_object_class(action);
316 var prompt = action_name;
318 prompt += TARGET_PROMPTS[target];
319 if (object_class != default_class)
320 prompt += " (" + object_class + ")";
323 var result = yield this.minibuffer.read_hinted_element(
324 $buffer = this.buffer,
326 $object_class = object_class,
329 yield co_return(result);
332 interactive("follow", function (I) {
333 var target = I.browse_target("follow");
334 var element = yield I.read_hinted_element_with_prompt("follow", "Follow", "links", target);
335 browser_element_follow(I.buffer, target, element);
338 interactive("follow-top", function (I) {
339 var target = I.browse_target("follow-top");
340 var element = yield I.read_hinted_element_with_prompt("follow_top", "Follow", "links", target);
341 browser_element_follow(I.buffer, target, element);
344 interactive("focus", function (I) {
345 var element = yield I.read_hinted_element_with_prompt("focus", "Focus");
346 browser_element_focus(I.buffer, element);
349 function element_get_load_target_label(element) {
350 if (element instanceof Ci.nsIDOMWindow)
352 if (element instanceof Ci.nsIDOMHTMLFrameElement)
354 if (element instanceof Ci.nsIDOMHTMLIFrameElement)
356 if (element instanceof media_spec)
361 function element_get_operation_label(element, op_name, suffix) {
362 var target_label = element_get_load_target_label(element);
363 if (target_label != null)
364 target_label = " " + target_label;
369 suffix = " " + suffix;
373 return op_name + target_label + suffix + ":";
376 interactive("save", function (I) {
377 var element = yield I.read_hinted_element_with_prompt("save", "Save", "links");
379 var load_spec = element_get_load_spec(element);
380 if (load_spec == null)
381 throw interactive_error("Element has no associated URI");
384 panel = create_info_panel(I.window, "download-panel",
386 element_get_operation_label(element, "Saving"),
387 uri_string_from_load_spec(load_spec)],
388 ["mime-type", "Mime type:", mime_type_from_load_spec(load_spec)]]);
391 var file = yield I.minibuffer.read_file_check_overwrite(
392 $prompt = "Save as:",
393 $initial_value = suggest_save_path_from_file_name(suggest_file_name(load_spec), I.buffer),
400 save_uri(load_spec, file,
405 function browser_element_copy(buffer, elem)
407 var text = element_get_url(elem);
409 if (!(elem instanceof Ci.nsIDOMNode))
410 throw interactive_error("Element has no associated text to copy.");
411 switch (elem.localName) {
417 if (elem.selectedIndex >= 0)
418 text = elem.item(elem.selectedIndex).text;
423 text = elem.textContent;
424 writeToClipboard (text);
425 buffer.window.minibuffer.message ("Copied: " + text);
429 interactive("copy", function (I) {
430 var element = yield I.read_hinted_element_with_prompt("copy", "Copy", "links");
431 browser_element_copy(I.buffer, element);
434 var view_source_use_external_editor = false, view_source_function = null;
435 function browser_element_view_source(buffer, target, elem)
437 if (view_source_use_external_editor || view_source_function)
439 var load_spec = element_get_load_spec(elem);
440 if (load_spec == null) {
441 throw interactive_error("Element has no associated URL");
445 let [file, temp] = yield download_as_temporary(load_spec,
447 $action = "View source");
448 if (view_source_use_external_editor)
449 yield open_file_with_external_editor(file, $temporary = temp);
451 yield view_source_function(file, $temporary = temp);
456 var window = buffer.window;
457 if (elem.localName) {
458 switch (elem.localName.toLowerCase()) {
459 case "frame": case "iframe":
460 win = elem.contentWindow;
463 view_mathml_source (window, charset, elem);
466 throw new Error("Invalid browser element");
472 var url_s = win.location.href;
473 if (url_s.substring (0,12) != "view-source:") {
475 open_in_browser(buffer, target, "view-source:" + url_s);
476 } catch(e) { dump_error(e); }
478 window.minibuffer.message ("Already viewing source");
482 interactive("view-source", function (I) {
483 var target = I.browse_target("follow");
484 var element = yield I.read_hinted_element_with_prompt("view_source", "View source", "frames", target);
485 yield browser_element_view_source(I.buffer, target, element);
488 interactive("shell-command-on-url", function (I) {
490 var element = yield I.read_hinted_element_with_prompt("shell_command_url", "URL shell command target", "links");
491 var load_spec = element_get_load_spec(element);
492 if (load_spec == null)
493 throw interactive_error("Unable to obtain URI from element");
495 var uri = uri_string_from_load_spec(load_spec);
498 panel = create_info_panel(I.window, "download-panel",
500 element_get_operation_label(element, "Running on", "URI"),
501 uri_string_from_load_spec(load_spec)],
502 ["mime-type", "Mime type:", mime_type_from_load_spec(load_spec)]]);
505 var cmd = yield I.minibuffer.read_shell_command(
507 $initial_value = default_shell_command_from_load_spec(load_spec));
512 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
515 function browser_element_shell_command(buffer, elem, command) {
516 var load_spec = element_get_load_spec(elem);
517 if (load_spec == null) {
518 throw interactive_error("Element has no associated URL");
521 yield download_as_temporary(load_spec,
523 $shell_command = command,
524 $shell_command_cwd = buffer.cwd);
527 interactive("shell-command-on-file", function (I) {
529 var element = yield I.read_hinted_element_with_prompt("shell_command", "Shell command target", "links");
531 var load_spec = element_get_load_spec(element);
532 if (load_spec == null)
533 throw interactive_error("Unable to obtain URI from element");
535 var uri = uri_string_from_load_spec(load_spec);
538 panel = create_info_panel(I.window, "download-panel",
540 element_get_operation_label(element, "Running on"),
541 uri_string_from_load_spec(load_spec)],
542 ["mime-type", "Mime type:", mime_type_from_load_spec(load_spec)]]);
546 var cmd = yield I.minibuffer.read_shell_command(
548 $initial_value = default_shell_command_from_load_spec(load_spec));
553 /* FIXME: specify cwd as well */
554 yield browser_element_shell_command(I.buffer, element, cmd);
557 interactive("bookmark", function (I) {
558 var element = yield I.read_hinted_element_with_prompt("bookmark", "Bookmark", "frames");
559 let [uri, suggested_title] = get_element_bookmark_info(element);
562 panel = create_info_panel(I.window, "bookmark-panel",
564 element_get_operation_label(element, "Bookmarking"),
567 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = suggested_title);
571 add_bookmark(uri, title);
572 I.minibuffer.message("Added bookmark: " + uri + " - " + title);
575 interactive("save-page", function (I) {
576 check_buffer(I.buffer, content_buffer);
577 var element = yield I.read_hinted_element_with_prompt("save_page", "Save page", "frames");
578 var load_spec = element_get_load_spec(element);
579 if (load_spec == null || typeof(load_spec) != "object" || load_spec.document == null)
580 throw interactive_error("Element is not associated with a document.");
581 var doc = load_spec.document;
582 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(doc), I.buffer);
585 panel = create_info_panel(I.window, "download-panel",
587 element_get_operation_label(element, "Saving"),
588 uri_string_from_load_spec(load_spec)],
589 ["mime-type", "Mime type:", mime_type_from_load_spec(load_spec)]]);
592 var file = yield I.minibuffer.read_file_check_overwrite(
593 $prompt = "Save page as:",
595 $initial_value = suggested_path);
600 save_uri(document_load_spec(doc), file, $buffer = I.buffer);
603 interactive("save-page-as-text", function (I) {
604 check_buffer(I.buffer, content_buffer);
605 var element = yield I.read_hinted_element_with_prompt("save_page_as_text", "Save page as text", "frames");
606 var load_spec = element_get_load_spec(element);
607 if (load_spec == null || typeof(load_spec) != "object" || load_spec.document == null)
608 throw interactive_error("Element is not associated with a document.");
609 var doc = load_spec.document;
610 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(doc, "txt"), I.buffer);
613 panel = create_info_panel(I.window, "download-panel",
615 element_get_operation_label(element, "Saving", "as text"),
616 uri_string_from_load_spec(load_spec)],
617 ["mime-type", "Mime type:", mime_type_from_load_spec(load_spec)]]);
620 var file = yield I.minibuffer.read_file_check_overwrite(
621 $prompt = "Save page as text:",
623 $initial_value = suggested_path);
628 save_document_as_text(doc, file, $buffer = I.buffer);
631 interactive("save-page-complete", function (I) {
632 check_buffer(I.buffer, content_buffer);
633 var element = yield I.read_hinted_element_with_prompt("save_page_complete", "Save page complete", "frames");
634 var load_spec = element_get_load_spec(element);
635 if (load_spec == null || typeof(load_spec) != "object" || load_spec.document == null)
636 throw interactive_error("Element is not associated with a document.");
637 var doc = load_spec.document;
638 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(doc), I.buffer);
641 panel = create_info_panel(I.window, "download-panel",
643 element_get_operation_label(element, "Saving complete"),
644 uri_string_from_load_spec(load_spec)],
645 ["mime-type", "Mime type:", mime_type_from_load_spec(load_spec)]]);
648 var file = yield I.minibuffer.read_file_check_overwrite(
649 $prompt = "Save page complete:",
651 $initial_value = suggested_path);
652 // FIXME: use proper read function
653 var dir = yield I.minibuffer.read_file(
654 $prompt = "Data Directory:",
656 $initial_value = file.path + ".support");
661 save_document_complete(doc, file, dir, $buffer = I.buffer);