img

Что такое сетевые стандарты и как работает веб-браузер?

 

Позвольте, я расскажу вам одну историю. Однажды я создавал очередной элемент выбора даты для нашей системы проектирования. Он состоит из поля текстового ввода и всплывающего окна с календарем, которое появлялось при нажатии на поле. Кроме того, всплывающее окно можно было закрыть, кликнув вне окна или выбрав дату. 

Date picker component

Элемент выбора даты

Большинство реализаций логики кликов вне окна выполнены с использованием реальных обработчиков на событие клика, привязанных к модели DOM. Однако я хотел сделать наш элемент выбора даты более удобным, чтобы у вас была возможность открывать календарь с помощью вкладок и также его закрывать. К тому же, обработчики могут конфликтовать друг с другом, если у вас на странице будет несколько элементов выбора даты. 

А что, если сделать акцент на встроенный фокус ввода и событие потери фокуса элементом управления вместо кликов вне окна? Они, конечно, поддерживают вкладки, события касания и клика и уже реализованы в браузере. Единственная проблема, которую в данном случае необходимо решить, - когда вы нажимаете на всплывающее окно, но не выбираете дату, фокус смещается на календарь, вызывает событие потери фокуса при вводе текста и всплывающее окно закрывается. 

И вот тут я задумался о том, есть ли способ кликнуть, но при этом не смещать фокус. Немного покопавшись в Гугл, я нашел способ сделать так: блокировать действие по умолчанию события mouseDown для всплывающего окна. Вот так просто в одну строку – все клики срабатывают, но фокус остается на поле ввода текста. 

Казалось бы, решение найдено, двигаемся дальше, но что-то внутри останавливало меня. Почему именно mouseDown, а не mouseUp блокирует фокус, и при этом распространяет событие клика? Можно ли назвать это рабочим проектом стандарта? Можно ли доверять этому решению? Является ли оно кроссбраузерным? К тому же библиотека тестирования React, которую мы использовали для интеграционных тестов, не поддерживала такое решение, и мне пришлось бы изменить имитационную функцию. 

Что такое сетевые стандарты?

Ладно, раз ответа Stack Overflow мне было недостаточно, что же тогда может быть лучше сетевых стандартов для того, чтобы изучить поведение браузера? 

Вы, скорее всего, слышали о W3C или World Wide Web Consortium (Консорциум Всемирной паутины). Это международное сообщество, которое разрабатывает открытые стандарты для Интернета. W3C следит за тем, чтобы все придерживались одних и тех де рекомендаций, вследствие чего пропадет необходимость поддерживать десятки совершенно разных сред. Если вы зайдете на их сайт, то вы найдете там список всех стандартов, над которыми они работают.

Давайте посмотрим на один документ, который может ответить на ваши вопросы - UI Events Standard (Стандарт событий пользовательского интерфейса). Этот документ устанавливает поток событий DOM, определяет список событий и порядок их выполнения. Если вы думаете, что стандарты – это скучный, непонятный и сложный текст, то переходите сразу к разделу «DOM Event Architecture» («Архитектура событий DOM»), в котором рассказывается о всплывании и погружении событий с помощью красивых картинок, но все же они достаточно специфичны, какими и должны быть стандарты. Вы удивитесь качеству документа, он действительно хорошо написан, и в нем приведено огромное количество примеров и рекомендаций. 

В нем даже есть определение нашего события mouseDown и его действий по умолчанию:

 «Во многих реализациях для запуска различных действий по умолчанию, которые зависят от контекста, используется событие mouseDown. Вы можете помешать этим действиям выполниться, если отмените это событие. Некоторые из действий по умолчанию могут включать в себя: начало взаимодействия с помощью перетаскивания изображения или ссылки, начало выделения текста и т.д. К тому же, в некоторых реализациях есть функция горизонтальной прокрутки с помощью мыши, которая активируется при нажатии средней кнопки мыши в момент отправки события mouseDown

Итак, у нашего события есть некоторые действия по умолчанию, но нет никакой конкретной информации о фокусе, а все потому, что это на самом деле зависит от реализации браузера. Давайте посмотрим.

Введение в браузерные движки

Современный браузер – это достаточно мудрёное программное обеспечение с кодовой базой, которая состоит из десятка миллионов строк кода. И потому его, как правило, разбивают на несколько частей. 

Для того, чтобы найти место, где определяются события фокуса, нам нужно выяснить, за что отвечает каждая из частей. Давайте начнем с Chromium и его проектной документации «Getting Around The Chrome Source Code» («Знакомство с исходным кодом Chrome»). Как мы можете видеть, здесь очень много различных модулей, которые отвечают за различную логику. 

Общий обзор на Chromium

