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 lspec = arguments.$load;
85 if (lspec.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);
182 this.last_user_input_received = null;
184 else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
185 aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
186 if (this.loading == true) {
187 //dumpln("*** finished loading");
188 content_buffer_finished_loading_hook.run(this);
189 this.loading = false;
193 if (aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP |
194 Ci.nsIWebProgressListener.STATE_START)) {
196 this.set_default_message("Done");
200 /* This method is called to indicate progress changes for the currently
202 onProgressChange: function(webProgress, request, curSelf, maxSelf,
203 curTotal, maxTotal) {
204 content_buffer_progress_change_hook.run(this, request, curSelf, maxSelf, curTotal, maxTotal);
207 /* This method is called to indicate a change to the current location.
208 The url can be gotten as location.spec. */
209 onLocationChange : function(webProgress, request, location) {
210 /* Attempt to ignore onLocationChange calls due to the initial
211 * loading of about:blank by all xul:browser elements. */
212 if (location.spec == "about:blank" && this.ignore_initial_blank)
215 this.ignore_initial_blank = false;
217 //dumpln("spec: " + location.spec +" ;;; " + this.display_URI_string);
218 /* Use the real location URI now */
219 this._display_URI = null;
220 content_buffer_location_change_hook.run(this, request, location);
221 this.last_user_input_received = null;
222 buffer_description_change_hook.run(this);
225 // This method is called to indicate a status changes for the currently
226 // loading page. The message is already formatted for display.
227 // Status messages could be displayed in the minibuffer output area.
228 onStatusChange: function(webProgress, request, status, msg) {
229 this.set_default_message(msg);
230 content_buffer_status_change_hook.run(this, request, status, msg);
233 // This method is called when the security state of the browser changes.
234 onSecurityChange: function(webProgress, request, state) {
235 /* FIXME: currently this isn't used */
238 const WPL = Components.interfaces.nsIWebProgressListener;
240 if (state & WPL.STATE_IS_INSECURE) {
241 // update visual indicator
243 var level = "unknown";
244 if (state & WPL.STATE_IS_SECURE) {
245 if (state & WPL.STATE_SECURE_HIGH)
247 else if (state & WPL.STATE_SECURE_MED)
249 else if (state & WPL.STATE_SECURE_LOW)
251 } else if (state & WPL_STATE_IS_BROKEN) {
254 // provide a visual indicator of the security state here.
259 /* Inherit from buffer */
261 __proto__ : buffer.prototype
265 add_hook("current_content_buffer_finished_loading_hook",
267 buffer.window.minibuffer.show("Done");
270 add_hook("current_content_buffer_status_change_hook",
271 function (buffer, request, status, msg) {
272 buffer.set_default_message(msg);
276 define_variable("url_completion_use_webjumps", true, "Specifies whether URL completion should complete webjumps.");
277 define_variable("url_completion_use_bookmarks", true, "Specifies whether URL completion should complete bookmarks.");
278 define_variable("url_completion_use_history", false,
279 "Specifies whether URL completion should complete using browser history.");
281 minibuffer_auto_complete_preferences["url"] = true;
282 minibuffer.prototype.read_url = function () {
283 keywords(arguments, $prompt = "URL:", $history = "url", $initial_value = "",
284 $use_webjumps = url_completion_use_webjumps,
285 $use_history = url_completion_use_history,
286 $use_bookmarks = url_completion_use_bookmarks);
287 var completer = url_completer ($use_webjumps = arguments.$use_webjumps,
288 $use_bookmarks = arguments.$use_bookmarks,
289 $use_history = arguments.$use_history);
290 var result = yield this.read(
291 $prompt = arguments.$prompt,
292 $history = arguments.$history,
293 $completer = completer,
294 $initial_value = arguments.$initial_value,
295 $auto_complete = "url",
297 $match_required = false);
298 if (result == "") // well-formedness check. (could be better!)
299 throw ("invalid url or webjump (\""+ result +"\")");
300 yield co_return(get_url_or_webjump(result));
303 I.content_charset = interactive_method(
304 $sync = function (ctx) {
305 var buffer = ctx.buffer;
306 if (!(buffer instanceof content_buffer))
307 throw new Error("Current buffer is of invalid type");
308 // -- Charset of content area of focusedWindow
309 var focusedWindow = buffer.focused_frame;
311 return focusedWindow.document.characterSet;
317 I.content_selection = interactive_method(
318 $sync = function (ctx) {
319 // -- Selection of content area of focusedWindow
320 var focusedWindow = this.buffers.current.focused_frame;
321 return focusedWindow.getSelection ();
324 function overlink_update_status(buffer, text) {
326 buffer.window.minibuffer.show("Link: " + text);
328 buffer.window.minibuffer.show("");
331 define_global_mode("overlink_mode",
333 add_hook("current_content_buffer_overlink_change_hook", overlink_update_status);
336 remove_hook("current_content_buffer_overlink_change_hook", overlink_update_status);
341 function open_in_browser(buffer, target, lspec)
344 case OPEN_CURRENT_BUFFER:
346 case FOLLOW_CURRENT_FRAME:
347 case FOLLOW_TOP_FRAME:
348 if (buffer instanceof content_buffer) {
349 apply_load_spec(buffer, lspec);
352 target = OPEN_NEW_BUFFER;
353 // If the current buffer is not a content_buffer, use a new buffer.
355 create_buffer(buffer.window,
356 buffer_creator(content_buffer,
358 $configuration = buffer.configuration),
364 interactive("open-url",
365 "Open a URL, reusing the current buffer by default",
367 var target = I.browse_target("open");
368 open_in_browser(I.buffer, target,
369 (yield I.minibuffer.read_url($prompt = browse_target_prompt(target))));
372 interactive("find-alternate-url",
373 "Edit the current URL in the minibuffer",
375 var target = I.browse_target("open");
376 check_buffer(I.buffer, content_buffer);
377 open_in_browser(I.buffer, target,
378 (yield I.minibuffer.read_url($prompt = browse_target_prompt(target),
379 $initial_value = I.buffer.display_URI_string)));
382 interactive("find-url",
383 "Open a URL in a new buffer",
385 var target = I.browse_target("find-url");
386 open_in_browser(I.buffer, target,
387 (yield I.minibuffer.read_url($prompt = browse_target_prompt(target))));
389 default_browse_targets["find-url"] = [OPEN_NEW_BUFFER, OPEN_NEW_WINDOW];
391 function go_up (b, target)
393 var url = Cc["@mozilla.org/network/standard-url;1"]
394 .createInstance (Ci.nsIURL);
395 url.spec = b.current_URI.spec;
397 if (url.param != "" || url.query != "")
399 else if (url.fileName != "")
403 open_in_browser(b, target, b.current_URI.resolve (up));
406 "Go to the parent directory of the current URL",
407 function (I) { go_up(check_buffer(I.buffer, content_buffer), I.browse_target("go-up")); });
408 default_browse_targets["go-up"] = "open";
411 function go_back (b, prefix)
414 go_forward(b, -prefix);
416 check_buffer(b, content_buffer);
418 if (b.web_navigation.canGoBack)
420 var hist = b.web_navigation.sessionHistory;
421 var idx = hist.index - prefix;
424 b.web_navigation.gotoIndex(idx);
426 throw interactive_error("Can't go back");
430 "Go back in the session hisory for the current buffer.",
431 function (I) {go_back(I.buffer, I.p);});
433 function go_forward (b, prefix)
438 check_buffer(b, content_buffer);
440 if (b.web_navigation.canGoForward)
442 var hist = b.web_navigation.sessionHistory;
443 var idx = hist.index + prefix;
444 if (idx >= hist.count) idx = hist.count-1;
445 b.web_navigation.gotoIndex(idx);
447 throw interactive_error("Can't go forward");
449 interactive("go-forward",
450 "Go back in the session hisory for the current buffer.",
451 function (I) {go_forward(I.buffer, I.p);});
453 function stop_loading (b)
455 check_buffer(b, content_buffer);
456 b.web_navigation.stop(Ci.nsIWebNavigation.STOP_NETWORK);
458 interactive("stop-loading",
459 "Stop loading the current document.",
460 function (I) {stop_loading(I.buffer);});
462 function reload (b, bypass_cache)
464 check_buffer(b, content_buffer);
465 var flags = bypass_cache != null ?
466 Ci.nsIWebNavigation.LOAD_FLAGS_NONE : Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
467 b.web_navigation.reload(flags);
469 interactive("reload",
470 "Reload the current document.\n" +
471 "If a prefix argument is specified, the cache is bypassed.",
472 function (I) {reload(I.buffer, I.P);});
475 * browserDOMWindow: intercept window opening
477 function initialize_browser_dom_window(window) {
478 window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
479 new browser_dom_window(window);
482 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'.");
484 function browser_dom_window(window) {
485 this.window = window;
486 this.next_target = null;
488 browser_dom_window.prototype = {
489 QueryInterface: generate_QI(Ci.nsIBrowserDOMWindow),
491 openURI : function(aURI, aOpener, aWhere, aContext) {
493 // Reference: http://www.xulplanet.com/references/xpcomref/ifaces/nsIBrowserDOMWindow.html
494 var target = this.next_target;
495 if (target == null || target == FOLLOW_DEFAULT)
496 target = browser_default_open_target;
497 this.next_target = null;
499 /* Determine the opener buffer */
500 var opener_buffer = get_buffer_from_frame(this.window, aOpener.top);
501 var config = opener_buffer ? opener_buffer.configuration : null;
503 switch (browser_default_open_target) {
504 case OPEN_CURRENT_BUFFER:
505 case FOLLOW_TOP_FRAME:
507 case FOLLOW_CURRENT_FRAME:
509 case OPEN_NEW_BUFFER:
510 var buffer = new content_buffer(this.window, null /* element */, $configuration = config);
511 this.window.buffers.current = buffer;
512 return buffer.top_frame;
513 case OPEN_NEW_BUFFER_BACKGROUND:
514 var buffer = new content_buffer(this.window, null /* element */, $configuration = config);
515 return buffer.top_frame;
516 case OPEN_NEW_WINDOW:
517 default: /* shouldn't be needed */
519 /* We don't call make_window here, because that will result
520 * in the URL being loaded as the top-level document,
521 * instead of within a browser buffer. Instead, we can
522 * rely on Mozilla using browser.chromeURL. */
523 window_set_extra_arguments({initial_buffer_configuration: config});
529 add_hook("window_initialize_early_hook", initialize_browser_dom_window);
531 define_keywords("$enable", "$disable", "$doc");
532 function define_page_mode(name, display_name) {
534 var enable = arguments.$enable;
535 var disable = arguments.$disable;
536 var doc = arguments.$doc;
537 define_buffer_mode(name, display_name,
538 $class = "page_mode",
540 $disable = function (buffer) {
543 buffer.local_variables = {};
547 ignore_function_for_get_caller_source_code_reference("define_page_mode");
549 define_variable("auto_mode_list", [], "A list of mappings from URI regular expressions to page modes.");
550 function page_mode_auto_update(buffer) {
551 var uri = buffer.current_URI.spec;
552 var mode = predicate_alist_match(auto_mode_list, uri);
555 else if (buffer.page_mode)
556 conkeror[buffer.page_mode](buffer, false);
559 add_hook("content_buffer_location_change_hook", page_mode_auto_update);