Frontend Digest: глубокое погружение в то, как браузеры работают за кулисами.

внешний интерфейс JavaScript браузер
Frontend Digest: глубокое погружение в то, как браузеры работают за кулисами.
Похожие статьи, которые могут вас заинтересовать

  

Это всеобъемлющий учебник по внутренней работе Webkit и Gecko, результат обширных исследований израильского разработчика Тали Гаир. За последние несколько лет она просмотрела все общедоступные данные о внутреннем устройстве браузера.(Видетьресурс), и потратил много времени на изучение исходного кода веб-браузеров. Она написала:

В то время, когда доля IE на рынке составляла 90%, мы не могли ничего поделать, кроме как относиться к браузеру как к «черному ящику». Но теперь у браузеров с открытым исходным кодом естьболее половины доли рынка, так что пришло время разгадать тайну и заглянуть внутрь веб-браузера. Ну, там всего миллионы строк кода на C++...
Талли вее сайтОпубликовали результаты наших собственных исследований, но мы считаем, что они заслуживают того, чтобы их поняли больше людей, поэтому мы реорганизуем и опубликуем их здесь.

Как веб-разработчик,Изучение внутренней работы браузеров поможет вам принимать более обоснованные решения и понимать причины этих передовых методов разработки.. Несмотря на то, что это довольно длинный документ, мы рекомендуем вам уделить время его внимательному прочтению, после прочтения вы обязательно почувствуете, что заплатили за него.Пол Айриш, отдел по работе с разработчиками браузера Chrome

Введение

Веб-браузеры, вероятно, являются наиболее широко используемым программным обеспечением. В этом учебнике я расскажу, как они работают за кулисами. Это мы узнаем от того, что вы введете в адресную строкуgoogle.comЧто происходит, пока вы не увидите домашнюю страницу Google на экране браузера.

содержание

  1. Введение
    1. браузер, о котором мы говорим
    2. Основные возможности браузера
    3. Высокоуровневая структура браузера
  2. движок рендеринга
    1. движок рендеринга
    2. основной процесс
    3. Пример основного процесса
  3. Парсинг и построение дерева DOM
    1. Анализ — Обзор
      1. грамматика
      2. Комбинация парсера и лексера
      3. переводить
      4. Пример разбора
      5. Формальное определение лексики и грамматики
      6. тип парсера
      7. автоматически сгенерированный парсер
    2. парсер HTML
      1. Определение синтаксиса HTML
      2. неконтекстно-свободная грамматика
      3. HTML DTD
      4. DOM
      5. алгоритм разбора
      6. алгоритм токенизации
      7. алгоритм построения дерева
      8. Действия после разбора
      9. Отказоустойчивость браузера
    3. Разбор CSS
      1. CSS-парсер Webkit
    4. Порядок, в котором обрабатываются скрипты и таблицы стилей
      1. сценарий
      2. готовить
      3. таблица стилей
  4. визуализировать построение дерева
    1. Связь между деревом рендеринга и деревом DOM
    2. Процесс построения дерева рендеринга
    3. расчет стиля
      1. Данные общего стиля
      2. Дерево правил Firefox
        1. Структурное подразделение
        2. Вычислить контекст стиля, используя дерево правил
      3. Манипулируйте правилами, чтобы упростить сопоставление
      4. Применение правил в правильном каскадном порядке
        1. каскадный порядок таблицы стилей
        2. специфичность
        3. Порядок правил
    4. прогрессивная обработка
  5. макет
    1. Грязная битовая система
    2. Глобальный макет и добавочный макет
    3. Асинхронный макет и синхронный макет
    4. оптимизация
    5. обработка макета
    6. расчет ширины
    7. новая линия
  6. рисовать
    1. Глобальная отрисовка и инкрементная отрисовка
    2. порядок розыгрыша
    3. Список отображения Firefox
    4. Прямоугольное хранилище Webkit
  7. Динамические изменения
  8. поток движка рендеринга
    1. цикл событий
  9. CSS2 Visual Model.
    1. холст
    2. Блочная модель CSS
    3. Схема позиционирования
    4. тип коробки
    5. позиция
      1. относительное позиционирование
      2. плавающее позиционирование
      3. Абсолютное и фиксированное позиционирование
    6. Многоуровневое отображение
  10. ресурс

браузер, о котором мы говорим

В настоящее время используются пять основных браузеров: Internet Explorer, Firefox, Safari, Chrome и Opera. В этой статье в качестве примеров используются браузеры с открытым исходным кодом, а именно Firefox, Chrome и Safari (частично с открытым исходным кодом). согласно с Статистика браузера StatCounter, в настоящее время (август 2011 г.) Firefox, Safari и Chrome имеют совокупную долю рынка почти 60%. Как видите, в наши дни браузеры с открытым исходным кодом занимают очень солидный сегмент на рынке браузеров.

Основные возможности браузера

Основная функция браузера — отправить запрос на сервер и отобразить выбранные вами сетевые ресурсы в окне браузера. Упомянутые здесь ресурсы обычно относятся к документам HTML, но также могут быть PDF-файлами, изображениями или другими типами. Местоположение ресурса указывается пользователем с помощью URI (унифицированного идентификатора ресурса).

То, как браузеры интерпретируют и отображают HTML-файлы, указано в спецификациях HTML и CSS. Эти спецификации организованы Организацией по стандартизации сетей.Поддерживается W3C (Консорциум World Wide Web). 
На протяжении многих лет браузеры не полностью соответствовали этим спецификациям при разработке собственных уникальных расширений, что создавало серьезные проблемы совместимости для веб-разработчиков. Сегодня большинство браузеров более или менее соответствуют спецификациям.

Пользовательский интерфейс браузера имеет много общих элементов, в том числе:

  • Адресная строка для ввода URI
  • кнопки вперед и назад
  • Опции настроек закладок
  • Кнопки «Обновить» и «Стоп», чтобы обновить и остановить загрузку текущего документа.
  • Кнопка «Домой» для возврата на главную страницу

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

Высокоуровневая структура браузера

Основными компонентами браузера являются (1.1):

  1. Пользовательский интерфейс- Включает адресную строку, кнопки вперед/назад, меню закладок и многое другое. За исключением запрошенной вами страницы, которая отображается в главном окне браузера, различные отображаемые части являются частью пользовательского интерфейса.
  2. браузерный движок- Передача инструкций между пользовательским интерфейсом и механизмом рендеринга.
  3. движок рендеринга- Отвечает за отображение запрошенного контента. Если запрошенный контент представляет собой HTML, он отвечает за синтаксический анализ содержимого HTML и CSS и отображение проанализированного содержимого на экране.
  4. Интернет- Для сетевых вызовов, таких как HTTP-запросы. Его интерфейс не зависит от платформы и обеспечивает базовую реализацию для всех платформ.
  5. Внутренний пользовательский интерфейс- Используется для рисования основных виджетов, таких как поля со списком и окна. Он предоставляет независимый от платформы общий интерфейс, используя под капотом методы пользовательского интерфейса операционной системы.
  6. Интерпретатор JavaScript. Используется для анализа и выполнения кода JavaScript.
  7. хранилище данных. Это слой сохранения. Браузер должен сохранять на жестком диске различные данные, например файлы cookie. Новая спецификация HTML (HTML5) определяет «веб-базу данных», полную (но облегченную) базу данных в браузере.
Рисунок: Основные компоненты браузера. Стоит отметить, что, в отличие от большинства браузеров, каждая вкладка браузера Chrome соответствует экземпляру движка рендеринга. Каждая вкладка — это отдельный процесс.

движок рендеринга

Роль движка рендеринга. Конечно, это «рендеринг», то есть отображение запрошенного контента на экране браузера.

По умолчанию механизм рендеринга может отображать документы и изображения HTML и XML. С помощью подключаемых модулей (или расширений браузера) также можно отображать другие типы содержимого, например, документы PDF можно отображать с помощью подключаемого модуля для просмотра PDF. Но в этой главе мы сосредоточимся на его основной цели: отображении содержимого HTML и изображений, отформатированных с помощью CSS.

