1 require("load-spec.js");
3 require_later("content-buffer-input.js");
5 define_buffer_local_hook("content_buffer_finished_loading_hook");
6 define_buffer_local_hook("content_buffer_started_loading_hook");
7 define_buffer_local_hook("content_buffer_progress_change_hook");
8 define_buffer_local_hook("content_buffer_location_change_hook");
9 define_buffer_local_hook("content_buffer_status_change_hook");
10 define_buffer_local_hook("content_buffer_focus_change_hook");
11 define_buffer_local_hook("content_buffer_overlink_change_hook");
12 define_buffer_local_hook("content_buffer_dom_link_added_hook");
14 define_current_buffer_hook("current_content_buffer_finished_loading_hook", "content_buffer_finished_loading_hook");
15 define_current_buffer_hook("current_content_buffer_progress_change_hook", "content_buffer_progress_change_hook");
16 define_current_buffer_hook("current_content_buffer_location_change_hook", "content_buffer_location_change_hook");
17 define_current_buffer_hook("current_content_buffer_status_change_hook", "content_buffer_status_change_hook");
18 define_current_buffer_hook("current_content_buffer_focus_change_hook", "content_buffer_focus_change_hook");
19 define_current_buffer_hook("current_content_buffer_overlink_change_hook", "content_buffer_overlink_change_hook");
21 /* If browser is null, create a new browser */
22 define_keywords("$load");
23 function content_buffer(window, element)
26 this.constructor_begin();
28 conkeror.buffer.call(this, window, element, forward_keywords(arguments));
30 this.browser.addProgressListener(this);
32 this.browser.addEventListener("DOMTitleChanged", function (event) {
33 buffer_title_change_hook.run(buffer);
34 }, true /* capture */, false /* ignore untrusted events */);
36 this.browser.addEventListener("scroll", function (event) {
37 buffer_scroll_hook.run(buffer);
38 }, true /* capture */, false /* ignore untrusted events */);
40 this.browser.addEventListener("focus", function (event) {
41 content_buffer_focus_change_hook.run(buffer, event);
42 }, true /* capture */, false /* ignore untrusted events */);
44 this.browser.addEventListener("mouseover", function (event) {
45 if (event.target instanceof Ci.nsIDOMHTMLAnchorElement) {
46 content_buffer_overlink_change_hook.run(buffer, event.target.href);
47 buffer.current_overlink = event.target;
51 this.browser.addEventListener("mouseout", function (event) {
52 if (buffer.current_overlink == event.target) {
53 buffer.current_overlink = null;
54 content_buffer_overlink_change_hook.run(buffer, "");
58 this.browser.addEventListener("mousedown", function (event) {
59 buffer.last_user_input_received = Date.now();
62 this.browser.addEventListener("keypress", function (event) {
63 buffer.last_user_input_received = Date.now();
66 this.browser.addEventListener("DOMLinkAdded", function (event) {
67 content_buffer_dom_link_added_hook.run(buffer, event);
70 buffer.last_user_input_received = null;
72 /* FIXME: Add a handler for blocked popups, and also PopupWindow event */
74 this.browser.addEventListener("DOMPopupBlocked", function (event) {
75 dumpln("PopupWindow: " + event);
79 normal_input_mode(this);
81 this.ignore_initial_blank = true;
83 var load_spec = arguments.$load;
85 if (load_spec.url == "about:blank")
86 this.ignore_initial_blank = false;
88 /* Ensure that an existing load of about:blank is stopped */
89 this.web_navigation.stop(Ci.nsIWebNavigation.STOP_ALL);
95 this.constructor_end();
97 content_buffer.prototype = {
98 constructor : content_buffer,
100 get scrollX () { return this.top_frame.scrollX; },
101 get scrollY () { return this.top_frame.scrollY; },
102 get scrollMaxX () { return this.top_frame.scrollMaxX; },
103 get scrollMaxY () { return this.top_frame.scrollMaxY; },
106 /* Used to display the correct URI when the buffer opens initially
107 * even before loading has progressed far enough for currentURI to
108 * contain the correct URI. */
111 get display_URI_string () {
112 if (this._display_URI)
113 return this._display_URI;
114 if (this.current_URI)
115 return this.current_URI.spec;
119 get title() { return this.browser.contentTitle; },
120 get description () { return this.display_URI_string; },
122 load : function (load_spec) {
123 apply_load_spec(this, load_spec);
130 /* nsIWebProgressListener */
131 QueryInterface: generate_QI(Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference),
133 // This method is called to indicate state changes.
134 onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
136 const WPL = Components.interfaces.nsIWebProgressListener;
139 if (aStateFlags & WPL.STATE_START)
141 if (aStateFlags & WPL.STATE_STOP)
143 if (aStateFlags & WPL.STATE_IS_REQUEST)
144 flagstr += ",request";
145 if (aStateFlags & WPL.STATE_IS_DOCUMENT)
146 flagstr += ",document";
147 if (aStateFlags & WPL.STATE_IS_NETWORK)
148 flagstr += ",network";
149 if (aStateFlags & WPL.STATE_IS_WINDOW)
150 flagstr += ",window";
151 dumpln("onStateChange: " + flagstr + ", status: " + aStatus);
157 if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) {
158 this._request_count++;
160 else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
161 const NS_ERROR_UNKNOWN_HOST = 2152398878;
162 if (--this._request_count > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
163 // to prevent bug 235825: wait for the request handled
164 // by the automatic keyword resolver
167 // since we (try to) only handle STATE_STOP of the last request,
168 // the count of open requests should now be 0
169 this._request_count = 0;
172 if (aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
173 aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
174 // It's okay to clear what the user typed when we start
175 // loading a document. If the user types, this counter gets
176 // set to zero, if the document load ends without an
177 // onLocationChange, this counter gets decremented
178 // (so we keep it while switching tabs after failed loads)
179 //dumpln("*** started loading");
181 content_buffer_started_loading_hook.run(this);
183 else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
184 aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
185 if (this.loading == true) {
186 //dumpln("*** finished loading");
187 content_buffer_finished_loading_hook.run(this);
188 this.loading = false;
192 if (aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP |
193 Ci.nsIWebProgressListener.STATE_START)) {
195 this.set_default_message("Done");
199 /* This method is called to indicate progress changes for the currently
201 onProgressChange: function(webProgress, request, curSelf, maxSelf,
202 curTotal, maxTotal) {
203 content_buffer_progress_change_hook.run(this, request, curSelf, maxSelf, curTotal, maxTotal);
206 /* This method is called to indicate a change to the current location.
207 The url can be gotten as location.spec. */
208 onLocationChange : function(webProgress, request, location) {
209 /* Attempt to ignore onLocationChange calls due to the initial
210 * loading of about:blank by all xul:browser elements. */
211 if (location.spec == "about:blank" && this.ignore_initial_blank)
214 this.ignore_initial_blank = false;
216 //dumpln("spec: " + location.spec +" ;;; " + this.display_URI_string);
217 /* Use the real location URI now */
218 this._display_URI = null;
219 content_buffer_location_change_hook.run(this, request, location);
220 buffer_description_change_hook.run(this);
223 // This method is called to indicate a status changes for the currently
224 // loading page. The message is already formatted for display.
225 // Status messages could be displayed in the minibuffer output area.
226 onStatusChange: function(webProgress, request, status, msg) {
227 this.set_default_message(msg);
228 content_buffer_status_change_hook.run(this, request, status, msg);
231 // This method is called when the security state of the browser changes.
232 onSecurityChange: function(webProgress, request, state) {
233 /* FIXME: currently this isn't used */
236 const WPL = Components.interfaces.nsIWebProgressListener;
238 if (state & WPL.STATE_IS_INSECURE) {
239 // update visual indicator
241 var level = "unknown";
242 if (state & WPL.STATE_IS_SECURE) {
243 if (state & WPL.STATE_SECURE_HIGH)
245 else if (state & WPL.STATE_SECURE_MED)
247 else if (state & WPL.STATE_SECURE_LOW)
249 } else if (state & WPL_STATE_IS_BROKEN) {
252 // provide a visual indicator of the security state here.
257 /* Inherit from buffer */
259 __proto__ : buffer.prototype
263 add_hook("current_content_buffer_finished_loading_hook",
265 buffer.window.minibuffer.show("Done");
268 add_hook("current_content_buffer_status_change_hook",
269 function (buffer, request, status, msg) {
270 buffer.set_default_message(msg);
274 define_variable("url_completion_use_webjumps", true, "Specifies whether URL completion should complete webjumps.");
275 define_variable("url_completion_use_bookmarks", true, "Specifies whether URL completion should complete bookmarks.");
276 define_variable("url_completion_use_history", false,
277 "Specifies whether URL completion should complete using browser history.");
279 minibuffer_auto_complete_preferences["url"] = true;
280 minibuffer.prototype.read_url = function () {
281 keywords(arguments, $prompt = "URL:", $history = "url", $initial_value = "",
282 $use_webjumps = url_completion_use_webjumps,
283 $use_history = url_completion_use_history,
284 $use_bookmarks = url_completion_use_bookmarks);
285 var completer = url_completer ($use_webjumps = arguments.$use_webjumps,
286 $use_bookmarks = arguments.$use_bookmarks,
287 $use_history = arguments.$use_history);
288 var result = yield this.read(
289 $prompt = arguments.$prompt,
290 $history = arguments.$history,
291 $completer = completer,
292 $initial_value = arguments.$initial_value,
293 $auto_complete = "url",
295 $match_required = false);
296 if (result == "") // well-formedness check. (could be better!)
297 throw ("invalid url or webjump (\""+ result +"\")");
298 yield co_return(get_url_or_webjump(result));
301 I.content_charset = interactive_method(
302 $sync = function (ctx) {
303 var buffer = ctx.buffer;
304 if (!(buffer instanceof content_buffer))
305 throw new Error("Current buffer is of invalid type");
306 // -- Charset of content area of focusedWindow
307 var focusedWindow = buffer.focused_frame;
309 return focusedWindow.document.characterSet;
315 I.content_selection = interactive_method(
316 $sync = function (ctx) {
317 // -- Selection of content area of focusedWindow
318 var focusedWindow = this.buffers.current.focused_frame;
319 return focusedWindow.getSelection ();
322 function overlink_update_status(buffer, text) {
324 buffer.window.minibuffer.show("Link: " + text);
326 buffer.window.minibuffer.show("");
329 define_global_mode("overlink_mode",
331 add_hook("current_content_buffer_overlink_change_hook", overlink_update_status);
334 remove_hook("current_content_buffer_overlink_change_hook", overlink_update_status);
339 function open_in_browser(buffer, target, load_spec)
342 case OPEN_CURRENT_BUFFER:
344 case FOLLOW_CURRENT_FRAME:
345 case FOLLOW_TOP_FRAME:
346 if (buffer instanceof content_buffer) {
347 apply_load_spec(buffer, load_spec);
350 target = OPEN_NEW_BUFFER;
351 // If the current buffer is not a content_buffer, use a new buffer.
353 create_buffer(buffer.window,
354 buffer_creator(content_buffer,
356 $configuration = buffer.configuration),
362 interactive("open-url",
363 "Open a URL, reusing the current buffer by default",
365 var target = I.browse_target("open");
366 open_in_browser(I.buffer, target,
367 (yield I.minibuffer.read_url($prompt = browse_target_prompt(target))));
370 interactive("find-alternate-url",
371 "Edit the current URL in the minibuffer",
373 var target = I.browse_target("open");
374 check_buffer(I.buffer, content_buffer);
375 open_in_browser(I.buffer, target,
376 (yield I.minibuffer.read_url($prompt = browse_target_prompt(target),
377 $initial_value = I.buffer.display_URI_string)));
380 interactive("find-url",
381 "Open a URL in a new buffer",
383 var target = I.browse_target("find-url");
384 open_in_browser(I.buffer, target,
385 (yield I.minibuffer.read_url($prompt = browse_target_prompt(target))));
387 default_browse_targets["find-url"] = [OPEN_NEW_BUFFER, OPEN_NEW_WINDOW];
389 function go_up (b, target)
391 var url = Cc["@mozilla.org/network/standard-url;1"]
392 .createInstance (Ci.nsIURL);
393 url.spec = b.current_URI.spec;
395 if (url.param != "" || url.query != "")
397 else if (url.fileName != "")
401 open_in_browser(b, target, b.current_URI.resolve (up));
404 "Go to the parent directory of the current URL",
405 function (I) { go_up(check_buffer(I.buffer, content_buffer), I.browse_target("go-up")); });
406 default_browse_targets["go-up"] = "open";
409 function go_back (b, prefix)
412 go_forward(b, -prefix);
414 check_buffer(b, content_buffer);
416 if (b.web_navigation.canGoBack)
418 var hist = b.web_navigation.sessionHistory;
419 var idx = hist.index - prefix;
422 b.web_navigation.gotoIndex(idx);
424 throw interactive_error("Can't go back");
428 "Go back in the session hisory for the current buffer.",
429 function (I) {go_back(I.buffer, I.p);});
431 function go_forward (b, prefix)
436 check_buffer(b, content_buffer);
438 if (b.web_navigation.canGoForward)
440 var hist = b.web_navigation.sessionHistory;
441 var idx = hist.index + prefix;
442 if (idx >= hist.count) idx = hist.count-1;
443 b.web_navigation.gotoIndex(idx);
445 throw interactive_error("Can't go forward");
447 interactive("go-forward",
448 "Go back in the session hisory for the current buffer.",
449 function (I) {go_forward(I.buffer, I.p);});
451 function stop_loading (b)
453 check_buffer(b, content_buffer);
454 b.web_navigation.stop(Ci.nsIWebNavigation.STOP_NETWORK);
456 interactive("stop-loading",
457 "Stop loading the current document.",
458 function (I) {stop_loading(I.buffer);});
460 function reload (b, bypass_cache)
462 check_buffer(b, content_buffer);
463 var flags = bypass_cache != null ?
464 Ci.nsIWebNavigation.LOAD_FLAGS_NONE : Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
465 b.web_navigation.reload(flags);
467 interactive("reload",
468 "Reload the current document.\n" +
469 "If a prefix argument is specified, the cache is bypassed.",
470 function (I) {reload(I.buffer, I.P);});
473 * browserDOMWindow: intercept window opening
475 function initialize_browser_dom_window(window) {
476 window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
477 new browser_dom_window(window);
480 define_variable("browser_default_open_target", OPEN_NEW_BUFFER, "Specifies how new window requests by content pages (e.g. by window.open from JavaScript or by using the target attribute of anchor and form elements) will be handled. This will generally be `OPEN_NEW_BUFFER', `OPEN_NEW_BUFFER_BACKGROUND', or `OPEN_NEW_WINDOW'.");
482 function browser_dom_window(window) {
483 this.window = window;
484 this.next_target = null;
486 browser_dom_window.prototype = {
487 QueryInterface: generate_QI(Ci.nsIBrowserDOMWindow),
489 openURI : function(aURI, aOpener, aWhere, aContext) {
491 // Reference: http://www.xulplanet.com/references/xpcomref/ifaces/nsIBrowserDOMWindow.html
492 var target = this.next_target;
493 if (target == null || target == FOLLOW_DEFAULT)
494 target = browser_default_open_target;
495 this.next_target = null;
497 /* Determine the opener buffer */
498 var opener_buffer = get_buffer_from_frame(this.window, aOpener.top);
499 var config = opener_buffer ? opener_buffer.configuration : null;
501 switch (browser_default_open_target) {
502 case OPEN_CURRENT_BUFFER:
503 case FOLLOW_TOP_FRAME:
505 case FOLLOW_CURRENT_FRAME:
507 case OPEN_NEW_BUFFER:
508 var buffer = new content_buffer(this.window, null /* element */, $configuration = config);
509 this.window.buffers.current = buffer;
510 return buffer.top_frame;
511 case OPEN_NEW_BUFFER_BACKGROUND:
512 var buffer = new content_buffer(this.window, null /* element */, $configuration = config);
513 return buffer.top_frame;
514 case OPEN_NEW_WINDOW:
515 default: /* shouldn't be needed */
517 /* We don't call make_window here, because that will result
518 * in the URL being loaded as the top-level document,
519 * instead of within a browser buffer. Instead, we can
520 * rely on Mozilla using browser.chromeURL. */
521 window_set_extra_arguments({initial_buffer_configuration: config});
527 add_hook("window_initialize_early_hook", initialize_browser_dom_window);
529 define_keywords("$enable", "$disable", "$doc");
530 function define_page_mode(name, display_name) {
532 var enable = arguments.$enable;
533 var disable = arguments.$disable;
534 var doc = arguments.$doc;
535 define_buffer_mode(name, display_name,
536 $class = "page_mode",
538 $disable = function (buffer) {
541 buffer.local_variables = {};
545 ignore_function_for_get_caller_source_code_reference("define_page_mode");
547 define_variable("auto_mode_list", [], "A list of mappings from URI regular expressions to page modes.");
548 function page_mode_auto_update(buffer) {
549 var uri = buffer.current_URI.spec;
550 var mode = predicate_alist_match(auto_mode_list, uri);
553 else if (buffer.page_mode)
554 conkeror[buffer.page_mode](buffer, false);
557 add_hook("content_buffer_location_change_hook", page_mode_auto_update);