mirror of
https://github.com/netsurf-browser/netsurf
synced 2025-01-01 00:34:31 +03:00
[project @ 2003-07-07 22:10:51 by jmb]
Rewrite plugin system backend. svn path=/import/netsurf/; revision=210
This commit is contained in:
parent
1abf8018a9
commit
133c3ee759
@ -1,11 +1,10 @@
|
|||||||
$Id: TODO-General,v 1.3 2003/06/01 21:56:27 jmb Exp $
|
$Id: TODO-General,v 1.4 2003/07/07 22:10:51 jmb Exp $
|
||||||
|
|
||||||
TODO-General for NetSurf.
|
TODO-General for NetSurf.
|
||||||
|
|
||||||
This file documents general things which need doing.
|
This file documents general things which need doing.
|
||||||
|
|
||||||
|
|
||||||
Browser Redirect
|
|
||||||
Disk Cache
|
Disk Cache
|
||||||
Saving
|
Saving
|
||||||
Printing
|
Printing
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
$Id: TODO-HTML,v 1.2 2003/06/01 01:02:29 jmb Exp $
|
$Id: TODO-HTML,v 1.3 2003/07/07 22:10:51 jmb Exp $
|
||||||
|
|
||||||
TODO-HTML file for NetSurf.
|
TODO-HTML file for NetSurf.
|
||||||
|
|
||||||
@ -15,13 +15,13 @@ Text Formatting:
|
|||||||
<VAR>, <ABBR> and <ACRONYM> tags
|
<VAR>, <ABBR> and <ACRONYM> tags
|
||||||
<Q> tag
|
<Q> tag
|
||||||
|
|
||||||
Lists
|
Lists:
|
||||||
<LI>
|
<LI>
|
||||||
<OL>,<UL>
|
<OL>,<UL>
|
||||||
|
|
||||||
Tables:
|
Tables:
|
||||||
Borders,
|
Borders,
|
||||||
row spanning
|
row spanning (only rowspan=0 needs implementing)
|
||||||
column spanning (only colspan=0 needs implementing)
|
column spanning (only colspan=0 needs implementing)
|
||||||
|
|
||||||
Images:
|
Images:
|
||||||
|
@ -11,6 +11,7 @@ The source is split at top level as follows:
|
|||||||
|
|
||||||
content -- fetching, caching, and converting content
|
content -- fetching, caching, and converting content
|
||||||
css -- CSS parser and interfaces
|
css -- CSS parser and interfaces
|
||||||
|
debug -- defines functions which allow debugging under *nix
|
||||||
desktop -- non-platform specific front-end
|
desktop -- non-platform specific front-end
|
||||||
render -- HTML processing and layout
|
render -- HTML processing and layout
|
||||||
riscos -- RISC OS specific code
|
riscos -- RISC OS specific code
|
||||||
@ -33,10 +34,12 @@ The cache stores this converted content. When content is retrieved from the
|
|||||||
cache, content_revive() should result in content which can be displayed (eg. by
|
cache, content_revive() should result in content which can be displayed (eg. by
|
||||||
loading any images and styles required and updating pointers to them).
|
loading any images and styles required and updating pointers to them).
|
||||||
|
|
||||||
Code should not usually use the fetch_* and cache_* functions directly, except
|
Code should not usually use the fetch_* and cache_* functions directly.
|
||||||
for cache_free(). Instead use fetchcache(), which checks the cache for a url and
|
Instead use fetchcache(), which checks the cache for a url and
|
||||||
fetches, converts, and caches it if not present.
|
fetches, converts, and caches it if not present.
|
||||||
|
|
||||||
|
See content/overview for more information.
|
||||||
|
|
||||||
________________________________________________________________________________
|
________________________________________________________________________________
|
||||||
|
|
||||||
css -- CSS parser and interfaces
|
css -- CSS parser and interfaces
|
||||||
|
@ -16,17 +16,22 @@
|
|||||||
#include "netsurf/riscos/jpeg.h"
|
#include "netsurf/riscos/jpeg.h"
|
||||||
#include "netsurf/riscos/png.h"
|
#include "netsurf/riscos/png.h"
|
||||||
#include "netsurf/riscos/gif.h"
|
#include "netsurf/riscos/gif.h"
|
||||||
|
#include "netsurf/riscos/plugin.h"
|
||||||
#include "netsurf/utils/log.h"
|
#include "netsurf/utils/log.h"
|
||||||
#include "netsurf/utils/utils.h"
|
#include "netsurf/utils/utils.h"
|
||||||
|
|
||||||
|
|
||||||
/* mime_map must be in sorted order by mime_type */
|
/* mime_map must be in sorted order by mime_type */
|
||||||
struct mime_entry {
|
struct mime_entry {
|
||||||
char mime_type[16];
|
char mime_type[40];
|
||||||
content_type type;
|
content_type type;
|
||||||
};
|
};
|
||||||
static const struct mime_entry mime_map[] = {
|
static const struct mime_entry mime_map[] = {
|
||||||
#ifdef riscos
|
#ifdef riscos
|
||||||
|
{"application/java-vm", CONTENT_PLUGIN},
|
||||||
|
{"application/x-shockwave-flash", CONTENT_PLUGIN},
|
||||||
|
{"audio/midi", CONTENT_PLUGIN},
|
||||||
|
{"audio/x-midi", CONTENT_PLUGIN},
|
||||||
{"image/gif", CONTENT_GIF},
|
{"image/gif", CONTENT_GIF},
|
||||||
{"image/jpeg", CONTENT_JPEG},
|
{"image/jpeg", CONTENT_JPEG},
|
||||||
{"image/png", CONTENT_PNG},
|
{"image/png", CONTENT_PNG},
|
||||||
@ -63,6 +68,8 @@ static const struct handler_entry handler_map[] = {
|
|||||||
nspng_reformat, nspng_destroy, nspng_redraw},
|
nspng_reformat, nspng_destroy, nspng_redraw},
|
||||||
{nsgif_create, nsgif_process_data, nsgif_convert, nsgif_revive,
|
{nsgif_create, nsgif_process_data, nsgif_convert, nsgif_revive,
|
||||||
nsgif_reformat, nsgif_destroy, nsgif_redraw},
|
nsgif_reformat, nsgif_destroy, nsgif_redraw},
|
||||||
|
{plugin_create, plugin_process_data, plugin_convert, plugin_revive,
|
||||||
|
plugin_reformat, plugin_destroy, plugin_redraw},
|
||||||
#endif
|
#endif
|
||||||
{other_create, other_process_data, other_convert, other_revive,
|
{other_create, other_process_data, other_convert, other_revive,
|
||||||
other_reformat, other_destroy, 0}
|
other_reformat, other_destroy, 0}
|
||||||
@ -114,12 +121,13 @@ struct content * content_create(char *url)
|
|||||||
* content_set_type -- initialise the content for the specified mime type
|
* content_set_type -- initialise the content for the specified mime type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void content_set_type(struct content *c, content_type type)
|
void content_set_type(struct content *c, content_type type, char* mime_type)
|
||||||
{
|
{
|
||||||
assert(c->status == CONTENT_STATUS_TYPE_UNKNOWN);
|
assert(c->status == CONTENT_STATUS_TYPE_UNKNOWN);
|
||||||
assert(type < CONTENT_UNKNOWN);
|
assert(type < CONTENT_UNKNOWN);
|
||||||
LOG(("content %s, type %i", c->url, type));
|
LOG(("content %s, type %i", c->url, type));
|
||||||
c->type = type;
|
c->type = type;
|
||||||
|
c->mime_type = mime_type;
|
||||||
c->status = CONTENT_STATUS_LOADING;
|
c->status = CONTENT_STATUS_LOADING;
|
||||||
content_broadcast(c, CONTENT_MSG_LOADING, 0);
|
content_broadcast(c, CONTENT_MSG_LOADING, 0);
|
||||||
handler_map[type].create(c);
|
handler_map[type].create(c);
|
||||||
|
@ -48,6 +48,7 @@ typedef enum {
|
|||||||
#ifdef riscos
|
#ifdef riscos
|
||||||
CONTENT_PNG,
|
CONTENT_PNG,
|
||||||
CONTENT_GIF,
|
CONTENT_GIF,
|
||||||
|
CONTENT_PLUGIN,
|
||||||
#endif
|
#endif
|
||||||
CONTENT_OTHER,
|
CONTENT_OTHER,
|
||||||
CONTENT_UNKNOWN /* content-type not received yet */
|
CONTENT_UNKNOWN /* content-type not received yet */
|
||||||
@ -85,6 +86,7 @@ struct content
|
|||||||
{
|
{
|
||||||
char *url;
|
char *url;
|
||||||
content_type type;
|
content_type type;
|
||||||
|
char *mime_type;
|
||||||
enum {
|
enum {
|
||||||
CONTENT_STATUS_TYPE_UNKNOWN, /* type not yet known */
|
CONTENT_STATUS_TYPE_UNKNOWN, /* type not yet known */
|
||||||
CONTENT_STATUS_LOADING, /* content is being fetched or converted
|
CONTENT_STATUS_LOADING, /* content is being fetched or converted
|
||||||
@ -156,6 +158,14 @@ struct content
|
|||||||
osspriteop_area *sprite_area; // Sprite area
|
osspriteop_area *sprite_area; // Sprite area
|
||||||
char *sprite_image; // Sprite image
|
char *sprite_image; // Sprite image
|
||||||
} gif;
|
} gif;
|
||||||
|
|
||||||
|
/* Structure for plugin */
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char *data; /* object data */
|
||||||
|
unsigned long length; /* object length */
|
||||||
|
char* sysvar; /* system variable set by plugin */
|
||||||
|
} plugin;
|
||||||
#endif
|
#endif
|
||||||
/* downloads */
|
/* downloads */
|
||||||
struct
|
struct
|
||||||
@ -180,7 +190,7 @@ struct content
|
|||||||
|
|
||||||
content_type content_lookup(const char *mime_type);
|
content_type content_lookup(const char *mime_type);
|
||||||
struct content * content_create(char *url);
|
struct content * content_create(char *url);
|
||||||
void content_set_type(struct content *c, content_type type);
|
void content_set_type(struct content *c, content_type type, char *mime_type);
|
||||||
void content_process_data(struct content *c, char *data, unsigned long size);
|
void content_process_data(struct content *c, char *data, unsigned long size);
|
||||||
void content_convert(struct content *c, unsigned long width, unsigned long height);
|
void content_convert(struct content *c, unsigned long width, unsigned long height);
|
||||||
void content_revive(struct content *c, unsigned long width, unsigned long height);
|
void content_revive(struct content *c, unsigned long width, unsigned long height);
|
||||||
|
@ -64,9 +64,9 @@ void fetchcache_callback(fetch_msg msg, void *p, char *data, unsigned long size)
|
|||||||
if ((semic = strchr(mime_type, ';')) != 0)
|
if ((semic = strchr(mime_type, ';')) != 0)
|
||||||
*semic = 0; /* remove "; charset=..." */
|
*semic = 0; /* remove "; charset=..." */
|
||||||
type = content_lookup(mime_type);
|
type = content_lookup(mime_type);
|
||||||
free(mime_type);
|
|
||||||
LOG(("FETCH_TYPE, type %u", type));
|
LOG(("FETCH_TYPE, type %u", type));
|
||||||
content_set_type(c, type);
|
content_set_type(c, type, mime_type);
|
||||||
|
free(mime_type);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FETCH_DATA:
|
case FETCH_DATA:
|
||||||
|
118
render/box.c
118
render/box.c
@ -75,12 +75,12 @@ void box_normalise_table_row(struct box *row,
|
|||||||
static void box_normalise_inline_container(struct box *cont);
|
static void box_normalise_inline_container(struct box *cont);
|
||||||
static void gadget_free(struct gui_gadget* g);
|
static void gadget_free(struct gui_gadget* g);
|
||||||
static void box_free_box(struct box *box);
|
static void box_free_box(struct box *box);
|
||||||
static struct box* box_object(xmlNode *n, struct content *content,
|
static struct result box_object(xmlNode *n, struct status *status,
|
||||||
struct css_style *style, char *href);
|
struct css_style *style);
|
||||||
static struct box* box_embed(xmlNode *n, struct content *content,
|
static struct result box_embed(xmlNode *n, struct status *status,
|
||||||
struct css_style *style, char *href);
|
struct css_style *style);
|
||||||
static struct box* box_applet(xmlNode *n, struct content *content,
|
static struct result box_applet(xmlNode *n, struct status *status,
|
||||||
struct css_style *style, char *href);
|
struct css_style *style);
|
||||||
static struct form* create_form(xmlNode* n);
|
static struct form* create_form(xmlNode* n);
|
||||||
static void add_form_element(struct page_elements* pe, struct form* f);
|
static void add_form_element(struct page_elements* pe, struct form* f);
|
||||||
static void add_gadget_element(struct page_elements* pe, struct gui_gadget* g);
|
static void add_gadget_element(struct page_elements* pe, struct gui_gadget* g);
|
||||||
@ -93,9 +93,12 @@ struct element_entry {
|
|||||||
};
|
};
|
||||||
static const struct element_entry element_table[] = {
|
static const struct element_entry element_table[] = {
|
||||||
{"a", box_a},
|
{"a", box_a},
|
||||||
|
// {"applet", box_applet},
|
||||||
|
{"embed", box_embed},
|
||||||
{"form", box_form},
|
{"form", box_form},
|
||||||
{"img", box_image},
|
{"img", box_image},
|
||||||
{"input", box_input},
|
{"input", box_input},
|
||||||
|
{"object", box_object},
|
||||||
{"select", box_select},
|
{"select", box_select},
|
||||||
{"textarea", box_textarea}
|
{"textarea", box_textarea}
|
||||||
};
|
};
|
||||||
@ -1385,48 +1388,33 @@ void add_gadget_element(struct page_elements* pe, struct gui_gadget* g)
|
|||||||
/**
|
/**
|
||||||
* add an object to the box tree
|
* add an object to the box tree
|
||||||
*/
|
*/
|
||||||
|
struct result box_object(xmlNode *n, struct status *status,
|
||||||
/* } else if (strcmp((const char*) n->name, "object") == 0) { LOG(("object"));
|
struct css_style *style)
|
||||||
box = box_object(n, content, style, href);
|
|
||||||
*/ /* TODO - param data structure
|
|
||||||
|
|
||||||
for (c = n->children; c != 0; c = c->next) {
|
|
||||||
|
|
||||||
if (strcmp((const char*) c->name, "param") == 0) {
|
|
||||||
|
|
||||||
LOG(("param"));
|
|
||||||
current_param = box_param(c, style, current_object);
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
/*
|
|
||||||
} else if (strcmp((const char*) n->name, "embed") == 0) { LOG(("embed"));
|
|
||||||
box = box_embed(n, content, style, href);
|
|
||||||
|
|
||||||
} else if (strcmp((const char*) n->name, "applet") == 0) { LOG(("applet"));
|
|
||||||
box = box_applet(n, content, style, href);
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
struct box* box_object(xmlNode *n, struct content *content,
|
|
||||||
struct css_style *style, char *href)
|
|
||||||
{
|
{
|
||||||
struct box *box;
|
struct box *box;
|
||||||
struct plugin_object *po;
|
struct plugin_object *po;
|
||||||
char *s, *url;
|
char *s, *url;
|
||||||
xmlChar *s2;
|
|
||||||
|
|
||||||
box = box_create(style, href, 0);
|
box = box_create(style, status->href, 0);
|
||||||
|
|
||||||
po = xcalloc(1,sizeof(*po));
|
po = xcalloc(1, sizeof(*po));
|
||||||
|
|
||||||
|
/* initialise po struct */
|
||||||
|
po->data = 0;
|
||||||
|
po->type = 0;
|
||||||
|
po->codetype = 0;
|
||||||
|
po->codebase = 0;
|
||||||
|
po->classid = 0;
|
||||||
|
po->paramds = 0;
|
||||||
|
po->width = 0;
|
||||||
|
po->height = 0;
|
||||||
|
|
||||||
/* object data */
|
/* object data */
|
||||||
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "data"))) {
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "data"))) {
|
||||||
|
|
||||||
po->data = strdup(s);
|
po->data = strdup(s);
|
||||||
url = url_join(strdup(s), content->url);
|
url = url_join(strdup(s), status->content->url);
|
||||||
LOG(("object '%s'", url));
|
LOG(("object '%s'", po->data));
|
||||||
xmlFree(s);
|
xmlFree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1434,7 +1422,7 @@ struct box* box_object(xmlNode *n, struct content *content,
|
|||||||
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "type"))) {
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "type"))) {
|
||||||
|
|
||||||
po->type = strdup(s);
|
po->type = strdup(s);
|
||||||
LOG(("type: %s", po->type));
|
LOG(("type: %s", s));
|
||||||
xmlFree(s);
|
xmlFree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1442,7 +1430,7 @@ struct box* box_object(xmlNode *n, struct content *content,
|
|||||||
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "codetype"))) {
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "codetype"))) {
|
||||||
|
|
||||||
po->codetype = strdup(s);
|
po->codetype = strdup(s);
|
||||||
LOG(("codetype: %s", po->codetype));
|
LOG(("codetype: %s", s));
|
||||||
xmlFree(s);
|
xmlFree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1450,7 +1438,7 @@ struct box* box_object(xmlNode *n, struct content *content,
|
|||||||
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "codebase"))) {
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "codebase"))) {
|
||||||
|
|
||||||
po->codebase = strdup(s);
|
po->codebase = strdup(s);
|
||||||
LOG(("codebase: %s", po->codebase));
|
LOG(("codebase: %s", s));
|
||||||
xmlFree(s);
|
xmlFree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1458,7 +1446,7 @@ struct box* box_object(xmlNode *n, struct content *content,
|
|||||||
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "classid"))) {
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "classid"))) {
|
||||||
|
|
||||||
po->classid = strdup(s);
|
po->classid = strdup(s);
|
||||||
LOG(("classid: %s", po->classid));
|
LOG(("classid: %s", s));
|
||||||
xmlFree(s);
|
xmlFree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1466,7 +1454,7 @@ struct box* box_object(xmlNode *n, struct content *content,
|
|||||||
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "width"))) {
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "width"))) {
|
||||||
|
|
||||||
po->width = (unsigned int)atoi(s);
|
po->width = (unsigned int)atoi(s);
|
||||||
LOG(("width: %u", po->width));
|
LOG(("width: %u", (unsigned int)atoi(s)));
|
||||||
xmlFree(s);
|
xmlFree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1474,45 +1462,54 @@ struct box* box_object(xmlNode *n, struct content *content,
|
|||||||
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "height"))) {
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "height"))) {
|
||||||
|
|
||||||
po->height = (unsigned int)atoi(s);
|
po->height = (unsigned int)atoi(s);
|
||||||
LOG(("height: %u", po->height));
|
LOG(("height: %u", (unsigned int)atoi(s)));
|
||||||
xmlFree(s);
|
xmlFree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start fetch */
|
/* start fetch */
|
||||||
plugin_decode(content, url, box, po);
|
plugin_decode(status->content, url, box, po);
|
||||||
|
|
||||||
return box;
|
return (struct result) {box, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add an embed to the box tree
|
* add an embed to the box tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct box* box_embed(xmlNode *n, struct content *content,
|
struct result box_embed(xmlNode *n, struct status *status,
|
||||||
struct css_style *style, char *href)
|
struct css_style *style)
|
||||||
{
|
{
|
||||||
struct box *box;
|
struct box *box;
|
||||||
struct plugin_object *po;
|
struct plugin_object *po;
|
||||||
char *s, *url;
|
char *s, *url;
|
||||||
xmlChar *s2;
|
|
||||||
|
|
||||||
box = box_create(style, href, 0);
|
box = box_create(style, status->href, 0);
|
||||||
|
|
||||||
po = xcalloc(1, sizeof(*po));
|
po = xcalloc(1, sizeof(*po));
|
||||||
|
|
||||||
|
/* initialise po struct */
|
||||||
|
po->data = 0;
|
||||||
|
po->type = 0;
|
||||||
|
po->codetype = 0;
|
||||||
|
po->codebase = 0;
|
||||||
|
po->classid = 0;
|
||||||
|
po->paramds = 0;
|
||||||
|
po->width = 0;
|
||||||
|
po->height = 0;
|
||||||
|
|
||||||
/* embed src */
|
/* embed src */
|
||||||
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "src"))) {
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "src"))) {
|
||||||
|
|
||||||
po->data = strdup(s);
|
po->data = strdup(s);
|
||||||
url = url_join(strdup(s), content->url);
|
url = url_join(strdup(s), status->content->url);
|
||||||
LOG(("embed '%s'", url));
|
LOG(("embed '%s'", url));
|
||||||
xmlFree(s);
|
xmlFree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start fetch */
|
/* start fetch */
|
||||||
plugin_decode(content, url, box, po);
|
plugin_decode(status->content, url, box, po);
|
||||||
|
|
||||||
return box;
|
return (struct result) {box,0};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1520,29 +1517,24 @@ struct box* box_embed(xmlNode *n, struct content *content,
|
|||||||
* add an applet to the box tree
|
* add an applet to the box tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct box* box_applet(xmlNode *n, struct content *content,
|
struct result box_applet(xmlNode *n, struct status *status,
|
||||||
struct css_style *style, char *href)
|
struct css_style *style)
|
||||||
{
|
{
|
||||||
struct box *box;
|
struct box *box;
|
||||||
struct plugin_object *po;
|
|
||||||
char *s, *url;
|
char *s, *url;
|
||||||
xmlChar *s2;
|
|
||||||
|
|
||||||
/* box type is decided by caller, BOX_INLINE is just a default */
|
box = box_create(style, status->href, 0);
|
||||||
box = box_create(style, href, 0);
|
|
||||||
|
|
||||||
po = xcalloc(1,sizeof(struct plugin_object));
|
|
||||||
|
|
||||||
/* object without data is an error */
|
/* object without data is an error */
|
||||||
if (!(s = (char *) xmlGetProp(n, (const xmlChar *) "data")))
|
if (!(s = (char *) xmlGetProp(n, (const xmlChar *) "data")))
|
||||||
return box;
|
return (struct result) {box,0};
|
||||||
|
|
||||||
url = url_join(strdup(s), content->url);
|
url = url_join(strdup(s), status->content->url);
|
||||||
LOG(("object '%s'", url));
|
LOG(("object '%s'", url));
|
||||||
xmlFree(s);
|
xmlFree(s);
|
||||||
|
|
||||||
/* start fetch */
|
/* start fetch */
|
||||||
//plugin_decode(content, url, box, po);
|
//plugin_decode(content, url, box, po);
|
||||||
|
|
||||||
return box;
|
return (struct result) {box,0};
|
||||||
}
|
}
|
||||||
|
@ -305,7 +305,7 @@ void html_find_stylesheets(struct content *c, xmlNode *head)
|
|||||||
LOG(("style element"));
|
LOG(("style element"));
|
||||||
if (c->data.html.stylesheet_content[1] == 0) {
|
if (c->data.html.stylesheet_content[1] == 0) {
|
||||||
c->data.html.stylesheet_content[1] = content_create(c->url);
|
c->data.html.stylesheet_content[1] = content_create(c->url);
|
||||||
content_set_type(c->data.html.stylesheet_content[1], CONTENT_CSS);
|
content_set_type(c->data.html.stylesheet_content[1], CONTENT_CSS, "text/css");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* can't just use xmlNodeGetContent(node), because that won't give
|
/* can't just use xmlNodeGetContent(node), because that won't give
|
||||||
|
469
riscos/plugin.c
469
riscos/plugin.c
@ -19,268 +19,275 @@
|
|||||||
|
|
||||||
#include "oslib/mimemap.h"
|
#include "oslib/mimemap.h"
|
||||||
|
|
||||||
char* create_mime_from_ext(char* data);
|
bool plugin_handleable(struct content* c);
|
||||||
char* create_sysvar(char* mime);
|
|
||||||
void plugin_fetch(struct plugin_object* po,
|
|
||||||
char* alias_sysvar/* vars here */);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* plugin_decode
|
* plugin_decode
|
||||||
* Processes the contents of the plugin_object struct (defined in plugin.h)
|
* This function checks that the contents of the plugin_object struct
|
||||||
* in order to work out if NetSurf can handle the object.
|
* are valid. If they are, it initiates the fetch process. If they are
|
||||||
* For more information, read
|
* not, it exits, leaving the box structure as it was on entry. This is
|
||||||
* http://www.ecs.soton.ac.uk/~jmb202/riscos/acorn/browse-plugins.html
|
* necessary as there are multiple ways of declaring an object's attributes.
|
||||||
* as this code is based heavily on that description.
|
*
|
||||||
|
* TODO: alt html
|
||||||
|
* params - create parameters file and put the filename string
|
||||||
|
* somewhere such that it is accessible from plugin_create.
|
||||||
*/
|
*/
|
||||||
void plugin_decode(struct content* content, char* url, struct box* box,
|
void plugin_decode(struct content* content, char* url, struct box* box,
|
||||||
struct plugin_object* po) {
|
struct plugin_object* po)
|
||||||
|
{
|
||||||
|
os_error *e;
|
||||||
|
unsigned int *fv;
|
||||||
|
|
||||||
|
/* Check if the codebase attribute is defined.
|
||||||
content_type mime_type;
|
* If it is not, set it to the codebase of the current document.
|
||||||
bool can_handle = TRUE;
|
|
||||||
char* alias_sysvar;
|
|
||||||
|
|
||||||
|
|
||||||
if (po->data != NULL) {
|
|
||||||
|
|
||||||
if (po->type != NULL) {
|
|
||||||
|
|
||||||
/* acquire NS mime type from actual mime type */
|
|
||||||
mime_type = content_lookup((const char*)po->type);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
/* create actual mime type - if we're wrong,
|
|
||||||
* it doesn't matter as the HTTP content-type
|
|
||||||
* header should override whatever we think.
|
|
||||||
* however, checking the header hasn't been
|
|
||||||
* implemented yet so it will just b0rk :(
|
|
||||||
*/
|
*/
|
||||||
po->type = strdup(create_mime_from_ext(po->data));
|
if(po->codebase == 0)
|
||||||
|
po->codebase = strdup(content->url);
|
||||||
|
else
|
||||||
|
po->codebase = url_join(po->codebase, content->url);
|
||||||
|
|
||||||
if (po->type != NULL)
|
/* Check that we have some data specified.
|
||||||
mime_type = content_lookup((const char*)po->type);
|
* First, check the data attribute.
|
||||||
|
* Second, check the classid attribute.
|
||||||
else {
|
* The data attribute takes precedence.
|
||||||
|
* If neither are specified or if classid begins "clsid:",
|
||||||
/* failed to create mime type, clean up and exit */
|
* we can't handle this object.
|
||||||
xfree(po);
|
|
||||||
can_handle = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
/* no data so try using classid instead */
|
|
||||||
|
|
||||||
if (po->classid != NULL) {
|
|
||||||
|
|
||||||
po->data = strdup(po->classid);
|
|
||||||
|
|
||||||
if (strnicmp(po->data,"clsid:",6) == 0) {
|
|
||||||
|
|
||||||
/* We can't handle ActiveX objects */
|
|
||||||
LOG(("Can't Handle ActiveX"));
|
|
||||||
xfree(po);
|
|
||||||
can_handle = FALSE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
if (po->codetype != NULL) {
|
|
||||||
|
|
||||||
/* use codetype instead of type if we can */
|
|
||||||
po->type = strdup(po->codetype);
|
|
||||||
mime_type = content_lookup(
|
|
||||||
(const char*)po->codetype);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
/* try ye olde file extension munging */
|
|
||||||
po->codetype = strdup(
|
|
||||||
create_mime_from_ext(po->data));
|
|
||||||
|
|
||||||
if (po->codetype != NULL) {
|
|
||||||
|
|
||||||
/* well, it appeared to work... */
|
|
||||||
mime_type = content_lookup(
|
|
||||||
(const char*)po->codetype);
|
|
||||||
po->type = strdup(
|
|
||||||
po->codetype);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
/* arse, failed. oh well */
|
|
||||||
xfree(po);
|
|
||||||
can_handle = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
/* we don't have sufficient data to handle this
|
|
||||||
* object :(
|
|
||||||
* TODO: start fetch anyway and check header.
|
|
||||||
* if we can handle the content, continue
|
|
||||||
* fetch and carry on as if the proper HTML
|
|
||||||
* was written.
|
|
||||||
* if we can't handle the content, stop fetch
|
|
||||||
* and clean up.
|
|
||||||
*/
|
*/
|
||||||
|
if(po->data == 0 && po->classid == 0) {
|
||||||
xfree(po);
|
xfree(po);
|
||||||
can_handle = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* so, you think you can handle it do you? */
|
|
||||||
if (can_handle == TRUE) {
|
|
||||||
|
|
||||||
/* We think we can handle this object. Now check that
|
|
||||||
* we can.
|
|
||||||
* 1) Is it an image? Yes - send to image handler
|
|
||||||
* No - continue checking
|
|
||||||
* 2) Is a suitable Alias$... System Variable set?
|
|
||||||
* Yes - invoke plugin
|
|
||||||
* No - we can't handle it. Display alternative HTML
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* TODO: There must be a better way than this...
|
|
||||||
* Perhaps checking if the mime type begins "image/"
|
|
||||||
* would be better?
|
|
||||||
*/
|
|
||||||
if (mime_type == CONTENT_JPEG || mime_type == CONTENT_PNG
|
|
||||||
|| mime_type == CONTENT_GIF) {
|
|
||||||
|
|
||||||
/* OK, we have an image. Let's make the image handler
|
|
||||||
* deal with it.
|
|
||||||
*/
|
|
||||||
xfree(po);
|
|
||||||
LOG(("sending data to image handler"));
|
|
||||||
html_fetch_object(content, url, box);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else { /* not an image; is sys var set? */
|
if(po->data == 0 && po->classid != 0) {
|
||||||
|
if(strnicmp(po->classid, "clsid:", 6) == 0) {
|
||||||
/* Create Alias variable */
|
LOG(("ActiveX object - n0"));
|
||||||
alias_sysvar = create_sysvar(po->type);
|
|
||||||
if (alias_sysvar == NULL) {
|
|
||||||
|
|
||||||
/* oh dear, you can't handle it */
|
|
||||||
xfree(po);
|
xfree(po);
|
||||||
xfree(alias_sysvar);
|
return;
|
||||||
can_handle = FALSE;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
url = url_join(po->classid, po->codebase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
url = url_join(po->data, po->codebase);
|
||||||
|
}
|
||||||
|
|
||||||
/* Right, we have a variable.
|
/* Check if the declared mime type is understandable.
|
||||||
* Does it actually exist?
|
* ie. is it referenced in the mimemap file?
|
||||||
|
* Checks type and codetype attributes.
|
||||||
*/
|
*/
|
||||||
|
if(po->type != 0) {
|
||||||
|
e = xmimemaptranslate_mime_type_to_filetype((const char*)po->type,
|
||||||
|
(unsigned int*)&fv);
|
||||||
|
LOG(("fv: &%x", (int) fv));
|
||||||
|
if(e != NULL) {
|
||||||
|
xfree(po);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* If a filetype of &ffd (Data) is returned,
|
||||||
|
* one of the following mime types is possible :
|
||||||
|
* application/octet-stream
|
||||||
|
* multipart/x-mixed-replace
|
||||||
|
* unknown mime type (* / *)
|
||||||
|
* we assume it to be the last one as the other two
|
||||||
|
* are unlikely to occur in an <object> definition.
|
||||||
|
*/
|
||||||
|
if((int)fv == 0xffd) {
|
||||||
|
xfree(po);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* TODO: implement GUI for iframes/frames
|
||||||
|
* For now, we just discard the data and
|
||||||
|
* render the alternative html
|
||||||
|
*/
|
||||||
|
if((int)fv == 0xfaf) {
|
||||||
|
xfree(po);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(po->codetype != 0) {
|
||||||
|
e = xmimemaptranslate_mime_type_to_filetype((const char*)po->codetype,
|
||||||
|
(unsigned int*)&fv);
|
||||||
|
if(e != NULL) {
|
||||||
|
xfree(po);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* If a filetype of &ffd (Data) is returned,
|
||||||
|
* one of the following mime types is possible :
|
||||||
|
* application/octet-stream
|
||||||
|
* multipart/x-mixed-replace
|
||||||
|
* unknown mime type (* / *)
|
||||||
|
* we assume it to be the last one as the other two
|
||||||
|
* are unlikely to occur in an <object> definition.
|
||||||
|
*/
|
||||||
|
if((int)fv == 0xffd) {
|
||||||
|
xfree(po);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* TODO: implement GUI for iframes/frames
|
||||||
|
* For now, we just discard the data and
|
||||||
|
* render the alternative html
|
||||||
|
*/
|
||||||
|
if((int)fv == 0xfaf) {
|
||||||
|
xfree(po);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we've got to here, the object declaration has provided us with
|
||||||
|
* enough data to enable us to have a go at downloading and displaying it.
|
||||||
|
*/
|
||||||
|
xfree(po);
|
||||||
|
html_fetch_object(content, url, box);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* plugin_create
|
||||||
|
* initialises plugin system in readiness for recieving object data
|
||||||
|
*
|
||||||
|
* TODO: implement aborting the fetch
|
||||||
|
* get parameter filename from wherever it was put by plugin_decode
|
||||||
|
* launch plugin system
|
||||||
|
*/
|
||||||
|
void plugin_create(struct content *c)
|
||||||
|
{
|
||||||
|
bool can_handle = TRUE; /* we assume we can handle all types */
|
||||||
|
|
||||||
|
LOG(("mime type: %s", c->mime_type));
|
||||||
|
|
||||||
|
/* check if we can handle this type */
|
||||||
|
can_handle = plugin_handleable(c);
|
||||||
|
LOG(("can_handle = %s", can_handle ? "TRUE" : "FALSE"));
|
||||||
|
LOG(("sysvar: %s", can_handle ? c->data.plugin.sysvar : "not set"));
|
||||||
|
|
||||||
|
if(!can_handle) {
|
||||||
|
/* TODO: need to find a way of stopping the fetch
|
||||||
|
* if we can't handle this type
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ok, it looks like we can handle this object.
|
||||||
|
* Broadcast Message_PlugIn_Open (&4D540) and listen for response
|
||||||
|
* Message_PlugIn_Opening (&4D541). If no response, try to launch
|
||||||
|
* plugin by Wimp_StartTask(sysvar). Then re-broadcast Message_PlugIn_Open
|
||||||
|
* and listen for response. If there is still no response, give up and set
|
||||||
|
* can_handle to FALSE.
|
||||||
|
* NB: For the bounding box in Message_PlugIn_Open, we choose arbitrary
|
||||||
|
* values outside the area displayed. This is corrected when
|
||||||
|
* plugin_redraw is called.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Recheck if can_handle is false. If it is, stop fetch and exit .*/
|
||||||
|
if(!can_handle) {
|
||||||
|
/* TODO: need to find a way of stopping the fetch
|
||||||
|
* if we can't handle this type
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * const ALIAS_PREFIX = "Alias$@PlugInType_";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* plugin_handleable
|
||||||
|
* Tests whether we can handle an object using a browser plugin
|
||||||
|
* returns TRUE if we can handle it, FALSE if we can't.
|
||||||
|
*/
|
||||||
|
bool plugin_handleable(struct content* c)
|
||||||
|
{
|
||||||
|
bool ret = TRUE;
|
||||||
|
char *sysvar;
|
||||||
|
unsigned int *fv;
|
||||||
int used;
|
int used;
|
||||||
xos_read_var_val_size(
|
os_error *e;
|
||||||
(const char*)alias_sysvar,
|
|
||||||
0, os_VARTYPE_STRING,
|
/* prefix + 3 for file type + 1 for terminating \0 */
|
||||||
|
sysvar = xcalloc(strlen(ALIAS_PREFIX)+4, sizeof(char));
|
||||||
|
|
||||||
|
e = xmimemaptranslate_mime_type_to_filetype((const char*)c->mime_type,
|
||||||
|
(unsigned int*)&fv);
|
||||||
|
|
||||||
|
sprintf(sysvar, "%s%x", ALIAS_PREFIX, e == NULL ? (int)fv : 0 );
|
||||||
|
|
||||||
|
xos_read_var_val_size((const char*)sysvar,0, os_VARTYPE_STRING,
|
||||||
&used, 0, os_VARTYPE_STRING);
|
&used, 0, os_VARTYPE_STRING);
|
||||||
|
|
||||||
if (used == 0) {
|
if(used == 0)
|
||||||
|
/* No system variable set => no plugin available */
|
||||||
|
ret = FALSE;
|
||||||
|
|
||||||
/* no, doesn't exist */
|
if(ret)
|
||||||
xfree(po);
|
c->data.plugin.sysvar = strdup(sysvar);
|
||||||
xfree(alias_sysvar);
|
|
||||||
can_handle = FALSE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* yes, it exists */
|
|
||||||
LOG(("%s exists", alias_sysvar));
|
|
||||||
plugin_fetch(po, alias_sysvar/* insert vars here */);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (can_handle == FALSE) {
|
xfree(sysvar);
|
||||||
|
|
||||||
/* Get alternative HTML as we can't handle the object */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create_mime_from_ext
|
|
||||||
* attempts to create a mime type from the filename extension.
|
|
||||||
* returns NULL if it fails.
|
|
||||||
*/
|
|
||||||
|
|
||||||
char* create_mime_from_ext(char* data){
|
|
||||||
|
|
||||||
char* ret;
|
|
||||||
os_error *e;
|
|
||||||
|
|
||||||
LOG(("Creating Mime Type from File Extension"));
|
|
||||||
|
|
||||||
ret = xcalloc(90, sizeof(char));
|
|
||||||
ret = strrchr(data, '.');
|
|
||||||
LOG(("Extension = %s", ret));
|
|
||||||
|
|
||||||
/* Let's make the mime map module do the work for us */
|
|
||||||
e = xmimemaptranslate_extension_to_mime_type((const char*)ret,
|
|
||||||
ret);
|
|
||||||
LOG(("Mime Type = %s", ret));
|
|
||||||
|
|
||||||
if (e != NULL) ret = NULL;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create_sysvar
|
* plugin_process_data
|
||||||
* attempts to create a system variable of the form Alias$@PlugInType_XXX
|
* processes data retrieved by the fetch process
|
||||||
* where XXX is the filetype.
|
*
|
||||||
* returns NULL if unsuccessful.
|
* TODO: plugin stream protocol
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void plugin_process_data(struct content *c, char *data, unsigned long size)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* If the plugin requests, we send the data to it via the
|
||||||
|
* plugin stream protocol.
|
||||||
|
* Also, we should listen for Message_PlugIn_URL_Access (&4D54D)
|
||||||
|
* as the plugin may need us to retrieve URLs for it.
|
||||||
|
* We should also listen for Message_PlugIn_Closed (&4D543).
|
||||||
|
* If this occurs, the plugin has exited with an error.
|
||||||
|
* Therefore, we need to stop the fetch and exit.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char* create_sysvar(char* mime) {
|
|
||||||
|
|
||||||
char* ret;
|
|
||||||
char* ft;
|
|
||||||
unsigned int* fv;
|
|
||||||
os_error *e;
|
|
||||||
|
|
||||||
LOG(("Creating System Variable from Mime Type"));
|
|
||||||
|
|
||||||
ret = xcalloc(22, sizeof(char));
|
|
||||||
ft = xcalloc(10, sizeof(char));
|
|
||||||
strcpy(ret, "Alias$@PlugInType_");
|
|
||||||
|
|
||||||
LOG(("Mime Type: %s", mime));
|
|
||||||
|
|
||||||
e = xmimemaptranslate_mime_type_to_filetype((const char*)mime,
|
|
||||||
(unsigned int*)&fv);
|
|
||||||
if (e != NULL) ret = NULL;
|
|
||||||
|
|
||||||
else {
|
|
||||||
|
|
||||||
sprintf(ft, "%x", (int)fv);
|
|
||||||
strcat(ret, ft);
|
|
||||||
LOG(("Alias Var: %s", ret));
|
|
||||||
}
|
|
||||||
|
|
||||||
xfree(ft);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* plugin_fetch
|
* plugin_convert
|
||||||
* attempts to negotiate with the plugin.
|
* This isn't needed by the plugin system as all the data processing is done
|
||||||
* also fetches the object for the plugin to handle.
|
* externally. Therefore, just tell NetSurf that everything's OK.
|
||||||
|
*/
|
||||||
|
int plugin_convert(struct content *c, unsigned int width, unsigned int height)
|
||||||
|
{
|
||||||
|
c->status=CONTENT_STATUS_DONE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plugin_revive(struct content *c, unsigned int width, unsigned int height)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void plugin_reformat(struct content *c, unsigned int width, unsigned int height)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* plugin_destroy
|
||||||
|
* we've finished with this data, destroy it. Also, shutdown plugin.
|
||||||
|
*
|
||||||
|
* TODO: clean up
|
||||||
|
*/
|
||||||
|
void plugin_destroy(struct content *c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* plugin_redraw
|
||||||
|
* redraw plugin on page.
|
||||||
|
*
|
||||||
|
* TODO: Message_PlugIn_Reshape
|
||||||
|
*/
|
||||||
|
void plugin_redraw(struct content *c, long x, long y,
|
||||||
|
unsigned long width, unsigned long height)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* By now, we've got the plugin up and running in a nested window
|
||||||
|
* off the viewable page area. Now we want to display it in its place.
|
||||||
|
* Therefore, broadcast a Message_PlugIn_Reshape (&4D544) with the values
|
||||||
|
* given to us.
|
||||||
*/
|
*/
|
||||||
void plugin_fetch (struct plugin_object* po,
|
|
||||||
char* alias_sysvar/* insert vars here */) {
|
|
||||||
|
|
||||||
LOG(("Entering plugin_fetch"));
|
|
||||||
xfree(po);
|
|
||||||
xfree(alias_sysvar);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#ifndef _NETSURF_RISCOS_PLUGIN_H_
|
#ifndef _NETSURF_RISCOS_PLUGIN_H_
|
||||||
#define _NETSURF_RISCOS_PLUGIN_H_
|
#define _NETSURF_RISCOS_PLUGIN_H_
|
||||||
|
|
||||||
|
#include "netsurf/content/content.h"
|
||||||
|
|
||||||
struct plugin_object {
|
struct plugin_object {
|
||||||
|
|
||||||
char* data;
|
char* data;
|
||||||
@ -21,8 +23,16 @@ struct plugin_object {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* function definitions */
|
||||||
void plugin_decode(struct content* content, char* url, struct box* box,
|
void plugin_decode(struct content* content, char* url, struct box* box,
|
||||||
struct plugin_object* po);
|
struct plugin_object* po);
|
||||||
|
void plugin_create(struct content *c);
|
||||||
|
void plugin_process_data(struct content *c, char *data, unsigned long size);
|
||||||
|
int plugin_convert(struct content *c, unsigned int width, unsigned int height);
|
||||||
|
void plugin_revive(struct content *c, unsigned int width, unsigned int height);
|
||||||
|
void plugin_reformat(struct content *c, unsigned int width, unsigned int height);
|
||||||
|
void plugin_destroy(struct content *c);
|
||||||
|
void plugin_redraw(struct content *c, long x, long y,
|
||||||
|
unsigned long width, unsigned long height);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user