diff --git a/.gitignore b/.gitignore index 9589cfe..8c7d19f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ # CI build output collected by build.sh /dist/ +.vm/ diff --git a/browser/TWB/TWB.c b/browser/TWB/TWB.c deleted file mode 100644 index 15402e7..0000000 --- a/browser/TWB/TWB.c +++ /dev/null @@ -1,285 +0,0 @@ -#include "TWB\colors.h" -#include "TWB\anchors.h" -#include "TWB\parse_tag.h" -#include "TWB\special.h" -#include "TWB\tag_list.h" -#define DEFAULT_BG_COL 0xffFBE5E4; //- peach; 0xffEBE8E9 - more grey -dword link_color_default; -dword link_color_active; -#include "TWB\links.h" - -#define BODY_MARGIN 6 -#define BASIC_CHAR_W 8 -#define BASIC_LINE_H 18 - -struct STYLE { - bool - b, u, s, h, - font, - pre, - blq, - button, - image, - nav, header; - LIST tag_list; - dword title; - dword cur_line_h; - void reset(); -}; - -void STYLE::reset() -{ - b = u = s = h = font = pre = blq = - title = button = image = nav = header = false; - cur_line_h = NULL; - tag_list.reset(); -} - -struct TWebBrowser { - llist list; - STYLE style; - dword draw_y, draw_x, draw_w, left_gap; - dword o_bufpointer; - int cur_encoding, custom_encoding; - bool link, t_html, t_body; - dword bufpointer; - dword bufsize; - dword is_html; - collection img_url; - char header[150]; - char linebuf[500]; - char redirect[URL_SIZE]; - - bool secondrun; - - void SetStyle(); - void RenderTextbuf(); - void RenderLine(); - bool RenderImage(); - - void SetPageDefaults(); - void ParseHtml(); - void Reparse(); - void NewLine(); - void ChangeEncoding(); - void AddCharToTheLine(); - void DrawPage(); - - void tag_a(); - void tag_p(); - void tag_img(); - void tag_kosicon(); - void tag_div(); - void tag_h1234_caption(); - void tag_ol_ul_dt(); - void tag_li(); - void tag_q(); - void tag_hr(); - void tag_code(); - void tag_meta_xml(); - void tag_body(); - void tag_iframe(); - void tag_title(); - void tag_font(); - void tag_table_reset(); - void tag_table(); - void tag_td(); - void tag_tr(); - void reset_font_style(); -}; - -#include "TWB\render.h" -#include "TWB\set_style.h" -//============================================================================================ -void TWebBrowser::SetPageDefaults() -{ - t_html = t_body = link = false; - style.reset(); - reset_font_style(); - link_color_default = 0x0000FF; - link_color_active = 0xFF0000; - links.clear(); - anchors.clear(); - img_url.drop(); - text_colors.drop(); - text_colors.add(0); - if (secondrun) { - canvas.Init(list.x, list.y, list.w, math.max(list.visible, list.count)+200); - canvas.Fill(0, bg_colors.get(0)); - } - bg_colors.drop(); - bg_colors.add(DEFAULT_BG_COL); - header = NULL; - draw_y = BODY_MARGIN; - draw_x = left_gap = BODY_MARGIN; - draw_w = list.w - BODY_MARGIN - BODY_MARGIN; - linebuf = 0; - redirect = '\0'; - tag_table_reset(); - is_html = true; - if (!strstri(bufpointer, ""); } - $push cur_encoding - SetStyle(); - $pop eax - // The thing is that UTF if longer than other encodings. - // So if encoding was changed from UTF to DOS than $bufpos position got wrong, - // and we have to start parse from the very beginning - if (EAX != cur_encoding) && (cur_encoding == CH_UTF8) { - ParseHtml(bufpointer, strlen(bufpointer)); - return; - } - } - continue; - } - } - AddCharToTheLine(ESBYTE[bufpos]); - } - - RenderTextbuf(); - list.count = draw_y + style.cur_line_h; - list.CheckDoesValuesOkey(); - anchors.current = NULL; - - if (!secondrun) { - secondrun = true; - goto _PARSE_START_; - } - if (!header) { - strncpy(#header, #version, sizeof(TWebBrowser.header)-1); - DrawTitle(#header); - } -} -//============================================================================================ -void TWebBrowser::AddCharToTheLine(unsigned char _char) -{ - dword line_len = strlen(#linebuf); - if (_char<=15) _char=' '; - if (!style.pre) && (_char == ' ') - { - if (linebuf[line_len-1]==' ') return; //no double spaces - if (draw_x==left_gap) && (!linebuf) return; //no paces at the beginning of the line - if (link) && (line_len==0) return; - } - if (line_len < sizeof(TWebBrowser.linebuf)) { - chrcat(#linebuf+line_len, _char); - } else { - RenderTextbuf(); - } -} -//============================================================================================ -void TWebBrowser::ChangeEncoding(int _new_encoding) -{ - if (cur_encoding == _new_encoding) return; - cur_encoding = _new_encoding; - bufpointer = ChangeCharset(cur_encoding, CH_CP866, bufpointer); - if (header) { - ChangeCharset(cur_encoding, CH_CP866, #header); - DrawTitle(#header); - } -} -//============================================================================================ -scroll_bar scroll_wv = { 15,NULL,NULL,NULL, - 0,2,NULL,0,0,0xeeeeee,0xBBBbbb,0xeeeeee}; - -void TWebBrowser::DrawPage() -{ - if (list.w!=canvas.bufw) Reparse(); - canvas.Show(list.first, list.h); - - scroll_wv.max_area = list.count; - scroll_wv.cur_area = list.visible; - scroll_wv.position = list.first; - scroll_wv.all_redraw = 0; - scroll_wv.start_x = list.x + list.w; - scroll_wv.start_y = list.y; - scroll_wv.size_y = list.h; - - if (list.count <= list.visible) { - DrawBar(scroll_wv.start_x, scroll_wv.start_y, scroll_wv.size_x, - scroll_wv.size_y, bg_colors.get(0) & 0x00FFFFFF); - } else { - scrollbar_v_draw(#scroll_wv); - } -} \ No newline at end of file diff --git a/browser/TWB/anchors.h b/browser/TWB/anchors.h deleted file mode 100644 index 819e6cb..0000000 --- a/browser/TWB/anchors.h +++ /dev/null @@ -1,43 +0,0 @@ - -struct _anchors { - char current[256]; - collection anchor_name; - collection anchor_position; - void add(); - int get_pos_by_name(); - void clear(); - void take_anchor_from(); -} anchors=0; - -void _anchors::add(dword _name, _pos) -{ - anchor_name.add(_name); - anchor_position.add(itoa(_pos)); -} - -int _anchors::get_pos_by_name(dword _get_name) -{ - dword pos_name = anchor_name.get_pos_by_name(_get_name); - if (ESBYTE[_get_name]==NULL) return 0; - if (pos_name==-1) { - return -1; - } else { - return atoi(anchor_position.get(pos_name)); - } -} - -void _anchors::clear() -{ - anchor_name.drop(); - anchor_position.drop(); -} - -void _anchors::take_anchor_from(dword _URL) -{ - int anchor_pos; - if (!current) && (_URL) && (anchor_pos = strrchr(_URL, '#')) { - strncpy(#current, _URL+anchor_pos-1, sizeof(_anchors.current)-1); - ESBYTE[_URL+anchor_pos-1] = '\0'; - } -} - diff --git a/browser/TWB/colors.h b/browser/TWB/colors.h deleted file mode 100644 index 073862d..0000000 --- a/browser/TWB/colors.h +++ /dev/null @@ -1,75 +0,0 @@ -collection_int text_colors; -collection_int bg_colors; - -struct WEB_COLORS { - char *name; - dword rgb; -} web_colors[] = { - "aliceblue", 0xF0F8FF, "antiquewhite", 0xFAEBD7, "aqua", 0x00FFFF, "aquamarine", 0x7FFFD4, - "azure", 0xF0FFFF, "beige", 0xF5F5DC, "bisque", 0xFFE4C4, "black", 0x000000, - "blanchedalmond", 0xFFEBCD, "blue", 0x0000FF, "blueviolet", 0x8A2BE2, "brown", 0xA52A2A, - "burlywood", 0xDEB887, "cadetblue", 0x5F9EA0, "chartreuse", 0x7FFF00, "chocolate", 0xD2691E, - "coral", 0xFF7F50, "cornflowerblue", 0x6495ED, "cornsilk", 0xFFF8DC, "crimson", 0xDC143C, - "cyan", 0x00FFFF, "darkblue", 0x00008B, "darkcyan", 0x008B8B, "darkgoldenrod", 0xB8860B, - "darkgray", 0xA9A9A9, "darkgreen", 0x006400, "darkkhaki", 0xBDB76B, "darkmagenta", 0x8B008B, - "darkolivegreen", 0x556B2F, "darkorange", 0xFF8C00, "darkorchid", 0x9932CC, "darkred", 0x8B0000, - "darksalmon", 0xE9967A, "darkseagreen", 0x8FBC8F, "darkslateblue", 0x483D8B, "darkslategray", 0x2F4F4F, - "darkturquoise", 0x00CED1, "darkviolet", 0x9400D3, "deeppink", 0xFF1493, "deepskyblue", 0x00BFFF, - "dimgray", 0x696969, "dodgerblue", 0x1E90FF, "firebrick", 0xB22222, "floralwhite", 0xFFFAF0, - "forestgreen", 0x228B22, "fuchsia", 0xFF00FF, "gainsboro", 0xDCDCDC, "ghostwhite", 0xF8F8FF, - "gold", 0xFFD700, "goldenrod", 0xDAA520, "gray", 0x808080, "green", 0x008000, - "greenyellow", 0xADFF2F, "honeydew", 0xF0FFF0, "hotpink", 0xFF69B4, "indianred", 0xCD5C5C, - "indigo", 0x4B0082, "ivory", 0xFFFFF0, "khaki", 0xF0E68C, "lavender", 0xE6E6FA, - "lavenderblush", 0xFFF0F5, "lawngreen", 0x7CFC00, "lemonchiffon", 0xFFFACD, "lightblue", 0xADD8E6, - "lightcoral", 0xF08080, "lightcyan", 0xE0FFFF, "lightgreen", 0x90EE90, "lightgrey", 0xD3D3D3, - "lightpink", 0xFFB6C1, "lightsalmon", 0xFFA07A, "lightseagreen", 0x20B2AA, "lightskyblue", 0x87CEFA, - "lightslategray", 0x778899, "lightsteelblue", 0xB0C4DE, "lightyellow", 0xFFFFE0, "lime", 0x00FF00, - "limegreen", 0x32CD32, "linen", 0xFAF0E6, "magenta", 0xFF00FF, "maroon", 0x800000, - "mediumaquamarine", 0x66CDAA, "mediumblue", 0x0000CD, "mediumorchid", 0xBA55D3, "mediumpurple", 0x9370DB, - "mediumseagreen", 0x3CB371, "mediumslateblue", 0x7B68EE, "mediumspringgreen",0x00FA9A, "mediumturquoise", 0x48D1CC, - "mediumvioletred", 0xC71585, "midnightblue", 0x191970, "mintcream", 0xF5FFFA, "mistyrose", 0xFFE4E1, - "moccasin", 0xFFE4B5, "navajowhite", 0xFFDEAD, "navy", 0x000080, "oldlace", 0xFDF5E6, - "olive", 0x808000, "olivedrab", 0x6B8E23, "orange", 0xFFA500, "orangered", 0xFF4500, - "orchid", 0xDA70D6, "palegoldenrod", 0xEEE8AA, "palegreen", 0x98FB98, "paleturquoise", 0xAFEEEE, - "palevioletred", 0xDB7093, "papayawhip", 0xFFEFD5, "peachpuff", 0xFFDAB9, "peru", 0xCD853F, - "pink", 0xFFC0CB, "plum", 0xDDA0DD, "powderblue", 0xB0E0E6, "purple", 0x800080, - "red", 0xFF0000, "rosybrown", 0xBC8F8F, "royalblue", 0x4169E1, "saddlebrown", 0x8B4513, - "salmon", 0xFA8072, "sandybrown", 0xF4A460, "seagreen", 0x2E8B57, "seashell", 0xFFF5EE, - "sienna", 0xA0522D, "silver", 0xC0C0C0, "skyblue", 0x87CEEB, "slateblue", 0x6A5ACD, - "slategray", 0x708090, "snow", 0xFFFAFA, "springgreen", 0x00FF7F, "steelblue", 0x4682B4, - "tan", 0xD2B48C, "teal", 0x008080, "thistle", 0xD8BFD8, "tomato", 0xFF6347, - "turquoise", 0x40E0D0, "violet", 0xEE82EE, "wheat", 0xF5DEB3, "white", 0xFFFFFF, - "whitesmoke", 0xF5F5F5, "yellow", 0xFFFF00, "yellowgreen", 0x9ACD32, 0}; - -dword GetColor(char* color_str) -{ - int ii; - dword textlen=0; - dword color=0; - - strlwr(color_str); - if (ESBYTE[color_str] == '#') { - color_str++; - } else { - for (ii=0; web_colors[ii].name!=0; ii++) - { - if (streq(color_str, web_colors[ii].name)) return web_colors[ii].rgb; - } - } - - textlen = strlen(color_str); - if (textlen==6) || (textlen==3) - { - FOR (ii=0; ii='0') && (BL<='9')) BL -= '0'; - //IF ((BL>='A') && (BL<='F')) BL -= 'A'-10; - IF ((BL>='a') && (BL<='f')) BL -= 'a'-10; - color = color*0x10 + BL; - if (textlen==3) color = color*0x10 + BL; //#abc ->> #aabbcc - } - return color; - } - return text_colors.get(0); -} diff --git a/browser/TWB/links.h b/browser/TWB/links.h deleted file mode 100644 index 21b4d2b..0000000 --- a/browser/TWB/links.h +++ /dev/null @@ -1,113 +0,0 @@ -CustomCursor CursorPointer; -dword CursorFile = FROM "TWB/pointer.cur"; -#include "..\lib\collection.h" - -struct PAGE_LINKS { - collection unic_links; - collection element_links; - - collection_int x; - collection_int y; - collection_int w; - collection_int h; - collection_int id; - collection_int underline_h; - - signed int active; - dword active_url; - bool hover(); - void add_link(); - void add_text(); - void clear(); - void draw_underline(); -} links; - -void PAGE_LINKS::add_link(dword lpath) -{ - unic_links.add(lpath); -} - -void PAGE_LINKS::add_text(dword _x, _y, _w, _h, _underline_h) -{ - x.add(_x); - y.add(_y); - w.add(_w); - h.add(_h); - underline_h.add(_underline_h); - element_links.add(unic_links.get_last()); - id.add(unic_links.count); -} - -void PAGE_LINKS::clear() -{ - x.drop(); - y.drop(); - w.drop(); - h.drop(); - underline_h.drop(); - id.drop(); - - element_links.drop(); - unic_links.drop(); - - active = -1; - active_url = 0; - CursorPointer.Restore(); -} - -void PAGE_LINKS::draw_underline(signed _id, dword list_first, list_y, color) -{ - int i; - if (_id == -1) return; - for (i=0; i list_y) { - DrawBar(x.get(i), y.get(i) + h.get(i) - list_first, - w.get(i), underline_h.get(i), color); - } - } -} - -bool PAGE_LINKS::hover(dword list_y, list_first) -{ - int i; - int mx = mouse.x; - int my = mouse.y + list_first; - if (!id.count) return false; - - //Here we check is any link hovered - for (i=0; ix.get(i)) && (my>y.get(i)) - && (mxlist_y+list_first) - { - if (active!=i) { - CursorPointer.Load(#CursorFile); - CursorPointer.Set(); - - draw_underline(active, list_first, list_y, link_color_default); - draw_underline(i, list_first, list_y, DEFAULT_BG_COL); - - active_url = element_links.get(i); - active = i; - DrawStatusBar(active_url); - } - if (mouse.lkm) && (mouse.down) { - DrawRectangle(x.get(active), -list_first + y.get(active), - w.get(active), h.get(active), 0); - } - return true; - } - } - if (active_url) { - CursorPointer.Restore(); - draw_underline(active, list_first, list_y, link_color_default); - active_url = 0; - active = -1; - DrawStatusBar(NULL); - } - return false; -} - diff --git a/browser/TWB/parse_tag.h b/browser/TWB/parse_tag.h deleted file mode 100644 index 1efb575..0000000 --- a/browser/TWB/parse_tag.h +++ /dev/null @@ -1,202 +0,0 @@ -/* -struct _DOM { - collection tag; - collection tagparam; - collection_int parent; -} dom; -*/ - -struct _tag -{ - char name[32]; - char prior[32]; - bool opened; - collection attributes; - collection values; - dword value; - dword number; - bool is(); - bool parse(); - dword get_next_param(); - dword get_value_of(); - signed get_number_of(); -} tag=0; - -bool _tag::is(dword _text) -{ - return streq(#name, _text); -} - -bool _tag::parse(dword _bufpos, bufend) -{ - bool retok = true; - dword bufpos = ESDWORD[_bufpos]; - dword params, paramsend; - - dword closepos; - dword whitepos; - dword openpos; - - if (name) strcpy(#prior, #name); else prior = '\0'; - name = '\0'; - attributes.drop(); - values.drop(); - - if (!strncmp(bufpos,"!--",3)) - { - bufpos+=3; - //STRSTR - while (strncmp(bufpos,"-->",3)!=0) && (bufpos < bufend) - { - bufpos++; - } - bufpos+=2; - retok = false; - goto _RET; - } - - if (ESBYTE[bufpos] == '/') { - opened = false; - bufpos++; - } else { - opened = true; - } - - closepos = strchr(bufpos, '>'); - whitepos = strchrw(bufpos, bufend-bufpos); - - if (!whitepos) || (whitepos > closepos) { - //no param - strncpy(#name, bufpos, math.min(closepos - bufpos, sizeof(tag.name)-1)); - bufpos = closepos; - } else { - //we have param - while (chrlnum(whitepos, '\"', closepos - whitepos)%2) { //alt="Next>>" - if (!openpos = strchr(closepos+1, '<')) break; - if (openpos < strchr(closepos+1, '>')) break; - if (!closepos = EAX) {closepos = bufend;break;} - } - strncpy(#name, bufpos, math.min(whitepos - bufpos, sizeof(tag.name)-1)); - bufpos = closepos; - - params = malloc(closepos - whitepos + 1); - strncpy(params, whitepos, closepos - whitepos); - paramsend = params + closepos - whitepos; - while (paramsend = get_next_param(params, paramsend-1)); - free(params); - } - - if (name) { - strlwr(#name); - // ignore text inside the next tags - if (is("script")) || (is("style")) || (is("binary")) || (is("select")) { - strcpy(#prior, #name); - sprintf(#name, "", #prior); - if (strstri(bufpos, #name)) bufpos = EAX-1; - retok = false; - } else { - if (name[strlen(#name)-1]=='/') name[strlen(#name)-1]=NULL; //for
- } - } else { - retok = false; - } - -_RET: - ESDWORD[_bufpos] = bufpos; - return retok; -} - -dword _tag::get_next_param(dword ps, pe) -{ - // "ps" - param start - // "pe" - param end - // "q" - quote char - char q = NULL; - dword initial_pe = pe; - dword fixeq; - dword val; - dword attr; - - if (ESBYTE[pe] == '/') pe--; - while (pe>ps) && (__isWhite(ESBYTE[pe])) pe--; - - if (ESBYTE[pe] == '"') || (ESBYTE[pe] == '\'') - { - //remove quote - q = ESBYTE[pe]; - ESBYTE[pe] = '\0'; - pe--; - - //find VAL start and copy - pe = strrchr(ps, q) + ps; - val = pe; - if (pe>ps) pe--; - ESBYTE[pe] = '\0'; - - //find ATTR end - while (pe > ps) && (ESBYTE[pe] != '=') pe--; - ESBYTE[pe] = '\0'; - } - else - { - //find VAL start and copy - while (pe > ps) && (ESBYTE[pe] != '=') pe--; - val = pe+1; - ESBYTE[pe] = '\0'; - //already have ATTR end - } - - if (pe > ps) pe--; - - //Fix case: "src =" - while (pe>ps) && (__isWhite(ESBYTE[pe])) { - ESBYTE[pe] = '\0'; - pe--; - } - - //find ATTR start and copy - while (pe>ps) && (!__isWhite(ESBYTE[pe])) pe--; - attr = pe + 1; - ESBYTE[pe] = '\0'; - - // Fix case: src=./images/logo?sid=e8ece8b38b - // Exchange '=' and '\0' position. - // attr: src=./images/logo?sid => src - // val: e8ece8b38b => ./images/logo?sid=e8ece8b38b - fixeq = strchr(attr,'='); - if (!q) && (fixeq) { - ESBYTE[val-1] >< ESBYTE[fixeq]; - val = fixeq+1; - } - strlwr(attr); - strrtrim(val); - //Fix case: " img.png" - while(__isWhite(ESBYTE[val])) && (val draw_w + BODY_MARGIN) { - debugln("anomaly"); - debugln(_line); - } - - if (debug_mode) { - canvas.DrawBar(draw_x, draw_y, pw, list.item_h, 0xCCCccc); - debugln(_line); - } - - style.cur_line_h = math.max(style.cur_line_h, list.item_h); - - if (!secondrun) goto _SKIP_DRAW; - if (bg_colors.get_last() - bg_colors.get(0)) { - canvas.DrawBar(draw_x, draw_y, pw, list.item_h, bg_colors.get_last()); - } - - if (style.image) { - canvas.DrawBar(draw_x, draw_y, pw, list.item_h-1, 0xF9DBCB); - } - if (style.button) { - canvas.DrawBar(draw_x, draw_y, pw, list.item_h - calc(zoom*2), 0xCCCccc); - canvas.DrawBar(draw_x, draw_y + list.item_h - calc(zoom*2), pw, zoom, 0x999999); - } - - pc = text_colors.get_last(); - if (link) && (pc == text_colors.get(0)) pc = link_color_default; - - canvas.WriteText(draw_x, draw_y+1, list.font_type, pc, _line, NULL); - if (style.b) canvas.WriteText(draw_x+1, draw_y+1, list.font_type, pc, _line, NULL); - if (style.s) canvas.DrawBar(draw_x, list.item_h / 2 - zoom + draw_y, pw, zoom, pc); - if (style.u) canvas.DrawBar(draw_x, draw_y + list.font_h, pw, zoom, pc); - if (link) { - if (ESBYTE[_line]==' ') && (ESBYTE[_line+1]==NULL) {} else { - canvas.DrawBar(draw_x, draw_y + list.font_h, pw, zoom, link_color_default); - links.add_text(draw_x, draw_y + list.y, pw, list.font_h, zoom); - } - } - _SKIP_DRAW: - draw_x += pw; - } - ESBYTE[_line] = NULL; -} - -void TWebBrowser::RenderTextbuf() -{ - dword lbp = #linebuf; - int br; //break position - char nul = '\0'; - unsigned int len; - - //Do we need a line break? - - if (!linebuf[0]) return; - - while (len = strlen(lbp)) && (len * list.font_w + draw_x - left_gap >= draw_w) { - //Yes, we do. Lets calculate where... - br = len; - - //Is a new line fits in the maximum line width? - if (br * list.font_w + draw_x - left_gap >= draw_w) { - br = draw_w - draw_x + left_gap - BODY_MARGIN / list.font_w; - if (br < 0) br = 0; - while(br) { - if (ESBYTE[lbp + br]==' ') || (ESBYTE[lbp + br]=='-') { - if (br < len) br++; - break; - } - br--; - } - } - //Maybe a new line is too big for the whole new line? Then we have to split it - if (!br) && (len * list.font_w >= draw_w) { - br = draw_w - draw_x + left_gap - BODY_MARGIN / list.font_w; - if (br < 0) { - NewLine(); - br = draw_w - draw_x + left_gap - BODY_MARGIN / list.font_w; - } - } - - if (br) { - ESBYTE[lbp + br] >< nul; - RenderLine(lbp); - ESBYTE[lbp + br] >< nul; - lbp += br; - while (ESBYTE[lbp]==' ') && (ESBYTE[lbp]) lbp++; - if (ESBYTE[lbp]) NewLine(); - } else { - NewLine(); - } - } - RenderLine(lbp); -} - -void TWebBrowser::NewLine() -{ - static bool empty_line = true; - - if (draw_x==left_gap) && (draw_y==BODY_MARGIN) return; - if (t_html) && (!t_body) return; - - if (draw_x == style.tag_list.level * 5 * list.font_w + left_gap) { - if (!empty_line) { - empty_line=true; - } else { - return; - } - } else { - empty_line = false; - } - - draw_y += style.cur_line_h; - style.cur_line_h = list.item_h; - if (style.blq) draw_x = 6 * list.font_w; else draw_x = 0; //left_gap += style.tag_list.level * 5 * list.font_w ??? - draw_x += style.tag_list.level * 5 * list.font_w + left_gap; -} - -bool TWebBrowser::RenderImage(dword cur_img) -{ - int img_x, img_y, img_w, img_h; - dword imgbuf[44]; - - if (!cur_img) || (!buf_data) return false; - - img_h = ESDWORD[cur_img+8]; - img_w = ESDWORD[cur_img+4]; - - if (img_w + draw_x - left_gap >= draw_w) NewLine(); - img_y = draw_y; - if (img_h < list.item_h) img_y += list.item_h - img_h / 2 - 1; else img_y -= 2; - style.cur_line_h = math.max(style.cur_line_h, img_h); - - img_w = math.min(img_w, canvas.bufw - draw_x); - - if (link) links.add_text(draw_x + list.x, img_y + list.y, img_w, img_h, 0); - - if (img_y + img_h >= canvas.bufh) canvas.IncreaseBufSize(); - - if (secondrun) - { - if (ESDWORD[cur_img+20] != IMAGE_BPP32) { - img_convert stdcall(cur_img, 0, IMAGE_BPP32, 0, 0); - $push eax - img_destroy stdcall(cur_img); - $pop eax - cur_img = EAX; - if (!EAX) return false; - } - imgbuf[04] = canvas.bufw; - imgbuf[08] = canvas.bufh; - imgbuf[20] = IMAGE_BPP32; - imgbuf[24] = buf_data+8; - img_blend stdcall(#imgbuf, cur_img, draw_x, img_y, 0, 0, img_w, img_h); - img_destroy stdcall(cur_img); - } - - draw_x += img_w; - if (draw_x - left_gap >= draw_w) NewLine(); - return true; -} \ No newline at end of file diff --git a/browser/TWB/set_style.h b/browser/TWB/set_style.h deleted file mode 100644 index d34b4b5..0000000 --- a/browser/TWB/set_style.h +++ /dev/null @@ -1,513 +0,0 @@ -void TWebBrowser::SetStyle() -{ - if (tag.get_value_of("name")) || (tag.get_value_of("id")) { - anchors.add(tag.value, draw_y); - if (anchors.current) && (streq(tag.value, #anchors.current+1)) { - list.first = draw_y; - anchors.current = NULL; - } - } - if (tag.is("a")) { tag_a(); return; } - if (tag.is("p")) { tag_p(); return; } - if (tag.is("img")) { tag_img(); return; } - if (tag.is("kosicon")) { tag_kosicon(); return; } - if (tag.is("div")) { tag_div(); return; } - if (tag.is("br")) { /*draw_x++;*/NewLine(); return; } - if (tag.is("nav")) { style.nav = tag.opened; return; } - if (tag.is("header")) { NewLine(); return; } - if (tag.is("article")) { NewLine(); return; } - if (tag.is("footer")) { NewLine(); return; } - if (tag.is("figure")) { NewLine(); return; } - if (tag.is("w:p")) { NewLine(); return; } - if (tag.is("b")) { style.b = tag.opened; return; } - if (tag.is("strong")) { style.b = tag.opened; return; } - if (tag.is("big")) { style.b = tag.opened; return; } - if (tag.is("w:b")) { style.b = tag.opened; return; } - if (tag.is("u")) { style.u = tag.opened; return; } - if (tag.is("ins")) { style.u = tag.opened; return; } - if (tag.is("s")) { style.s = tag.opened; return; } - if (tag.is("strike")) { style.s = tag.opened; return; } - if (tag.is("del")) { style.s = tag.opened; return; } - if (tag.is("pre")) { style.pre = tag.opened; return; } - if (tag.is("blockquote")) { style.blq = tag.opened; return; } - if (tag.is("button")) { style.button = tag.opened; return; } - if (tag.is("dl")) { if (tag.opened) NewLine(); return; } - if (tag.is("w:r")) { if (!tag.opened) style.b = false; return; } - if (tag.is("h1")) { tag_h1234_caption(); return; } - if (tag.is("h2")) { tag_h1234_caption(); return; } - if (tag.is("h3")) { tag_h1234_caption(); return; } - if (tag.is("h4")) { tag_h1234_caption(); return; } - if (tag.is("font")) { tag_font(); return; } - if (tag.is("dt")) { tag_ol_ul_dt(); return; } - if (tag.is("dd")) { tag_ol_ul_dt(); return; } - if (tag.is("ul")) { tag_ol_ul_dt(); return; } - if (tag.is("ol")) { tag_ol_ul_dt(); return; } - if (tag.is("li")) { tag_li(); return; } - if (tag.is("q")) { tag_q(); return; } - if (tag.is("hr")) { tag_hr(); return; } - if (tag.is("meta")) { tag_meta_xml(); return; } - if (tag.is("?xml")) { tag_meta_xml(); return; } - if (tag.is("code")) { tag_code(); return; } - if (tag.is("iframe")) { tag_iframe(); return; } - if (tag.is("caption")) { tag_h1234_caption(); return; } - if (tag.is("title")) { tag_title(); return; } - if (tag.is("body")) { tag_body(); return; } - if (tag.is("html")) { t_html = tag.opened; return; } - if (tag.is("table")) { tag_table(); return; } - if (tag.is("tr")) { tag_table(); return; } - if (tag.is("th")) { tag_table(); return; } - if (tag.is("td")) { tag_table(); return; } -} - -void TWebBrowser::tag_p() -{ - IF (tag.prior[0] == 'h') || (streq(#tag.prior,"td")) return; - IF (!tag.opened) && (streq(#tag.prior,"p")) return; - NewLine(); -} - -void TWebBrowser::tag_title() -{ - style.title = tag.opened; - if (!tag.opened) DrawTitle(#header); -} - -void TWebBrowser::tag_font() -{ - style.font = tag.opened; - if (tag.opened) - { - if (tag.get_value_of("bg")) { - bg_colors.add(GetColor(tag.value)); - } else { - bg_colors.add(bg_colors.get_last()); - } - if (tag.get_value_of("color")) { - text_colors.add(GetColor(tag.value)); - } else { - text_colors.add(text_colors.get_last()); - } - } - else { - if (text_colors.count>1) text_colors.pop(); - if (bg_colors.count>1) bg_colors.pop(); //never pop the last color - } -} - -void TWebBrowser::tag_div() -{ - //if (streq(#tag.prior,"div")) && (tag.opened) return; - if (streq(#tag.prior,"td")) return; - if (streq(#tag.prior,"div")) return; - if (!tag.opened) && (style.font) text_colors.pop(); - NewLine(); -} - -void TWebBrowser::tag_iframe() -{ - if (tag.get_value_of("src")) { - NewLine(); - strcpy(#linebuf, "IFRAME: "); - RenderTextbuf(); - link=true; - links.add_link(tag.value); - strncpy(#linebuf, tag.value, sizeof(TWebBrowser.linebuf)-1); - RenderTextbuf(); - link=false; - NewLine(); - } -} - -void TWebBrowser::tag_a() -{ - if (!secondrun) return; - if (tag.opened) - { - if (tag.get_value_of("href")) && (!strstr(tag.value,"javascript:")) - { - link = true; - links.add_link(tag.value); - } - } else { - link = false; - } -} - -void TWebBrowser::tag_meta_xml() -{ - if (secondrun) return; - if (custom_encoding == -1) if (tag.get_value_of("charset")) - || (tag.get_value_of("content")) || (tag.get_value_of("encoding")) - { - EDX = strrchr(tag.value, '=') + tag.value; //search in content= - if (ESBYTE[EDX] == '"') EDX++; - strlwr(EDX); - EAX = get_encoding_type_by_name(EDX); - if (EAX!=-1) ChangeEncoding(EAX); - } - if (streq(tag.get_value_of("http-equiv"), "refresh")) && (tag.get_value_of("content")) { - if (tag.value = strstri(tag.value, "url")) strcpy(#redirect, tag.value); - } -} - -signed int get_encoding_type_by_name(dword name) -{ - EDX = name; - if (streqrp(EDX,"utf-8")) || (streqrp(EDX,"utf8")) return CH_UTF8; - else if (streqrp(EDX,"windows-1251")) || (streqrp(EDX,"windows1251")) return CH_CP1251; - else if (streqrp(EDX,"dos")) || (streqrp(EDX,"cp-866")) return CH_CP866; - else if (streqrp(EDX,"iso-8859-5")) || (streqrp(EDX,"iso8859-5")) return CH_ISO8859_5; - else if (streqrp(EDX,"koi8-r")) || (streqrp(EDX,"koi8-u")) return CH_KOI8; - return -1; -} - -void TWebBrowser::tag_code() -{ - if (style.pre = tag.opened) { - bg_colors.add(0xe4ffcb); - } else { - if (bg_colors.count>1) bg_colors.pop(); //never pop the last color - } -} - -void TWebBrowser::tag_ol_ul_dt() -{ - char type = ESBYTE[#tag.name]; - switch(type) - { - case 'd': - if (ESBYTE[#tag.name+1]=='d') style.tag_list.upd_level(tag.opened, type); - if (ESBYTE[#tag.name+1]=='t') style.tag_list.upd_level(false, 'd'); - if (tag.opened) NewLine(); - break; - case 'u': - case 'o': - if (style.nav) && (style.tag_list.level) NewLine(); - style.tag_list.upd_level(tag.opened, type); - if (!tag.opened) && (!style.pre) NewLine(); - } -} - -void TWebBrowser::tag_li() -{ - if (style.nav) { - chrncat(#linebuf, ' ', sizeof(TWebBrowser.linebuf)); - } else if (tag.opened) { - if (!style.tag_list.level) style.tag_list.upd_level(1, 'u'); - if (!style.pre) NewLine(); - if (style.tag_list.order_type() == 'u') { - strcpy(#linebuf, "\31 "); - draw_x = style.tag_list.level * 5 - 2 * list.font_w + left_gap; - } - if (style.tag_list.order_type() == 'o') { - sprintf(#linebuf, "%i. ", style.tag_list.inc_counter()); - draw_x = style.tag_list.level * 5 - 1 - strlen(#linebuf) * list.font_w + left_gap; - } - } -} - -void TWebBrowser::tag_hr() -{ - dword hrcol = 0x00777777; - dword hr_width = draw_w-BODY_MARGIN-BODY_MARGIN; - dword hr_size = 1; - if (tag.get_value_of("color")) { - hrcol = GetColor(tag.value); - } - if (tag.get_value_of("width")) && (!strchr(tag.value, '%')) { - hr_width = math.min(hr_width, tag.get_number_of("width")); - } - if (tag.get_number_of("size")) { - hr_size = math.min(500, tag.number); - } - if (draw_x != left_gap) NewLine(); - if (secondrun) canvas.DrawBar(left_gap, style.cur_line_h / 2 + draw_y - 1, hr_width, hr_size, hrcol); - draw_y += hr_size - 3; - draw_x++; - NewLine(); - return; -} - -void TWebBrowser::tag_body() -{ - t_body = tag.opened; - if (tag.get_value_of("link")) link_color_default = GetColor(tag.value); - if (tag.get_value_of("alink")) link_color_active = GetColor(tag.value); - if (tag.get_value_of("text")) text_colors.set(0, GetColor(tag.value)); - if (tag.get_value_of("bgcolor")) bg_colors.set(0, GetColor(tag.value)); - // Autodetecting encoding if no encoding was set - if (tag.opened) && (custom_encoding==-1) && (cur_encoding == CH_CP866) { - if (strstr(bufpointer, "\208\190")) ChangeEncoding(CH_UTF8); - else if (chrnum(bufpointer, '\246')>5) ChangeEncoding(CH_CP1251); - } -} - -void TWebBrowser::tag_q() -{ - chrncat(#linebuf, '\"', sizeof(TWebBrowser.linebuf)); -} - -void TWebBrowser::reset_font_style() -{ - list.SetFont(BASIC_CHAR_W, 14, 10011000b); - style.cur_line_h = list.item_h = list.font_h + 5; -} - -void TWebBrowser::tag_h1234_caption() -{ - if (ESBYTE[#tag.name+1]=='4') { - NewLine(); - NewLine(); - style.h = tag.opened; - style.b = tag.opened; - } else { - style.h = tag.opened; - if (tag.opened) { - if (!style.pre) { - NewLine(); - NewLine(); - } - if (tag.is("h1")) { - list.SetFont(BASIC_CHAR_W*2, 14+13, 10011001b); - style.b = true; - } else if (tag.is("h2")) { - list.SetFont(BASIC_CHAR_W*2, 14+13, 10011001b); - } else { - list.SetFont(6*2, 9+8, 10001001b); - } - style.cur_line_h = list.item_h = list.font_h + 3; - } else { - if (tag.is("h1")) style.b = false; - NewLine(); - reset_font_style(); - } - } -} - -void TWebBrowser::tag_kosicon() -{ - dword imgbuf[44]; - dword maxicon; - dword shared_i18 = memopen("ICONS18", NULL, SHM_READ); - maxicon = EDX / 18 / 18 / 4; - if (shared_i18) && (tag.get_number_of("n")) { - if (tag.number < maxicon) { - if (draw_x + 18 > canvas.bufw) NewLine(); - canvas.DrawImage(draw_x, draw_y-1, 18, 18, 18*18*4*tag.number+shared_i18); - draw_x += 22; - } - } - -} - -void TWebBrowser::tag_img() -{ - char img_path[4096]=0; - dword base64img; - - if (!tag.get_value_of("data-large-image")) - if (!tag.get_value_of("data-src")) - if (!tag.get_value_of("src")) return; - - if (streqrp(tag.value, "data:")) { - if (!strstr(tag.value, "base64,")) goto NOIMG; - EDX = EAX+7; - if (ESBYTE[EDX]==' ') EDX++; - base64img = malloc(strlen(EDX)); - base64_decode stdcall (EDX, base64img, strlen(EDX)); - img_decode stdcall (base64img, EAX, 0); - $push eax - free(base64img); - $pop eax - if (EAX) goto IMGOK; else goto NOIMG; - } - - if (!strcmp(tag.value + strrchr(tag.value, '.'), "svg")) goto NOIMG; - if (!strcmp(tag.value + strrchr(tag.value, '.'), "webp")) goto NOIMG; - - strlcpy(#img_path, tag.value, sizeof(img_path)-1); - replace_char(#img_path, ' ', '\0', sizeof(img_path)-1); - get_absolute_url(#img_path, history.current()); - - if (check_is_the_adress_local(#img_path)) { - img_from_file stdcall(#img_path); - if (EAX) goto IMGOK; else goto NOIMG; - } - - if (cache.has(#img_path)) { - img_decode stdcall (cache.current_buf, cache.current_size, 0); - if (EAX) goto IMGOK; else goto NOIMG; - } else { - if (img_url.get_pos_by_name(#img_path)==-1) img_url.add(#img_path); - goto NOIMG; - } - -IMGOK: - if (RenderImage(EAX)) return; - -NOIMG: - if (tag.get_value_of("title")) || (tag.get_value_of("alt")) { - strncpy(#img_path, tag.value, sizeof(TWebBrowser.linebuf)-3); - sprintf(#linebuf, "[%s]", #img_path); - } else { - if (streqrp(#img_path, "data:")) img_path=0; - replace_char(#img_path, '?', NULL, strlen(#img_path)); - img_path[sizeof(TWebBrowser.linebuf)-3] = '\0'; //prevent overflow in sprintf - sprintf(#linebuf, "[%s]", #img_path+strrchr(#img_path, '/')); - linebuf[50]= NULL; - } - text_colors.add(0x9A6F29); - style.image = true; - RenderTextbuf(); - style.image = false; - text_colors.pop(); -} - -struct TABLE { - int depth; - int margin; - collection_int cols; -} table; - -unsigned table_id; -unsigned colcount; -unsigned tr_pos; -unsigned td_pos; -unsigned row_start_y; -unsigned tallest_cell_in_row; -unsigned cur_cell_w; - -void TWebBrowser::tag_table_reset() -{ - table_id = 0; - colcount = 0; - tr_pos = 0; - td_pos = 0; - table.depth = 0; - cur_cell_w = 0; -} - -void TWebBrowser::tag_table() -{ - if (!tag.opened) { - if (style.font) tag_font(); - if (link) tag_a(); - style.b = false; - } - if (tag.is("table")) { - if(tag.opened) { - table.depth++; - if (table.depth==1) { - table_id++; - colcount = 0; - td_pos = 0; - row_start_y = draw_y; - if (tag.get_number_of("width")) { - if (strchr(tag.value, '%')) tag.number = list.w * tag.number / 100; - cur_cell_w = math.min(tag.number,list.w); - } else { - cur_cell_w = list.w; - } - } - } else { - if (table.depth>0) { - table.depth--; - } - if (table.depth==0) { - draw_x = left_gap = style.tag_list.level * 5 * list.font_w + BODY_MARGIN; - draw_w = list.w; - if (td_pos) draw_y = math.max(draw_y, tallest_cell_in_row); - draw_y = math.max(draw_y+style.cur_line_h, tallest_cell_in_row); - row_start_y = tallest_cell_in_row = draw_y; - style.cur_line_h = list.item_h; - } - } - } - if (table.depth>1) { - if (tag.is("tr")) && (tag.opened) NewLine(); - return; - } - - if (!secondrun) { - if (tag.is("tr")) { - if (tag.opened) { - table.cols.add(1); - } - colcount = 0; - } - if (tag.opened) if (tag.is("td")) || (tag.is("th")) { - if (!table.cols.count) { table.cols.add(1); colcount = 0;} - colcount++; - if (colcount) table.cols.set(table.cols.count-1, colcount); - //if (tag.get_number_of("colspan")) colcount += tag.number-1; - } - } else { - if (tag.is("tr")) { - if (tag.opened) { - if (td_pos) draw_y = math.max(draw_y, tallest_cell_in_row); - _TR_FIX: - if (draw_x==left_gap) && (draw_y==BODY_MARGIN) { - row_start_y = tallest_cell_in_row = draw_y; - } else { - row_start_y = tallest_cell_in_row = draw_y = draw_y + style.cur_line_h; - } - style.cur_line_h = list.item_h; - tr_pos++; - td_pos = 0; - } else { - draw_y = math.max(draw_y, tallest_cell_in_row); - td_pos = 0; - } - draw_x = left_gap = style.tag_list.level * 5 * list.font_w + BODY_MARGIN; - draw_w = list.w; - } - if (tag.is("td")) || (tag.is("th")) { - - if (!tr_pos) goto _TR_FIX; - - tallest_cell_in_row = math.max(draw_y+style.cur_line_h-list.item_h+1, tallest_cell_in_row); - style.cur_line_h = list.item_h; - if (tag.opened) - { - if (!td_pos) { - table.margin = list.w - cur_cell_w / 2 + BODY_MARGIN; - draw_x = left_gap = table.margin; - draw_w = cur_cell_w - BODY_MARGIN; - } else { - draw_x = left_gap = left_gap + draw_w; - draw_w = cur_cell_w - left_gap + table.margin - BODY_MARGIN; - } - - if (EAX = table.cols.get(tr_pos-1)-td_pos) { - draw_w /= EAX; - } else { - draw_y = row_start_y; - draw_x = left_gap = table.margin; - } - if (table.cols.get(tr_pos-1)-td_pos>1) && (tag.get_number_of("width")) { - if (strchr(tag.value, '%')) { - tag.number = cur_cell_w * tag.number / 100; // - } - if (tag.number < list.w - table.margin - left_gap) draw_w = tag.number; - - } - draw_y = row_start_y; - td_pos++; - } - } - } - if (draw_x > cur_cell_w) { - draw_x = left_gap = table.margin; - draw_w = cur_cell_w - table.margin - 23 - left_gap; - table.depth = 0; - NewLine(); - if (debug_mode) { - debugln("anomaly draw_x"); - canvas.DrawBar(0, draw_y, 20, 20, 0xFF0000); - } - } - //if (left_gap + draw_w > list.w) { - // draw_w = list.w - left_gap; - // if (debug_mode) debugln("anomaly draw_W"); - //} -} \ No newline at end of file diff --git a/browser/TWB/special.h b/browser/TWB/special.h deleted file mode 100644 index 9dae661..0000000 --- a/browser/TWB/special.h +++ /dev/null @@ -1,117 +0,0 @@ -char *unicode_symbols[]={ -"quot","\"", -"amp", "&", -"lt", "<", -"gt", ">", -"#183","\31", "middot", "\31", -"#149","-", -"#151","-", -"#160"," ", "nbsp", "\t", "emsp", " ", -"#169","(c)", "copy", "(c)", -"#171","<<", "laquo","<<", -"#174","(r)", "reg", "(r)", -"#187",">>", "raquo",">>", -"hellip", "...", - -"trade", "[TM]", -"bdquo", ",,", - -"minus", "-", -"ndash", "-", -"mdash", "-", //-- - -"rsquo", "'", "apos", "'", -"sect", "#", - -"ensp", " ", -"emsp13", " ", -"emsp14", " ", -"numsp", " ", -"puncsp", " ", -"thinsp", " ", - -"#1028", "\242", -"#1030", "I", -"#1031", "\244", - -"#8211", "-", -"#8212", "-", -"#8217", "'", -"#8220", "\"", -"#8222", "\"", "ldquo", "\"", -"#8221", "\"", "rdquo", "\"", -"#8470", "N", -"#8722", "-", -"#9642", "-", //square in the middle of the line -"#9658", ">", -"#9660", "v", -"#10094", "<", -"#10095", ">", -"#65122", "+", - -"#8594", "->", -"#8627", "->", - -"uarr", "^", -"darr", "v", -"rarr", "->", -"larr", "<-", - -"bull", "\31", -"percnt","%", - -"#xfeff", "", - -"times", "x", -"lowast","*", - -"#128154","<3", -"#128545",":(", - -0}; - - -unsigned char unicode_chars[] = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяЁёhг\243i\105\244\0"; - -bool GetUnicodeSymbol(dword _line, line_size, bufpos, buf_end) -{ - int i; - int code; - char special_code[10]; - bool white_end = false; - dword bufstart = bufpos; - - for (i=0; i<9; i++, bufpos++) - { - if (__isWhite(ESBYTE[bufpos])) {bufpos--; break;} - if (ESBYTE[bufpos] == ';') || (bufpos >= buf_end) break; - if (!strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#", ESBYTE[bufpos])) {bufpos--; break;} - special_code[i] = ESBYTE[bufpos]; - } - special_code[i] = '\0'; - - for (i=0; unicode_symbols[i]!=0; i+=2;) - { - if (!strcmp(#special_code, unicode_symbols[i])) - { - strncat(_line, unicode_symbols[i+1], line_size); - return bufpos; - } - } - - if (special_code[0]=='#') - { - code = atoi(#special_code + 1); - if (code>=0) && (code<=255) { - chrncat(_line, code, line_size); //NOT ALL ASCII CODES IN KOLIBRI ARE COMPATABLE WITH STANDARDS - return bufpos; - } - if (code>=1040) && (code<=1040+72) { - chrncat(_line, unicode_chars[code-1040], line_size); - return bufpos; - } - } - - chrncat(_line, '&', line_size); - return bufstart; -} diff --git a/browser/TWB/tag_list.h b/browser/TWB/tag_list.h deleted file mode 100644 index a8b3a00..0000000 --- a/browser/TWB/tag_list.h +++ /dev/null @@ -1,38 +0,0 @@ -struct LIST -{ - int level; - char ordered[5]; - int counter[5]; - void reset(); - void upd_level(); - int inc_counter(); - char order_type(); -}; - -void LIST::reset() -{ - level = 0; -} - -void LIST::upd_level(int direction, char type) -{ - if (direction == 1) && (level<5) { - level++; - counter[level] = 0; - ordered[level] = type; - } - if (direction == 0) && (level>0) { - level--; - } -} - -int LIST::inc_counter() -{ - counter[level]++; - return counter[level]; -} - -char LIST::order_type() -{ - return ordered[level]; -} diff --git a/browser/WebView.c b/browser/WebView.c index 4e1641a..a0e3b11 100644 --- a/browser/WebView.c +++ b/browser/WebView.c @@ -11,7 +11,10 @@ // // //===================================================// -#define MEMSIZE 1024 * 160 +// code + data + globals take ~320 KB, the rest is the stack: keep ~90 KB of it for +// the nested char[URL_SIZE+1] locals of @import and chains +#define MEMSIZE 1024 * 416 +#define PAGE_FAR 0x3FFFFFFF // page_scroll() to an end of the page #include "..\lib\gui.h" #include "..\lib\draw_buf.h" @@ -36,6 +39,7 @@ #include "const.h" #include "cache.h" +#include "cookies.h" #include "show_src.h" //===================================================// @@ -43,30 +47,28 @@ // DATA // // // //===================================================// -bool debug_mode = false; bool show_images = true; -bool source_mode = false; +bool show_css = true; // false: +
+
pinned right
+

header of the box

+
sidebar out of the flow, it must not push the main column down
+
main column, shifted right by a margin. It has to sit beside the sidebar and wrap inside its own 400 pixels.
+
this block is pulled up 30px by a negative top margin
+
file-one.zip8.21M
+
another-file-with-a-longer-name.rar640K
+
+

text after the box, in the normal flow

+ \ No newline at end of file diff --git a/browser/tests/basic.htm b/browser/tests/basic.htm new file mode 100644 index 0000000..720f6b9 --- /dev/null +++ b/browser/tests/basic.htm @@ -0,0 +1,30 @@ +Basic layout test + + +

Heading one

+

A paragraph with bold, underlined, struck, italic and a link to kolibrios.org. Some more words so that the paragraph wraps onto several lines when the window is not very wide, and even more words to be sure.

+

Heading two

+

Right aligned paragraph via CSS class.

+
A div with id box: light blue background, padding, 60% width. Red span inside.
+

Lists

+
  • first bullet
  • second bullet with code text
  • +
  • nested:
    1. one
    2. two
+
Blockquote text is indented from the left edge.
+
pre  keeps   spaces
+and line breaks
+    indented line
+
+

Centered paragraph with an image placeholder missing picture and icon.

+

Entities: <tag> & "quoted" © — «text»   end.

+ +

Last paragraph. anchor link

+ + + +
Descendant selector: green bold, inline style.
+ +

End of page.

diff --git a/browser/tests/calc.htm b/browser/tests/calc.htm new file mode 100644 index 0000000..6861719 --- /dev/null +++ b/browser/tests/calc.htm @@ -0,0 +1,20 @@ +calc() + +
no padding (0)
+
calc(1.25em - 1px) = 16px
+
calc(10px + 2 * 5px) = 20px
+
calc( -5px + 45px ) = 40px
+
padding shorthand calc(100px / 2) = 50px
+
calc(20px*3) = 60px
+
calc(100% - 20px): dropped, 0
+
CALC(2EM + 2PX) = 30px
+ diff --git a/browser/tests/checkbox.htm b/browser/tests/checkbox.htm new file mode 100644 index 0000000..8fcd640 --- /dev/null +++ b/browser/tests/checkbox.htm @@ -0,0 +1,34 @@ +Checkboxes + +

Native controls

+

unchecked   checked   + disabled

+

one   two   three

+

+

+ +

Spoiler: display

+ +
Hidden text that opens on click.
+ +

Spoiler: grid 0fr/1fr

+ +
Folded with grid-template-rows.
+ +

Slides

+
+

End.

+ diff --git a/browser/tests/csslink.htm b/browser/tests/csslink.htm new file mode 100644 index 0000000..8afb8f5 --- /dev/null +++ b/browser/tests/csslink.htm @@ -0,0 +1 @@ +css link

Heading

A note paragraph.

Plain paragraph.

diff --git a/browser/tests/cssvars.htm b/browser/tests/cssvars.htm new file mode 100644 index 0000000..9f0cedc --- /dev/null +++ b/browser/tests/cssvars.htm @@ -0,0 +1 @@ +

Vars

Fallback red.

Body text. diff --git a/browser/tests/details.htm b/browser/tests/details.htm new file mode 100644 index 0000000..e1b4af2 --- /dev/null +++ b/browser/tests/details.htm @@ -0,0 +1,12 @@ +details + +

1. Text summary:

+
Click to open

Hidden text of details 1.

+

2. Open by markup:

+
Open from the start

Text of details 2.

+

3. SVG-only summary (bbc.com without scripts):

+ +

End.

+ diff --git a/browser/tests/disptable.htm b/browser/tests/disptable.htm new file mode 100644 index 0000000..33264a6 --- /dev/null +++ b/browser/tests/disptable.htm @@ -0,0 +1,21 @@ +CSS display:table +

display:table with table-cell children (anonymous row)

+
+
left column, narrow, wraps a little
+
middle column, the wide one, with enough text in it that it has to wrap over several lines
+
right column, narrow too
+
+

display:table without any cells (plain block)

+
+

a heading

+

This div says display:table but has no cells at all. It must render like a block, not vanish.

+
+

end of page

+ diff --git a/browser/tests/entities.htm b/browser/tests/entities.htm new file mode 100644 index 0000000..a968ff9 --- /dev/null +++ b/browser/tests/entities.htm @@ -0,0 +1 @@ +

Fish & chips, a & b, 1 < 2 > 0, café © © &unknown; x

link »A <B> diff --git a/browser/tests/flex.htm b/browser/tests/flex.htm new file mode 100644 index 0000000..4ce54d0 --- /dev/null +++ b/browser/tests/flex.htm @@ -0,0 +1,38 @@ +flex +

1 three items in a row, content width

+
one
second item
third
+ +

2 flex: 1 - three equal columns

+
A
B with more text in it
C
+ +

3 header: logo left, links pushed right by margin-left: auto, centred vertically

+ + +

4 space-between and center

+
left
middle
right
+
centred
pair
+ +

5 wrap: 200px cards go onto new rows

+
card 1
card 2
card 3
card 4
card 5
card 6
+ +

6 stretch: the short item is as tall as the tall one

+
tall 60px
short
+ +

7 a long row squeezed: no letter-per-line columns

+
averyveryverylongwordthatcannotbreak
another long piece of text that wraps inside its item when the row is too narrow for everything
third item with text
+

end

+ diff --git a/browser/tests/floatcarry.htm b/browser/tests/floatcarry.htm new file mode 100644 index 0000000..e74250e --- /dev/null +++ b/browser/tests/floatcarry.htm @@ -0,0 +1,7 @@ +
+
float
1
2
3
4
5
6
7
8
9
10
11
12
+

1 a float reaching below its section: this short intro is beside it

+
2 next section
+

its text must be beside the float too, not below it (Wikipedia infobox and the sections after it)

+

3 below the float: full width again, more text more text more text more text more text more text

+
diff --git a/browser/tests/floats.htm b/browser/tests/floats.htm new file mode 100644 index 0000000..c04d697 --- /dev/null +++ b/browser/tests/floats.htm @@ -0,0 +1 @@ +

One

Column one text that wraps around a bit.

Two

Second column.

Three

Third column with more text than the others so it is taller.

Paragraph after the columns.

Full width float
A
B
C
D wraps to next row
diff --git a/browser/tests/fonts.htm b/browser/tests/fonts.htm new file mode 100644 index 0000000..9e38d7b --- /dev/null +++ b/browser/tests/fonts.htm @@ -0,0 +1,75 @@ +Font test + + +

The four fonts

+

There are two system fonts, 6x9 and 8x16, each one either as it is or doubled by the +kernel. Everything below is one of these four; nothing else can be drawn.

+ +
FONT_*kernelw×huprightitalicboldbold italic +
SMALL6x96×9 + Handgloves + Handgloves + Handgloves + Handgloves +
NORMAL8x168×14 + HandglovesHandglovesHandglovesHandgloves +
MEDIUM6x9 ×212×17 + Handgloves + Handgloves + Handgloves + Handgloves +
BIG8x16 ×216×27 + Handgloves + Handgloves + Handgloves + Handgloves +
+

The plain fonts take one 1px step across the middle of the line, the doubled ones a +step per third, 2px in all. If the doubled rows look overdone next to the plain ones, +or the plain ones too upright, those are the two numbers to change.

+ +

Where font-size switches over

+

A size only picks one of the four: 22px and up is BIG, 16px and up is MEDIUM, anything +else is NORMAL. Each pair below must look identical, and each pair must differ from the +next.

+

10px   15px +   → both NORMAL

+

16px   21px +   → both MEDIUM

+

22px   40px +   → both BIG

+

The keywords: xx-small + x-small + small + medium + large + x-large + xx-large +   → only the three "small" ones reach FONT_SMALL.

+ +

Tags that lean

+

i   em   cite   var   +dfn   and

address, which is a block

+

font-style: italic   +oblique   +an i turned back to normal

+

italic underline   italic strike   +an italic link

+ +

Ctrl+- and Ctrl++

+

Small print moves every slot one step down: NORMAL becomes 6x9, MEDIUM becomes 8x16, +BIG becomes the doubled 6x9, SMALL stays 6x9. Press Ctrl+- and the four rows of the first +table must still be four distinct sizes, and every italic must still lean.

+ +

Edges

+

A run leans only while there is a column to slide into; at the very edge of the canvas +it is drawn upright. Narrow the window over this line and watch where it stops.

+

iiiiiiiiii iiiiiiiiii iiiiiiiiii iiiiiiiiii iiiiiiiiii iiiiiiiiii iiiiiiiiii iiiiiiiiii iiiiiiiiii iiiiiiiiii iiiiiiiiii iiiiiiiiii

+ +

Inheritance and pre

+
A block set to italic: this sentence, this bold run, +and this span turned back to normal.
+
pre keeps the spacing    and still leans
+normal line for comparison
+ + diff --git a/browser/tests/grid.htm b/browser/tests/grid.htm new file mode 100644 index 0000000..f4887b3 --- /dev/null +++ b/browser/tests/grid.htm @@ -0,0 +1,28 @@ +grid + + +
one
two two two two two two two two two two two two two two
three
wide: 1 / -1
four
five
+
card 1
card 2
card 3
card 4
card 5
card 6
card 7
+
120px
2fr
1fr
+ diff --git a/browser/tests/gridareas.htm b/browser/tests/gridareas.htm new file mode 100644 index 0000000..8b3e1ef --- /dev/null +++ b/browser/tests/gridareas.htm @@ -0,0 +1,32 @@ + +
+
1 grid-template-areas: notice over both columns
+
Contents
(Top)
Development
Features
Reception
+
+

Title

+
Article Talk
+
Tools column
spans three
rows
and
is
tall
at
the right
+
Article text in the content area: left of the tools column, below the toolbar.
+
+
Footer over both columns
+
+
+
Legal
third column, first in the source
+
2 freebsd.org footer: copyright over all three columns
+
Logo
+
About
second column
+
+ diff --git a/browser/tests/iswhere.htm b/browser/tests/iswhere.htm new file mode 100644 index 0000000..8e1c238 --- /dev/null +++ b/browser/tests/iswhere.htm @@ -0,0 +1,35 @@ +:is() / :where() + +

Every line must be green (none red, none white)

+

1. :is(h3, h4) span

+

2. :is(h3, h4) span

+

3. .box :where(.a, .b)

4. .box :where(.a, .b)

+

5. :where(#w1) red loses to p.w green (where counts 0)

+
  • 6. li:is(.x .y)
+

7. .list > :where(*)

+

8. p:not(:where(.skip, .other *))

9. .skip: not matched (inline green)

+
  1. 10. :is(ul, ol) :is(.deep)
+

11. .c:is(p) = p.c

+

12. div:is(p).never never matches (inline green)

+

13. :where(.m1, .m2):first-child

+

14. :IS(.up) upper case

+

15. a later .pw :where(p) red loses to .pw p green

+

16. .k:not(:where(b,i,u,...14 tags)) on a p

+ diff --git a/browser/tests/minheight.htm b/browser/tests/minheight.htm new file mode 100644 index 0000000..d0ff268 --- /dev/null +++ b/browser/tests/minheight.htm @@ -0,0 +1,27 @@ +min-height / max-height + +

1. min-height:120px, one line - box 120px tall (orange):

+
hero
+

2. min-height:20px, three lines - as tall as the text:

+
line one
line two
line three
+

3. max-height:0 + overflow:hidden - nothing between the two rulers:

+

+

4. max-height:40px + overflow:hidden, six lines - two lines are left:

+
cut 1
cut 2
cut 3
cut 4
cut 5
cut 6
+

5. height:200px + max-height:80px - 80px tall (green):

+
capped
+

6. min-height:150px + max-height:60px - 60px tall (blue):

+
both
+

7. max-height:30px without overflow - all four lines are shown:

+
a
b
c
d
+

end

+ diff --git a/browser/tests/nav.htm b/browser/tests/nav.htm new file mode 100644 index 0000000..5bac65e --- /dev/null +++ b/browser/tests/nav.htm @@ -0,0 +1,16 @@ + + +

Text after nav.

+
  • Normal list
  • Second
+ diff --git a/browser/tests/nbsp8.htm b/browser/tests/nbsp8.htm new file mode 100644 index 0000000..c4e072b --- /dev/null +++ b/browser/tests/nbsp8.htm @@ -0,0 +1 @@ +nbsp utf8
1.    x

 

a b ┬а c

diff --git a/browser/tests/noscript.htm b/browser/tests/noscript.htm new file mode 100644 index 0000000..2a2aed4 --- /dev/null +++ b/browser/tests/noscript.htm @@ -0,0 +1,7 @@ + +

1 lazy picture + noscript copy: ONE picture below

+
+
+

2 noscript with text is shown:

+
+ diff --git a/browser/tests/pct.htm b/browser/tests/pct.htm new file mode 100644 index 0000000..e7a361d --- /dev/null +++ b/browser/tests/pct.htm @@ -0,0 +1 @@ +
left cell with text[btn1] [btn2]
seventyseventy again
half of an auto tablerest
diff --git a/browser/tests/pctheight.htm b/browser/tests/pctheight.htm new file mode 100644 index 0000000..444b447 --- /dev/null +++ b/browser/tests/pctheight.htm @@ -0,0 +1,34 @@ + + +Percent heights + + + +

1. The picture fits the green box (255px tall) and is centred in it:

+ + +

2. The red strip is half of the 120px pink box:

+
50%
+ +

3. A percentage of an auto height is dropped: the blue strip is one line:

+
50% of auto
+ +

4. max-height: 60px on a picture:

+
tire
+ + diff --git a/browser/tests/seltest.htm b/browser/tests/seltest.htm new file mode 100644 index 0000000..1b56103 --- /dev/null +++ b/browser/tests/seltest.htm @@ -0,0 +1,17 @@ +selectors and positioning +

1 three part selector, single class (row must be GREEN)

+ +

2 multi class li (GREEN too)

+ +

3 child combinator: the span sits inside the link, so it must NOT turn red, +and the size must be pinned to the right of the green row

+ +

4 a real direct child span of the li (must be RED)

+
  • direct child span
+ \ No newline at end of file diff --git a/browser/tests/showroom.htm b/browser/tests/showroom.htm new file mode 100644 index 0000000..c87c6d4 --- /dev/null +++ b/browser/tests/showroom.htm @@ -0,0 +1,58 @@ + +TEST + + + +

+

Demoscene

+Amiga Music Preservation +Pouet.net +scene.org +menuetos.net + +

Tools

+Total Hardware 1999 +Vodafone Test Files +chipmunk.nl + +

Test pages

+pixeljoint.com +www.kernel.org +github +lospec.com +browser.engineering +145 images + + + + diff --git a/browser/tests/sitetest/.gitignore b/browser/tests/sitetest/.gitignore new file mode 100644 index 0000000..89f9ac0 --- /dev/null +++ b/browser/tests/sitetest/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/browser/tests/sitetest/ref.png b/browser/tests/sitetest/ref.png new file mode 100644 index 0000000..0c1c1d4 Binary files /dev/null and b/browser/tests/sitetest/ref.png differ diff --git a/browser/tests/sitetest/sites.txt b/browser/tests/sitetest/sites.txt new file mode 100644 index 0000000..ef95a56 --- /dev/null +++ b/browser/tests/sitetest/sites.txt @@ -0,0 +1,101 @@ +# group url (modern = current JS-heavy sites, old = classic / lightweight markup) +modern https://www.google.com +modern https://www.youtube.com +modern https://www.facebook.com +modern https://x.com +modern https://www.instagram.com +modern https://www.linkedin.com +modern https://www.reddit.com +modern https://www.amazon.com +modern https://www.netflix.com +modern https://www.microsoft.com +modern https://www.apple.com +modern https://github.com +modern https://stackoverflow.com +modern https://en.wikipedia.org/wiki/Main_Page +modern https://www.bbc.com +modern https://edition.cnn.com +modern https://www.nytimes.com +modern https://www.theguardian.com +modern https://www.twitch.tv +modern https://open.spotify.com +modern https://www.paypal.com +modern https://www.ebay.com +modern https://www.pinterest.com +modern https://www.tumblr.com +modern https://medium.com +modern https://www.dropbox.com +modern https://zoom.us +modern https://slack.com +modern https://www.notion.so +modern https://www.figma.com +modern https://www.airbnb.com +modern https://www.booking.com +modern https://www.imdb.com +modern https://www.espn.com +modern https://www.walmart.com +modern https://www.target.com +modern https://www.etsy.com +modern https://www.quora.com +modern https://discord.com +modern https://openai.com +modern https://www.adobe.com +modern https://www.cloudflare.com +modern https://vercel.com +modern https://stripe.com +modern https://www.shopify.com +modern https://www.nike.com +modern https://www.washingtonpost.com +modern https://weather.com +modern https://www.yahoo.com +modern https://www.bing.com +old https://news.ycombinator.com +old https://www.craigslist.org +old https://slashdot.org +old https://www.drudgereport.com +old https://lite.cnn.com +old https://text.npr.org +old https://www.gnu.org +old https://www.kernel.org +old https://www.debian.org +old https://www.openbsd.org +old https://www.w3.org +old https://www.ietf.org +old https://www.rfc-editor.org +old http://paulgraham.com +old https://danluu.com +old https://www.berkshirehathaway.com +old https://www.spacejam.com/1996/ +old https://www.lingscars.com +old https://archive.org +old http://www.textfiles.com +old https://www.sqlite.org +old https://www.lua.org +old https://www.python.org +old https://www.perl.org +old https://www.php.net +old https://www.apache.org +old https://www.freebsd.org +old https://www.netbsd.org +old http://www.slackware.com +old https://www.gentoo.org +old http://www.catb.org/~esr/ +old https://norvig.com +old https://www.arngren.net +old http://68k.news +old http://frogfind.com +old https://wiby.me +old https://lobste.rs +old https://www.metafilter.com +old https://www.fark.com +old https://kottke.org +old https://daringfireball.net +old https://xkcd.com +old https://www.penny-arcade.com +old https://www.snopes.com +old https://www.weather.gov +old https://www.fileformat.info +old https://www.jwz.org +old https://www.7-zip.org +old https://www.virtualbox.org +old https://www.nasa.gov diff --git a/browser/tests/sitetest/sitetest.py b/browser/tests/sitetest/sitetest.py new file mode 100644 index 0000000..a3ddee9 --- /dev/null +++ b/browser/tests/sitetest/sitetest.py @@ -0,0 +1,190 @@ +"""WebView site smoke test: opens every URL of sites.txt in the KolibriOS test VM, +waits for "Done in" in the status bar, saves a screenshot and records load time, +crashes (the WebView window is gone) and hangs (no "Done" within the timeout). + + python sitetest.py VMDIR [OUTDIR] [--from N] [--only substr] [--timeout SEC] [--report] + +VMDIR holds vm.py of the test VM (see .vm/vm.py): its monitor port is used for keys +and screendumps. The VM must be booted with AUTORUN starting WebView (reset restores it). +Results: OUTDIR/results.json, OUTDIR/report.html (screenshots embedded), OUTDIR/shots/NN.png +--report rebuilds report.html from results.json without the VM. +""" +import sys, os, time, json, argparse, html, warnings, io, base64 +warnings.filterwarnings("ignore") +from PIL import Image, ImageChops + +HERE = os.path.dirname(os.path.abspath(__file__)) +ap = argparse.ArgumentParser() +ap.add_argument("vmdir") +ap.add_argument("out", nargs="?", default=os.path.join(HERE, "out")) +ap.add_argument("--from", dest="start", type=int, default=1) +ap.add_argument("--only", default="") +ap.add_argument("--timeout", type=float, default=90) +ap.add_argument("--report", action="store_true", help="only rebuild report.html from results.json") +opt = ap.parse_args() +sys.path.insert(0, opt.vmdir) +import vm + +WIN = (80, 0, 930, 768) # WebView window at its default position +BACK_BTN = (92, 32, 120, 60) # toolbar "back" button: gone = the window is gone +REFRESH = (163, 32, 190, 60) # refresh / stop button: stop icon while loading +DONE = (90, 750, 136, 762) # "Done in" at the start of the status bar +STATUS = (90, 750, 400, 762) +TITLE = (84, 2, 700, 22) +CONTENT = (84, 92, 926, 745) + +def grab(name="_poll"): + png = os.path.join(opt.vmdir, name + ".png") + if os.path.exists(png): os.remove(png) + s = vm._conn() + s.sendall(("screendump " + png + " -f png\n").encode()) + t = time.time() + while time.time() - t < 5: + time.sleep(0.1) + try: + im = Image.open(png); im.load(); s.close(); return im.convert("RGB") + except Exception: pass + s.close() + raise RuntimeError("screendump failed") + +def ink(im): + """share of content pixels that differ from the dominant (background) colour""" + c = im.crop(sh(CONTENT)).resize((210, 163)) + px = list(c.getdata()); bg = max(set(px), key=px.count) + return round(100.0 * sum(1 for p in px if sum(abs(x - y) for x, y in zip(p, bg)) > 30) / len(px), 1) + +DX = 0 # window x shift from the reference position + +def sh(box, dx=None): + dx = DX if dx is None else dx + return (box[0] + dx, box[1], box[2] + dx, box[3]) + +def find_window(im): + """x shift of the WebView window (it opens at a varying place after a reboot), None if gone""" + ref = REF.crop(BACK_BTN) + for dx in [0] + [d for d in range(-80, 150) if d]: + if BACK_BTN[0] + dx < 0: continue + d = ImageChops.difference(im.crop(sh(BACK_BTN, dx)), ref).convert("L") + if sum(1 for p in d.getdata() if p > 40) <= 8: return dx + return None + +def webview_alive(im): + global DX + dx = find_window(im) + if dx is None: return False + DX = dx; return True + +def same(a, b, box, tol=0): + """box is in reference coordinates: a is shifted by DX, b is REF (unshifted) or a live shot""" + bb = box if b is REF else sh(box) + d = ImageChops.difference(a.crop(sh(box)), b.crop(bb)).convert("L") + return sum(1 for p in d.getdata() if p > 40) <= tol + +def reboot(): + print(" rebooting VM...", flush=True) + vm.cmd("system_reset") + time.sleep(20) + t = time.time() + while time.time() - t < 120: + time.sleep(2) + try: + im = grab() + if webview_alive(im): time.sleep(3); return True + except Exception: pass + return False + +def open_url(url): + # Ctrl+A in the omnibox is not always taken: select the old address with End, + # Shift+Home as well, typing then replaces it + vm.cmd("sendkey f6", 0.15); vm.cmd("sendkey ctrl-a", 0.15) + vm.cmd("sendkey end", 0.1); vm.cmd("sendkey shift-home", 0.15) + vm.type_text(url); vm.cmd("sendkey ret", 0.05) + +def run_one(n, group, url): + before = grab() + webview_alive(before) + open_url(url) + t0 = time.time() + r = dict(n=n, group=group, url=url, status="timeout", seconds=None) + changed = False + last = before + while True: + im = grab(); el = time.time() - t0 + last = im + if not webview_alive(im): + time.sleep(1.5); im = grab() # a window redraw can blank the toolbar for a moment + if not webview_alive(im): + r.update(status="crash", seconds=round(el, 1)); last = im; break + # "Done in N sec (M milisec)" of the previous page stays until the new one starts: + # wait for a different status text, or a different title after a while + if not same(im, before, TITLE): changed = True + if same(im, REF, DONE, 4) and (not same(im, before, STATUS) or (changed and el > 8)): + r.update(status="ok", seconds=round(el, 1)); break + if el > opt.timeout: + r["loading_icon"] = not same(im, REF, REFRESH, 8) + break + shot = os.path.join(opt.out, "shots", "%03d.png" % n) + last.crop(sh(WIN)).save(shot) + r["shot"] = "shots/%03d.png" % n + r["ink"] = ink(last) if r["status"] != "crash" else 0 + if r["status"] == "ok" and r["ink"] < 0.5: r["status"] = "blank" + return r + +def report(res): + tot = len(res); by = {} + for r in res: by[r["status"]] = by.get(r["status"], 0) + 1 + def stats(g): + rs = [r for r in res if r["group"] == g] + ok = [r["seconds"] for r in rs if r["status"] in ("ok", "blank")] + ok.sort() + med = ok[len(ok) // 2] if ok else 0 + return "%s: %d sites, ok %d, blank %d, crash %d, timeout %d, median %.1fs" % (g, len(rs), + sum(r["status"] == "ok" for r in rs), sum(r["status"] == "blank" for r in rs), + sum(r["status"] == "crash" for r in rs), sum(r["status"] == "timeout" for r in rs), med) + def embed(shot): + # the screenshot inside the page as a JPEG: report.html is one file to send around + try: + im = Image.open(os.path.join(opt.out, shot)).convert("RGB") + except OSError: + return "" + b = io.BytesIO(); im.save(b, "JPEG", quality=75) + return "data:image/jpeg;base64," + base64.b64encode(b.getvalue()).decode() + rows = [] + for r in res: + rows.append('
%d. %s
%s · %s · ink %s%%
' % ( + r["status"], embed(r["shot"]), r["n"], html.escape(r["url"]), r["status"].upper(), + ("%.1f s" % r["seconds"]) if r["seconds"] is not None else ">%ds" % opt.timeout, r["ink"])) + page = """WebView site test +

WebView site test

%s

%s

%s

%s
""" % ( + time.strftime("%Y-%m-%d %H:%M"), stats("modern"), stats("old"), "\n".join(rows)) + open(os.path.join(opt.out, "report.html"), "w", encoding="utf-8").write(page) + +jf = os.path.join(opt.out, "results.json") +if opt.report: + report(json.load(open(jf))) + print("report:", os.path.join(opt.out, "report.html")) + sys.exit(0) +os.makedirs(os.path.join(opt.out, "shots"), exist_ok=True) +REF = Image.open(os.path.join(HERE, "ref.png")).convert("RGB") +sites = [] +for l in open(os.path.join(HERE, "sites.txt")): + l = l.strip() + if l and not l.startswith("#"): + g, u = l.split(None, 1); sites.append((len(sites) + 1, g, u)) +vm.move_to(1010, 700) # keep the pointer out of the screenshots +res = {r["n"]: r for r in json.load(open(jf))} if os.path.exists(jf) else {} +for n, g, u in sites: + if n < opt.start or opt.only not in u: continue + print("%3d %-45s" % (n, u), end=" ", flush=True) + r = run_one(n, g, u) + print(r["status"], r["seconds"], "ink", r["ink"], flush=True) + res[n] = r + json.dump([res[k] for k in sorted(res)], open(jf, "w"), indent=1) + report([res[k] for k in sorted(res)]) + if r["status"] in ("crash", "timeout"): + if not reboot(): print("VM did not come back"); sys.exit(1) +print("report:", os.path.join(opt.out, "report.html")) diff --git a/browser/tests/six.htm b/browser/tests/six.htm new file mode 100644 index 0000000..3f96b35 --- /dev/null +++ b/browser/tests/six.htm @@ -0,0 +1,38 @@ +six features +

1 max-width + margin auto: the blue box is 400px and centred

+
centred box with max-width 400px, text wraps inside it
+
pushed right
+

Search

+wiby.org +frogfind.com +duckduckgo.com + +

News

+osnews.com +hackaday.com +slashdot.org +news.ycombinator.com +habr.com + +

Oldware

+Aminet +majorgeeks.com +macintoshgarden.org +macos9lives.com +dosdays.co.uk +GUI Gallery +games.freebasic.net + +

Cyrillic

+dgmag.in old-dos.ru retrolib.info +artcon.ru nostalgy.net.ru acmp.ru +opennet LOR +samlib.ru/b +os-menuet.narod.ru coolthemes.narod.ru + +
table with margin: auto
+ +

2 lazy images: all three must show the picture

+ + + + +

3 pseudo classes: red link (a:link), list with first yellow and last green

+
  • first
  • middle
  • last
+ +

4 height: a grey 60px strip below, empty

+
+ +

5 floats without width: tabs in a row, one on the right

+
  • Read
  • Edit
  • History
  • Search
+

text after the floats

+ +

6 noscript content is shown:

+ + diff --git a/browser/tests/tables.htm b/browser/tests/tables.htm new file mode 100644 index 0000000..d51890f --- /dev/null +++ b/browser/tests/tables.htm @@ -0,0 +1,26 @@ +Tables test +

Simple table with border

+ + + + +
NameSizeDescription
WebView24 KBThe browser itself, a fairly long description cell that should wrap when needed
dl5 KBDownloader
+

colspan / rowspan, width 100%, bgcolor

+ + + + +
Header spanning three columns
spans
two
rows
r1 c2r1 c3 (40%)
r2 c2r2 c3
+

Nested tables

+ + + + +
outer 1
inner ainner b
inner wide
outer 2plain cell
+

Layout table without border (menu | content)

+ + + +
Menu one
Menu two
Menu three
Content column text that is long enough to wrap several times inside the second column of the layout table so we can see the widths were distributed properly between the two columns.
+

Text after the tables.

+ diff --git a/browser/tests/test.css b/browser/tests/test.css new file mode 100644 index 0000000..4ffcc45 --- /dev/null +++ b/browser/tests/test.css @@ -0,0 +1,2 @@ +h1 { color: #c00; } +.note { background: #ffe; font-weight: bold; } diff --git a/browser/tests/unquoted.htm b/browser/tests/unquoted.htm new file mode 100644 index 0000000..99442e6 --- /dev/null +++ b/browser/tests/unquoted.htm @@ -0,0 +1,8 @@ +Unquoted attrs +

1 Pouet.net should be a link

+

2 vetusware.com should be a link

+

3 Scene.org quoted should be a link

+

4 self-closing with a space: after the image

+

5
line after a br

+

6 Aminet, two unquoted attrs

+ \ No newline at end of file diff --git a/browser/tests/webp.htm b/browser/tests/webp.htm new file mode 100644 index 0000000..79917b4 --- /dev/null +++ b/browser/tests/webp.htm @@ -0,0 +1,8 @@ +webp twin +

1. src=...jpg.webp (should load the .jpg twin):

+webp twin +

2. src=...jpg directly:

+plain jpg +

3. srcset only, first entry .jpg.webp:

+srcset twin + diff --git a/browser/tests/welcome_en.htm b/browser/tests/welcome_en.htm new file mode 100644 index 0000000..2343d11 --- /dev/null +++ b/browser/tests/welcome_en.htm @@ -0,0 +1,97 @@ + + + +Welcome notes + + + +

+ Welcome to KolibriOS +

+
+Dear friends,
+
+What you see now is the most compact operating system in the whole world. KolibriOS is a piece of programming art that is based on logic and coffee.
+
+As no one reads manuals, I'm here to help with the most common issues that new users of KolibriOS are facing. This document is also required reading for any YouTube blogger who wants to make a review of the system :)
+
+

Floppy version

+The asterisk [*] in the Menu means the app isn't available in the Floppy version, only in the Big distro (Flash/ISO/UEFI-HDD). Not all programs are available via Menu to encourage you to explore KolibriOS files by yourself. + +

If you have problems with USB

+Only USB 1.1 and USB 2.0 ports are supported, not USB 3+. And yep, the USB driver is written entirely in assembler. Some motherboards have options like "USB Legacy Support" or something similar, which may help the system detect your device. Also, some ports on your motherboard may work while others may not, only G-d knows why. So, play with the BIOS/UEFI settings and try different ports. + +

Sound

+List of supported devices: +- Most AC97 cards: Intel, nForce, VIA +- A variety of HD Audio devices: Intel, Realtek... +- SiS PCI audio (SiS 7012) +- Creative Labs EMU10K1X (Dell OEM) +- ForteMedia FM801 +- ISA Sound Blaster 16 + Only THIS particular model works, + not other cards that have software SB16-emulation + +

Video

+By default, you can change the resolution only on the blue boot screen. The modes listed there are provided by the video card's BIOS. It is impossible to 'simply add additional screen modes to the list'. If video card's BIOS doesn't provide some modes then you need a driver. It is not possible to write a generic video card driver. Separate driver must be written for each specific video card model. +Intel and AMD drivers for some video cards are ported from Linux and are available in the Big distro. + +Check SYSPANEL. Features: ability to change resolution in KolibriOS, hardware cursor (not actual anymore), bit-blit operations acceleration. + +

Wi-Fi

+Not supported. You should know that the Wi-Fi specification is so complex that it is almost impossible to write the code for it from scratch. Only big OSes like Windows / Linux / BSD have it. macOS and Haiku just ported the BSD drivers. I don't say porting them to KolibriOS is not possible, but the required effort is going to be tremendous. + +

LAN/Ethernet

+List of supported devices: +- 3c59x 3Com Fast EtherLink (3c59x/450/555/556/575/900/905/980) +- ar81xx Atheros 8131/8132/8151/8152/8161/8162/8171/8172 + Killer E2200/E2400/E2500 +- dec21x4x* Accton EN1207B-TX, DEC 21140, ..., Virtual PC +- forcedeth* NVIDIA nForce chipset integrated Ethernet +- i8255x Intel PRO/100 (i8255x, i82562, i82801, ...), QEMU +- i8254x Intel PRO/1000 (i8254x), VirtualBox, Bochs, QEMU +- mtd80x Myson MTD803 +- pcnet32 AMD PCnet (79c970, 79c978, 79c973, ...), VirtualBox, QEMU +- r6040 RDC Semiconductor R6040 +- rhine VIA Rhine II and III +- rtl8029 Realtek 8029/8019, NE2000 PCI, Bochs, QEMU +- rtl8139 Realtek 8139 and clones, QEMU +- rtl8169 Realtek 8110/8111/8167/8168/8169 +- sis900 SiS900, 7016 + +* - in development + +Blessed be our Flemish friend Jeffrey, who has written most of the Network stuff by himself: stack, drivers, and a lot of apps (except WebView). + +

WebView

+It was never intended to be a browser, but rather a Help Center that uses HTML markup. It fits this purpose well, doesn't it? According to current WebView's codebase (that was my first app ever) it will never become a normal web browser. Please help us and write/port something good. For example, it would be nice to try to finish the NetSurf port. + +

How did you achieve so much while being so small?

+- Kernel, almost all drivers and most applications are written in assembly language +- Files in IMG are packed via the LZMA algorithm from 7-Zip. They are unpacked to RAM when accessed +- Most of the images are manually optimized: posterization, dithering and other techniques were used +- Files names in IMG fit 8.3 naming convention (name.extension) which doesn't use additional FAT12 space +- Image files are combined into a single file +- Compact binary header of applications, no sections +- Resources are reused, and file duplication is avoided, for example: + ~ Many apps use shared libraries + ~ KFM2 and Eolite are a single app, just with different appearance + ~ APP+, Games Center and System Panel are a single app also + ~ Memory Blocks game uses system icons as images + + +See more http://wiki.kolibrios.org/wiki/Hardware_Support +
+ +

Last words

+We want to express our gratitude to every KolibriOS contributor. Also, we would like to give special thanks to Ville M. Turjanmaa, the author of MenuetOS, the predecessor of our system. + +Finally, we want to thank you, brave and curious users, as we are doing it all for you! + +Have fun! + +Sincerely yours, +KolibriOS Team + +You have to use Big Distro to see this image! + \ No newline at end of file diff --git a/c--/c--.exe b/c--/c--.exe new file mode 100644 index 0000000..3d5eed8 Binary files /dev/null and b/c--/c--.exe differ diff --git a/downloader/const.h b/downloader/const.h index a06ea48..fd447e7 100644 --- a/downloader/const.h +++ b/downloader/const.h @@ -21,6 +21,8 @@ #define T_FILE_SAVED_AS "'Менеджер загрузок\nФайл сохранен как %s' -Dt" #define T_FILE_NOT_SAVED "'Менеджер загрузок\nОшибка! Файл не может быть сохранен как\n%s' -Et" + #define T_STATUS_BROKEN "Загрузка прервана, файл не сохранён. " + #define T_ERROR_DOWNLOAD_BROKEN "'Менеджер загрузок\nСоединение оборвалось до конца файла.\nНичего не сохранено.' -Et" #define T_ERROR_STARTING_DOWNLOAD "'Невозможно начать скачивание.\nПроверьте введенный путь и соединение с Интернетом.' -E" char accept_language[]= "Accept-Language: ru\n"; #else @@ -44,6 +46,8 @@ #define T_FILE_SAVED_AS "'Download manager\nFile saved as %s' -Dt" #define T_FILE_NOT_SAVED "'Download manager\nError! Can\96t save file as %s' -Et" + #define T_STATUS_BROKEN "Download interrupted, nothing saved. " + #define T_ERROR_DOWNLOAD_BROKEN "'Download manager\nThe connection was lost before the end of the file.\nNothing was saved.' -Et" #define T_ERROR_STARTING_DOWNLOAD "'Error while starting download process.\nCheck entered path and Internet connection.' -E" char accept_language[]= "Accept-Language: en\n"; #endif diff --git a/downloader/dl.c b/downloader/dl.c index bd5dc80..267cd32 100644 --- a/downloader/dl.c +++ b/downloader/dl.c @@ -13,6 +13,12 @@ #include "const.h" bool open_file = false; +bool invisible = false; + +// a hidden dl gives up by itself: the one who started it must not kill it, +// the kernel does not close the sockets of a killed process +#define INVISIBLE_TIMEOUT 2500 // 1/100 s +dword start_time; dword speed; @@ -58,7 +64,7 @@ void main() sc.get(); InitDownload(); - if (param) { + if (param[0]) { path_pointer = #param; //save path specified? Example: https://example.com/file|/sys/media/ac97snd if (save_path_separator_pos = strchr(#param, '|')) { @@ -66,11 +72,19 @@ void main() strcpy(#filepath, save_path_separator_pos+1); ESBYTE[save_path_separator_pos] = '\0'; } - if (streqrp(#param, "-e ")) { + // invisible window + if (!strncmp(#param, "-i ", 3)) { + autoclose.checked = true; + invisible = true; + path_pointer += 3; + } + // autoclose + if (!strncmp(#param, "-e ", 3)) { autoclose.checked = true; path_pointer += 3; } - if (streqrp(#param, "-eo ")) { + // autoclose and open file + if (!strncmp(#param, "-eo ", 4)) { autoclose.checked = true; open_file = true; path_pointer += 4; @@ -95,7 +109,8 @@ void main() } @SetEventMask(EVM_REDRAW + EVM_KEY + EVM_BUTTON + EVM_MOUSE + EVM_MOUSE_FILTER + EVM_STACK); - loop() switch(@WaitEvent()) + start_time = GetStartTime(); + loop() switch(@WaitEventTimeout(10)) { case evMouse: edit_box_mouse stdcall (#ed); break; case evButton: ProcessButtonClick(@GetButtonID()); break; @@ -151,6 +166,8 @@ void DrawDlButton(int x, id, t) void DefineWindow() { + if (invisible) return; + sc.get(); pb.frame_color = ed.border_color = ed.focus_border_color = sc.line; DefineAndDrawWindow(110 + random(300), 100 + random(300), WIN_W+9, @@ -161,6 +178,9 @@ void DefineWindow() void DrawWindow() { #define BUT_Y 58; + + if (invisible) return; + if (!http.transfer) && (!filepath) { DrawBar(GAPX, 102, WIN_W - GAPX - GAPX+1, 24+1, sc.work); DeleteButton(BTN_DIR); @@ -200,6 +220,7 @@ void StartDownloading() pb.back_color = 0xFFFfff; if (!strncmp(#uEdit,"https://",8)) { if (!proxy_address[0]) { + if (invisible) DlExit(); pb.progress_color = PB_COL_ERROR; notify("'HTTPS URLs require proxy_address in app.ini' -E"); StopDownloading(); @@ -208,7 +229,7 @@ void StartDownloading() return; } strcpy(#get_url, #proxy_address); - strncat(#get_url, #uEdit, URL_SIZE); + strcat_proxy_site(#get_url, #uEdit, sizeof(get_url)); } else { strcpy(#get_url, #uEdit); } @@ -220,6 +241,7 @@ void StartDownloading() DrawWindow(); } else { pb.progress_color = PB_COL_ERROR; + if (invisible) DlExit(); notify(T_ERROR_STARTING_DOWNLOAD); StopDownloading(); EditBox_UpdateText(#ed, ed_focus + ed_always_focus); @@ -255,10 +277,18 @@ void DrawDownloadingProgress() WriteTextWithBg(GAPX, pb.top + 22, 0xD0, sc.work_text, active_status, sc.work); } +// leave without a socket behind: a connection nobody frees keeps getting data for a +// thread that is gone ("SOCKET_notify: error finding thread" on the debug board) +void DlExit() +{ + StopDownloading(); + ExitProcess(); +} + void StopDownloading() { - http.stop(); - if (http.content_pointer) http.content_pointer = free(http.content_pointer); + http.stop(); // http_free() also releases content_ptr, do not free it twice + http.content_pointer = 0; http.content_received = http.content_length = 0; } @@ -275,6 +305,11 @@ void InitDownload() void MonitorProgress() { char redirect_url[URL_SIZE]; + dword t, got_all; + if (invisible) { + t = GetStartTime() - start_time; + if (t > INVISIBLE_TIMEOUT) DlExit(); + } if (http.transfer <= 0) return; http.receive(); if (!http.content_length) http.content_length = http.content_received * 20; //MOVE? @@ -285,6 +320,7 @@ void MonitorProgress() if (http.status_code >= 300) && (http.status_code < 400) { redirect_count++; if (redirect_count >= 5) { + if (invisible) DlExit(); notify(T_ERROR_TOO_MANY_REDIRECTS); ProcessButtonClick(BTN_STOP); EditBox_UpdateText(#ed, ed_focus + ed_always_focus); @@ -297,15 +333,38 @@ void MonitorProgress() StartDownloading(); return; } else { + // a hidden downloader must not store an error page as the requested file + if (invisible) && (http.status_code >= 400) DlExit(); + got_all = http.flags & HTTP_GOT_ALL_DATA; + if (!got_all) { + DownloadBroken(); + return; + } CompleteDownload(); } } } +// The transfer ended before the whole file came: libHTTP gave up after 5 s without +// data, the connection dropped, or the server closed it before Content-Length. +// Nothing is saved: a cut archive or picture would pass for the real file. A hidden +// dl just leaves (WebView sees no file and treats it as a failed download). +void DownloadBroken() +{ + if (invisible) DlExit(); + StopDownloading(); + pb.progress_color = PB_COL_ERROR; + progressbar_draw stdcall(#pb); + active_status = T_STATUS_BROKEN; + notify(T_ERROR_DOWNLOAD_BROKEN); + EditBox_UpdateText(#ed, ed_focus + ed_always_focus); + DrawWindow(); +} + void CompleteDownload() { SaveFile(0); - if (autoclose.checked) ExitProcess(); + if (autoclose.checked) DlExit(); pb.value = pb.max; pb.progress_color = PB_COL_COMPLETE; StopDownloading(); @@ -318,6 +377,7 @@ void SaveFile(int attempt) int i; char notify_message[4296]; char file_name[URL_SIZE+96]; + dword filename_pos = 0; //if save_path is not set then we have to combine it if (!save_path_set) { @@ -336,11 +396,22 @@ void SaveFile(int attempt) //Content-Disposition: attachment; filename="RealFootball_2018_Nokia_5800_EN_IGP_EU_TS_101.zip" if (http.header_field("content-disposition", #file_name, sizeof(file_name))) { - if (EDX = strstr(#file_name,"filename=\"")) { - strcat(#filepath, EDX+10); - ESBYTE[strchr(#filepath,'\"')] = '\0'; + filename_pos = strstr(#file_name,"filename="); + if (filename_pos) { + filename_pos += 9; + if (ESBYTE[filename_pos] == '\"') { + // filename="name.ext" + filename_pos++; + if (EAX = strchr(filename_pos, '\"')) ESBYTE[EAX] = '\0'; + } else { + // filename=name.ext; other=param + if (EAX = strchr(filename_pos, ';')) ESBYTE[EAX] = '\0'; + if (EAX = strchr(filename_pos, ' ')) ESBYTE[EAX] = '\0'; + } + strcat(#filepath, filename_pos); } - } else { + } + if (!filename_pos) { // Clean all slashes at the end strcpy(#file_name, #uEdit); while (file_name[strlen(#file_name)-1] == '/') { @@ -367,7 +438,7 @@ void SaveFile(int attempt) miniprintf(#notify_message, T_FILE_NOT_SAVED, #filepath); } - if (!autoclose.checked) notify(#notify_message); + notify(#notify_message); if (!strcmpi(#filepath+strlen(#filepath)-4, ".zip")) @@ -376,7 +447,7 @@ void SaveFile(int attempt) } if (open_file) ProcessButtonClick(BTN_RUN); - if (autoclose.checked) ExitProcess(); + if (autoclose.checked) DlExit(); } void Unarchive(dword _arc) diff --git a/lib/collection.h b/lib/collection.h index 7b1c162..ec78c23 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -93,6 +93,7 @@ struct collection void drop(); dword get_last(); bool pop(); + void swap(); }; :dword collection::add(dword in) { @@ -144,6 +145,12 @@ struct collection count = 0; } +// exchange two entries: only their offsets move, the strings stay where they are +:void collection::swap(dword pos1, pos2) { + if (pos1 >= count) || (pos2 >= count) return; + offset.swap(pos1, pos2); +} + :bool collection::pop() { if (count>0) count--; } diff --git a/lib/draw_buf.h b/lib/draw_buf.h index 2f9cbac..a47ca47 100644 --- a/lib/draw_buf.h +++ b/lib/draw_buf.h @@ -8,6 +8,10 @@ dword buf_data=0; +// The canvas keeps the size Init() gives it: what is drawn below its bottom is not +// (WebView and Quark size it to the window). A program that needs more calls +// IncreaseBufSize() itself (+50 MB a step) or writes with WriteTextLongBuf(). +// ":" before a method: it is compiled only into a program that calls it. struct CANVAS { dword bufx, bufy, bufw, bufh; @@ -19,6 +23,7 @@ struct CANVAS { void DrawBar(); void DrawImage(); void WriteText(); + void WriteTextLongBuf(); void PutPixel(); void AlignCenter(); void AlignRight(); @@ -26,10 +31,6 @@ struct CANVAS { bool BufIsInvalid(); }; -char draw_buf_not_enaught_ram[] = -"'CANVAS requested %i MB more memory than the system has. -Application could be unstable.' -E"; - bool CANVAS::BufIsInvalid() { if (!buf_data) || (!bufw) return true; @@ -40,10 +41,10 @@ bool CANVAS::Init(dword i_bufx, i_bufy, i_bufw, i_bufh) { bufx = i_bufx; bufy = i_bufy; - bufw = i_bufw; + bufw = i_bufw; bufh = i_bufh; if (buf_data) buf_data = free(buf_data); - IncreaseBufSize(); + if (bufw) buf_data = malloc(bufh * bufw * 4 + 8); if (!buf_data) return false; ESDWORD[buf_data] = bufw; ESDWORD[buf_data+4] = bufh; @@ -60,24 +61,23 @@ void CANVAS::Fill(dword start_pointer, i_fill_color) void CANVAS::DrawBar(dword x, y, w, h, color) { - dword i, j; + dword j, p, row; if (BufIsInvalid()) return; - if (y + h >= bufh) IncreaseBufSize(); + row = bufw * 4; + p = y * bufw + x * 4 + 8 + buf_data; for (j=0; j= bufh) IncreaseBufSize(); //WHY NOT WORKING? for (j=0; j= bufh) IncreaseBufSize(); + if (y + 32 >= bufh) IncreaseBufSize(); if (y < BUGFIX_32000) { ESI = strlen; WriteBufText(x, y, fontType, color, str_offset, buf_data); @@ -117,14 +130,14 @@ void CANVAS::WriteText(dword x, y, byte fontType, dword color, str_offset, strle } } -void CANVAS::PutPixel(dword x, y, color) +:void CANVAS::PutPixel(dword x, y, color) { dword pos = y*bufw+x*4+8+buf_data; if (BufIsInvalid()) return; ESDWORD[pos] = color; } -void CANVAS::AlignRight(dword x,y,w,h, content_width) +:void CANVAS::AlignRight(dword x,y,w,h, content_width) { dword i, j, l; dword content_left = w - content_width / 2; @@ -138,7 +151,7 @@ void CANVAS::AlignRight(dword x,y,w,h, content_width) } } -void CANVAS::AlignCenter(dword x,y,w,h, content_width) +:void CANVAS::AlignCenter(dword x,y,w,h, content_width) { dword i, j, l; dword content_left = w - content_width / 2; @@ -158,9 +171,10 @@ void CANVAS::Show(dword _y_offset, _h) PutPaletteImage(_y_offset * bufw * 4 + buf_data+8, bufw, _h, bufx, bufy, 32, 0); } -void CANVAS::IncreaseBufSize() +// the canvas 50 MB taller, the new rows filled with the last Fill() color +:void CANVAS::IncreaseBufSize() { - static dword bufh_initial; + dword bufh_old; dword alloc_size; dword free_ram_size; char error_str[256]; @@ -171,19 +185,19 @@ void CANVAS::IncreaseBufSize() alloc_size = bufh * bufw * 4 + 8; buf_data = malloc(alloc_size); } else { - if (bufh_initial != bufh) bufh_initial = bufh; + bufh_old = bufh; bufh += 4096*1600/bufw; //+50 Mb alloc_size = bufh * bufw * 4 + 8; buf_data = realloc(buf_data, alloc_size); - Fill(bufh_initial * bufw * 4 + 8, fill_color); + Fill(bufh_old * bufw * 4 + 8, fill_color); } - bufh_initial = bufh; free_ram_size = GetFreeRAM() * 1024; if (alloc_size > free_ram_size) { - sprintf(#error_str, #draw_buf_not_enaught_ram, alloc_size - free_ram_size/1048576); + sprintf(#error_str, "'CANVAS requested %i MB more memory than the system has. +Application could be unstable.' -E", alloc_size - free_ram_size/1048576); notify(#error_str); } } -#endif \ No newline at end of file +#endif diff --git a/lib/gui/menu.h b/lib/gui/menu.h index 1a12b31..c8e1e7f 100644 --- a/lib/gui/menu.h +++ b/lib/gui/menu.h @@ -16,6 +16,7 @@ shared_mem = memopen(#shared_name, 16, SHM_CREATE + SHM_WRITE); if (EDX) shared_mem = memopen(#shared_name, 16, SHM_WRITE); } + GetProcessInfo(#Form, SelfInfo); ESDWORD[shared_mem ] = _selected; ESDWORD[shared_mem + 4] = _x + Form.left + 5; ESDWORD[shared_mem + 8] = _y + Form.top + skin_h; diff --git a/lib/keyboard.h b/lib/keyboard.h index 6e32694..de623e5 100644 --- a/lib/keyboard.h +++ b/lib/keyboard.h @@ -107,6 +107,8 @@ #define KEY_SCROLLLOCK 00100000000b #define KEY_LWIN 01000000000b #define KEY_RWIN 10000000000b +//Only real modifiers, without CapsLock/NumLock/ScrollLock/Win toggles +#define KEY_MODIFIERS 00000111111b :unsigned char key_ascii; :dword key_scancode, key_modifier, key_editbox; diff --git a/lib/obj/box_lib.h b/lib/obj/box_lib.h index 6edf688..4e0d08f 100644 --- a/lib/obj/box_lib.h +++ b/lib/obj/box_lib.h @@ -83,6 +83,7 @@ PathShow_draw stdcall(#PathShow); #define ed_pass 1b #define ed_focus 10b //focused #define ed_shift 100b //flag is set when Shift is pressed +#define ed_select 100b //rewritten box_lib (ES.SELECT): text between shift and pos is selected #define ed_shift_on 1000b #define ed_shift_bac 10000b //bit for Shift reset, if set the smth is selected #define ed_left_fl 100000b @@ -110,10 +111,10 @@ dword width, left, top, text, mouse_variable, flags, - size, pos, offset, - cl_curs_x, cl_curs_y, - shift, shift_old, - height, char_width; + size, pos, offset; +word cl_curs_x, cl_curs_y, // words in box_lib: dwords here put shift into ed_height + shift, shift_old; +dword height, char_width; }; :void EditBox_UpdateText(dword ed, _flags) diff --git a/lib/obj/http.h b/lib/obj/http.h index d9cbf99..4347e55 100644 --- a/lib/obj/http.h +++ b/lib/obj/http.h @@ -69,6 +69,10 @@ char aURIunescape[] = "unescape"; #define FLAG_TIMEOUT_ERROR 1 << 19 #define FLAG_TRANSFER_FAILED 1 << 20 +// FLAG_GOT_ALL_DATA as a plain number for tests: C-- has no precedence, so +// "flags & FLAG_GOT_ALL_DATA" would be "(flags & 1) << 2" +#define HTTP_GOT_ALL_DATA 4 + struct http_msg{ dword socket; //socket on which the actual transfer happens dword flags; //flags, reflects status of the transfer using bitflags @@ -101,8 +105,14 @@ struct _http dword status_code; dword receive_result; dword content_pointer; + dword headers; // extra request headers, 0 means accept_language + // http_msg.flags after the last receive(). receive_result 0 means the transfer is + // over, not that it succeeded: a timeout or a dropped connection ends it the same + // way. Only (flags & HTTP_GOT_ALL_DATA) says the whole answer came. + dword flags; dword get(); + dword post(); bool stop(); void hfree(); void receive(); @@ -112,11 +122,31 @@ struct _http dword _http::get(dword _url) { cur_url = _url; - http_get stdcall (_url, 0, 0, #accept_language); + if (headers) http_get stdcall (_url, 0, 0, headers); + else http_get stdcall (_url, 0, 0, #accept_language); transfer = EAX; return EAX; } +// Send a form by POST: _data holds _len bytes of application/x-www-form-urlencoded +// body. libHTTP opens the connection with the headers, the body follows over the +// same socket; the answer is then received like any other page. +char post_form_type[] = "application/x-www-form-urlencoded"; + +dword _http::post(dword _url, _data, _len) +{ + dword h, t; + cur_url = _url; + h = headers; + if (!h) h = #accept_language; + http_post stdcall (_url, 0, 0, h, #post_form_type, _len); + t = EAX; + transfer = t; + if (!t) return 0; + http_send stdcall (t, _data, _len); + return t; +} + void _http::hfree() { http_free stdcall (transfer); @@ -154,6 +184,7 @@ void _http::receive() } content_length = EDI.http_msg.content_length; content_received = EDI.http_msg.content_received; + flags = EDI.http_msg.flags; } :dword _http::header_field(dword _header_field, _dst, _size) @@ -181,24 +212,122 @@ void _http::receive() // // //===================================================// +:bool local_disk_num(dword _p) +{ + if (ESBYTE[_p] < '0') return false; + if (ESBYTE[_p] > '9') return false; + return true; +} + :int check_is_the_adress_local(dword _in) { if (ESBYTE[_in]!='/') return false; _in++; if(!strncmp(_in,"rd/",3)) return true; if(!strncmp(_in,"fd/",3)) return true; - if(!strncmp(_in,"hd",2)) return true; - if(!strncmp(_in,"bd",2)) return true; - if(!strncmp(_in,"cd",2)) return true; + // a disk name is followed by its number: "/cdn-cgi/image/..." (Cloudflare) or + // "/hdimages/" on a site is a path of that site, not a CD or a hard disk + if(!strncmp(_in,"hd",2)) && (local_disk_num(_in+2)) return true; + if(!strncmp(_in,"bd",2)) && (local_disk_num(_in+2)) return true; + if(!strncmp(_in,"cd",2)) && (local_disk_num(_in+2)) return true; if(!strncmp(_in,"sys/",4)) return true; - if(!strncmp(_in,"tmp",3)) return true; - if(!strncmp(_in,"usbhd",5)) return true; + if(!strncmp(_in,"tmp",3)) && (local_disk_num(_in+3)) return true; + if(!strncmp(_in,"usbhd",5)) && (local_disk_num(_in+5)) return true; if(!strncmp(_in,"kolibrios",9)) return true; return false; } +// "file:///sys/x" -> "/sys/x" in place; the rest of the code works with plain paths +:void strip_file_scheme(dword _in) +{ + if (!strncmp(_in, "file://", 7)) strcpy(_in, _in + 7); +} + +// A space is not allowed in a request line, but pages do write it: +// href="/theme/M9L reloaded/style.css" has to travel as "M9L%20reloaded". +:void url_escape_spaces(dword _url) +{ + char buf[URL_SIZE+1]; + dword s, d, e, c; + if (!strchr(_url, ' ')) return; + s = _url; + d = #buf; + e = #buf; + e += URL_SIZE; + e -= 4; + while (ESBYTE[s]) { + if (d >= e) break; + c = ESBYTE[s]; + if (c == ' ') { + ESBYTE[d] = '%'; d++; + ESBYTE[d] = '2'; d++; + ESBYTE[d] = '0'; d++; + } else { + ESBYTE[d] = c; d++; + } + s++; + } + ESBYTE[d] = 0; + strcpy(_url, #buf); +} + +// A url as the value of the proxy's "?site=": the proxy decodes it once, so the +// characters that split or end a query value travel escaped. Without this +// "index.php?ct=core&action=css&id=2" reaches the site as "index.php?ct=core". +// Appends to _dst, which holds _size bytes. +:void strcat_proxy_site(dword _dst, _url, _size) +{ + dword d, e, c; + d = strlen(_dst); + d += _dst; + e = _dst; + e += _size; + e -= 4; + while (ESBYTE[_url]) { + if (d >= e) break; + c = ESBYTE[_url]; + if (c == '%') || (c == '&') || (c == '+') || (c == '#') { + ESBYTE[d] = '%'; d++; + ESBYTE[d] = '2'; d++; + if (c == '%') ESBYTE[d] = '5'; + else if (c == '&') ESBYTE[d] = '6'; + else if (c == '+') ESBYTE[d] = 'B'; + else ESBYTE[d] = '3'; + d++; + } else { + ESBYTE[d] = c; d++; + } + _url++; + } + ESBYTE[d] = 0; +} + +// the inverse of strcat_proxy_site(), in place +:void proxy_site_unescape(dword _url) +{ + dword s, d, c, h; + s = _url; + d = _url; + while (ESBYTE[s]) { + c = ESBYTE[s]; + if (c == '%') && (ESBYTE[s+1] == '2') { + h = ESBYTE[s+2]; + if (h == '5') c = '%'; + else if (h == '6') c = '&'; + else if (h == 'B') || (h == 'b') c = '+'; + else if (h == '3') c = '#'; + if (c != '%') || (h == '5') s += 2; + } + ESBYTE[d] = c; + d++; + s++; + } + ESBYTE[d] = 0; +} + :int check_is_the_url_absolute(dword _in) { + if(!strncmp(_in,"file:",5)) return true; if(!strncmp(_in,"ftp:",4)) return true; if(!strncmp(_in,"http:",5)) return true; if(!strncmp(_in,"https:",6)) return true; @@ -218,19 +347,20 @@ void _http::receive() strcpy(#newurl, base_URL); while (i=strstr(new_URL, "&")) strcpy(i+1, i+5); + strip_file_scheme(new_URL); + url_escape_spaces(new_URL); if (check_is_the_url_absolute(new_URL)) return new_URL; switch(ESBYTE[new_URL]) { case '.': - new_URL++; - if (ESBYTE[new_URL] == '/') { - new_URL++; - } - else if (ESBYTE[new_URL] == '.') && (ESBYTE[new_URL+1] == '/') { - _GO_UP: + if (ESBYTE[new_URL+1] == '/') { new_URL+=2; - newurl[strrchr(#newurl, '/')-1] = '\0'; + } + else if (ESBYTE[new_URL+1] == '.') && (ESBYTE[new_URL+2] == '/') { + _GO_UP: + new_URL+=3; + newurl[strrchr(#newurl, '/')-1] = '\0'; } goto _DEFAULT; case '?': diff --git a/lib/obj/iconv.h b/lib/obj/iconv.h index ab257cf..05a6b5d 100644 --- a/lib/obj/iconv.h +++ b/lib/obj/iconv.h @@ -29,7 +29,6 @@ dword ChangeCharset(dword from_chs, to_chs, conv_buf) iconv_open cdecl (to_chs*10+#charsets, from_chs*10+#charsets); if (EAX==-1) { - debugln("iconv: unsupported charset"); return 0; } cd = EAX; @@ -41,7 +40,6 @@ dword ChangeCharset(dword from_chs, to_chs, conv_buf) if (EAX!=0) { cd = EAX; - debugval("iconv failed", cd); if (from_chs == CH_UTF8) && (to_chs == CH_CP866) { utf8rutodos(conv_buf); } diff --git a/menu/menu.c b/menu/menu.c index 60e37bd..9782fcc 100644 --- a/menu/menu.c +++ b/menu/menu.c @@ -18,6 +18,7 @@ collection real_names=0; //contains only real items without separators collection hotkeys=0; int selected, win_x, win_y; +bool window_defined = false; int max_name_len; int max_hotkey_len; @@ -151,7 +152,11 @@ void main() @SetEventMask(EVM_REDRAW + EVM_KEY + EVM_BUTTON + EVM_MOUSE); loop() switch(WaitEvent()) { - case evMouse: + case evMouse: + // The kernel hands out a pending mouse event before the first + // redraw, i.e. before our window exists and can be active: a + // mouse move during startup would close the menu at once. + if (!window_defined) break; GetProcessInfo(#Form, SelfInfo); if (!CheckActiveProcess(Form.ID)) exit(); mouse.get(); @@ -168,6 +173,7 @@ void main() case evReDraw: DefineAndDrawWindow(win_x, win_y, menu1.w+4, menu1.h+4, 0x01, 0, 0, 0x01fffFFF); + window_defined = true; Draw3DPopup(0,0,menu1.w+2,menu1.h+2); draw_list(); }