движок рендеринга

Браузеры, обсуждаемые в этой статье (Firefox, Chrome и Safari), построены на двух механизмах рендеринга. Firefox использует Gecko, самодельный движок рендеринга от Mozilla. Браузеры Safari и Chrome используют Webkit.

Webkit — это механизм рендеринга с открытым исходным кодом, первоначально разработанный для платформы Linux, а затем модифицированный Apple для поддержки Mac и Windows. Подробнее см.webkit.org.

основной процесс

Механизм рендеринга изначально получает содержимое запрошенного документа с сетевого уровня, а размер содержимого обычно ограничен 8000 блоков.

Затем выполните основной процесс, как показано ниже:

Рисунок: Основной поток механизма рендеринга. 

Механизм рендеринга начнет синтаксический анализ HTML-документа и преобразует каждый тег один за другим в тег в «дереве контента».DOMузел. Он также анализирует внешние файлы CSS и данные стиля в элементах стиля. Эта информация о стиле с визуальными инструкциями в HTML будет использоваться для создания другой древовидной структуры:визуализировать дерево.

Содержит дерево рендер с множеством прямоугольных визуальных атрибутов (таких как цвет и размер). Порядок расположения прямоугольников — это порядок, в котором они будут отображаться на экране.

После того, как дерево презентации построено, перейдите к «макет"этап обработки, то есть присвоение каждому узлу точной координаты, где он должен появиться на экране. Следующий этап -рисовать- Механизм рендеринга проходит по дереву рендеринга и рисует каждый узел на внутреннем уровне пользовательского интерфейса.

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

Пример основного процесса

Рисунок: Основной процесс WebkitРисунок: Основной процесс движка рендеринга Mozilla Gecko (3.6)  

Как видно из рисунков 3 и 4, хотя Webkit и Gecko используют немного разную терминологию, общий процесс в основном одинаков.

Gecko называет дерево элементов визуального форматирования «деревом фреймов». Каждый элемент является рамкой. Webkit использует термин «дерево рендеринга», состоящее из «объектов рендеринга». Для размещения элементов в Webkit используется термин «макет», а в Gecko — «перекомпоновка». Webkit использует термин «дополнение» для процесса соединения узлов DOM и визуальной информации для создания дерева рендеринга. Тонкое несемантическое отличие заключается в том, что Gecko также имеет слой между HTML и деревом DOM, называемый «слотом контента» для создания элементов DOM. Мы рассмотрим каждую часть процесса одну за другой:

Анализ — Обзор

Синтаксический анализ — очень важная часть движка рендеринга, поэтому мы объясним его более подробно. Во-первых, давайте представим анализ.

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

Пример — разбор2 + 3 - 1Это выражение вернет следующее дерево:

Рисунок: Узел дерева математических выражений

грамматика

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

Комбинация парсера и лексера

Процесс синтаксического анализа можно разделить на два подпроцесса: лексический анализ и синтаксический анализ.

Лексический анализ — это процесс разделения входных данных на большое количество токенов. Токены — это слова в языке, единицы, из которых состоит контент. В человеческом языке это эквивалентно слову в языковом словаре.

Синтаксический анализ — это процесс применения грамматических правил языка.

Синтаксический анализатор обычно делит работу по анализу на следующие два компонента:лексический анализатор(иногда называемый токенизатором), который отвечает за разбиение ввода на действительные токены; ипарсерОн отвечает за анализ структуры документа в соответствии с правилами грамматики языка, тем самым строя дерево синтаксического анализа. Лексер знает, как отделить посторонние символы, такие как пробелы и символы новой строки.

Рис.: От исходного документа к дереву синтаксического анализа

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

Если правил для сопоставления нет, синтаксический анализатор сохраняет маркеры внутри и продолжает запрашивать маркеры, пока не найдет правило, которое соответствует всем сохраненным внутри маркерам. Если соответствующие правила не найдены, синтаксический анализатор выдает исключение. Это означает, что документ недействителен и содержит синтаксические ошибки.

переводить

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

Рисунок: Процесс компиляции

Пример разбора

На рисунке 5 мы строим дерево синтаксического анализа из математического выражения. Теперь давайте попробуем определить простой математический язык для демонстрации процесса синтаксического анализа.

Словарь: язык, который мы используем, может содержать целые числа, знаки плюс и минус. грамматика:

  1. Синтаксическими единицами языка являются выражения, термины и операторы.
  2. Используемый нами язык может содержать любое количество выражений.
  3. Определение выражения: «элемент», за которым следует «оператор», за которым следует «элемент».
  4. Оператор - знак плюс или минус.
  5. Элемент представляет собой целое число или выражение.

Давайте проанализируем2 + 3 - 1
Первая подстрока, соответствующая грамматическому правилу, равна2, который является термином согласно грамматическому правилу 5. Вторая подстрока, соответствующая грамматическому правилу, равна2 + 3, которое является выражением по правилу 3 (за одним термином следует один оператор, затем еще один термин). Следующее совпадение достигло конца ввода.2 + 3 - 1является выражением, так как мы уже знаем, что2 + 3является термином, поэтому он следует правилу «за одним термином следует один оператор, за которым следует другой термин».2 + +не соответствует ни одному правилу и, следовательно, является недопустимым вводом.

Формальное определение лексики и грамматики

Обычно используется словарный запасрегулярное выражениеВыражать.

Например, наш пример языка может быть определен следующим образом:

?
INTEGER :0|[1-9][0-9]* PLUS : + MINUS: -
Как видите, здесь определение целого числа дается с использованием регулярного выражения.

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

?
expression :=  term  operation  term operation :=  PLUS | MINUS term := INTEGER | expression

Ранее мы говорили, что если грамматика языкаЗатем контекстно-свободные грамматики могут анализироваться обычными синтаксическими анализаторами. Интуитивное определение контекстно-свободной грамматики — это определение, которое может быть полностью выражено в формате BNF. Формальное определение см.Статья в Википедии о контекстно-свободной грамматике.

тип парсера

Существует два основных типа синтаксических анализаторов: синтаксические анализаторы «сверху вниз» и синтаксические анализаторы «снизу вверх». Интуитивно понятно, что нисходящие синтаксические анализаторы начинают с высокоуровневой структуры грамматики и пытаются найти из нее соответствующие структуры. Восходящий синтаксический анализатор начинает с правил нижнего уровня и постепенно преобразует входное содержимое в правила грамматики, пока не будут удовлетворены правила высокого уровня.

Давайте посмотрим, как эти два парсера будут анализировать наш пример:

Нисходящий парсер начинается с правил высокого уровня:2 + 3идентифицируется как выражение, а затем2 + 3 - 1Идентифицируется как выражение (процесс идентификации выражения включает сопоставление с другими правилами, но отправной точкой является правило самого высокого уровня).

  自下而上的解析器将扫描输入内容,找到匹配的规则后,将匹配的输入内容替换成规则。 Подстановка продолжается таким образом до конца ввода. Частично совпавшие выражения сохраняются в стеке парсера.

куча войти
  2 + 3 - 1
пункт + 3 - 1
срок эксплуатации 3 - 1
выражение - 1
оператор выражения 1
выражение  
Такие восходящие синтаксические анализаторы называются синтаксическими анализаторами сдвига-уменьшения, потому что ввод сдвигается вправо (представьте, что указатель перемещается от начала к концу ввода) и постепенно сводится к правилам грамматики.

автоматически сгенерированный парсер

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

Webkit использует два очень известных генератора синтаксических анализаторов: тот, который использовался для создания лексераFlexи для создания парсераBison(Вы также можете встретить такие псевдонимы, как Lex и Yacc). Входными данными для Flex является файл, содержащий определения регулярных выражений для токенов. Бизон На вход подаются правила грамматики языка в формате BNF.

парсер HTML

Работа синтаксического анализатора HTML заключается в анализе тегов HTML в дереве синтаксического анализа.

Определение синтаксиса HTML

Словарь и грамматика HTML, созданные организацией W3CТехнические характеристикиопределено в. Текущая версия — HTML4, HTML5 находится в процессе обработки.