Давайте кратко пройдемся по ним, чтобы понять, как они все вместе работают:

  • оболочка пользовательского интерфейса (chrome): это базовое приложение, в котором содержится логика запуска, пользовательский интерфейс и все окна. Оно содержит проекты для chrome.exe и chrome.dll. Также здесь можно найти вспомогательные средства, такие как иконки и курсоры. 
  • наполнение (content): это серверная часть приложения, которая обеспечивает связь с дочерними процессами.
  • net: это библиотека для взаимодействия в сети, она помогает делать запросы к веб-сайтам.
  • основа (base): место расположения общего кода, который используют все подпроекты. В него могут входить такие вещи, как работа со строками, универсальные утилиты и т.д.
  • blink: это механизм визуализации, который отвечает за весь конвейер визуализации, включая DOM-деревья, стили, события, интеграцию с V8. 
  • V8: последняя большая часть браузера – движок JavaScript. Его задача – компилировать JavaScript в собственный машинный код.

Как вы можете видеть, браузер состоит из нескольких независимых частей, которые взаимодействуют друг с другом через API. Самый большой интерес для разработчиков представляю Blink и V8. Действия по умолчанию, которые определены браузером, не являются частью V8, но при этом они все должны быть определены и реализованы в Blink. Однако перед тем, как мы перейдем к рассмотрению кодовой базы Blink, давайте разберем, как работают браузеры с точки зрения пользователя.  

Конвейер визуализации

Представьте, что вы вводите адрес домена в браузере, после чего он выбирает и загружает какое-то количество ресурсов: файлы HTML, CSS и JS, изображения, иконки. Но что происходит дальше?

Конвейер визуализации веб-браузера

На первом этапе HTML-файлы будут проанализированы с точки зрения синтаксиса и преобразованы в DOM-дерево. DOM – это не только внутреннее представление страницы, но и API, который предоставляется JavaScript для запрашивания и изменения визуализации через систему, которая называется «привязками».

После того, как DOM-дерево будет сформировано, следующий шаг - обработка стилей CSS. Для этой цели в браузерах есть синтаксический анализатор CSS, который создает модель правил стиля. Создав модель правил стиля, мы можем объединить ее с набором стилей по умолчанию, которые есть в браузере, и вычислить конечное значение каждого свойства стиля для каждого элемента модели DOM. Такой процесс называется разбиением стилей (или перерасчетом стилей)

В следующей части построения макета страницы мы должны определить наглядную геометрию всех элементов. На данном этапе каждому элементу присваиваются свои координаты (х и у), ширина и высота. Механизм визуализации вычисляет и фиксирует все области переполнения – какие элементы видны, а какие нет. 

Как только мы получили координаты всех элементов, мы можем приступить к расцвечиванию. Для этого нам потребуются координаты, полученные на предыдущем этапе, и цвет из правил стиля. Мы объединяем их в список команд для расцвечивания. Важно расцвечивать элементы в правильном порядке, чтобы они правильно наслаивались. Порядок можно изменить с помощью правила стиля z-index.

Давайте выполним наш список команд по расцвечиванию и преобразуем их в битовый массив значений цвета. Этот этап называется созданием растрового изображения. Здесь же мы берем наши изображения и аналогично декодируем их в битовый массив. 

В дальнейшем растровое изображение будет храниться в памяти графического процессора. На этом этапе подключаются библиотеки, которые отделяют аппаратное обеспечение и выполняют вызовы OpenGL и DirectX в Windows. Когда графический процессор получает команды для отображения битового массива, он начинает рисовать пиксели на вашем экране. 

На данный момент у нас есть самые важные части конвейера визуализации. Но что будет, если вы прокрутите страницу или если на ней будет какая-нибудь анимация? В принципе, визуализация не является статичной. Изменения отображаются с помощью кадров анимации. Каждый кадр – это полная визуализация состояния содержимого страницы в какой-то определенный момент времени. Реальной проблемой в данной ситуации является ее производительность. Анимация с плавным переходом между кадрами требует генерации хотя бы 60 кадров в секунду. Было бы практически невозможно пройти весь конвейер 60 раз за секунду, особенно на медленных устройствах.

А что, если вместо того, чтобы каждый раз заново визуализировать все, мы, сделаем так, что можно будет на каком-то определенном этапе объявить элемент недействительным. Например, если вы динамически измените цвет кнопки, то браузер отметит этот элемент как недействительный, и в следующем кадре анимации он будет перерисован. Если никаких изменений не было, то мы можем повторно использовать старый кадр. 

Это отличный способ оптимизировать небольшие динамические изменения в содержимом страницы. А теперь давайте поразмышляем об изменениях в больших областях страницы. Например, при прокрутке страницы все пиксели должны поменяться. Для этого страницу разбивают на слои, которые будут переформировываться в растровое изображение независимо друг от друга. Слой может быть довольно небольшим и отображать только один DOM-узел. Далее эти слои объединяются в связку, называемую потоком компоновщика. Оптимизировав процесс таким образом, вам не нужно будет заново создавать растровое изображения для всего подряд, вы будете делать это для слоев небольшого размера, а затем правильно комбинировать их. 

