Как браузер точно анализирует тег script?
Я просто столкнулся с патологическим случаем с разбором HTML. Я всегда думал, что тег <script>
будет работать до первого закрывающего тега </script>
. Но оказывается, что это не всегда так.
Это действительно:
<script><!--
alert('<script></script>');
--></script>
И даже это действительно:
<script><!--
alert('<script></script>');
</script>
Но это не так:
<script><!--
alert('</script>');
--></script>
И это не так:
<script>
alert('<script></script>');
</script>
Такое поведение согласуется с Firefox и Chrome. Таким образом, как трудно полагать, браузеры, похоже, принимают тег open + close script внутри комментария html внутри тега script. Итак, вопрос в том, как браузер действительно анализирует теги script?
Это имеет значение, потому что библиотека разбора HTML, которую я использую, Nokogiri, принимала очевидное (но неправильное) правило до первого закрытия и не обрабатывала этот край. Я полагаю, что большинство других библиотек тоже не справятся.
Ответы
Ответ 1
После просмотра ссылок предоставленных Тимом и Jukka Я пришел к следующему ответу:
- после открытия тега
<script>
, синтаксический анализатор переходит в состояние data1
- Если
<!--
встречается в состоянии data1, переключитесь в состояние data2
- Если
-->
встречается в любом состоянии, переключитесь в состояние data1
- Если
<script[\s/>]
встречается в состоянии data2, переключитесь в состояние data3
- Если
</script[\s/>]
встречается в состоянии data3, переключитесь в состояние data2
- Если
</script[\s/>]
встречается в любом другом состоянии, прекратите синтаксический анализ
Ответ 2
Все примеры недопустимы в соответствии со спецификацией HTML 4.01: содержимое script
объявляется как CDATA
, а description из CDATA
говорит:
"Хотя элементы STYLE и SCRIPT используют CDATA для своей модели данных, для этих элементов CDATA должен обрабатываться по-разному с помощью пользовательских агентов. Разметка и объекты должны обрабатываться как исходный текст и передаваться в приложение как есть. первое вхождение символьной последовательности" </
"(разделитель конца конечного тега) рассматривается как завершение конца содержимого элемента. В действительных документах это будет конечным тегом для элемента."
Как вы заметили, браузеры могут не применять это правило, но вместо этого распознавать пары стартовых и конечных тегов в некоторых ситуациях. С точки зрения спецификации это обработка недопустимых документов, то есть обработка ошибок. Неясно, что именно они здесь делают и почему. Кажется, что это зависит от наличия <!--
, что не должно влиять на разбор HTML 4.01 (он не является комментарием в тексте CDATA
).
В XHTML применяются несколько другие правила, поскольку в XHTML <!--
открывается комментарий в содержимом элемента script
.
В стороне все примеры недействительны HTML 4.01 и неверный XHTML из-за отсутствия атрибута type
в script
. Атрибут не требуется (браузеры по умолчанию обрабатывают контент как JavaScript), но его требуется для этих спецификаций.
В HTML5 применяются другие правила. Они довольно сложны, и они должны описывать поведение браузера. В дополнение к наложению ограничений на контент (запрещающий, например, <!--
без соответствия -->
), HTML5 также указывает правила анализа.
Ответ 3
Содержимое тегов по-прежнему является HTML, если вы не отметили его как не HTML. В HTML <word>
воспринимается как тег, <
необходимо записать как <
, чтобы избежать такого поведения. В качестве альтернативы вы хотите сделать содержимое <script>
текстовым node; используйте эту формулу:
<script type="text/javascript">
//<![CDATA[
// your code, with < and & and "", woohoo!
//]]>
</script>
<![CDATA[ ... ]]>
определяет часть документа как чистый текст без разметки. Слэш там, поэтому JavaScript не запутался; первый набор косой черты находится вне CDATA, но они безопасны для HTML, поэтому проблем нет.
EDIT: Просто понял, что речь идет о разборе, а не написании HTML. К сожалению.
Ответ 4
Гипотетически, если теги сначала разбираются и комментарии анализируются позже, анализатор HTML даст вам эти результаты.
(я не имею в виду, что это обязательно так, просто возможное объяснение.)
1-й случай
<script><!--
alert('<script></script>');
--></script>
Внутри другого <script></script>
есть набор <script></script>
. Парсер может сначала игнорировать имя тегов и просто проверяет правильность открытия и закрытия этих тегов. Затем он анализирует комментарии.
<script><!--
--></script>
Итак, это действительно.
Второй случай
<script><!--
alert('<script></script>');
</script>
Внутри другого <script></script>
есть набор <script></script>
. Затем он анализирует комментарии.
<script><!--
Комментарий распространяется до конца документа. Это не является строго правильным, но браузер правильно его обрабатывает.
Третий случай
<script><!--
alert('</script>');
--></script>
В наборе <script></script>
есть один закрытый тег. Он недействителен перед тем, как он проанализирует </script>
как комментарии.
4-й случай
<script>
alert('<script></script>');
</script>
Внутри другого <script></script>
есть набор <script></script>
, и комментариев нет. Первый проход действителен, но затем он действительно просматривает теги, чтобы узнать, что они собой представляют. Он может не принимать пару тегов <script>
внутри другого, что делает его недействительным.