Рендеринг: - Новый движок вместо TWB: страница проходит через DOM, вычисление стилей, раскладку и отрисовку отдельными этапами - Рисуется только видимая область, холст размером с окно - расход памяти не растёт с длиной страницы - Прокрутка сдвигает уже нарисованный холст и дорисовывает только появившуюся полосу - Страница показывается, не дожидаясь картинок; таблицы стилей, наоборот, дожидаются - до них на экране остаётся предыдущая страница CSS: - Источники правил: <style>, атрибут style="", <link> и @import - Селекторы: тег, .class, #id, *, атрибутные, комбинаторы - пробел, >, +, ~ - :is() и :where() раскрываются в обычные правила - Псевдоклассы: first-child, last-child, only-child, first-of-type, last-of-type, only-of-type, root, empty, checked, disabled, not, nth-child, nth-last-child, nth-of-type, nth-last-of-type - Псевдоэлементы ::before и ::after - @media по ширине и высоте окна - var(), calc(), min(), max(), clamp() - Блочная модель целиком: margin, padding, border, min/max-width и height - float, flex, grid - position: static, relative, absolute, fixed, sticky, z-index - vertical-align, в том числе super и sub; теги <sup> и <sub> Таблицы: - colspan и rowspan, неявные строки и ячейки, <caption>, вложенные таблицы - Ширины колонок считаются по содержимому и по заданным процентам - <table align> и <center> вокруг таблицы Сеть и кэш: - Картинки и таблицы стилей грузятся параллельно потоками самого WebView - до 12 одновременно, вместо отдельного процесса dl на каждый файл - Снято ограничение в 256 байт на адрес, и запросы уходят с куками - Кэш файлов в /tmp0/1/Cache с индексом: повторный заход на страницу не перекачивает картинки - SVG и WebP открываются через конвертер (по умолчанию wsrv.nl, адрес меняется в app.ini) - Куки: приём, хранение и отправка - Оборванное соединение распознаётся - обрезанный файл не сохраняется и не попадает в кэш, страница помечается неполной Формы: - Отправка по GET и по POST - Контролы: текстовое поле, пароль, чекбокс, радиокнопка, обычная кнопка и кнопка отправки, <select>, <textarea> - Размеры контролов берутся из CSS и атрибутов страницы, а не фиксированы Текст и шрифты: - Выделение и копирование текста, поиск по странице - Курсив для <i>, <em>, <cite>, <var>, <dfn>, <address> и font-style: системные шрифты курсива не имеют, поэтому нарисованная строка наклоняется сдвигом пикселей - Мелкий шрифт для текста 10 px и меньше - но только если такого текста меньше половины страницы - Изменение размера шрифта: Ctrl+- и Ctrl++ - Определение кодировки по <meta charset> и по содержимому Интерфейс: - Обновлены встроенные страницы: новая вкладка и страница ошибки - Пункт меню "Открыть в NetSurf" - Кнопка папки загрузок на панели - Правый клик по картинке: открыть, сохранить, скопировать адрес - До 5 вкладок Убрано: - Страница истории WebView:history и пункт меню к ней Изменения вне браузера: - dl: оборванная загрузка считается ошибкой, обрезанный файл не сохраняется; путь назначения принимается после | в параметре - menu: не реагирует на события мыши до создания окна - иначе меню закрывалось сразу после запуска - lib/obj/http.h: в _http появилось поле flags, наружу выведен HTTP_GOT_ALL_DATA - lib/draw_buf.h: холст держит размер, заданный Init(); неиспользуемые методы не попадают в программу - lib/collection.h: swap() для двух записей - lib/obj/box_lib.h: edit_box приведена к раскладке переписанного box_lib (OS PR #662) Размер запакованного приложения - 64 КБ: 65 261 байт (EN) и 65 456 (RU). --------- Co-authored-by: Burer <burer@kolibrios.org> Reviewed-on: #14 Reviewed-by: Burer <burer@kolibrios.org> Co-authored-by: leency <lipatov.kiril@gmail.com>
245 lines
6.2 KiB
C
245 lines
6.2 KiB
C
enum {
|
|
PAGE=1, IMG, FILE
|
|
};
|
|
|
|
struct _cache
|
|
{
|
|
dword current_buf;
|
|
dword current_size;
|
|
dword current_charset;
|
|
collection url;
|
|
collection_int data;
|
|
collection_int size;
|
|
collection_int charset;
|
|
void add();
|
|
bool has();
|
|
void clear();
|
|
} cache;
|
|
|
|
void _cache::add(dword _url, _data, _size, _charset)
|
|
{
|
|
dword data_pointer;
|
|
data_pointer = malloc(_size);
|
|
memmov(data_pointer, _data, _size);
|
|
data.add(data_pointer);
|
|
|
|
url.add(_url);
|
|
size.add(_size);
|
|
charset.add(_charset);
|
|
|
|
current_buf = data_pointer;
|
|
current_size = _size;
|
|
}
|
|
|
|
bool _cache::has(dword _link)
|
|
{
|
|
int pos;
|
|
pos = url.get_pos_by_name(_link);
|
|
IF (pos != -1) {
|
|
current_buf = data.get(pos);
|
|
current_size = size.get(pos);
|
|
current_charset = charset.get(pos);
|
|
RETURN true;
|
|
}
|
|
RETURN false;
|
|
}
|
|
|
|
void _cache::clear()
|
|
{
|
|
int i;
|
|
FOR (i=0; i<data.count; i++) free(data.get(i));
|
|
url.drop();
|
|
data.drop();
|
|
size.drop();
|
|
charset.drop();
|
|
current_buf = NULL;
|
|
current_size = NULL;
|
|
current_charset = NULL;
|
|
}
|
|
|
|
|
|
// Image and stylesheet cache stored on the RAM disk, index is kept in _list_db.ini
|
|
// as "path=url" (the url may contain '=', the path never does): sheets in [css],
|
|
// pictures in [img].
|
|
#define IMG_CACHE_DIR "/tmp0/1/Cache"
|
|
#define IMG_CACHE_DB "/tmp0/1/Cache/_list_db.ini"
|
|
#define IMG_CACHE_LIMIT 8388608
|
|
#define IMG_CACHE_BIG 262144 // a bigger picture is not kept once its page is laid out
|
|
|
|
// Plain functions on the global instance, not methods: C-- miscompiles a member
|
|
// collection's method called from a method of the owner.
|
|
struct _cache2
|
|
{
|
|
collection url;
|
|
collection path;
|
|
collection_int fsize; // 0 => entry was evicted
|
|
collection_int css; // 1 => a stylesheet: evicted only after every picture
|
|
dword total_size;
|
|
dword cur;
|
|
} cache2;
|
|
|
|
bool cache2_is_contain(dword _url)
|
|
{
|
|
dword i, sz, u;
|
|
FOR (i = 0; i < cache2.url.count; i++) {
|
|
sz = cache2.fsize.get(i);
|
|
IF (!sz) CONTINUE;
|
|
u = cache2.url.get(i);
|
|
IF (streq(u, _url)) {
|
|
cache2.cur = i;
|
|
RETURN true;
|
|
}
|
|
}
|
|
cache2.cur = -1;
|
|
RETURN false;
|
|
}
|
|
|
|
// Register a downloaded file. Returns false and leaves the collections
|
|
// untouched if the file is missing or empty (download failed, disk full).
|
|
bool cache2_add(dword _url, _path, _css)
|
|
{
|
|
dword sz;
|
|
IF (cache2_is_contain(_url)) RETURN true;
|
|
sz = get_file_size(_path);
|
|
IF (!sz) {
|
|
DeleteFile(_path);
|
|
RETURN false;
|
|
}
|
|
cache2.url.add(_url);
|
|
cache2.path.add(_path);
|
|
cache2.fsize.add(sz);
|
|
cache2.css.add(_css);
|
|
cache2.total_size += sz;
|
|
RETURN true;
|
|
}
|
|
|
|
// Evict one entry: delete the file and mark the slot as free.
|
|
void cache2_evict(dword _pos)
|
|
{
|
|
dword sz, p;
|
|
IF (_pos >= cache2.url.count) RETURN;
|
|
sz = cache2.fsize.get(_pos);
|
|
IF (!sz) RETURN;
|
|
p = cache2.path.get(_pos);
|
|
DeleteFile(p);
|
|
cache2.total_size -= sz;
|
|
cache2.fsize.set(_pos, 0);
|
|
}
|
|
|
|
// Keep the cache under IMG_CACHE_LIMIT by evicting the oldest entries: pictures
|
|
// first, stylesheets only when the pictures alone are not enough. A page shown
|
|
// again (tab switch) is restyled from here: without its sheets it is unreadable,
|
|
// a missing picture is only a placeholder.
|
|
void cache2_trim()
|
|
{
|
|
dword i, c;
|
|
FOR (i = 0; i < cache2.url.count; i++) {
|
|
IF (cache2.total_size <= IMG_CACHE_LIMIT) RETURN;
|
|
c = cache2.css.get(i);
|
|
IF (!c) cache2_evict(i);
|
|
}
|
|
FOR (i = 0; i < cache2.url.count; i++) {
|
|
IF (cache2.total_size <= IMG_CACHE_LIMIT) RETURN;
|
|
cache2_evict(i);
|
|
}
|
|
}
|
|
|
|
// Pictures over IMG_CACHE_BIG are dropped once the page is laid out: its nodes hold
|
|
// the decoded copies, and a few photos would push every stylesheet out otherwise.
|
|
void cache2_drop_big()
|
|
{
|
|
dword i, c, sz;
|
|
FOR (i = 0; i < cache2.url.count; i++) {
|
|
c = cache2.css.get(i);
|
|
IF (c) CONTINUE;
|
|
sz = cache2.fsize.get(i);
|
|
IF (sz > IMG_CACHE_BIG) cache2_evict(i);
|
|
}
|
|
}
|
|
|
|
void cache2_clear()
|
|
{
|
|
dword i;
|
|
FOR (i = 0; i < cache2.url.count; i++) cache2_evict(i);
|
|
cache2.url.drop();
|
|
cache2.path.drop();
|
|
cache2.fsize.drop();
|
|
cache2.css.drop();
|
|
cache2.total_size = 0;
|
|
}
|
|
|
|
// Delete "wv_*" files in the cache dir that are not in the index
|
|
// (left over after a crash or another WebView instance).
|
|
void cache2_remove_orphans()
|
|
{
|
|
dword dirbuf, fcount, i, name;
|
|
int pos;
|
|
char fpath[600];
|
|
IF (GetDir(#dirbuf, #fcount, IMG_CACHE_DIR, DIRS_ONLYREAL)) RETURN;
|
|
FOR (i = 0; i < fcount; i++) {
|
|
name = i * 304 + dirbuf + 32 + 40;
|
|
IF (strncmp(name, "wv_", 3)) CONTINUE;
|
|
sprintf(#fpath, "%s/%s", IMG_CACHE_DIR, name);
|
|
pos = cache2.path.get_pos_by_name(#fpath);
|
|
IF (pos == -1) DeleteFile(#fpath);
|
|
}
|
|
free(dirbuf);
|
|
}
|
|
|
|
// the live entries of one kind as "path=url" lines
|
|
void cache2_save_part(dword _buf, _css)
|
|
{
|
|
dword i, sz, c, p;
|
|
for (i = 0; i < cache2.url.count; i++) {
|
|
sz = cache2.fsize.get(i);
|
|
IF (!sz) CONTINUE;
|
|
c = cache2.css.get(i);
|
|
IF (c != _css) CONTINUE;
|
|
p = cache2.path.get(i);
|
|
strcat(_buf, p);
|
|
strcat(_buf, "=");
|
|
p = cache2.url.get(i);
|
|
strcat(_buf, p);
|
|
strcat(_buf, "\n");
|
|
}
|
|
}
|
|
|
|
void cache2_save()
|
|
{
|
|
dword buf, buf_size;
|
|
buf_size = cache2.url.data_size + cache2.path.data_size + cache2.url.count + cache2.url.count + 13;
|
|
buf = malloc(buf_size);
|
|
strcpy(buf, "[css]\n");
|
|
cache2_save_part(buf, 1);
|
|
strcat(buf, "[img]\n");
|
|
cache2_save_part(buf, 0);
|
|
CreateDir(IMG_CACHE_DIR);
|
|
CreateFile(strlen(buf), buf, IMG_CACHE_DB);
|
|
free(buf);
|
|
}
|
|
|
|
byte _cache2_load_css(dword key_value, key_name, sec_name, f_name)
|
|
{
|
|
// key_name = path, key_value = url; add() drops entries whose file is gone
|
|
cache2_add(key_value, key_name, 1);
|
|
RETURN true;
|
|
}
|
|
|
|
byte _cache2_load_sections(dword key_value, key_name, sec_name, f_name)
|
|
{
|
|
cache2_add(key_value, key_name, 0);
|
|
RETURN true;
|
|
}
|
|
|
|
void cache2_load()
|
|
{
|
|
CreateDir(IMG_CACHE_DIR);
|
|
ini_enum_keys stdcall (IMG_CACHE_DB, "css", #_cache2_load_css);
|
|
ini_enum_keys stdcall (IMG_CACHE_DB, "img", #_cache2_load_sections);
|
|
// another window (Ctrl+N, -new) keeps its index in memory until it exits, and its
|
|
// dl children may be writing wv_* files right now: only the sole instance cleans up
|
|
IF (GetProcessesCount(#webview_shared) == 1) cache2_remove_orphans();
|
|
cache2_drop_big(); // an index written before the limit may still list big pictures
|
|
cache2_trim();
|
|
}
|