Мы уже поняли, что делает Blink и как выглядит конвейер визуализации. Давайте углубимся в код. 

Навигация по кодовой базе Blink

Похоже, что мы уже на финишной прямой. Давайте откроем репозиторий Blink и посмотрим на него.

The root folder of Blink repository

Корневая папка репозитория Blink

Несмотря на то, что мы значительно сузили наш первоначальный вопрос, найти вручную конкретную строку кода, которая отвечает за блокировку фокуса, будет трудно. 

Давайте попробуем произвести поиск в Google по названию нашего события:

mousedown site:https://chromium.googlesource.com/chromium/blink/+/master/Source

Поиск проводит нас к файлу EventHandler, где вы можете найти детали реализации многих входных событий. В частности, и самую важную для нас строчку:

bool swallowEvent = !dispatchMouseEvent(EventTypeNames::mousedown, mev.innerNode(), m_clickCount, mouseEvent);

Событие dispatchMouseEvent возвращает значение, которое означает «продолжить обработку по умолчанию», поэтому в случае использования preventDefault событие swallowEvent будет принимать значение true.

 А чуть ниже вызывается событие фокуса, но оно срабатывает только, если swallowEvent == false.

swallowEvent = swallowEvent || handleMouseFocus(MouseEventWithHitTestResults(mouseEvent, hitTestResult), sourceCapabilities);

Вы можете изучить все действия по умолчанию для события нажатия мыши, а не только обработку фокуса, в том числе выбор, перетаскивание и прокрутку. Также в нем реализованы событие отпускания кнопки мыши и событие двойного щелчка. В принципе, там есть все. 

Gecko и WebKit

На данный момент мы уже потратили какое-то время на то, чтобы изучить исходный код браузеров, и довольно хорошо понимаем их структуру, так почему бы на не посмотреть на Firefox и Safari в целом. Браузерный движок Firefox называется Gecko, а для Safari – это WebKit. 

У Gecko есть страница обзора для разработчиков, то есть вы сможете получить представление об основных концепциях. Действуя по аналогии с Chrome, вы можете найти скромный файл EventStateManager ,который состоит всего из 6000 строк кода. В нем описаны действия и поведение событий по умолчанию. Ссылка приведет вас к конкретной строке, так что вам не придется искать ее по всему файлу.

WebKit – это браузерный движок от Apple, который используется в Safari и других продуктах Apple. Blink (Chrome) – это ответвление WebKit, поэтому у них много общего, так что найти реализацию событий в их версии файла EventHandler было не так сложно. 

Теперь, когда мы убедились в том, что можем безопасно блокировать событие mousedown, я могу вернуться и внести изменения в элемент выбора даты. 

Заключение

Вместе мы прошли путь от простой задачи до введения в сетевые стандарты и деталей реализации браузера. 

Не стоит бояться скрытой сложности используемых модулей, даже если это браузер или компилятор. В итоге вы поймете, что это был увлекательный путь. Есть шансы, что вы с легкостью найдете то, что можно улучшить, и, что не менее важно, получите уникальное понимание того, как все на самом деле работает. Я узнал много нового в процессе углубления в тему и хочу убедить вас сделать тоже самое. У браузеров есть отличная документация, и я уверен, что больше мне ничего не нужно.

 

Ссылка
скопирована
Программирование
Скидка 25%
Python-программист с нуля
Стань разработчиком на одном из самых популярных языков программирования.
Получи бесплатный
вводный урок!
Пожалуйста, укажите корректный e-mail
отправили вводный урок на твой e-mail!
Получи все материалы в telegram и ускорь обучение!
img
Еще по теме:
img
Введение Podman – это механизм управления OCI-контейнерами, который не требует запуска демон-процесса. Он нацелен на то, чтобы
img
За последние годы микросервисы прошли путь от обычного переоцененного модного словечка до вещи, которую вы, как специалист по пр
img
Введение Резидентные базы данных (или хранилища в памяти) по большей части делают упор на хранилище данных в памяти, а не на жес
img
  Многие люди рассуждают так: «зачем, ну зачем мне изучать еще один язык программирования?» Для них это лишнее, и они стараютс
img
Введение Объекты в Kubernetes – это базовые постоянные сущности, которые описывают состояние кластера Kubernetes. Модули – это э
img
  Довольно часто мы встречаемся с компонентами архитектуры программного обеспечения, которые являются частью любой системы, но п
Комментарии
ЛЕТНИЕ СКИДКИ
40%
50%
60%
До конца акции: 30 дней 24 : 59 : 59