неконтекстно-свободная грамматика

Как мы уже видели во введении к процессу синтаксического анализа, грамматики могут быть формально определены в таких форматах, как BNF.

К сожалению, все обычные парсеры не работают с HTML (я не шучу, их можно использовать для парсинга CSS и JavaScript). HTML нелегко определить с помощью контекстно-свободной грамматики, необходимой для синтаксических анализаторов. Существует формальный формат, в котором может быть определен HTML: DTD (определение типа документа), но это не контекстно-свободная грамматика.

Сначала это может показаться странным: HTML и XML очень похожи. Доступно множество парсеров XML. Существует XML-вариант HTML (XHTML), так в чем же большая разница? Разница в том, что к HTML относятся более «снисходительно», что позволяет вам опустить некоторые неявно добавленные теги, иногда некоторые открывающие или закрывающие теги и т. д. В отличие от строгого синтаксиса XML, HTML в целом является «мягким» синтаксисом.

Очевидно, что это, казалось бы, незначительное различие на самом деле имеет огромное значение. С одной стороны, именно поэтому HTML так популярен: он учитывает ваши ошибки и упрощает веб-разработку. С другой стороны, это затрудняет написание формальной грамматики. Короче говоря, HTML не может быть легко проанализирован обычными синтаксическими анализаторами (поскольку его грамматика не является контекстно-свободной) и не может быть проанализирован синтаксическими анализаторами XML.

HTML DTD

HTML определяется в формате DTD. Этот формат можно использовать для определенияSGMLязык племени. Он включает определения всех разрешенных элементов, их атрибутов и иерархий. Как упоминалось выше, HTML DTD не может представлять собой контекстно-свободную грамматику.

Существуют некоторые варианты DTD. Строгий режим полностью соответствует спецификации HTML, в то время как другие режимы поддерживают разметку, используемую предыдущими браузерами. Целью этого является обеспечение обратной совместимости с некоторыми более ранними версиями контента. Последний строгий режим DTD можно найти здесь:Я 3.org/TR/HTML4/body…

DOM

Выходное «дерево синтаксического анализа» синтаксического анализатора представляет собой древовидную структуру элементов DOM и узлов атрибутов. DOM — это сокращение от объектной модели документа. Это объектное представление документа HTML, а также интерфейс между внешним содержимым (например, JavaScript) и элементами HTML. 
Корневой узел дерева синтаксического анализа: "Document"Объект.ДОМ С тегами почти однозначное соответствие. Например, следующая разметка:

?
<html>   <body>     <p>       Hello World     </p>     <div> <img src="example.png" /></div>   </body> </html>
Можно перевести в следующее дерево DOM:

 

Рисунок: DOM-дерево для примера разметки

Как и HTML, DOM определяется организацией W3C. Видетьwww.w3.org/DOM/DOMTR. Это общая спецификация для работы с документами. Один из конкретных модулей описывает элементы для HTML. Определение HTML можно найти здесь:Ууху. Я 3.org/TR/2003/rec….

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

алгоритм разбора

Как мы говорили в предыдущих главах, HTML нельзя анализировать с помощью обычного анализатора «сверху вниз» или «снизу вверх». причина в следующем:

  1. Толерантный характер языка.
  2. Браузеры исторически были терпимы к некоторым распространенным недопустимым использованиям HTML.
  3. Процесс синтаксического анализа требует постоянной итерации. Исходный контент обычно не изменяется во время синтаксического анализа, но в HTML теги сценария, содержащиеdocument.write, добавляются дополнительные токены, так что процесс синтаксического анализа фактически изменяет входные данные.

Поскольку обычные методы синтаксического анализа использовать нельзя, браузеры создают собственные синтаксические анализаторы для анализа HTML.Спецификация HTML5 подробно описывает алгоритм синтаксического анализа.. Этот алгоритм состоит из двух этапов: токенизация и построение дерева.

Токенизация — это процесс лексического анализа, разбивающий входной контент на несколько токенов. Теги HTML включают открывающие теги, закрывающие теги, имена атрибутов и значения атрибутов. Токенизатор распознает токен, передает его построителю дерева, а затем принимает следующий символ для распознавания следующего токена и так далее до конца ввода.

Рисунок: Процесс синтаксического анализа HTML (из спецификации HTML5)

алгоритм токенизации

Результатом работы алгоритма является HTML-разметка. Алгоритм представлен с помощью конечного автомата. Каждое состояние получает один или несколько символов из входного потока и обновляет следующее состояние на основе этих символов. Текущее состояние токенизации и состояние древовидной структуры влияют на решение о переходе в следующее состояние. Это означает, что даже если полученные символы одинаковы, для следующего правильного состояния будут получены разные результаты в зависимости от текущего состояния. Алгоритм слишком сложен, чтобы описывать его здесь, поэтому давайте воспользуемся простым примером, чтобы помочь вам понять, как он работает.

Базовый пример — токенизировать следующий HTML-код:

?
<html>   <body>     Hello world   </body> </html>

Исходное состояние — это состояние данных. столкнуться с персонажем<, состояние меняется на"отметить открытое состояние". получитьa-zПерсонаж создает «начальный маркер», и его состояние меняется на"статус имени тега". Это состояние будет сохраняться до получения>Персонаж. Каждый символ, полученный за это время, добавляется к имени нового тега.在本例中,我们创建的标记是 htmlотметка.

встретить >При пометке будет отправлен текущий флаг и состояние будет изменено обратно«Статус данных».<body>Точно так же обрабатываются маркеры. В настоящее время html а также bodyтеги выданы. теперь мы возвращаемся к«Статус данных». получено Hello world середина Hсимвол, токен персонажа будет создан и отправлен до получения</body> середина<. мы будемHello worldКаждый персонаж в отправляет токен персонажа.

теперь мы возвращаемся"отметить открытое состояние". Получить следующий входной символ/, создастend tag tokenи изменился на"статус имени тега". Мы снова будем сохранять это состояние до тех пор, пока не получим>. Затем отправьте новые теги и обратно«Статус данных».</html>Ввод обрабатывается так же.

Рис.: Токенизация примера ввода

алгоритм построения дерева

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

Давайте посмотрим на процесс построения дерева для примера ввода:

?
<html>   <body>     Hello world   </body> </html>

Входными данными для этапа построения дерева является последовательность токенов с этапа токенизации. Первый режим"начальный режим". После получения тегов HTML конвертировать в"до html"режиме и повторно обработайте токен в этом режиме. Это создает элемент HTMLHtmlElement и прикрепляет его к корневому объекту документа.

то статус изменится на"перед головой". В этот момент мы получаем тег body. Несмотря на то, что в нашем примере нет тега «head», HTMLHeadElement неявно создается и добавляется в дерево.

Теперь мы в"в голове"режим, затем перейдите к"после головы"модель. Система повторно обрабатывает тег body, создает и вставляет HTMLBodyElement, а режим изменяется на"Тело".

Теперь получите последовательность токенов символов, сгенерированных из строки «Hello world». Узел «Текст» создается и вставляется при получении первого символа, а к этому узлу добавляются другие символы.

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

Рисунок: Здание дерева Например HTML

Действия после разбора

На этом этапе браузер пометит документ как интерактивный и начнет парсить те скрипты, которые находятся в «отложенном» режиме, то есть те скрипты, которые должны выполняться после парсинга документа. Затем состояние документа будет установлено как «завершено», и с ним сработает событие «загрузка». ты сможешьСм. полный алгоритм токенизации и построения дерева в спецификации HTML5.

Отказоустойчивость браузера

Вы никогда не увидите ошибку «недопустимый синтаксис» при просмотре HTML-страниц. Это связано с тем, что браузер исправляет любой недопустимый контент и продолжает работать.

В качестве примера возьмем следующий HTML-код:

?
<html>   <mytag>   </mytag>   <div>   <p>   </div>     Really lousy HTML   </p> </html>

Здесь я нарушил множество синтаксических правил ("mytag" не является стандартным тегом, неправильное вложение между элементами "p" и "div" и т. д.), но браузеры по-прежнему отображают их правильно, и никаких жалоб. Потому что существует много кода парсера, исправляющего ошибки авторов HTML-страниц.

