Files
LeencyandBurer 145ac1ad7b Webview 4.0 (#14)
Рендеринг:
- Новый движок вместо 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>
2026-09-27 09:45:21 +03:00

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();
}