mirror of
https://github.com/rkd77/elinks.git
synced 2024-10-08 05:04:16 -04:00
1671 lines
42 KiB
C
1671 lines
42 KiB
C
/** Sessions managment - you'll find things here which you wouldn't expect
|
|
* @file */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "elinks.h"
|
|
|
|
#include "bfu/dialog.h"
|
|
#include "bookmarks/bookmarks.h"
|
|
#include "cache/cache.h"
|
|
#include "config/home.h"
|
|
#include "config/options.h"
|
|
#include "dialogs/menu.h"
|
|
#include "dialogs/status.h"
|
|
#include "document/document.h"
|
|
#include "document/html/frames.h"
|
|
#include "document/html/iframes.h"
|
|
#include "document/refresh.h"
|
|
#include "document/renderer.h"
|
|
#include "document/view.h"
|
|
#include "globhist/globhist.h"
|
|
#include "intl/libintl.h"
|
|
#include "main/event.h"
|
|
#include "main/object.h"
|
|
#include "main/timer.h"
|
|
#include "network/connection.h"
|
|
#include "network/state.h"
|
|
#include "osdep/newwin.h"
|
|
#include "protocol/http/blacklist.h"
|
|
#include "protocol/protocol.h"
|
|
#include "protocol/uri.h"
|
|
#ifdef CONFIG_SCRIPTING_SPIDERMONKEY
|
|
# include "scripting/smjs/smjs.h"
|
|
#endif
|
|
#include "session/download.h"
|
|
#include "session/history.h"
|
|
#include "session/location.h"
|
|
#include "session/session.h"
|
|
#include "session/task.h"
|
|
#include "terminal/tab.h"
|
|
#include "terminal/terminal.h"
|
|
#include "terminal/window.h"
|
|
#include "util/conv.h"
|
|
#include "util/error.h"
|
|
#include "util/memlist.h"
|
|
#include "util/memory.h"
|
|
#include "util/string.h"
|
|
#include "util/time.h"
|
|
#include "viewer/text/draw.h"
|
|
#include "viewer/text/form.h"
|
|
#include "viewer/text/link.h"
|
|
#include "viewer/text/view.h"
|
|
#include "viewer/text/search.h"
|
|
|
|
|
|
struct file_to_load {
|
|
LIST_HEAD(struct file_to_load);
|
|
|
|
struct session *ses;
|
|
unsigned int req_sent:1;
|
|
int pri;
|
|
struct cache_entry *cached;
|
|
char *target_frame;
|
|
struct uri *uri;
|
|
struct download download;
|
|
};
|
|
|
|
/** This structure and related functions are used to maintain information
|
|
* for instances opened in new windows. We store all related session info like
|
|
* URI and base session to clone from so that when the new instance connects
|
|
* we can look up this information. In case of failure the session information
|
|
* has a timeout */
|
|
struct session_info {
|
|
LIST_HEAD(struct session_info);
|
|
|
|
int id;
|
|
timer_id_T timer;
|
|
struct session *ses;
|
|
struct uri *uri;
|
|
struct uri *referrer;
|
|
enum task_type task;
|
|
cache_mode_T cache_mode;
|
|
};
|
|
|
|
#define file_to_load_is_active(ftl) ((ftl)->req_sent && is_in_progress_state((ftl)->download.state))
|
|
|
|
|
|
INIT_LIST_OF(struct session, sessions);
|
|
|
|
remote_session_flags_T remote_session_flags;
|
|
|
|
|
|
static struct file_to_load *request_additional_file(struct session *,
|
|
const char *,
|
|
struct uri *, int);
|
|
|
|
static window_handler_T tabwin_func;
|
|
|
|
|
|
static INIT_LIST_OF(struct session_info, session_info);
|
|
static int session_info_id = 1;
|
|
|
|
static struct session_info *
|
|
get_session_info(int id)
|
|
{
|
|
struct session_info *info;
|
|
|
|
foreach (info, session_info) {
|
|
struct session *ses;
|
|
|
|
if (info->id != id) continue;
|
|
|
|
/* Make sure the info->ses session is still with us. */
|
|
foreach (ses, sessions)
|
|
if (ses == info->ses)
|
|
return info;
|
|
|
|
info->ses = NULL;
|
|
return info;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
done_session_info(struct session_info *info)
|
|
{
|
|
del_from_list(info);
|
|
kill_timer(&info->timer);
|
|
|
|
if (info->uri) done_uri(info->uri);
|
|
if (info->referrer) done_uri(info->referrer);
|
|
mem_free(info);
|
|
}
|
|
|
|
void
|
|
done_saved_session_info(void)
|
|
{
|
|
while (!list_empty(session_info))
|
|
done_session_info((struct session_info *)session_info.next);
|
|
}
|
|
|
|
/** Timer callback for session_info.timer. As explained in install_timer(),
|
|
* this function must erase the expired timer ID from all variables. */
|
|
static void
|
|
session_info_timeout(int id)
|
|
{
|
|
struct session_info *info = get_session_info(id);
|
|
|
|
if (!info) return;
|
|
info->timer = TIMER_ID_UNDEF;
|
|
/* The expired timer ID has now been erased. */
|
|
done_session_info(info);
|
|
}
|
|
|
|
int
|
|
add_session_info(struct session *ses, struct uri *uri, struct uri *referrer,
|
|
cache_mode_T cache_mode, enum task_type task)
|
|
{
|
|
struct session_info *info = (struct session_info *)mem_calloc(1, sizeof(*info));
|
|
|
|
if (!info) return -1;
|
|
|
|
info->id = session_info_id++;
|
|
/* I don't know what a reasonable start up time for a new instance is
|
|
* but it won't hurt to have a few seconds atleast. --jonas */
|
|
install_timer(&info->timer, (milliseconds_T) 10000,
|
|
(void (*)(void *)) session_info_timeout,
|
|
(void *) (long) info->id);
|
|
|
|
info->ses = ses;
|
|
info->task = task;
|
|
info->cache_mode = cache_mode;
|
|
|
|
if (uri) info->uri = get_uri_reference(uri);
|
|
if (referrer) info->referrer = get_uri_reference(referrer);
|
|
|
|
add_to_list(session_info, info);
|
|
|
|
return info->id;
|
|
}
|
|
|
|
static struct session *
|
|
init_saved_session(struct terminal *term, int id)
|
|
{
|
|
struct session_info *info = get_session_info(id);
|
|
struct session *ses;
|
|
|
|
if (!info) return NULL;
|
|
|
|
ses = init_session(info->ses, term, info->uri, 0);
|
|
|
|
if (!ses) {
|
|
done_session_info(info);
|
|
return ses;
|
|
}
|
|
|
|
/* Only do the ses_goto()-thing for target=_blank. */
|
|
if (info->uri && info->task != TASK_NONE) {
|
|
/* The init_session() call would have started the download but
|
|
* not with the requested cache mode etc. so interrupt that
|
|
* download . */
|
|
abort_loading(ses, 1);
|
|
|
|
ses->reloadlevel = info->cache_mode;
|
|
set_session_referrer(ses, info->referrer);
|
|
ses_goto(ses, info->uri, NULL, NULL, info->cache_mode,
|
|
info->task, 0);
|
|
}
|
|
|
|
done_session_info(info);
|
|
|
|
return ses;
|
|
}
|
|
|
|
static struct session *
|
|
get_master_session(void)
|
|
{
|
|
struct session *ses;
|
|
|
|
foreach (ses, sessions)
|
|
if (ses->tab->term->master) {
|
|
struct window *current_tab = get_current_tab(ses->tab->term);
|
|
|
|
return (struct session *)(current_tab ? current_tab->data : NULL);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** @relates session */
|
|
struct download *
|
|
get_current_download(struct session *ses)
|
|
{
|
|
struct download *download = NULL;
|
|
|
|
if (!ses) return NULL;
|
|
|
|
if (ses->task.type)
|
|
download = &ses->loading;
|
|
else if (have_location(ses))
|
|
download = &cur_loc(ses)->download;
|
|
|
|
if (download && is_in_state(download->state, S_OK)) {
|
|
struct file_to_load *ftl;
|
|
|
|
foreach (ftl, ses->more_files)
|
|
if (file_to_load_is_active(ftl))
|
|
return &ftl->download;
|
|
}
|
|
|
|
/* Note that @download isn't necessarily NULL here,
|
|
* if @ses->more_files is empty. -- Miciah */
|
|
return download;
|
|
}
|
|
|
|
static void
|
|
done_retry_connection_without_verification(void *data)
|
|
{
|
|
struct delayed_open *deo = (struct delayed_open *)data;
|
|
|
|
if (deo) {
|
|
done_uri(deo->uri);
|
|
mem_free(deo);
|
|
}
|
|
}
|
|
|
|
static void
|
|
retry_connection_without_verification(void *data)
|
|
{
|
|
struct delayed_open *deo = (struct delayed_open *)data;
|
|
|
|
if (deo) {
|
|
if (deo->uri->hostlen) {
|
|
add_blacklist_entry(deo->uri, SERVER_BLACKLIST_NO_CERT_VERIFY);
|
|
}
|
|
goto_uri(deo->ses, deo->uri);
|
|
done_uri(deo->uri);
|
|
mem_free(deo);
|
|
}
|
|
}
|
|
|
|
void
|
|
print_error_dialog(struct session *ses, struct connection_state state,
|
|
struct uri *uri, connection_priority_T priority)
|
|
{
|
|
struct string msg;
|
|
char *uristring;
|
|
|
|
/* Don't show error dialogs for missing CSS stylesheets */
|
|
if (priority == PRI_CSS
|
|
|| !init_string(&msg))
|
|
return;
|
|
|
|
uristring = uri ? get_uri_string(uri, URI_PUBLIC) : NULL;
|
|
if (uristring) {
|
|
#ifdef CONFIG_UTF8
|
|
if (ses->tab->term->utf8_cp)
|
|
decode_uri(uristring);
|
|
else
|
|
#endif /* CONFIG_UTF8 */
|
|
decode_uri_for_display(uristring);
|
|
add_format_to_string(&msg,
|
|
_("Unable to retrieve %s", ses->tab->term),
|
|
uristring);
|
|
mem_free(uristring);
|
|
add_to_string(&msg, ":\n\n");
|
|
}
|
|
|
|
add_to_string(&msg, get_state_message(state, ses->tab->term));
|
|
|
|
if (!uri || (uri->protocol != PROTOCOL_HTTPS && uri->protocol != PROTOCOL_GEMINI)) {
|
|
info_box(ses->tab->term, MSGBOX_FREE_TEXT,
|
|
N_("Error"), ALIGN_CENTER,
|
|
msg.source);
|
|
} else {
|
|
struct delayed_open *deo = (struct delayed_open *)mem_calloc(1, sizeof(*deo));
|
|
|
|
if (!deo) return;
|
|
|
|
add_to_string(&msg, "\n\n");
|
|
add_to_string(&msg, N_("Retry without verification?"));
|
|
deo->ses = ses;
|
|
deo->uri = get_uri_reference(uri);
|
|
|
|
msg_box(ses->tab->term, NULL, MSGBOX_FREE_TEXT,
|
|
N_("Error"), ALIGN_CENTER,
|
|
msg.source,
|
|
deo, 2,
|
|
MSG_BOX_BUTTON(N_("~Yes"), retry_connection_without_verification, B_ENTER),
|
|
MSG_BOX_BUTTON(N_("~No"), done_retry_connection_without_verification, B_ESC));
|
|
}
|
|
|
|
/* TODO: retry */
|
|
}
|
|
|
|
static void
|
|
abort_files_load(struct session *ses, int interrupt)
|
|
{
|
|
while (1) {
|
|
struct file_to_load *ftl;
|
|
int more = 0;
|
|
|
|
foreach (ftl, ses->more_files) {
|
|
if (!file_to_load_is_active(ftl))
|
|
continue;
|
|
|
|
more = 1;
|
|
cancel_download(&ftl->download, interrupt);
|
|
}
|
|
|
|
if (!more) break;
|
|
};
|
|
}
|
|
|
|
void
|
|
free_files(struct session *ses)
|
|
{
|
|
struct file_to_load *ftl;
|
|
|
|
abort_files_load(ses, 0);
|
|
foreach (ftl, ses->more_files) {
|
|
if (ftl->cached) object_unlock(ftl->cached);
|
|
if (ftl->uri) done_uri(ftl->uri);
|
|
mem_free_if(ftl->target_frame);
|
|
}
|
|
free_list(ses->more_files);
|
|
}
|
|
|
|
|
|
|
|
|
|
static void request_frameset(struct session *, struct frameset_desc *, int);
|
|
|
|
static void
|
|
request_frame(struct session *ses, char *name,
|
|
struct uri *uri, int depth)
|
|
{
|
|
struct location *loc = cur_loc(ses);
|
|
struct frame *frame;
|
|
|
|
assertm(have_location(ses), "request_frame: no location");
|
|
if_assert_failed return;
|
|
|
|
foreach (frame, loc->frames) {
|
|
struct document_view *doc_view;
|
|
|
|
if (c_strcasecmp(frame->name, name))
|
|
continue;
|
|
|
|
foreach (doc_view, ses->scrn_frames) {
|
|
if (doc_view->vs == &frame->vs && doc_view->document->frame_desc) {
|
|
request_frameset(ses, doc_view->document->frame_desc, depth);
|
|
return;
|
|
}
|
|
}
|
|
|
|
request_additional_file(ses, name, frame->vs.uri, PRI_FRAME);
|
|
return;
|
|
}
|
|
|
|
frame = (struct frame *)mem_calloc(1, sizeof(*frame));
|
|
if (!frame) return;
|
|
|
|
frame->name = stracpy(name);
|
|
if (!frame->name) {
|
|
mem_free(frame);
|
|
return;
|
|
}
|
|
|
|
init_vs(&frame->vs, uri, -1);
|
|
|
|
add_to_list(loc->frames, frame);
|
|
|
|
request_additional_file(ses, name, frame->vs.uri, PRI_FRAME);
|
|
}
|
|
|
|
static void
|
|
request_iframe(struct session *ses, char *name,
|
|
struct uri *uri, int depth)
|
|
{
|
|
struct location *loc = cur_loc(ses);
|
|
struct frame *iframe;
|
|
|
|
assertm(have_location(ses), "request_frame: no location");
|
|
if_assert_failed return;
|
|
|
|
foreach (iframe, loc->iframes) {
|
|
if (c_strcasecmp(iframe->name, name))
|
|
continue;
|
|
|
|
request_additional_file(ses, name, iframe->vs.uri, PRI_IFRAME);
|
|
return;
|
|
}
|
|
|
|
iframe = (struct frame *)mem_calloc(1, sizeof(*iframe));
|
|
|
|
if (!iframe) return;
|
|
|
|
iframe->name = stracpy(name);
|
|
if (!iframe->name) {
|
|
mem_free(iframe);
|
|
return;
|
|
}
|
|
|
|
init_vs(&iframe->vs, uri, -1);
|
|
|
|
add_to_list(loc->iframes, iframe);
|
|
|
|
request_additional_file(ses, name, iframe->vs.uri, PRI_IFRAME);
|
|
}
|
|
|
|
static void
|
|
request_frameset(struct session *ses, struct frameset_desc *frameset_desc, int depth)
|
|
{
|
|
int i;
|
|
|
|
if (depth > HTML_MAX_FRAME_DEPTH) return;
|
|
|
|
depth++; /* Inheritation counter (recursion brake ;) */
|
|
|
|
for (i = 0; i < frameset_desc->n; i++) {
|
|
struct frame_desc *frame_desc = &frameset_desc->frame_desc[i];
|
|
|
|
if (frame_desc->subframe) {
|
|
request_frameset(ses, frame_desc->subframe, depth);
|
|
} else if (frame_desc->name && frame_desc->uri) {
|
|
request_frame(ses, frame_desc->name,
|
|
frame_desc->uri, depth);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
request_iframes(struct session *ses, struct iframeset_desc *iframeset_desc, int depth)
|
|
{
|
|
int i;
|
|
|
|
if (depth > HTML_MAX_FRAME_DEPTH) return;
|
|
|
|
depth++; /* Inheritation counter (recursion brake ;) */
|
|
|
|
for (i = 0; i < iframeset_desc->n; i++) {
|
|
struct iframe_desc *iframe_desc = &iframeset_desc->iframe_desc[i];
|
|
|
|
if (iframe_desc->uri) {
|
|
request_iframe(ses, iframe_desc->name,
|
|
iframe_desc->uri, depth);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_CSS
|
|
static inline void
|
|
load_css_imports(struct session *ses, struct document_view *doc_view)
|
|
{
|
|
struct document *document = doc_view->document;
|
|
struct uri *uri;
|
|
int index;
|
|
|
|
if (!document) return;
|
|
|
|
foreach_uri (uri, index, &document->css_imports) {
|
|
request_additional_file(ses, "", uri, PRI_CSS);
|
|
}
|
|
}
|
|
#else
|
|
#define load_css_imports(ses, doc_view)
|
|
#endif
|
|
|
|
#ifdef CONFIG_ECMASCRIPT
|
|
static inline void
|
|
load_ecmascript_imports(struct session *ses, struct document_view *doc_view)
|
|
{
|
|
struct document *document = doc_view->document;
|
|
struct uri *uri;
|
|
int index;
|
|
|
|
if (!document) return;
|
|
|
|
foreach_uri (uri, index, &document->ecmascript_imports) {
|
|
request_additional_file(ses, "", uri, /* XXX */ PRI_CSS);
|
|
}
|
|
}
|
|
#else
|
|
#define load_ecmascript_imports(ses, doc_view)
|
|
#endif
|
|
|
|
static void
|
|
load_iframes(struct session *ses, struct document_view *doc_view)
|
|
{
|
|
struct document *document = doc_view->document;
|
|
|
|
if (!document || !document->iframe_desc) return;
|
|
|
|
request_iframes(ses, document->iframe_desc, 0);
|
|
}
|
|
|
|
NONSTATIC_INLINE void
|
|
load_frames(struct session *ses, struct document_view *doc_view)
|
|
{
|
|
struct document *document = doc_view->document;
|
|
|
|
if (!document || !document->frame_desc) return;
|
|
request_frameset(ses, document->frame_desc, 0);
|
|
|
|
foreach (doc_view, ses->scrn_frames) {
|
|
load_css_imports(ses, doc_view);
|
|
load_ecmascript_imports(ses, doc_view);
|
|
}
|
|
}
|
|
|
|
/** Timer callback for session.display_timer. As explained in install_timer(),
|
|
* this function must erase the expired timer ID from all variables. */
|
|
void
|
|
display_timer(struct session *ses)
|
|
{
|
|
timeval_T start, stop, duration;
|
|
milliseconds_T t;
|
|
|
|
timeval_now(&start);
|
|
draw_formatted(ses, 3);
|
|
timeval_now(&stop);
|
|
timeval_sub(&duration, &start, &stop);
|
|
|
|
t = mult_ms(timeval_to_milliseconds(&duration), DISPLAY_TIME);
|
|
if (t < DISPLAY_TIME_MIN) t = DISPLAY_TIME_MIN;
|
|
install_timer(&ses->display_timer, t,
|
|
(void (*)(void *)) display_timer,
|
|
ses);
|
|
/* The expired timer ID has now been erased. */
|
|
|
|
load_frames(ses, ses->doc_view);
|
|
load_css_imports(ses, ses->doc_view);
|
|
load_ecmascript_imports(ses, ses->doc_view);
|
|
load_iframes(ses, ses->doc_view);
|
|
process_file_requests(ses);
|
|
}
|
|
|
|
|
|
struct questions_entry {
|
|
LIST_HEAD(struct questions_entry);
|
|
|
|
void (*callback)(struct session *, void *);
|
|
void *data;
|
|
};
|
|
|
|
INIT_LIST_OF(struct questions_entry, questions_queue);
|
|
|
|
|
|
void
|
|
check_questions_queue(struct session *ses)
|
|
{
|
|
while (!list_empty(questions_queue)) {
|
|
struct questions_entry *q = (struct questions_entry *)questions_queue.next;
|
|
|
|
q->callback(ses, q->data);
|
|
del_from_list(q);
|
|
mem_free(q);
|
|
}
|
|
}
|
|
|
|
void
|
|
add_questions_entry(void (*callback)(struct session *, void *), void *data)
|
|
{
|
|
struct questions_entry *q = (struct questions_entry *)mem_alloc(sizeof(*q));
|
|
|
|
if (!q) return;
|
|
|
|
q->callback = callback;
|
|
q->data = data;
|
|
add_to_list(questions_queue, q);
|
|
}
|
|
|
|
#ifdef CONFIG_SCRIPTING
|
|
void
|
|
maybe_pre_format_html(struct cache_entry *cached, struct session *ses)
|
|
{
|
|
struct fragment *fragment;
|
|
static int pre_format_html_event = EVENT_NONE;
|
|
|
|
if (!cached || cached->preformatted)
|
|
return;
|
|
|
|
/* The script called from here may indirectly call
|
|
* garbage_collection(). If the refcount of the cache entry
|
|
* were 0, it could then be freed, and the
|
|
* cached->preformatted assignment at the end of this function
|
|
* would crash. Normally, the document has a reference to the
|
|
* cache entry, and that suffices. However, if the cache
|
|
* entry was loaded to satisfy e.g. USEMAP="imgmap.html#map",
|
|
* then cached->object.refcount == 0 here, and must be
|
|
* incremented.
|
|
*
|
|
* cached->object.refcount == 0 is safe while the cache entry
|
|
* is being loaded, because garbage_collection() calls
|
|
* is_entry_used(), which checks whether any connection is
|
|
* using the cache entry. But loading has ended before this
|
|
* point. */
|
|
object_lock(cached);
|
|
|
|
fragment = get_cache_fragment(cached);
|
|
if (!fragment) goto unlock_and_return;
|
|
|
|
/* We cannot do anything if the data are fragmented. */
|
|
if (!list_is_singleton(cached->frag)) goto unlock_and_return;
|
|
|
|
set_event_id(pre_format_html_event, "pre-format-html");
|
|
trigger_event(pre_format_html_event, ses, cached);
|
|
|
|
/* XXX: Keep this after the trigger_event, because hooks might call
|
|
* normalize_cache_entry()! */
|
|
cached->preformatted = 1;
|
|
|
|
unlock_and_return:
|
|
object_unlock(cached);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
check_incomplete_redirects(struct cache_entry *cached)
|
|
{
|
|
assert(cached);
|
|
|
|
cached = follow_cached_redirects(cached);
|
|
if (cached && !cached->redirect) {
|
|
/* XXX: This is not quite true, but does that difference
|
|
* matter here? */
|
|
return cached->incomplete;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
session_is_loading(struct session *ses)
|
|
{
|
|
struct download *download = get_current_download(ses);
|
|
|
|
if (!download) return 0;
|
|
|
|
if (!is_in_result_state(download->state))
|
|
return 1;
|
|
|
|
/* The validness of download->cached (especially the download struct in
|
|
* ses->loading) is hard to maintain so check before using it.
|
|
* Related to bug 559. */
|
|
if (download->cached
|
|
&& cache_entry_is_valid(download->cached)
|
|
&& check_incomplete_redirects(download->cached))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_ECMASCRIPT
|
|
void
|
|
doc_rerender_after_document_update(struct session *ses) {
|
|
/** This is really not nice. But that's the way
|
|
** how to display the final Javascript render
|
|
** taken from toggle_plain_html(ses, ses->doc_view, 0);
|
|
** This is toggled */
|
|
|
|
assert(ses && ses->doc_view && ses->tab && ses->tab->term);
|
|
if_assert_failed {
|
|
return;
|
|
}
|
|
if (ses->doc_view->document->ecmascript_counter > 0) {
|
|
if (ses->doc_view->vs) {
|
|
ses->doc_view->vs->plain = !ses->doc_view->vs->plain;
|
|
draw_formatted(ses, 1);
|
|
ses->doc_view->vs->plain = !ses->doc_view->vs->plain;
|
|
draw_formatted(ses, 1);
|
|
//DBG("REDRAWING...");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void
|
|
doc_loading_callback(struct download *download, struct session *ses)
|
|
{
|
|
int submit = 0;
|
|
|
|
if (is_in_result_state(download->state)) {
|
|
#ifdef CONFIG_SCRIPTING
|
|
maybe_pre_format_html(download->cached, ses);
|
|
#endif
|
|
kill_timer(&ses->display_timer);
|
|
|
|
draw_formatted(ses, 1);
|
|
|
|
#ifdef CONFIG_ECMASCRIPT
|
|
/* This is implemented to rerender the document
|
|
* in case it was modified by document.replace
|
|
* or document write */
|
|
doc_rerender_after_document_update(ses);
|
|
#endif
|
|
|
|
|
|
if (get_cmd_opt_bool("auto-submit")) {
|
|
if (!list_empty(ses->doc_view->document->forms)) {
|
|
get_cmd_opt_bool("auto-submit") = 0;
|
|
submit = 1;
|
|
}
|
|
}
|
|
|
|
load_frames(ses, ses->doc_view);
|
|
load_css_imports(ses, ses->doc_view);
|
|
load_ecmascript_imports(ses, ses->doc_view);
|
|
process_file_requests(ses);
|
|
|
|
start_document_refreshes(ses);
|
|
|
|
if (!is_in_state(download->state, S_OK)) {
|
|
print_error_dialog(ses, download->state,
|
|
ses->doc_view->document->uri,
|
|
download->pri);
|
|
}
|
|
|
|
} else if (is_in_transfering_state(download->state)
|
|
&& ses->display_timer == TIMER_ID_UNDEF) {
|
|
display_timer(ses);
|
|
}
|
|
|
|
check_questions_queue(ses);
|
|
print_screen_status(ses);
|
|
|
|
#ifdef CONFIG_GLOBHIST
|
|
if (download->pri != PRI_CSS) {
|
|
char *title = ses->doc_view->document->title;
|
|
struct uri *uri;
|
|
|
|
if (download->conn)
|
|
uri = download->conn->proxied_uri;
|
|
else if (download->cached)
|
|
uri = download->cached->uri;
|
|
else
|
|
uri = NULL;
|
|
|
|
if (uri)
|
|
add_global_history_item(struri(uri), title, time(NULL));
|
|
}
|
|
#endif
|
|
|
|
if (submit) auto_submit_form(ses);
|
|
}
|
|
|
|
static void
|
|
file_loading_callback(struct download *download, struct file_to_load *ftl)
|
|
{
|
|
if (ftl->download.cached && ftl->cached != ftl->download.cached) {
|
|
if (ftl->cached) object_unlock(ftl->cached);
|
|
ftl->cached = ftl->download.cached;
|
|
object_lock(ftl->cached);
|
|
}
|
|
|
|
/* FIXME: We need to do content-type check here! However, we won't
|
|
* handle properly the "Choose action" dialog now :(. */
|
|
if (ftl->cached && !ftl->cached->redirect_get && download->pri != PRI_CSS) {
|
|
struct session *ses = ftl->ses;
|
|
struct uri *loading_uri = ses->loading_uri;
|
|
char *target_frame = null_or_stracpy(ses->task.target.frame);
|
|
|
|
ses->loading_uri = ftl->uri;
|
|
mem_free_set(&ses->task.target.frame, null_or_stracpy(ftl->target_frame));
|
|
setup_download_handler(ses, &ftl->download, ftl->cached, 1 + (download->pri == PRI_IFRAME));
|
|
ses->loading_uri = loading_uri;
|
|
mem_free_set(&ses->task.target.frame, target_frame);
|
|
}
|
|
|
|
doc_loading_callback(download, ftl->ses);
|
|
}
|
|
|
|
static struct file_to_load *
|
|
request_additional_file(struct session *ses, const char *name, struct uri *uri, int pri)
|
|
{
|
|
struct file_to_load *ftl;
|
|
|
|
if (uri->protocol == PROTOCOL_UNKNOWN) {
|
|
return NULL;
|
|
}
|
|
|
|
/* XXX: We cannot run the external handler here, because
|
|
* request_additional_file() is called many times for a single URL
|
|
* (normally the foreach() right below catches them all). Anyway,
|
|
* having <frame src="mailto:foo"> would be just weird, wouldn't it?
|
|
* --pasky */
|
|
if (get_protocol_external_handler(ses->tab->term, uri)) {
|
|
return NULL;
|
|
}
|
|
|
|
foreach (ftl, ses->more_files) {
|
|
if (compare_uri(ftl->uri, uri, URI_BASE)) {
|
|
if (ftl->pri > pri) {
|
|
ftl->pri = pri;
|
|
move_download(&ftl->download, &ftl->download, pri);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
ftl = (struct file_to_load *)mem_calloc(1, sizeof(*ftl));
|
|
|
|
if (!ftl) return NULL;
|
|
|
|
ftl->uri = get_uri_reference(uri);
|
|
ftl->target_frame = stracpy(name);
|
|
ftl->download.callback = (download_callback_T *) file_loading_callback;
|
|
ftl->download.data = ftl;
|
|
ftl->pri = pri;
|
|
ftl->ses = ses;
|
|
|
|
add_to_list(ses->more_files, ftl);
|
|
|
|
return ftl;
|
|
}
|
|
|
|
static void
|
|
load_additional_file(struct file_to_load *ftl, cache_mode_T cache_mode)
|
|
{
|
|
struct document_view *doc_view = current_frame(ftl->ses);
|
|
struct uri *referrer = doc_view && doc_view->document
|
|
? doc_view->document->uri : NULL;
|
|
|
|
load_uri(ftl->uri, referrer, &ftl->download, ftl->pri, cache_mode, -1);
|
|
}
|
|
|
|
void
|
|
process_file_requests(struct session *ses)
|
|
{
|
|
if (ses->status.processing_file_requests) return;
|
|
ses->status.processing_file_requests = 1;
|
|
|
|
while (1) {
|
|
struct file_to_load *ftl;
|
|
int more = 0;
|
|
|
|
foreach (ftl, ses->more_files) {
|
|
if (ftl->req_sent)
|
|
continue;
|
|
|
|
ftl->req_sent = 1;
|
|
|
|
load_additional_file(ftl, CACHE_MODE_NORMAL);
|
|
more = 1;
|
|
}
|
|
|
|
if (!more) break;
|
|
};
|
|
|
|
ses->status.processing_file_requests = 0;
|
|
}
|
|
|
|
|
|
static void
|
|
dialog_goto_url_open(void *data)
|
|
{
|
|
dialog_goto_url((struct session *) data, NULL);
|
|
}
|
|
|
|
/** @returns 0 if the first session was not properly initialized and
|
|
* setup_session() should be called on the session as well. */
|
|
static int
|
|
setup_first_session(struct session *ses, struct uri *uri)
|
|
{
|
|
/* [gettext_accelerator_context(setup_first_session)] */
|
|
struct terminal *term = ses->tab->term;
|
|
|
|
if (!*get_opt_str("protocol.http.user_agent", NULL)) {
|
|
info_box(term, 0,
|
|
N_("Warning"), ALIGN_CENTER,
|
|
N_("You have an empty string in protocol.http.user_agent - "
|
|
"this was a default value in the past, substituted by "
|
|
"default ELinks User-Agent string. However, currently "
|
|
"this means that NO User-Agent HEADER "
|
|
"WILL BE SENT AT ALL - if this is really what you want, "
|
|
"set its value to \" \", otherwise please delete the line "
|
|
"with this setting from your configuration file (if you "
|
|
"have no idea what I'm talking about, just do this), so "
|
|
"that the correct default setting will be used. Apologies for "
|
|
"any inconvience caused."));
|
|
}
|
|
|
|
if (!get_opt_bool("config.saving_style_w", NULL)) {
|
|
struct option *opt = get_opt_rec(config_options, "config.saving_style_w");
|
|
opt->value.number = 1;
|
|
option_changed(ses, opt);
|
|
if (get_opt_int("config.saving_style", NULL) != 3) {
|
|
info_box(term, 0,
|
|
N_("Warning"), ALIGN_CENTER,
|
|
N_("You have option config.saving_style set to "
|
|
"a de facto obsolete value. The configuration "
|
|
"saving algorithms of ELinks were changed from "
|
|
"the last time you upgraded ELinks. Now, only "
|
|
"those options which you actually changed are "
|
|
"saved to the configuration file, instead of "
|
|
"all the options. This simplifies our "
|
|
"situation greatly when we see that some option "
|
|
"has an inappropriate default value or we need to "
|
|
"change the semantics of some option in a subtle way. "
|
|
"Thus, we recommend you change the value of "
|
|
"config.saving_style option to 3 in order to get "
|
|
"the \"right\" behaviour. Apologies for any "
|
|
"inconvience caused."));
|
|
}
|
|
}
|
|
|
|
if (first_use) {
|
|
/* Only open the goto URL dialog if no URI was passed on the
|
|
* command line. */
|
|
void (*handler)(void *) = uri ? NULL : dialog_goto_url_open;
|
|
|
|
first_use = 0;
|
|
|
|
msg_box(term, NULL, 0,
|
|
N_("Welcome"), ALIGN_CENTER,
|
|
N_("Welcome to ELinks!\n\n"
|
|
"Press ESC for menu. Documentation is available in "
|
|
"Help menu."),
|
|
ses, 1,
|
|
MSG_BOX_BUTTON(N_("~OK"), (void (*)(void *))handler, B_ENTER | B_ESC));
|
|
|
|
/* If there is no URI the goto dialog will pop up so there is
|
|
* no need to call setup_session(). */
|
|
if (!uri) return 1;
|
|
|
|
#ifdef CONFIG_BOOKMARKS
|
|
} else if (!uri && get_opt_bool("ui.sessions.auto_restore", NULL)) {
|
|
char *folder; /* UTF-8 */
|
|
|
|
folder = get_auto_save_bookmark_foldername_utf8();
|
|
if (folder) {
|
|
open_bookmark_folder(ses, folder);
|
|
mem_free(folder);
|
|
}
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
/* If there's a URI to load we have to call */
|
|
return 0;
|
|
}
|
|
|
|
/** First load the current URI of the base session. In most cases it will just
|
|
* be fetched from the cache so that the new tab will not appear ``empty' while
|
|
* loading the real URI or showing the goto URL dialog. */
|
|
static void
|
|
setup_session(struct session *ses, struct uri *uri, struct session *base)
|
|
{
|
|
if (base && !get_opt_bool("document.browse.search.reset", NULL)) {
|
|
ses->search_word = null_or_stracpy(base->search_word);
|
|
}
|
|
|
|
if (base && have_location(base)) {
|
|
ses_load(ses, get_uri_reference(cur_loc(base)->vs.uri),
|
|
NULL, NULL, CACHE_MODE_ALWAYS, TASK_FORWARD);
|
|
if (ses->doc_view && ses->doc_view->vs
|
|
&& base->doc_view && base->doc_view->vs) {
|
|
struct view_state *vs = ses->doc_view->vs;
|
|
|
|
destroy_vs(vs, 1);
|
|
copy_vs(vs, base->doc_view->vs);
|
|
|
|
ses->doc_view->vs = vs;
|
|
}
|
|
}
|
|
|
|
if (uri) {
|
|
goto_uri(ses, uri);
|
|
|
|
} else if (!goto_url_home(ses)) {
|
|
if (get_opt_bool("ui.startup_goto_dialog", NULL)) {
|
|
dialog_goto_url_open(ses);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @relates session */
|
|
struct session *
|
|
init_session(struct session *base_session, struct terminal *term,
|
|
struct uri *uri, int in_background)
|
|
{
|
|
struct session *ses = (struct session *)mem_calloc(1, sizeof(*ses));
|
|
|
|
if (!ses) return NULL;
|
|
|
|
ses->tab = init_tab(term, ses, tabwin_func);
|
|
if (!ses->tab) {
|
|
mem_free(ses);
|
|
return NULL;
|
|
}
|
|
|
|
ses->option = copy_option(config_options,
|
|
CO_SHALLOW | CO_NO_LISTBOX_ITEM);
|
|
create_history(&ses->history);
|
|
init_list(ses->scrn_frames);
|
|
init_list(ses->scrn_iframes);
|
|
init_list(ses->more_files);
|
|
init_list(ses->type_queries);
|
|
ses->task.type = TASK_NONE;
|
|
ses->display_timer = TIMER_ID_UNDEF;
|
|
|
|
#ifdef CONFIG_LEDS
|
|
init_led_panel(&ses->status.leds);
|
|
ses->status.ssl_led = register_led(ses, 0);
|
|
ses->status.insert_mode_led = register_led(ses, 1);
|
|
ses->status.ecmascript_led = register_led(ses, 2);
|
|
ses->status.popup_led = register_led(ses, 3);
|
|
|
|
ses->status.download_led = register_led(ses, 5);
|
|
#endif
|
|
ses->status.force_show_status_bar = -1;
|
|
ses->status.force_show_title_bar = -1;
|
|
|
|
add_to_list(sessions, ses);
|
|
|
|
/* Update the status -- most importantly the info about whether to the
|
|
* show the title, tab and status bar -- _before_ loading the URI so
|
|
* the document cache is not filled with useless documents if the
|
|
* content is already cached.
|
|
*
|
|
* For big document it also reduces memory usage quite a bit because
|
|
* (atleast that is my interpretation --jonas) the old document will
|
|
* have a chance to be released before rendering a new one. A few
|
|
* numbers when opening a new tab while viewing debians package list
|
|
* for unstable:
|
|
*
|
|
* 9307 jonas 15 0 34252 30m 5088 S 0.0 12.2 0:03.63 elinks-old
|
|
* 9305 jonas 15 0 17060 13m 5088 S 0.0 5.5 0:02.07 elinks-new
|
|
*/
|
|
update_status();
|
|
|
|
/* Check if the newly inserted session is the only in the list and do
|
|
* the special setup for the first session, */
|
|
if (!list_is_singleton(sessions) || !setup_first_session(ses, uri)) {
|
|
setup_session(ses, uri, base_session);
|
|
}
|
|
|
|
if (!in_background)
|
|
switch_to_tab(term, get_tab_number(ses->tab), -1);
|
|
|
|
if (!term->main_menu) activate_bfu_technology(ses, -1);
|
|
return ses;
|
|
}
|
|
|
|
static void
|
|
init_remote_session(struct session *ses, remote_session_flags_T *remote_ptr,
|
|
struct uri *uri)
|
|
{
|
|
remote_session_flags_T remote = *remote_ptr;
|
|
|
|
if (remote & SES_REMOTE_CURRENT_TAB) {
|
|
goto_uri(ses, uri);
|
|
/* Mask out the current tab flag */
|
|
*remote_ptr = remote & ~SES_REMOTE_CURRENT_TAB;
|
|
|
|
/* Remote session was masked out. Open all following URIs in
|
|
* new tabs, */
|
|
if (!*remote_ptr)
|
|
*remote_ptr = SES_REMOTE_NEW_TAB;
|
|
|
|
} else if (remote & SES_REMOTE_NEW_TAB) {
|
|
/* FIXME: This is not perfect. Doing multiple -remote
|
|
* with no URLs will make the goto dialogs
|
|
* inaccessible. Maybe we should not support this kind
|
|
* of thing or make the window focus detecting code
|
|
* more intelligent. --jonas */
|
|
open_uri_in_new_tab(ses, uri, 0, 0);
|
|
|
|
if (remote & SES_REMOTE_PROMPT_URL) {
|
|
dialog_goto_url_open(ses);
|
|
}
|
|
|
|
} else if (remote & SES_REMOTE_NEW_WINDOW) {
|
|
/* FIXME: It is quite rude because we just take the first
|
|
* possibility and should maybe make it possible to specify
|
|
* new-screen etc via -remote "openURL(..., new-*)" --jonas */
|
|
if (!can_open_in_new(ses->tab->term))
|
|
return;
|
|
|
|
open_uri_in_new_window(ses, uri, NULL,
|
|
ses->tab->term->environment,
|
|
CACHE_MODE_NORMAL, TASK_NONE);
|
|
|
|
} else if (remote & SES_REMOTE_ADD_BOOKMARK) {
|
|
#ifdef CONFIG_BOOKMARKS
|
|
int uri_cp;
|
|
|
|
if (!uri) return;
|
|
/** @todo Bug 1066: What is the encoding of struri()?
|
|
* This code currently assumes the system charset.
|
|
* It might be best to keep URIs in plain ASCII and
|
|
* then have a function that reversibly converts them
|
|
* to IRIs for display in a given encoding. */
|
|
uri_cp = get_cp_index("System");
|
|
add_bookmark_cp(NULL, 1, uri_cp, struri(uri), struri(uri));
|
|
#endif
|
|
|
|
} else if (remote & SES_REMOTE_INFO_BOX) {
|
|
char *text;
|
|
|
|
if (!uri) return;
|
|
|
|
text = memacpy(uri->data, uri->datalen);
|
|
if (!text) return;
|
|
|
|
info_box(ses->tab->term, MSGBOX_FREE_TEXT,
|
|
N_("Error"), ALIGN_CENTER,
|
|
text);
|
|
|
|
} else if (remote & SES_REMOTE_PROMPT_URL) {
|
|
dialog_goto_url_open(ses);
|
|
} else if (remote & SES_REMOTE_RELOAD) {
|
|
reload(ses, CACHE_MODE_FORCE_RELOAD);
|
|
} else if (remote & SES_REMOTE_SEARCH) {
|
|
if (!uri)
|
|
return;
|
|
if (strncmp(uri->string, "search:", sizeof("search:") - 1)) {
|
|
info_box(ses->tab->term, MSGBOX_FREE_TEXT,
|
|
N_("Incorrect search uri"), ALIGN_CENTER,
|
|
uri->string);
|
|
return;
|
|
}
|
|
|
|
search_for(ses, uri->data);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
struct string *
|
|
encode_session_info(struct string *info,
|
|
LIST_OF(struct string_list_item) *url_list)
|
|
{
|
|
struct string_list_item *url;
|
|
|
|
if (!init_string(info)) return NULL;
|
|
|
|
foreach (url, *url_list) {
|
|
struct string *str = &url->string;
|
|
|
|
add_bytes_to_string(info, str->source, str->length + 1);
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
/** Older elinks versions (up to and including 0.9.1) sends no magic variable and if
|
|
* this is detected we fallback to the old session info format. For this format
|
|
* the magic member of terminal_info hold the length of the URI string. The
|
|
* old format is handled by the default label in the switch.
|
|
*
|
|
* The new session info format supports extraction of multiple URIS from the
|
|
* terminal_info data member. The magic variable controls how to interpret
|
|
* the fields:
|
|
*
|
|
* - INTERLINK_NORMAL_MAGIC means use the terminal_info session_info
|
|
* variable as an id for a saved session.
|
|
*
|
|
* - INTERLINK_REMOTE_MAGIC means use the terminal_info session_info
|
|
* variable as the remote session flags. */
|
|
int
|
|
decode_session_info(struct terminal *term, struct terminal_info *info)
|
|
{
|
|
int len = info->length;
|
|
struct session *base_session = NULL;
|
|
remote_session_flags_T remote = 0;
|
|
char *str;
|
|
|
|
switch (info->magic) {
|
|
case INTERLINK_NORMAL_MAGIC:
|
|
/* Lookup if there are any saved sessions that should be
|
|
* resumed using the session_info as an id. The id is derived
|
|
* from the value of -base-session command line option in the
|
|
* connecting instance.
|
|
*
|
|
* At the moment it is only used when opening instances in new
|
|
* window to figure out how to initialize it when the new
|
|
* instance connects to the master.
|
|
*
|
|
* We don't need to handle it for the old format since new
|
|
* instances connecting this way will always be of the same
|
|
* origin as the master. */
|
|
if (init_saved_session(term, info->session_info))
|
|
return 1;
|
|
break;
|
|
|
|
case INTERLINK_REMOTE_MAGIC:
|
|
/* This could mean some fatal bug but I am unsure if we can
|
|
* actually assert it since people could pour all kinds of crap
|
|
* down the socket. */
|
|
if (!info->session_info) {
|
|
INTERNAL("Remote magic with no remote flags");
|
|
return 0;
|
|
}
|
|
|
|
remote = info->session_info;
|
|
|
|
/* If processing session info from a -remote instance we want
|
|
* to hook up with the master so we can handle request for
|
|
* stuff in current tab. */
|
|
base_session = (struct session *)(get_master_session() ?: sessions.next);
|
|
|
|
if (!base_session) {
|
|
return 0;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
/* The old format calculates the session_info and magic members
|
|
* as part of the data that should be decoded so we have to
|
|
* substract it to get the size of the actual URI data. */
|
|
len -= sizeof(info->session_info) + sizeof(info->magic);
|
|
|
|
/* Extract URI containing @magic bytes */
|
|
if (info->magic <= 0 || info->magic > len)
|
|
len = 0;
|
|
else
|
|
len = info->magic;
|
|
}
|
|
|
|
if (len <= 0) {
|
|
if (!remote)
|
|
return !!init_session(base_session, term, NULL, 0);
|
|
|
|
/* Even though there are no URIs we still have to
|
|
* handle remote stuff. */
|
|
init_remote_session(base_session, &remote, NULL);
|
|
return 0;
|
|
}
|
|
|
|
str = info->data;
|
|
|
|
/* Extract multiple (possible) NUL terminated URIs */
|
|
while (len > 0) {
|
|
char *end = (char *)memchr(str, 0, len);
|
|
int urilength = end ? end - str : len;
|
|
struct uri *uri = NULL;
|
|
char *uristring = memacpy(str, urilength);
|
|
|
|
if (uristring) {
|
|
uri = get_hooked_uri(uristring, base_session, term->cwd);
|
|
mem_free(uristring);
|
|
}
|
|
|
|
len -= urilength + 1;
|
|
str += urilength + 1;
|
|
|
|
if (remote) {
|
|
if (!uri) continue;
|
|
|
|
init_remote_session(base_session, &remote, uri);
|
|
|
|
} else {
|
|
int backgrounded = !list_empty(term->windows);
|
|
int bad_url = !uri;
|
|
struct session *ses;
|
|
static char about_blank[] = "about:blank";
|
|
|
|
if (!uri)
|
|
uri = get_uri(about_blank, URI_NONE);
|
|
|
|
ses = init_session(base_session, term, uri, backgrounded);
|
|
if (!ses) {
|
|
/* End loop if initialization fails */
|
|
len = 0;
|
|
} else if (bad_url) {
|
|
print_error_dialog(ses, connection_state(S_BAD_URL),
|
|
NULL, PRI_MAIN);
|
|
}
|
|
|
|
}
|
|
|
|
if (uri) done_uri(uri);
|
|
}
|
|
|
|
/* If we only initialized remote sessions or didn't manage to add any
|
|
* new tabs return zero so the terminal will be destroyed ASAP. */
|
|
return remote ? 0 : !list_empty(term->windows);
|
|
}
|
|
|
|
|
|
void
|
|
abort_loading(struct session *ses, int interrupt)
|
|
{
|
|
if (have_location(ses)) {
|
|
struct location *loc = cur_loc(ses);
|
|
|
|
cancel_download(&loc->download, interrupt);
|
|
abort_files_load(ses, interrupt);
|
|
}
|
|
abort_preloading(ses, interrupt);
|
|
}
|
|
|
|
static void
|
|
destroy_session(struct session *ses)
|
|
{
|
|
struct document_view *doc_view;
|
|
|
|
assert(ses);
|
|
if_assert_failed return;
|
|
|
|
#ifdef CONFIG_SCRIPTING_SPIDERMONKEY
|
|
smjs_detach_session_object(ses);
|
|
#endif
|
|
destroy_downloads(ses);
|
|
abort_loading(ses, 0);
|
|
free_files(ses);
|
|
if (ses->doc_view) {
|
|
detach_formatted(ses->doc_view);
|
|
mem_free(ses->doc_view);
|
|
}
|
|
|
|
foreach (doc_view, ses->scrn_frames)
|
|
detach_formatted(doc_view);
|
|
|
|
free_list(ses->scrn_frames);
|
|
|
|
foreach (doc_view, ses->scrn_iframes)
|
|
detach_formatted(doc_view);
|
|
|
|
free_list(ses->scrn_iframes);
|
|
|
|
destroy_history(&ses->history);
|
|
set_session_referrer(ses, NULL);
|
|
|
|
if (ses->loading_uri) done_uri(ses->loading_uri);
|
|
|
|
kill_timer(&ses->display_timer);
|
|
|
|
while (!list_empty(ses->type_queries))
|
|
done_type_query((struct type_query *)ses->type_queries.next);
|
|
|
|
if (ses->download_uri) done_uri(ses->download_uri);
|
|
mem_free_if(ses->search_word);
|
|
mem_free_if(ses->last_search_word);
|
|
mem_free_if(ses->status.last_title);
|
|
mem_free_if(ses->status.window_status);
|
|
|
|
if (ses->option) {
|
|
delete_option(ses->option);
|
|
ses->option = NULL;
|
|
}
|
|
del_from_list(ses);
|
|
}
|
|
|
|
void
|
|
reload(struct session *ses, cache_mode_T cache_mode)
|
|
{
|
|
reload_frame(ses, NULL, cache_mode);
|
|
}
|
|
|
|
void
|
|
reload_frame(struct session *ses, char *name,
|
|
cache_mode_T cache_mode)
|
|
{
|
|
abort_loading(ses, 0);
|
|
|
|
if (cache_mode == CACHE_MODE_INCREMENT) {
|
|
cache_mode = CACHE_MODE_NEVER;
|
|
if (ses->reloadlevel < CACHE_MODE_NEVER)
|
|
cache_mode = ++ses->reloadlevel;
|
|
} else {
|
|
ses->reloadlevel = cache_mode;
|
|
}
|
|
|
|
if (have_location(ses)) {
|
|
struct location *loc = cur_loc(ses);
|
|
struct file_to_load *ftl;
|
|
|
|
#ifdef CONFIG_ECMASCRIPT
|
|
loc->vs.ecmascript_fragile = 1;
|
|
#endif
|
|
|
|
/* FIXME: When reloading use loading_callback and set up a
|
|
* session task so that the reloading will work even when the
|
|
* reloaded document contains redirects. This is needed atleast
|
|
* when reloading HTTP auth document after the user has entered
|
|
* credentials. */
|
|
loc->download.data = ses;
|
|
loc->download.callback = (download_callback_T *) doc_loading_callback;
|
|
|
|
mem_free_set(&ses->task.target.frame, null_or_stracpy(name));
|
|
|
|
load_uri(loc->vs.uri, ses->referrer, &loc->download, PRI_MAIN, cache_mode, -1);
|
|
|
|
foreach (ftl, ses->more_files) {
|
|
if (file_to_load_is_active(ftl))
|
|
continue;
|
|
|
|
ftl->download.data = ftl;
|
|
ftl->download.callback = (download_callback_T *) file_loading_callback;
|
|
|
|
load_additional_file(ftl, cache_mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
struct frame *
|
|
ses_find_frame(struct session *ses, const char *name)
|
|
{
|
|
struct location *loc = cur_loc(ses);
|
|
struct frame *frame;
|
|
|
|
assertm(have_location(ses), "ses_request_frame: no location yet");
|
|
if_assert_failed return NULL;
|
|
|
|
foreachback (frame, loc->frames)
|
|
if (!c_strcasecmp(frame->name, name))
|
|
return frame;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct frame *
|
|
ses_find_iframe(struct session *ses, char *name)
|
|
{
|
|
struct location *loc = cur_loc(ses);
|
|
struct frame *iframe;
|
|
|
|
assertm(have_location(ses), "ses_request_frame: no location yet");
|
|
if_assert_failed return NULL;
|
|
|
|
foreachback (iframe, loc->iframes)
|
|
if (!c_strcasecmp(iframe->name, name))
|
|
return iframe;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
set_session_referrer(struct session *ses, struct uri *referrer)
|
|
{
|
|
if (ses->referrer) done_uri(ses->referrer);
|
|
ses->referrer = referrer ? get_uri_reference(referrer) : NULL;
|
|
}
|
|
|
|
static void
|
|
tabwin_func(struct window *tab, struct term_event *ev)
|
|
{
|
|
struct session *ses = (struct session *)tab->data;
|
|
|
|
switch (ev->ev) {
|
|
case EVENT_ABORT:
|
|
if (ses) destroy_session(ses);
|
|
if (!list_empty(sessions)) update_status();
|
|
break;
|
|
case EVENT_INIT:
|
|
case EVENT_RESIZE:
|
|
tab->resize = 1;
|
|
/* fall-through */
|
|
case EVENT_REDRAW:
|
|
if (!ses || ses->tab != get_current_tab(ses->tab->term))
|
|
break;
|
|
|
|
draw_formatted(ses, tab->resize);
|
|
if (tab->resize) {
|
|
load_frames(ses, ses->doc_view);
|
|
process_file_requests(ses);
|
|
tab->resize = 0;
|
|
}
|
|
print_screen_status(ses);
|
|
break;
|
|
case EVENT_KBD:
|
|
case EVENT_MOUSE:
|
|
if (!ses) break;
|
|
/* This check is related to bug 296 */
|
|
assert(ses->tab == get_current_tab(ses->tab->term));
|
|
send_event(ses, ev);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the url being viewed by this session. Writes it into @a str.
|
|
* A maximum of @a str_size bytes (including null) will be written.
|
|
* @relates session
|
|
*/
|
|
char *
|
|
get_current_url(struct session *ses, char *str, size_t str_size)
|
|
{
|
|
struct uri *uri;
|
|
int length;
|
|
|
|
assert(str && str_size > 0);
|
|
|
|
uri = have_location(ses) ? cur_loc(ses)->vs.uri : ses->loading_uri;
|
|
|
|
/* Not looking or loading anything */
|
|
if (!uri) return NULL;
|
|
|
|
/* Ensure that the url size is not greater than str_size.
|
|
* We can't just happily strncpy(str, here, str_size)
|
|
* because we have to stop at POST_CHAR, not only at NULL. */
|
|
length = int_min(get_real_uri_length(uri), str_size - 1);
|
|
|
|
return safe_strncpy(str, struri(uri), length + 1);
|
|
}
|
|
|
|
/**
|
|
* Gets the title of the page being viewed by this session. Writes it into
|
|
* @a str. A maximum of @a str_size bytes (including null) will be written.
|
|
* @relates session
|
|
*/
|
|
char *
|
|
get_current_title(struct session *ses, char *str, size_t str_size)
|
|
{
|
|
struct document_view *doc_view = current_frame(ses);
|
|
|
|
assert(str && str_size > 0);
|
|
|
|
/* Ensure that the title is defined */
|
|
/* TODO: Try globhist --jonas */
|
|
if (doc_view && doc_view->document->title)
|
|
return safe_strncpy(str, doc_view->document->title, str_size);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Gets the url of the link currently selected. Writes it into @a str.
|
|
* A maximum of @a str_size bytes (including null) will be written.
|
|
* @relates session
|
|
*/
|
|
char *
|
|
get_current_link_url(struct session *ses, char *str, size_t str_size)
|
|
{
|
|
struct link *link = get_current_session_link(ses);
|
|
|
|
assert(str && str_size > 0);
|
|
|
|
if (!link) return NULL;
|
|
|
|
return safe_strncpy(str, link->where ? link->where : link->where_img, str_size);
|
|
}
|
|
|
|
/** get_current_link_name: returns the name of the current link
|
|
* (the text between @<A> and @</A>), @a str is a preallocated string,
|
|
* @a str_size includes the null char.
|
|
* @relates session */
|
|
char *
|
|
get_current_link_name(struct session *ses, char *str, size_t str_size)
|
|
{
|
|
struct link *link = get_current_session_link(ses);
|
|
char *where, *name = NULL;
|
|
|
|
assert(str && str_size > 0);
|
|
|
|
if (!link) return NULL;
|
|
|
|
where = link->where ? link->where : link->where_img;
|
|
#ifdef CONFIG_GLOBHIST
|
|
{
|
|
struct global_history_item *item;
|
|
|
|
item = get_global_history_item(where);
|
|
if (item) name = item->title;
|
|
}
|
|
#endif
|
|
if (!name) name = get_link_name(link);
|
|
if (!name) name = where;
|
|
|
|
return safe_strncpy(str, name, str_size);
|
|
}
|
|
|
|
struct link *
|
|
get_current_link_in_view(struct document_view *doc_view)
|
|
{
|
|
struct link *link = get_current_link(doc_view);
|
|
|
|
return link && !link_is_form(link) ? link : NULL;
|
|
}
|
|
|
|
/** @relates session */
|
|
struct link *
|
|
get_current_session_link(struct session *ses)
|
|
{
|
|
return get_current_link_in_view(current_frame(ses));
|
|
}
|
|
|
|
/** @relates session */
|
|
int
|
|
eat_kbd_repeat_count(struct session *ses)
|
|
{
|
|
int count = ses->kbdprefix.repeat_count;
|
|
|
|
set_kbd_repeat_count(ses, 0);
|
|
|
|
/* Clear status bar when prefix is eaten (bug 930) */
|
|
print_screen_status(ses);
|
|
return count;
|
|
}
|
|
|
|
/** @relates session */
|
|
int
|
|
set_kbd_repeat_count(struct session *ses, int new_count)
|
|
{
|
|
int old_count = ses->kbdprefix.repeat_count;
|
|
|
|
if (new_count == old_count)
|
|
return old_count;
|
|
|
|
ses->kbdprefix.repeat_count = new_count;
|
|
|
|
/* Update the status bar. */
|
|
print_screen_status(ses);
|
|
|
|
/* Clear the old link highlighting. */
|
|
draw_formatted(ses, 0);
|
|
|
|
if (new_count != 0) {
|
|
struct document_view *doc_view = current_frame(ses);
|
|
|
|
highlight_links_with_prefixes_that_start_with_n(ses->tab->term,
|
|
doc_view,
|
|
new_count);
|
|
}
|
|
|
|
return new_count;
|
|
}
|