Механизм обработки ошибок различных браузеров довольно согласован, но, что удивительно, этот механизм не является частью текущей спецификации HTML. Подобно управлению закладками и кнопкам «вперед/назад», это продукт разработки браузеров на протяжении многих лет. Существуют известные недопустимые структуры HTML, общие для многих веб-сайтов, и каждый браузер пытается исправить эти недопустимые структуры так же, как и другие браузеры.

Спецификация HTML5 определяет некоторые из этих требований. Webkit хорошо резюмирует это во вступительном комментарии класса парсера HTML.

Синтаксический анализатор анализирует токенизированный ввод для построения дерева документа. Если документ имеет правильный формат, он сразу переходит к синтаксическому анализу.

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

Как минимум, мы должны иметь возможность обрабатывать следующие состояния ошибок:

  1. Элементы, которые явно нельзя добавить в какую-то внешнюю разметку. В этом случае мы должны закрыть все теги до тех пор, пока не появится запрещенный элемент, а затем добавить этот элемент.
  2. Элементы, которые мы не можем добавить напрямую. Очень вероятно, что автор страницы забыл добавить некоторые из этих тегов (или некоторые из них были необязательными). Эти теги могут включать: HTML HEAD BODY TBODY TR TD LI (чего еще не хватает?).
  3. Добавьте блочный элемент внутрь встроенного элемента. Закрывает все встроенные элементы до тех пор, пока не появится следующий более высокий блочный элемент.
  4. Если это все еще не работает, закройте все элементы, пока элементы не будут добавлены, или проигнорируйте тег.

Давайте рассмотрим несколько примеров отказоустойчивости Webkit:

используется вместо

Некоторые сайты используют вместо
. Для совместимости с IE и Firefox Webkit обрабатывает его так же, как
. код показывает, как показано ниже:

