Привет! Меня зовут Дмитрий Козичев.
Сегодня я вам расскажу о моей попытке создать собственный современный веб-браузерный движок с нуля.
Мой движок называется Newtoo.
Что за Newtoo
Итак, Newtoo. Зачем я его создал?
Так уж получилось, что в мире есть всего 4 популярных браузерных движка, которые настолько сложны, что сами разработчики не знают и половины их кодовой базы, и настолько продвинутые по технологиям, что начать их догонять — пустая трата времени.
А так ли это на самом деле? Мой проект создан, чтобы повторить подвиги современных браузерных движков и проверить, насколько ли реально создать достойную альтернативу крупным проектам, история которых начинается с девяностых годов. Мой новый движок создается с нуля, а значит его история начинается — сегодня.
Идеология Newtoo — показать страницу быстрее, чем остальные.
Как Newtoo работает быстрее
Как я говорил ранее, основные браузерные движки развиваются не первый год. Те ошибки, которые были допущены на начальных стадиях разработки остаются в проекте до конца. Самый яркий пример этому — умные указатели в C++ — это еще более сложный синтаксис, большой оверхед при работе, создании и удалении умных указателей. Кроме того, есть очень много типов умных указателей и нужно знать, какой когда использовать, ведь у каждого есть свои сюрпризы ньюансы. Посмотрите на этот файл из WebKit. Когда видишь такой код, синтаксис умных указателей, пытаешься успокоится и дышать ровно, но такого рода код — это весь вебкит с ног до головы. В моем движке нет таких недостатков.
Что в коробочке
Давайте посмотрим из чего состоит Newtoo
На данный момент реализованы следующие части проекта:
- Парсер HTML
- Сериализатор HTML
- Парсер CSS (селекторы, правила и свойства)
- Сериализатор CSS
- Основное DOM API1
Остальные части проекта, которые пока не реализованы:
- Каскадинг CSS (вычисление css стилей)
- Компоновщик
- Рендер
- Виртуальная машина JS и события
- Обработчик событий и интерактивное выделение страницы
Парсер HTML
Мой парсер HTML вполне можно назвать современным. Начнем с того, что он построен по стандарту HTML5. Он учитывает любую вашу ошибку.
Например, вы забыли поставить кавычки, набирая атрибут
<article id=hello></article>
Движок вас поймет, есть значение атрибута написано без пробелов
Вы можете не закрывать тег, когда это не обязательно
<div>
<p>First line
<p>Second line
<img src="ru/images/2019.png" alt="С новым годом!">
<p>Third line <br> Last line
</div>
Парсер поддерживает префиксы
<myprefix:span>Hello, world!</myprefix:span>
Для того, чтобы обратно превратить элементы страницы в код, я написал сериализатор HTML. Я думаю, вы догадались, что он делает.
Как работает парсер HTML
Для начала наш парсер режет наш html код на кусочки и определяет их тип.
К примеру вот это:
<!doctype html><html><head><title>Lorem ipsum</title></head></html>
Превращается в это:
<!doctype html> - doctype token
<html> - tag token
<head> - tag token
<title> - tag token
Lorem ipsum - text token
</title> - close tag token
</head> - close tag token
</html> - close tag token
Эти кусочки называются токены.
Токены делятся на 6 типов:
- Тег
- Закрывающийся тег
- Текст
- Комментарий
- Тип документа (doctype)
- Javascript или css код
Парсер читает токены слева направо. Для каждого типа свой подход парсинга.
Когда парсер читает содержимое тега, сам тег регистрируется в иерархии (иерархия от ребенка до родителя вниз), а когда парсер закончил читать содержимое тега, он удаляет его из иерархии.
Если это открывающийся тег, он парсит его название тега, атрибуты, а затем, если это параграф и в иерархии тоже есть параграф, удаляет существующий в иерархии тег параграфа и добавляет туда новый, если это не одиночный тег (тег без закрывающегося тега). Если это закрывающийся тег, парсер удаляет из иерархии последний тег и если последний тег был параграфом, то удаляет сразу два последних. А если это код, в нем разрешены спецсимволы.
Используя такой метод парсинга токенов, можно писать <p> без закрывающегося тега.
Парсер CSS
На данный момент движок умеет парсить только стилевые css правила, например:
.flex[alignment="right"] { font-weight: light; color: #999 }
Поддерживая только одни стилевые правила, можно уже нормально отобразить настольную версию какого-нибудь сайта.
В отличии от других движков, Newtoo поддерживает одиночные комментарии '//' в css коде и не удаляет их при взаимодействии с css через javascript.
Парсер CSS селекторов
Чтобы узнать, какие именно html элементы страницы нужно форматировать стилями css, был придуман язык селекторов. Вы наверное его уже знаете.
Парсер селекторов поддерживает все комбинаторы, два вида кавычек, селекторы тегов, классов, атрибутов, множественные селекторы и классы.
Вот полный список всех поддерживаемых селекторов:
TagName
#Id
.Class
[attr=value]
[attr|=value]
[attr$=value]
[attr~=value]
[attr^=value]
[attr*=value]
.Multi.Class
#Mix#ed.Selec[tor=s]
"Quotes"
'Alternative quotes'
#descedant #child
#parent < #child
#previous + #this
#other ~ #this
.multi, .selectors
#element:hover
#element:active
#element:...
Да, селекторы четвертого уровня движок пока не поддерживает, но я работаю на этим.
DOM API
Когда мой парсер HTML читает наш код, он создает объектную модель документа (DOM). DOM выглядит как дерево из узлов, где корень — окно браузера, от него ответвляется документ, а от документа уже элементы страницы. Cо всеми узлами DOM можно взаимодействовать через JavaScript c помощью DOM API.
Мой движок поддерживает любые изменения изменения DOM. Например, можно переделать html код любого элемента:
document.getElementById("article").innerHTML = "Статья исчезла. <b>Бум!</b>";
Сейчас не буду перечислять все функции работы с элементами, документом, текстом, выделением, поверьте, их много!
Виртуальную машину JavaScript пока не написал, но API уже есть и хорошо работает.
Будущее проекта
Про перспективы проекта ничего не могу сказать, вам решать.
Если вам понравился мой движок, значит я хорошо постарался.
Автор: FlightBlaze
Смешно. Одному челу нереально тянуть сложные мегапроекты, только унылые небольшие программки. Даже Майкрософт сдался в браузерной войне.