1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
|
/*
netrik -- The ANTRIK Internet Viewer
Copyright (C) Olaf D. Buddenhagen AKA antrik, et al (see AUTHORS)
Published under the GNU GPL; see LICENSE for details.
*/
/* page.c -- page/history management
*
* (C) 2002, 2003 antrik
*
* This file contains all functions and data structures necessary for managing
* the page to be displayed in the viewer as well as the page history.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "debug.h"
#include "layout.h"
#include "links.h"
#include "page.h"
static void add_page(void);
struct Page_list page_list={
0, /* count */
-1, /* pos */
NULL /* **page */
};
/* Create a new entry in page list, after the one of the currently active page;
* set the new one as current. */
static void add_page(void)
{
struct Page *new_page;
/* resize array to hold all pages */
page_list.page=realloc(page_list.page, (page_list.count=page_list.pos+1)*sizeof(struct Page *));
if(page_list.page==NULL) {
fprintf(stderr, "memory allocation error while storing url to history\n");
exit(1);
}
/* create new page descriptor */
new_page=page_list.page[page_list.pos]=malloc(sizeof(struct Page));
if(new_page==NULL) {
fprintf(stderr, "memory allocation error while preparing page load\n");
exit(1);
}
/* init pager */
new_page->pager_pos=0; /* beginning of page will be shown in pager after loading */
new_page->cursor_x=new_page->cursor_y=0;
new_page->sticky_cursor=0;
new_page->active_link=-1; /* no link highlighted */
new_page->active_anchor=-1; /* no anchor marked */
new_page->mark=0; /* new pages not marked */
}
/* free all memory used by page descriptors and by page list struct itself */
void free_page_list(void)
{
int del_page;
DMSG((" freeing memory used by page descriptors...\n"));
for(del_page=0; del_page<page_list.count; ++del_page)
free_page(page_list.page[del_page]);
DMSG((" freeing memory used by list...\n"));
free(page_list.page);
}
/*
* load a HTML page so it can be displayed
*
* Creates a descriptor for the page, and loads the requested document. The
* entry number in the page list of the new page descriptor is returned.
*
* The URL of the page to be loaded is determined by combining the "url" with
* fallback values from "base_url" (allowing relative links etc.); if
* "base_url" is NULL, default values are used as fallback. If "url" is NULL,
* "base_url" is taken as the final url. (For already prepared URLs from
* history.)
*
* The optional "form" argument points to a form item in the structure tree,
* describing a form to submit with the HTTP request.
*
* When called with the a "reference" page (meaning the new page is identical
* to that one, and only a link to a local anchor was followed), the document
* isn't reloaded, and the layouting passes aren't performed; only a new
* descriptor is created, and the data structures are copied from the
* reference.
*
* If no "url" is given, meaning that a page from the page list (history) is to
* be reloaded, no new descriptor is created; the old one from the list is
* reused. Only the document is reloaded/layouted. (Unless a "reference" is
* given.)
*/
int load_page(base_url, url, form, reference, page_width, syntax_err)
const struct Url *base_url; /* fallback values */
const char *url; /* main URL */
const struct Item *form; /* form item of form to upload */
const struct Page *reference; /* old page; if not NULL, take layout data from it instead of reloading */
int page_width;
enum Syntax_error *syntax_err; /* return: parse_syntax() found HTML syntax errors in page */
{
struct Page *page; /* descriptor for new page */
if(reference==NULL) { /* new document (not already in memory) -> load */
if(url!=NULL) { /* new page (not from history) */
int del_page;
int last;
for(last=page_list.pos; last>=0 && page_list.page[last]->url->proto.type==PT_INTERNAL; --last); /* find last non-internal page in history */
page_list.pos=last+1; /* add new page after last */
/* free descriptors of all pages lost from history by loading some new page when not at end (after going back, or when overwriting internal pages) */
DMSG(("freeing memory used by page descriptors lost from history\n"));
for(del_page=page_list.pos; del_page<page_list.count; ++del_page)
free_page(page_list.page[del_page]);
add_page();
} else { /* from history */
int del_page;
int last;
for(last=page_list.count-1; last>=0 && page_list.page[last]->url->proto.type==PT_INTERNAL; --last); /* find last non-internal page in history */
DMSG(("freeing memory used by page descriptors of internal page...\n"));
for(del_page=last+1; del_page<page_list.count; ++del_page)
free_page(page_list.page[del_page]);
page_list.count=last+1;
}
page=page_list.page[page_list.pos];
page->layout=layout(base_url, url, form, page_width, syntax_err);
if(url!=NULL) /* new page -> need to store effective URL */
page->url=page->layout->url;
if(url==NULL && page->active_link >= 0) { /* page from history with some link previously active */
const int links_count=page->layout->links->count;
struct Link *link;
if(page->active_link >= links_count /* no longer valid */
|| (link=get_link(page->layout, page->active_link), page->cursor_x != link->x_start)
|| page->cursor_y != link->y_start
|| page->url_fingerprint != link->url_fingerprint
|| page->text_fingerprint != link->text_fingerprint
) { /* link doesn't match the old one exactly -> look for best match */
int candidate;
int best;
float best_deviation;
best=-1;
best_deviation=1.0; /* accept anything better than (or equal to) this */
for(candidate=0; candidate < links_count; ++candidate) { /* all links in page */
const struct Link *link=get_link(page->layout, candidate);
int url_dev=(page->url_fingerprint != link->url_fingerprint);
int text_dev=(page->text_fingerprint != link->text_fingerprint);
int x_dev=(page->cursor_x != link->x_start); /* only test whether x mateches exactly */
float y_dev=(float)abs(page->cursor_y - link->y_start)/page->layout->item_tree->y_end; /* deviation relative to page length */
float num_dev=(float)abs(page->active_link - candidate)/links_count; /* deviation relative to link count */
float deviation=0.5*url_dev+0.5*text_dev+1.5*num_dev+1.5*y_dev+0.1*x_dev;
if(deviation<=best_deviation) { /* best one so far -> save */
best=candidate;
best_deviation=deviation;
}
} /* for all links */
page->active_link=best;
} /* search best matching link */
} /* from history, with some link active */
} else { /* document already in memory -> copy layouting data */
if(reference->active_link>=0) /* some link is active in old page */
highlight_link(reference->layout, reference->active_link, 1); /* unhighlight (not active in new one) */
page=page_list.page[page_list.pos]; /* assume page from history */
if(url!=NULL) { /* new page (not from history) -> create */
struct Url *new_url;
new_url=merge_urls(base_url, url, NULL);
if(new_url->proto.type==PT_INTERNAL) /* couldn't merge URLs */
return page_list.pos; /* -> don't load anything */
++page_list.pos; /* add new after current page */
add_page();
page=page_list.page[page_list.pos];
page->url=new_url;
}
page->layout=reference->layout;
} /* reuse document */
if(*page->url->frag && url!=NULL) { /* fragment identifier specified (and not from history) -> go to anchor */
int anchor;
/* find desired anchor */
for(anchor=0; anchor < page->layout->anchors->count; ++anchor) { /* all anchors in list */
const struct Item *item=page->layout->anchors->anchor_item[anchor]; /* item of currently tested anchor */
const char *id=item->type==ITEM_BLOCK_ANCHOR?item->data.block_anchor->id:item->data.inline_anchor->id; /* name of current anchor */
if(strcasecmp(id, page->url->frag)==0) /* found right one -> don't search further */
break;
}
if(anchor < page->layout->anchors->count) /* found the anchor -> store */
page->active_anchor=anchor;
else {
fprintf(stderr, "\nAnchor \"%s\" not found in page.\n", page->url->frag);
*syntax_err=1;
}
} /* fragment identifier */
return page_list.pos;
}
/*
* unallocate page data
*
* Frees the split URL, and the page struct itself. (All other data should be
* already cleared when calling this.)
*/
void free_page(page)
struct Page *page;
{
if(page->url!=NULL)
free_url(page->url);
DMSG((" freeing page struct...\n"));
free(page);
}
|