?
if (t->isCloseTag(brTag) && m_document->inCompatMode()) {      reportError(MalformedBRError);      t->beginTag = true;
Обратите внимание, что обработка ошибок выполняется внутри и не видна пользователю.
дискретная таблица

Дискретная форма относится к столу, расположенной в других формах содержания, но не в любой клетке. Например, следующий пример:

?
<table>     <table>         <tr><td>inner table</td></tr>     </table>     <tr><td>outer table</td></tr> </table>
Webkit изменит свою иерархию на две одноуровневые таблицы:?
<table>     <tr><td>outer table</td></tr> </table> <table>     <tr><td>inner table</td></tr> </table>
код показывает, как показано ниже:?
if (m_inStrayTableContent && localName == tableTag)         popBlock(tableTag);
Webkit использует стек для хранения содержимого текущего элемента и извлекает внутреннюю таблицу из стека внешней таблицы. Теперь две таблицы становятся братьями и сестрами.
Вложенные элементы формы

Если пользователь поместит другую форму внутри элемента формы, вторая форма будет проигнорирована. код показывает, как показано ниже:

?
if (!m_currentFormElement) {         m_currentFormElement = new HTMLFormElement(formTag,    m_document);
Слишком сложная иерархия разметки

  代码的注释已经说得很清楚了。 

Образец веб-сайта www.liceo.edu.mx содержит около 1500 тегов, все из набора тегов . Мы разрешаем только до 20 уровней вложенности тегов одного типа, и любые другие вложения будут игнорироваться.
?
bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName) {   unsigned i = 0; for (HTMLStackElem* curr = m_blockStack;          i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;      curr = curr->next, i++) { } return i != cMaxRedundantTagDepth; }
Неуместные html или закрывающие теги тела

Опять же, комментарии к коду сделали это очень ясным.

Поддерживается очень плохо отформатированный HTML-код. Мы никогда не закрываем тег body, потому что некоторые дурацкие веб-страницы закрываются до окончания фактического документа. Мы выполняем операцию закрытия, вызывая end().
?
if (t->tagName == htmlTag || t->tagName == bodyTag )         return;
Таким образом, авторы веб-страниц должны принять к сведению, что если вы не хотите оказаться в чужих руках в примерах отказоустойчивых фрагментов Webkit, также пишите правильно сформированный HTML-код.

Разбор CSS

Помните концепцию синтаксического анализа во введении? В отличие от HTML, CSS — это контекстно-свободная грамматика, которую можно анализировать с помощью различных парсеров, описанных во введении. Фактически,Спецификация CSS определяет лексику и синтаксис CSS.. Давайте рассмотрим несколько примеров:

Лексические грамматики (словари) определяются с помощью регулярных выражений для каждого токена:

comment   \/\*[^*]*\*+([^/*][^*]*\*+)*\/
num   [0-9]+|[0-9]*"."[0-9]+
nonascii  [\200-\377]
nmstart   [_a-z]|{nonascii}|{escape}
nmchar    [_a-z0-9-]|{nonascii}|{escape}
name    {nmchar}+
ident   {nmstart}{nmchar}*

«ident» — это аббревиатура идентификатора, такого как имя класса. «имя» — это идентификатор элемента (обозначается знаком «#»).

Грамматика описывается в формате BNF.

?
ruleset   : selector [ ',' S* selector ]*     '{' S* declaration [ ';' S* declaration ]* '}' S*   ; selector   : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]?   ; simple_selector   : element_name [ HASH | class | attrib | pseudo ]*   | [ HASH | class | attrib | pseudo ]+   ; class   : '.' IDENT   ; element_name   : IDENT | '*'   ; attrib   : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*     [ IDENT | STRING ] S* ] ']'   ; pseudo   : ':' [ IDENT | FUNCTION S* [IDENT S*] ')' ]   
Объяснение: Это структура набора правил:?
div.error , a.error {   color:red;   font-weight:bold; }
div.error и a.error — это селекторы. Раздел в фигурных скобках содержит правила, применяемые этим набором правил. Формальное определение этой структуры таково:?
ruleset   : selector [ ',' S* selector ]*     '{' S* declaration [ ';' S* declaration ]* '}' S*   ;
Это означает, что набор правил представляет собой либо селектор, либо несколько (необязательных) селекторов, разделенных запятыми и пробелами (S для пробела). Набор правил состоит из фигурных скобок и одного или нескольких (необязательных) объявлений, разделенных точкой с запятой. «Объявление» и «Селектор» будут определены следующим форматом BNF.

CSS-парсер Webkit

Использование вебкитаФлекс и БизонГенератор парсеров, который автоматически создает парсеры из файлов грамматики CSS. Как мы уже говорили во введении к синтаксическим анализаторам, Bison создает синтаксические анализаторы с уменьшением и сдвигом снизу вверх. Firefox использует парсер сверху вниз, написанный человеком. Оба синтаксических анализатора будут анализировать файлы CSS в объекты StyleSheet, каждый из которых содержит правила CSS. Объекты правила CSS содержат объекты селектора и объявления, а также другие объекты, соответствующие синтаксису CSS.

Рис.: Разбор CSS

Порядок, в котором обрабатываются скрипты и таблицы стилей

сценарий

Модель сети синхронная. Авторы веб-страниц хотят, чтобы синтаксический анализатор анализировал и выполнял скрипт, как только встречал тег

готовить

И Webkit, и Firefox реализуют эту оптимизацию. Пока скрипт выполняется, другие потоки анализируют остальную часть документа, находя и загружая другие ресурсы, которые необходимо загрузить по сети. Таким образом, ресурсы могут загружаться по параллельным соединениям, повышая общую скорость. Обратите внимание, что препарсер не изменяет дерево DOM, а передает работу основному парсеру; препарсер разрешает только ссылки на внешние ресурсы, такие как внешние скрипты, таблицы стилей и изображения.

таблица стилей

С другой стороны, таблицы стилей имеют другую модель. Теоретически применение таблицы стилей не меняет дерево DOM, поэтому нет необходимости ждать таблицы стилей и останавливать синтаксический анализ документа. Но это влечет за собой проблему, заключающуюся в том, что сценарий будет запрашивать информацию о стиле на этапе синтаксического анализа документа. Если в это время стили не были загружены и проанализированы, скрипт получит неправильный ответ, что, очевидно, создаст массу проблем. Это может показаться нетипичным случаем, но на самом деле это очень распространенное явление. Firefox отключает все сценарии во время загрузки и анализа таблицы стилей. В случае с Webkit он подавляет сценарий только в том случае, если сценарий пытается получить доступ к свойству стиля, на которое может повлиять еще не загруженная таблица стилей.

визуализировать построение дерева

Одновременно с построением дерева DOM браузер также строит другую древовидную структуру: дерево рендеринга. Это дерево визуальных элементов в том порядке, в котором они отображаются, и визуальное представление документа. Что он делает, так это позволяет вам рисовать вещи в правильном порядке.

Firefox называет элементы в дереве рендеринга «фреймами». Термин, используемый Webkit, — средство визуализации или объект визуализации. 
Визуализатор знает, как размещать и рисовать себя и своих потомков. 
Класс Webkits RenderObject является базовым классом для всех средств визуализации и определяется следующим образом:

?
class RenderObject{   virtual void layout();   virtual void paint(PaintInfo);   virtual void rect repaintRect();   Node* node;  //the DOM node   RenderStyle* style;  // the computed style   RenderLayer* containgLayer; //the containing z-index layer }

Каждый модуль визуализации представляет собой прямоугольную область, обычно соответствующую блоку CSS связанного узла, как описано в спецификации CSS2. Он содержит геометрическую информацию, такую ​​как ширина, высота и положение. 
На тип блока влияет атрибут стиля display, связанный с узлом (см.расчет стиляглава).下面这段 Webkit 代码描述了根据 display 属性的不同,针对同一个 DOM 节点应创建什么类型的呈现器。

?
RenderObject* RenderObject::createObject(Node* node, RenderStyle* style) {     Document* doc = node->document();     RenderArena* arena = doc->renderArena();     ...     RenderObject* o = 0;       switch (style->display()) {         case NONE:             break;         case INLINE:             o = new (arena) RenderInline(node);             break;         case BLOCK:             o = new (arena) RenderBlock(node);             break;         case INLINE_BLOCK:             o = new (arena) RenderBlock(node);             break;         case LIST_ITEM:             o = new (arena) RenderListItem(node);             break;        ...     }       return o; }
Тип элемента также имеет значение, например, элементы управления формы и таблицы имеют специальные рамки. 
В Webkit, если для элемента нужно создать специальный рендерер, он заменитcreateRendererметод. Объект стиля, на который указывает средство визуализации, содержит некоторую независимую от геометрии информацию.
Связь между деревом рендеринга и деревом DOM
Рендереры соответствуют элементам DOM, но не взаимно однозначно. Невизуальные элементы DOM, такие как элемент «голова», не вставляются в дерево визуализации. Если элемент имеет значение свойства отображения «нет», он не будет отображаться в дереве рендеринга (но элементы со значением свойства видимости «скрытый» все равно будут отображаться).

Некоторые элементы DOM соответствуют нескольким визуализациям. Как правило, это элементы со сложной структурой, которые не могут быть описаны одним прямоугольником. Например, элемент «выбрать» имеет 3 средства визуализации: один для области отображения, один для раскрывающегося списка и один для кнопки. Если текст разбит на несколько строк из-за того, что он недостаточно широк для размещения в одной строке, новые строки также добавляются как новые средства визуализации. Другой пример нескольких средств визуализации — недопустимый HTML. Согласно спецификации CSS встроенный элемент может содержать только один из блочных элементов или встроенный элемент. Если присутствует смешанный контент, следует создать анонимные блочные рендереры для обертывания. встроенный элемент.

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

  Рисунок: Дерево рендеринга и соответствующее ему дерево DOM (3.1). Начальный блок-контейнер — это «окно просмотра», а в Webkit — объект «RenderView».
Процесс построения дерева рендеринга

В Firefox уровень представления регистрируется как прослушиватель обновлений DOM. Уровень представления делегирует создание кадраFrameConstructor, стили анализируются этим конструктором (см.расчет стиля) И создать рамки.

  在 Webkit 中,解析样式和创建呈现器的过程称为“附加”。 У каждого узла DOM есть метод «присоединения». Присоединение выполняется синхронно, и вставка узла в дерево DOM требует вызова метода «присоединить» нового узла.

Обработка тегов html и body создает корневой узел дерева рендеринга. Этот объект рендеринга корневого узла соответствует блоку-контейнеру в спецификации CSS, который является блоком верхнего уровня, содержащим все остальные блоки. Его размер — это область просмотра, размер области отображения окна браузера. Фаерфокс называет этоViewPortFrame, в то время как Webkit называет этоRenderView. Это объект рендеринга, на который указывает документ. Остальная часть дерева рендеринга построена в виде вставок узлов дерева DOM.

видетьСпецификация CSS2 по обработке моделей.

расчет стиля

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

Стили включают таблицы стилей из различных источников, встроенные элементы стилей и визуальные атрибуты в HTML (например, атрибут «bgcolor»). Последний из них будет преобразован в соответствии со свойствами стиля CSS.

Источники таблиц стилей включают таблицу стилей браузера по умолчанию, таблицы стилей, предоставленные авторами веб-страниц, и пользовательские таблицы стилей, предоставленные пользователями браузера (браузер позволяет вам определять ваши любимые стили. В Firefox, например, пользователи могут размещать таблицы стилей в папку «Профиль Firefox»).

Расчет стиля имеет следующие трудности:

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

    Например, следующий комбинаторный селектор:

    div div div div{...}
    Это означает, что правила применяются к элементам div, которые являются дочерними элементами трех элементов div.<div>. Если вы хотите проверить, применяется ли правило к указанному<div>элемент, путь вверх по дереву должен быть выбран для проверки. Возможно, вам придется пройтись по дереву узлов и обнаружить, что есть только два div и правила не применяются. Тогда вы должны попробовать другие пути в дереве.
  3. Применение правил включает довольно сложные каскадные правила (иерархия, используемая для определения этих правил).
Давайте посмотрим, как браузеры справляются с этими проблемами:
Данные общего стиля

Узел Webkit ссылается на объект стиля (RenderStyle). В некоторых случаях эти объекты могут совместно использоваться разными узлами. Эти узлы являются родственными и:

  1. Элементы должны находиться в одном и том же состоянии мыши (например, один не может находиться в состоянии «:hover», а другой — нет)
  2. Ни один элемент не имеет идентификатора
  3. Имена тегов должны совпадать
  4. атрибут класса должен соответствовать
  5. Набор сопоставленных свойств должен быть точно таким же
  6. состояние ссылки должно совпадать
  7. состояние фокуса должно совпадать
  8. Ни на один элемент не должен влиять селектор атрибута, под «влиянием» я подразумеваю любой селектор, соответствующий использованию селектора атрибута в любом месте селектора.
  9. Элементы не могут иметь встроенных атрибутов стиля.
  10. Нельзя использовать родственные селекторы. WebCore просто активирует глобальный переключатель, когда встречает любой родственный селектор, и отключает общий доступ к стилю для всего документа (если он существует). Сюда входят селектор + и такие селекторы, как :first-child и :last-child.
Дерево правил Firefox

Чтобы упростить вычисления стилей, Firefox также использует два других дерева: дерево правил и дерево контекста стиля. Webkit также имеет объекты стиля, но они не хранятся в древовидной структуре, как дерево контекста стиля, а просто указываются на соответствующие стили таких объектов узлами DOM.

Рисунок: контекстное дерево в стиле Firefox (2.2)  

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

Все правила сопоставления хранятся в дереве. Узлы более низкого уровня в пути имеют более высокий приоритет. Дерево правил содержит все известные пути сопоставления правил. Хранение правил выполняется лениво. Дерево правил не начинает вычисления для всех узлов, а только добавляет вычисленные пути к дереву правил, когда необходимо вычислить определенный стиль узла.

Идея эквивалентна трактовке путей дерева правил как слов в словаре. Если мы рассчитали следующее дерево правил:

Предположим, нам нужно сопоставить правило для другого элемента в дереве содержимого и найти соответствующий путь B — E — I (в таком порядке). Так как мы уже рассчитали путь A — B — E — I — L в дереве, этот путь уже есть, что уменьшает объем требуемой сейчас работы.

Давайте посмотрим, как деревья правил могут помочь нам сократить объем работы.

Структурное подразделение

Контекст стиля можно разделить на несколько структур. Эти структуры содержат информацию о стиле для определенных категорий, таких как граница или цвет. Свойства в структуре либо наследуются, либо не наследуются. Унаследованные свойства, если они не определены элементом, наследуются от его родителя. Ненаследуемые свойства (также известные как свойства «сброса») используют значения по умолчанию, если они не определены.

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

Вычислить контекст стиля, используя дерево правил

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

Если мы не можем найти какое-либо определение структуры, то, если структура является «унаследованным» типом, мы будемконтекстное деревоструктуры, которые указывают на родителя, так что структуры также могут быть общими. В случае сброса структуры типа используется значение по умолчанию.

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

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

Давайте рассмотрим пример, предположим, что у нас есть следующий HTML-код:

?
<html>   <body>     <div class="err" id="div1">       <p>         this is a <span class ="big"> big error </span>         this is also a         <span class= "big"> very  big  error</span> error       </p>     </div>     <div class="err" id="div2">another error</div>   </body> </html>
И следующие правила:
  1. div {margin:5px;color:black }
  2. .err {color:red}
  3. .big {margin-top:3px}
  4. div span {margin-bottom:4px}
  5. #div1 {color:blue}
  6. #div2 {color:green}

Для простоты нам нужно заполнить только две структуры: структуру цвета и структуру полей. Структура цвета содержит только один элемент («цвет»), а структура поля содержит четыре стороны. Сформированное дерево правил показано на следующем рисунке (узлы отмечены как «имя узла: номер правила, на который указывает»):

Рисунок: Дерево правил Дерево контекста показано на следующем рисунке (имя узла: узел правила, на который указывает):Рисунок: контекстное дерево

Предположим, мы столкнулись со вторым тегом

при разборе HTML, нам нужно создать контекст стиля для этого узла и заполнить его структуру стилей. 
После сопоставления правил мы обнаружили, что правила сопоставления
— это 1, 2 и 6. Это означает, что путь в дереве правил для нашего элемента уже есть, нам просто нужно добавить к нему еще один узел для соответствия правилу 6 (узел F в дереве правил). 
Мы создадим контекст стиля и поместим его в дерево контекста. Новый контекст стиля будет указывать на узел F в дереве правил.

Теперь нам нужно заполнить структуру стилей. Первое, что нужно заполнить, это структура маржи. Поскольку последний узел правила (F) не добавляется в структуру поля, нам нужно подниматься по дереву правил, пока мы не найдем структуру кэша, вычисленную при вставке предыдущего узла, а затем использовать эту структуру. Мы найдем эту структуру на самом верхнем узле (т. е. узле B), определяющем правило маржи.

У нас уже определена цветовая структура, поэтому мы не можем использовать кешированную структуру. Поскольку у цвета есть атрибут, нам не нужно подниматься по дереву правил, чтобы заполнить другие атрибуты. Мы вычислим конечные точки (преобразуем строки в RGB и т. д.) и кэшируем вычисленную структуру на этом узле.

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

Для структур, содержащих правила, унаследованные от родителя, кэширование выполняется в дереве контекста (на самом деле свойство цвета наследуется, но Firefox обрабатывает его как свойство сброса и кэширует его в дереве правил). Например, если мы добавим правило шрифта к абзацу:

?
p {font-family:Verdana;font size :10px;font-weight:bold}
Затем элемент абзаца, как дочерний элемент div в дереве контекста, будет использовать ту же структуру шрифта, что и его родитель (при условии, что для абзаца не заданы правила шрифта).

В Webkit нет дерева правил, поэтому совпадающие объявления перебираются 4 раза. Сначала применяются неважные свойства с высоким приоритетом (свойства, которые следует применять в первую очередь, поскольку они являются основой для других свойств, таких как отображение), за ними следуют важные правила с высоким приоритетом, затем неважные правила с обычным приоритетом и, наконец, важное правило с нормальным приоритетом. Это означает, что несколько вхождений свойств разрешаются в соответствии с правильным порядком наложения. Последний, появившийся, наконец, вступает в силу. 

Короче говоря, совместное использование объектов стиля (всего объекта или части структуры в объекте) может решить проблему.1и вопросы3. Дерево правил Firefox также помогает применять свойства в правильном порядке.

Манипулируйте правилами, чтобы упростить сопоставление

Есть несколько источников правил стиля:

  • Правила CSS во внешних таблицах стилей или элементах стиля
    p {color:blue}
  • встроенные атрибуты стиля и тому подобное
    <pstyle="color:blue"/>
  • Визуальные атрибуты HTML (сопоставленные с соответствующими правилами стиля)
    <pbgcolor="blue"/>

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

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

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

В качестве примера возьмем следующие правила стиля:

?
p.error {color:red} #messageDiv {height:50px} div {margin:5px}
Первое правило будет вставлено в таблицу классов, второе — в таблицу идентификаторов, а третье — в таблицу тегов. 
Для следующего фрагмента HTML:?
<p class="error">an error occurred </p> <div id=" messageDiv">this is a message</div>

Сначала мы ищем правило сопоставления для элемента p. В таблице классов есть ключ «ошибка», ниже которого можно найти правила для «p.error». Элемент div имеет связанные с ним правила в таблице идентификаторов (ключ — идентификатор) и таблице тегов. Осталось выяснить, какие правила, выбранные по ключу, действительно совпадают. Например, если соответствующие правила для div следующие:

?
table div {margin:5px}
Это правило по-прежнему будет извлечено из таблицы тегов, поскольку ключ является самым правым селектором, но это правило не будет соответствовать нашему элементу div, поскольку у div нет таблицы-предка.

И Webkit, и Firefox делают это.

Применение правил в правильном каскадном порядке

Объект стиля имеет свойство «один к одному» для каждого визуального свойства (оба являются свойствами CSS, но являются более общими). Если свойство не определено какими-либо правилами сопоставления, некоторые свойства могут быть унаследованы объектом стиля родительского элемента. Другие свойства имеют значения по умолчанию.

Если вы определите более одного, возникнут проблемы, которые необходимо решить с помощью каскадного порядка.

каскадный порядок таблицы стилей
Объявление свойства стиля может появляться в нескольких таблицах стилей или несколько раз в одной таблице стилей. Это означает, что порядок, в котором применяются правила, чрезвычайно важен. Это называется «каскадным» порядком. Согласно спецификации CSS2 порядок каскадирования (от низкого к высокому приоритету):
  1. декларация браузера
  2. Общее заявление пользователя
  3. Общее заявление автора
  4. Важное заявление от автора
  5. Важное замечание для пользователей

Объявление браузера является наименее важным, и пользователи могут заменить объявление автора веб-страницы только пометив объявление как «важное». Объявления в том же порядке будутспецифичностьсортируется, а затем в указанном порядке. Визуальные атрибуты HTML преобразуются в соответствующие объявления CSS. Они считаются правилами автора веб-страницы с низким приоритетом.

специфичность

Специфика селектора определяетсяСпецификация CSS2Определяется следующим образом:

  • 1, если объявление исходит из атрибута «стиль», а не правила с селектором, иначе 0 (= а)
  • подсчитывается как количество атрибутов ID в селекторе (= b)
  • Подсчитывается как количество других атрибутов и псевдоклассов в селекторе (= c)
  • обозначают имя элемента и количество псевдоэлементов в селекторе (= d)
Соединение четырех чисел таким образом, как a-b-c-d (в системе больших чисел), составляет специфичность.

База, которую вы используете, зависит от наибольшего количества в вышеуказанных категориях. Например, если a=14, вы можете использовать шестнадцатеричный формат. Если a=17, то нужно использовать hex; конечно это маловероятно, если только нет селектора типа этого: html body div div p ... (в селекторе появляется 17 тегов, это крайне маловероятно).

Некоторые примеры:

?
*             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */ li            {}  /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */ li:first-line {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */ ul li         {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */ ul ol+li      {}  /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */ h1 + *[rel=up]{}  /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */ ul ol li.red  {}  /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */ li.red.level  {}  /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */ #x34y         {}  /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */ style=""          /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

 

Порядок правил

После нахождения совпадающих правил их следует отсортировать в порядке каскада. Webkit использует пузырьковую сортировку для небольших списков и сортировку слиянием для больших списков. Webkit реализует сортировку, заменяя оператор «>» для следующих правил:

?
static bool operator >(CSSRuleData& r1, CSSRuleData& r2) {     int spec1 = r1.selector()->specificity();     int spec2 = r2.selector()->specificity();     return (spec1 == spec2) : r1.position() > r2.position() : spec1 > spec2; }

прогрессивная обработка

Webkit использует флаг, чтобы указать, были ли загружены все таблицы стилей верхнего уровня (включая @imports). Если стиль загружается не полностью во время процесса добавления, используйте заполнитель, отметьте его в документе и пересчитайте его при загрузке таблицы стилей.

макет

Средство визуализации не содержит информации о положении и размере при создании и добавлении в дерево визуализации. Процесс вычисления этих значений называется компоновкой или перекомпоновкой.

HTML использует поточную модель компоновки, что означает, что в большинстве случаев геометрия может быть рассчитана за один проход. Элементы, расположенные позже в потоке, обычно не влияют на геометрию элементов, находящихся раньше в потоке, поэтому макет может перемещаться по документу слева направо и сверху вниз. Но есть исключения, такие как расчеты HTML-таблицы, требующие более одного обхода (3.5).

Система координат устанавливается относительно корневого кадра с использованием верхней и левой координат.

Макет — это рекурсивный процесс. Он начинается с корневого рендерера (соответствующего HTML-документу).<html>element), затем рекурсивно обходят часть или всю иерархию кадров, вычисляя информацию о геометрии для каждого средства визуализации, которое необходимо вычислить.

Позиция корневого рендерера — 0,0 левее, а его размер — область просмотра (то есть видимая область окна браузера).

Все средства визуализации имеют метод «макета» или «перекомпоновки», и каждый модуль визуализации вызывает метод макета своих дочерних элементов, которые необходимо разместить.

Грязная битовая система

Чтобы избежать общего расположения всех мелких изменений, браузеры используют систему «грязных битов». Макет требуется, если средство визуализации изменяется или помечает себя и свои дочерние элементы как «грязные».

Есть два флага: «грязный» и «дети грязные». "дети грязные" означает, что хоть сам рендерер и не изменился, но у него есть хотя бы один дочерний элемент, который нужно выложить.

Глобальный макет и добавочный макет

Глобальный макет относится к макету, который запускает всю область дерева рендеринга.Причины срабатывания могут включать:

  1. Глобальные изменения стиля, влияющие на все средства визуализации, например, изменение размера шрифта.
  2. Изменение размера экрана.

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

  Рисунок: Инкрементная компоновка — выкладываются только грязные рендереры и их дочерние элементы (3.6).

Асинхронный макет и синхронный макет

Инкрементная компоновка выполняется асинхронно. Firefox ставит в очередь «команды перекомпоновки» добавочного макета, а планировщик запускает пакетное выполнение этих команд. В Webkit также есть таймеры для выполнения инкрементных макетов: обхода дерева рендеринга и размещения грязных рендереров. 
Сценарии, запрашивающие информацию о стиле (например, «offsetHeight»), могут синхронно запускать добавочные макеты. 
Глобальные макеты часто запускаются синхронно. 
Иногда после того, как первоначальный макет завершен, макет запускается как обратный вызов, если некоторые свойства (например, положение прокрутки) изменяются.

оптимизация

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

обработка макета

Макеты обычно имеют следующие шаблоны:

  1. Родительский рендерер определяет свою ширину.
  2. Родительский модуль визуализации по очереди обрабатывает дочерние модули визуализации и:
    1. Поместите дочерний рендерер (установите координаты x, y).
    2. При необходимости вызвать макет дочернего рендерера (если дочерний рендерер грязный, или это глобальный макет, или по какой-то другой причине), который вычисляет высоту дочернего рендерера.
  3. Родительский модуль визуализации устанавливает свою собственную высоту на основе накопленной высоты дочернего модуля визуализации и высоты полей и отступов, и это значение также доступно родителю родительского модуля визуализации.
  4. Установите его грязный бит в false.

Firefox использует объект «состояние» (nsHTMLReflowState) в качестве параметра макета (называемого «перекомпоновка»), который включает в себя ширину родительского средства визуализации. Выходные данные макета Firefox представляют собой объект «метрик» (nsHTMLReflowMetrics), который содержит рассчитанную высоту средства визуализации.

расчет ширины

Ширина средства визуализации рассчитывается на основе ширины блока контейнера, свойства «ширина» в стиле средства визуализации, а также полей и границ. Например, ширина следующего div:

<divstyle="width:30%"/>
будет рассчитываться Webkit следующим образом (класс BenderBox, метод calcWidth):
  • Ширина контейнера принимает большее значение из доступной ширины контейнера и равно 0. availableWidth эквивалентен contentWidth в этом примере и рассчитывается следующим образом:
    clientWidth()- paddingLeft()- paddingRight()
    clientWidth и clientHeight представляют внутреннюю часть объекта (за исключением границ и полос прокрутки).
  • Ширина элемента является атрибутом стиля "width". Он вычисляет абсолютное значение на основе процента ширины контейнера.
  • Затем добавьте горизонтальную границу и отступы.
Теперь рассчитывается «предпочтительная ширина». Затем необходимо рассчитать минимальную и максимальную ширину. 
Если предпочтительная ширина больше максимальной ширины, следует использовать максимальную ширину. Если предпочтительная ширина меньше минимальной ширины (наименьшая неразрывная единица), то следует использовать минимальную ширину.

Эти значения кэшируются для использования в тех случаях, когда требуется макет, но ширина не меняется.

новая линия

Если рендереру необходимо выполнить перенос во время макета, он немедленно остановит макет и сообщит своему родителю, что требуется перенос. Родитель создает дополнительные средства визуализации и вызывает для них layout.

рисовать

На этапе рисования система просматривает дерево рендеринга и вызывает метод «paint» модуля рендеринга для отображения содержимого модуля рендеринга на экране. Работа по рисованию выполняется с использованием основных компонентов пользовательского интерфейса.

Глобальная отрисовка и инкрементная отрисовка

Как и компоновка, отрисовка делится на глобальную (отрисовка всего дерева рендеринга) и пошаговую. При добавочном рисовании изменяется часть средства визуализации, но не затрагивается все дерево. Измененный рендерер делает недействительной соответствующую прямоугольную область на экране, что заставляет ОС рассматривать ее как «грязную область» и генерировать событие «закрашивание». ОС аккуратно объединяет несколько регионов в один. В Chrome ситуация немного сложнее, так как рендерер Chrome не находится на основном процессе. Браузер Chrome в некоторой степени эмулирует поведение ОС. Уровень представления прослушивает эти события и делегирует сообщения корневому узлу рендеринга. Затем дерево рендеринга просматривается до тех пор, пока не будет найден соответствующий рендерер, который перерисовывает себя (и, как правило, своих дочерних элементов).

порядок розыгрыша

Спецификация CSS2 определяет порядок процесса рисования.. Порядок отрисовки - это на самом деле элемент, входящийконтекст стиля стекаЗаказ. Эти стеки рисуются сзади наперед, поэтому этот порядок влияет на рисование. Порядок укладки рендереров блоков следующий:
  1. фоновый цвет
  2. Фоновое изображение
  3. Рамка
  4. потомство
  5. контур

Список отображения Firefox

Firefox просматривает все дерево рендеринга, создавая список отображения для нарисованных прямоугольников. Список содержит средства визуализации, связанные с прямоугольником, в правильном порядке отрисовки (сначала фон средства визуализации, затем граница и т. д.). Таким образом, когда придет время перерисовывать, вам нужно будет пройти дерево рендеринга только один раз вместо нескольких обходов (отрисовать все фоны, затем все изображения, затем все границы и т. д.).

Firefox оптимизирует этот процесс, не добавляя скрытые элементы, например, полностью закрытые непрозрачными элементами.

Прямоугольное хранилище Webkit

Перед перерисовкой Webkit сохранит исходный прямоугольник как растровое изображение, а затем только нарисует разницу между старым и новым прямоугольниками. 

Динамические изменения

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

поток движка рендеринга

Механизм рендеринга является однопоточным. Почти все операции (кроме сетевых) выполняются в одном потоке. В Firefox и Safari этот поток является основным потоком браузера. В браузере Chrome этот поток является основным потоком процесса вкладки. 
Сетевые операции могут выполняться несколькими параллельными потоками. Количество одновременных подключений ограничено (обычно от 2 до 6, например, 6 в Firefox 3).

цикл событий

Основной поток браузера — это цикл обработки событий. Это бесконечный цикл, который всегда находится в состоянии принятия, ожидая возникновения событий (таких как события компоновки и рисования) и обрабатывая их. Вот код основного цикла обработки событий в Firefox:
while(!mExiting)
    NS_ProcessNextEvent(thread);

Визуальная модель CSS2

холст

согласно с Спецификация CSS2, термин «холст» относится к «пространству, используемому для отображения структуры форматирования», то есть к области, в которой браузер рисует содержимое. Размер холста не ограничен, но браузер выбирает начальную ширину в зависимости от размера области просмотра.

согласно с Уууу. Я 3.org/TR/CSS2/children…, холст прозрачен, если он содержится в других холстах, в противном случае браузер назначает цвет.

Блочная модель CSS

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

Рисунок: Блочная модель CSS2

Каждый узел генерирует 0..n таких ящиков. Все элементы имеют свойство «отображать», которое определяет тип создаваемого ими поля. Пример:

?
block  - generates a block box. inline - generates one or more inline boxes. none - no box is generated.
По умолчанию используется встроенный, но таблица стилей браузера устанавливает другие значения по умолчанию. Например, значением по умолчанию свойства отображения элемента "div" является блок. 
Вы можете найти примеры таблиц стилей по умолчанию здесь:Уууу, я 3.org/TR/CSS2/Сэм… 

Схема позиционирования

Существует три схемы позиционирования:

  1. Обычный: объект позиционируется в соответствии с его положением в документе, то есть положение объекта в дереве рендеринга аналогично его положению в дереве DOM, и он размещается в соответствии с типом и размером блока.
  2. Плавающий: объект размещается в обычном потоке, а затем перемещается как можно дальше влево или вправо.
  3. Абсолютный: положение объекта в дереве рендеринга отличается от его положения в дереве DOM.

Схема позиционирования задается свойствами position и loat.

  • Если значение статичное и относительное, это нормальный поток.
  • Если значение абсолютное и фиксированное, это абсолютное позиционирование.
Статическое позиционирование не требует определения положения, оно использует позиционирование по умолчанию. Для других схем автору веб-страницы необходимо указать положение: сверху, снизу, слева, справа.

Способ выкладки ящиков определяется следующими факторами:

  • тип коробки
  • размер коробки
  • Схема позиционирования
  • Внешняя информация, такая как размер изображения и размер экрана

тип коробки

block box: формирует блок, который имеет собственную прямоугольную область в окне браузера.

Рис.: блок-бокс

встроенный блок: не имеет собственного блока, но находится внутри блока-контейнера.

Рисунок: встроенный блок

блок находится в вертикальном формате один за другим, а встроенный в горизонтальном формате.

Рисунок: блочные и встроенные форматы

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

Рис.: ряд

позиция

относительно

Относительное позиционирование: сначала позиционируйте обычным образом, а затем двигайтесь в соответствии с желаемым смещением.

Рис.: Относительное расположение

плавать

Поплавок будет перемещаться влево или вправо от строки. Интересной особенностью является то, что вокруг него плавают другие ящики. Следующий HTML-код:

?
<p>   <img style="float:right" src="images/image.gif" width="100" height="100">   Lorem ipsum dolor sit amet, consectetuer... </p>
Эффект отображения следующий:

Рис.: Поплавок

Абсолютное и фиксированное позиционирование

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

Рис.: Фиксированное позиционирование
Обратите внимание, что фиксированное поле не перемещается даже при прокрутке документа.

Многоуровневое отображение

Это определяется свойством CSS z-index. Он представляет собой третье измерение поля, то есть его положение вдоль «оси Z».

Эти блоки распределены по нескольким стекам (называемым контекстами стека). В каждом стеке более поздние элементы отрисовываются первыми, а затем более ранние элементы сверху, чтобы быть ближе к пользователю. Если есть перекрытие, новый нарисованный элемент перезапишет предыдущий элемент. Стеки сортируются по свойству z-index. Ящики со свойством "z-index" образуют локальный стек. Окна просмотра имеют внешние стеки.

Пример:

?
<style type="text/css">       div {         position: absolute;         left: 2in;         top: 2in;       } </style>   <p>     <div          style="z-index: 3;background-color:red; width: 1in; height: 1in; " >     </div>     <div          style="z-index: 1;background-color:green;width: 2in; height: 2in;" >     </div>  </p>
Результат выглядит следующим образом:

 

Рис.: Фиксированное позиционирование

В то время как красный div находится выше в разметке, чем зеленый div (и должен быть нарисован первым в обычном потоке), атрибут z-index имеет приоритет, поэтому он перемещается выше в стеке, удерживаемом корневым полем Location.

использованная литература

  1. архитектура браузера
    1. Grosskurth, Alan. A Reference Architecture for Web Browsers (pdf)
    2. Gupta, Vineet. How Browsers Work - Part 1 - Architecture
  2. Разобрать
    1. Ахо, Сетхи, Ульман, Компиляторы: принципы, методы и инструменты («Книга дракона»), Аддисон-Уэсли, 1986 г.
    2. Rick Jelliffe. The Bold and the Beautiful: two new drafts for HTML 5.
  3. Firefox
    1. L. David Baron, Faster HTML and CSS: Layout Engine Internals for Web Developers.
    2. L. David Baron, Быстрее HTML и CSS: внутреннее устройство механизма компоновки для веб-разработчиков (видео технического интервью Google)
    3. L. David Baron, Mozilla's Layout Engine
    4. L. David Baron, Mozilla Style System Documentation
    5. Chris Waterson, Notes on HTML Reflow
    6. Chris Waterson, Gecko Overview
    7. Alexander Larsson, The life of an HTML HTTP request
  4. Webkit
    1. David Hyatt, Реализация CSS (часть 1)
    2. David Hyatt, An Overview of WebCore
    3. David Hyatt, WebCore Rendering
    4. David Hyatt, The FOUC Problem
  5. Спецификация W3C
    1. Спецификация HTML 4.01
    2. Спецификация W3C HTML5
    3. Каскадные таблицы стилей, уровень 2, версия 1 (CSS 2.1), спецификация
  6. Инструкции по сборке браузера
    1. Firefox. developer.Mozilla.org/en/build_do…
    1. Webkit. WebKit.org/building/no…
Похожие статьи, которые могут вас заинтересовать

 

Перевод с:Frontend Digest: глубокое погружение в то, как браузеры работают за кулисами.

Компилировать источник:Dream Sky ◆ Обратите внимание на технологию разработки интерфейса ◆ Делитесь ресурсами веб-дизайна

Источник этой статьиwww.cnblogs.com/lhb25/

Рекомендуемые сообщения

Web 开发中很实用的10个效果【源码下载】 Новый сайт этого блога ◆ В интерфейсе ◆ Добро пожаловать, зрители :)

автор:горный ручей
Источник:jizhula.comЗапомни :)
Любая форма перепечатки приветствуется, но обязательно с указанием источника.