mirror of
https://git.checksum.fail/alec/Web.git
synced 2026-05-26 22:28:26 +00:00
404 lines
9.8 KiB
HolyC
404 lines
9.8 KiB
HolyC
CDC *hr_dc = DCNew(640, 2);
|
|
DCFill(hr_dc, DKGRAY);
|
|
CSprite *hr_s = DC2Sprite(hr_dc);
|
|
|
|
I64 img_resource_count = 0;
|
|
|
|
U0 @animation_task(CAnimation *a) {
|
|
|
|
CDocEntry *de;
|
|
CDocEntry *gif;
|
|
U8 buf[512];
|
|
I64 i;
|
|
|
|
while (progress1_max)
|
|
Sleep(1);
|
|
|
|
de = sys_focus_task->put_doc->head.next;
|
|
while (de != sys_focus_task->put_doc) {
|
|
if (de->user_data) { // FIXME: multiple animations per document
|
|
gif = de;
|
|
goto begin_animate_loop;
|
|
}
|
|
de = de->next;
|
|
}
|
|
|
|
begin_animate_loop:
|
|
|
|
a->ticks = cnts.jiffies;
|
|
a->current_frame = a->total_frames - 1;
|
|
|
|
while (1) {
|
|
|
|
gif->bin_data->data = a->sprite[a->current_frame];
|
|
|
|
if (cnts.jiffies >
|
|
a->ticks + a->delays[a->total_frames - 1 - a->current_frame]) {
|
|
a->current_frame--;
|
|
|
|
if (a->current_frame < 0)
|
|
a->current_frame = a->total_frames - 1;
|
|
|
|
a->ticks = cnts.jiffies;
|
|
}
|
|
|
|
Sleep(1);
|
|
}
|
|
}
|
|
|
|
class @gce {
|
|
U8 sig[2];
|
|
U8 num_bytes;
|
|
U8 flags;
|
|
U16 delay;
|
|
U8 invisible_color;
|
|
U8 end_block;
|
|
};
|
|
|
|
class @idesc {
|
|
U8 sig;
|
|
U16 x1;
|
|
U16 y1;
|
|
U16 w;
|
|
U16 h;
|
|
U8 lct;
|
|
U8 min_lzw_size;
|
|
};
|
|
|
|
U0 @get_animation_frame_delays(I64 *delay, U8 *data, I64 size) {
|
|
|
|
@gce *gce;
|
|
@idesc *idesc;
|
|
|
|
I64 i;
|
|
I64 pos = 0x320; // first Graphic Control Extension for frame #1
|
|
U8 buf[512];
|
|
I64 frame = 0;
|
|
|
|
while (pos < size) {
|
|
gce = data + pos;
|
|
if (gce->sig[0] == 0x21 && gce->sig[1] == 0xf9) {
|
|
delay[frame] = gce->delay * 10;
|
|
if (delay[frame] < 51)
|
|
delay[frame] = 100;
|
|
pos += sizeof(@gce);
|
|
frame++;
|
|
}
|
|
idesc = data + pos;
|
|
if (idesc->sig == 0x2c) {
|
|
pos += sizeof(@idesc);
|
|
while (data[pos]) {
|
|
pos += data[pos] + 1;
|
|
}
|
|
}
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
Bool @is_resource_cached(U8 *src) {
|
|
U8 buf[512];
|
|
U8 *src_md5 = md5_string(src, StrLen(src));
|
|
StrPrint(buf, "C:/Web/Cache/%s", src_md5);
|
|
return FileFind(buf);
|
|
}
|
|
|
|
U0 @cache_resource(U8 *src, U8 *data, I64 size) {
|
|
U8 buf[512];
|
|
U8 *src_md5 = md5_string(src, StrLen(src));
|
|
StrPrint(buf, "C:/Web/Cache/%s", src_md5);
|
|
FileWrite(buf, data, size);
|
|
}
|
|
|
|
U8 *@get_cached_resource_filename(U8 *src) {
|
|
U8 buf = CAlloc(512);
|
|
U8 *src_md5 = md5_string(src, StrLen(src));
|
|
StrPrint(buf, "C:/Web/Cache/%s", src_md5);
|
|
return buf;
|
|
}
|
|
|
|
Bool @render_image(CDoc *doc, Node *node) {
|
|
U8 buf[512];
|
|
U8 buf2[512];
|
|
StrCpy(buf, "https:"); // FIXME
|
|
StrCpy(buf + StrLen(buf), Json.Get(node->attributes, "src"));
|
|
|
|
I64 i;
|
|
I64 content_length = 0;
|
|
Bool is_animated_gif = FALSE;
|
|
|
|
@http_response *resp = NULL;
|
|
|
|
if (@is_resource_cached(buf)) {
|
|
|
|
// Load resource from cache
|
|
|
|
resp = CAlloc(sizeof(@http_response));
|
|
resp->body.data =
|
|
FileRead(@get_cached_resource_filename(buf), &content_length);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Load resource from the web
|
|
|
|
MemSet(web_resource_buffer, NULL, WEB_RESPONSE_BUFFER_SIZE);
|
|
resp = @http_get(@http_parse_url(buf), web_resource_buffer);
|
|
if (!resp->body.data || !resp->body.length ||
|
|
resp->state != HTTP_STATE_DONE)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < resp->headers.count; i++) {
|
|
if (!StrICmp(resp->headers.header[i]->key, "Content-Length")) {
|
|
content_length = Str2I64(resp->headers.header[i]->value);
|
|
i = resp->headers.count;
|
|
}
|
|
}
|
|
|
|
if (!MemCmp(resp->body.data, "GIF89a", 6))
|
|
is_animated_gif = TRUE;
|
|
|
|
U8 *ext;
|
|
|
|
// FIXME: A bug in TLS implementation chops off the last byte, which could
|
|
// potentially break GIF rendering. Let's add 0x3b at the end of the buffer
|
|
// to workaround this for now.
|
|
ext = StrLastOcc(Json.Get(node->attributes, "src"), ".") + 1;
|
|
if (!StrICmp(ext, "gif"))
|
|
resp->body.data[content_length - 1] = 0x3b;
|
|
|
|
// FIXME: A bug in TLS implementation chops off the last byte, which breaks
|
|
// JPEG rendering. Let's add 0xd9 at the end of the buffer to workaround
|
|
// this for now.
|
|
ext = StrLastOcc(Json.Get(node->attributes, "src"), ".") + 1;
|
|
if (!StrICmp(ext, "jpg") || !StrICmp(ext, "jpeg"))
|
|
resp->body.data[content_length - 1] = 0xd9;
|
|
|
|
@cache_resource(buf, resp->body.data, content_length);
|
|
}
|
|
|
|
CDC *img = Image.FromBuffer(resp->body.data, content_length);
|
|
|
|
CDocEntry *de = NULL;
|
|
|
|
if (img) {
|
|
CSprite *s = DC2Sprite(img);
|
|
de = DocSprite(doc, s);
|
|
DCDel(img);
|
|
Free(s);
|
|
|
|
if (is_animated_gif) {
|
|
CAnimation *a = Animation.FromBuffer(resp->body.data, content_length);
|
|
a->de = de;
|
|
a->de->user_data = "GIF89a";
|
|
@get_animation_frame_delays(a->delays, resp->body.data, content_length);
|
|
a->task = Fs;
|
|
Spawn(&@animation_task, a, "AnimationTask");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
U0 @calculate_indents_for_sprites(CDoc *haystack_doc) {
|
|
U8 buf[512];
|
|
|
|
CSpritePtWH *ptwh;
|
|
CDocEntry *doc_e;
|
|
doc_e = haystack_doc->head.next;
|
|
while (doc_e != haystack_doc) {
|
|
if (doc_e->type_u8 == DOCT_SPRITE) {
|
|
ptwh = doc_e->bin_data->data;
|
|
if (!(ptwh->width == 640 && ptwh->height == 2)) {
|
|
haystack_doc->cur_entry = doc_e->next;
|
|
StrPrint(buf, "\dID,%d\d", (ptwh->width / 8) + 2);
|
|
DocPutS(haystack_doc, buf);
|
|
DocRecalc(haystack_doc);
|
|
DocGoToLine(haystack_doc, haystack_doc->y + (ptwh->height / 8) + 16);
|
|
haystack_doc->x = 0;
|
|
DocRecalc(haystack_doc, RECALCt_FIND_CURSOR);
|
|
StrPrint(buf, "\dID,-%d\d", (ptwh->width / 8) + 2);
|
|
DocPutS(haystack_doc, buf);
|
|
}
|
|
}
|
|
doc_e = doc_e->next;
|
|
}
|
|
}
|
|
|
|
U8 *@sanitize_node_text(U8 *text) {
|
|
U8 *ch = text;
|
|
Bool needs_sanitization = FALSE;
|
|
while (*ch++ && !needs_sanitization) {
|
|
switch (*ch) {
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x24:
|
|
case 0xc2:
|
|
case 0xe2:
|
|
needs_sanitization = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (!needs_sanitization)
|
|
return text;
|
|
U8 *new_text = CAlloc(StrLen(text) * 2);
|
|
I64 i = 0;
|
|
while (i < StrLen(text)) {
|
|
switch (text[i]) {
|
|
case 0x11:
|
|
StrCpy(new_text + StrLen(new_text), "&");
|
|
i++;
|
|
break;
|
|
case 0x12:
|
|
StrCpy(new_text + StrLen(new_text), "<");
|
|
i++;
|
|
break;
|
|
case 0x24:
|
|
StrCpy(new_text + StrLen(new_text), "\d\d");
|
|
i++;
|
|
break;
|
|
case 0xc2:
|
|
if (text[i + 1] == 0xa9) {
|
|
StrCpy(new_text + StrLen(new_text), "(C)");
|
|
i += 2;
|
|
break;
|
|
}
|
|
if (text[i + 1] == 0xae) {
|
|
StrCpy(new_text + StrLen(new_text), "(R)");
|
|
i += 2;
|
|
break;
|
|
}
|
|
break;
|
|
case 0xe2:
|
|
if (text[i + 1] == 0x80 && text[i + 2] == 0x99) {
|
|
StrCpy(new_text + StrLen(new_text), "'");
|
|
i += 3;
|
|
break;
|
|
}
|
|
if (text[i + 1] == 0x84 && text[i + 2] == 0xa2) {
|
|
StrCpy(new_text + StrLen(new_text), "\dSY,-3\dtm\dSY,0\d");
|
|
i += 3;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
StrPrint(new_text + StrLen(new_text), "%c", text[i]);
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
Free(text);
|
|
return new_text;
|
|
}
|
|
extern I64 Navigate(CTask *task, U8 *url_string);
|
|
|
|
U0 NavigateTo(CDoc *doc, U8 *url) {
|
|
switch (url[0]) {
|
|
case '#':
|
|
DocAnchorFind(DocPut, url + 1);
|
|
return;
|
|
break;
|
|
default:
|
|
Navigate(doc, url);
|
|
break;
|
|
}
|
|
}
|
|
|
|
U0 @render_node_tree(CDoc *render_doc, CDoc *doc, Node *node) {
|
|
|
|
I64 i;
|
|
I64 imgAnchorCount = 0;
|
|
U8 buf[512];
|
|
|
|
if (!render_doc)
|
|
render_doc = doc;
|
|
|
|
if (Json.Get(node->attributes, "id")) {
|
|
StrPrint(buf, "\dAN,\"\",A=\"%s\"\d", Json.Get(node->attributes, "id"));
|
|
DocPutS(doc, buf);
|
|
}
|
|
|
|
if (!StrICmp(node->tagName, "a")) {
|
|
doc = DocNew;
|
|
}
|
|
|
|
if (!StrICmp(node->tagName, "b") || !StrICmp(node->tagName, "strong"))
|
|
DocPutS(doc, "\dFG,0\d");
|
|
|
|
if (!StrICmp(node->tagName, "blockquote"))
|
|
DocPutS(doc, "\n\n\dID,2\d");
|
|
|
|
if (!StrICmp(node->tagName, "br"))
|
|
DocPutS(doc, "\n");
|
|
|
|
if (!StrICmp(node->tagName, "hr")) {
|
|
DocPutS(doc, "\n");
|
|
DocSprite(doc, hr_s);
|
|
DocPutS(doc, "\n");
|
|
}
|
|
|
|
if (!StrICmp(node->tagName, "span")) {
|
|
if (!StrICmp(Json.Get(node->attributes, "class"), "quote") ||
|
|
!StrICmp(Json.Get(node->attributes, "class"), "name"))
|
|
DocPutS(doc, "\dFG,GREEN\d");
|
|
}
|
|
|
|
if (!StrICmp(node->tagName, "InternalTextNode")) {
|
|
node->text = @sanitize_node_text(node->text);
|
|
if (StrICmp(node->parentNode->tagName, "option") &&
|
|
StrICmp(node->parentNode->tagName, "script") &&
|
|
StrICmp(node->parentNode->tagName, "style") &&
|
|
StrICmp(node->parentNode->tagName, "title"))
|
|
DocPutS(doc, node->text);
|
|
if (!StrICmp(node->parentNode->tagName, "title"))
|
|
MemCpy(Fs->task_title, node->text, STR_LEN);
|
|
}
|
|
|
|
if (!StrICmp(node->tagName, "img")) {
|
|
// StrPrint(buf, "\dAN,\"\",A=\"img%d\"\d", imgAnchorCount);
|
|
// DocPutS(doc, buf);
|
|
// load images async (is this thread safe?)
|
|
|
|
StrPrint(progress1_desc, "Loading image %d of %d ...", progress1 + 1,
|
|
progress1_max);
|
|
@render_image(render_doc, node);
|
|
progress1++;
|
|
|
|
imgAnchorCount++;
|
|
}
|
|
|
|
if (node->children->length) {
|
|
for (i = 0; i < node->children->length; i++)
|
|
@render_node_tree(render_doc, doc, Json.ArrayIndex(node->children, i));
|
|
}
|
|
|
|
if (!StrICmp(node->tagName, "a")) {
|
|
U8 *plain_text_buf = Doc2PlainText(doc, doc->head.next);
|
|
plain_text_buf[StrLen(plain_text_buf) - 1] = NULL;
|
|
StrCpy(buf, "\dMA+LIS,\"");
|
|
StrCpy(buf + StrLen(buf), plain_text_buf + 4);
|
|
StrPrint(buf + StrLen(buf), "\",LM=\"NavigateTo(0x%08x,\\\"%s\\\");\"\d",
|
|
DocPut, Json.Get(node->attributes, "href"));
|
|
DocPutS(render_doc, buf);
|
|
Free(plain_text_buf);
|
|
}
|
|
|
|
if (!StrICmp(node->tagName, "b") || !StrICmp(node->tagName, "strong"))
|
|
DocPutS(doc, "\dFG,8\d");
|
|
|
|
if (!StrICmp(node->tagName, "blockquote"))
|
|
DocPutS(doc, "\dID,-2\d\n\n");
|
|
|
|
if (!StrICmp(node->tagName, "div"))
|
|
DocPutS(doc, "\n");
|
|
|
|
if (!StrICmp(node->tagName, "span")) {
|
|
if (!StrICmp(Json.Get(node->attributes, "class"), "quote") ||
|
|
!StrICmp(Json.Get(node->attributes, "class"), "name"))
|
|
DocPutS(doc, "\dFG,DKGRAY\d");
|
|
}
|
|
}
|