From 12e07fdf217eda724e703e32ec5c8b968bb3a727 Mon Sep 17 00:00:00 2001 From: Jason Woofenden Date: Wed, 23 Dec 2015 13:42:52 -0500 Subject: [PATCH] fix main loop, process foreign content --- parse-html.coffee | 236 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 200 insertions(+), 36 deletions(-) diff --git a/parse-html.coffee b/parse-html.coffee index ca5a804..31a46f4 100644 --- a/parse-html.coffee +++ b/parse-html.coffee @@ -320,6 +320,24 @@ formatting_elements = { u: true } +mathml_text_integration = { + mi: NS_MATHML, mo: NS_MATHML, mn: NS_MATHML, ms: NS_MATHML, mtext: NS_MATHML +} +is_mathml_text_integration_point = (el) -> + return mathml_text_integration[el.name] = el.namespace +is_html_integration = (el) -> # DON'T PASS A TOKEN + if el.namespace is NS_MATHML and el.name is 'annotation-xml' + if el.attrs.encoding? + if el.attrs.encoding.toLowerCase() is 'text/html' + return true + if el.attrs.encoding.toLowerCase() is 'application/xhtml+xml' + return true + return false + if el.namespace is NS_SVG + if el.name is 'foreignObject' or el.name is 'desc' or el.name is 'title' + return true + return false + h_tags = { h1:NS_HTML, h2:NS_HTML, h3:NS_HTML, h4:NS_HTML, h5:NS_HTML, h6:NS_HTML } @@ -355,6 +373,45 @@ adp_els = { address: NS_HTML, div: NS_HTML, p: NS_HTML } el_is_special_not_adp = (el) -> return special_elements[el.name] is el.namespace and adp_els[el.name] isnt el.namespace +svg_name_fixes = { + altglyph: 'altGlyph' + altglyphdef: 'altGlyphDef' + altglyphitem: 'altGlyphItem' + animatecolor: 'animateColor' + animatemotion: 'animateMotion' + animatetransform: 'animateTransform' + clippath: 'clipPath' + feblend: 'feBlend' + fecolormatrix: 'feColorMatrix' + fecomponenttransfer: 'feComponentTransfer' + fecomposite: 'feComposite' + feconvolvematrix: 'feConvolveMatrix' + fediffuselighting: 'feDiffuseLighting' + fedisplacementmap: 'feDisplacementMap' + fedistantlight: 'feDistantLight' + fedropshadow: 'feDropShadow' + feflood: 'feFlood' + fefunca: 'feFuncA' + fefuncb: 'feFuncB' + fefuncg: 'feFuncG' + fefuncr: 'feFuncR' + fegaussianblur: 'feGaussianBlur' + feimage: 'feImage' + femerge: 'feMerge' + femergenode: 'feMergeNode' + femorphology: 'feMorphology' + feoffset: 'feOffset' + fepointlight: 'fePointLight' + fespecularlighting: 'feSpecularLighting' + fespotlight: 'feSpotLight' + fetile: 'feTile' + feturbulence: 'feTurbulence' + foreignobject: 'foreignObject' + glyphref: 'glyphRef' + lineargradient: 'linearGradient' + radialgradient: 'radialGradient' + textpath: 'textPath' +} svg_attribute_fixes = { attributename: 'attributeName' attributetype: 'attributeType' @@ -1081,6 +1138,36 @@ parse_html = (txt, parse_error_cb = null) -> return dest[0].children.splice dest[1], 0, t + + # 8.2.5 http://www.w3.org/TR/html5/syntax.html#tree-construction + process_token = (t) -> + acn = adjusted_current_node() + unless acn? + ins_mode t + return + if acn.namespace is NS_HTML + ins_mode t + return + if is_mathml_text_integration_point(acn) + if t.type is TYPE_START_TAG and (t.name is 'mglyph' or t.name is 'malignmark') + ins_mode t + return + if t.type is TYPE_TEXT + ins_mode t + return + if acn.namespace is NS_MATHML and acn.name is 'annotation-xml' and t.type is TYPE_START_TAG and t.name is 'svg' + ins_mode t + return + if is_html_integration acn + if t.type is TYPE_START_TAG or t.type is TYPE_TEXT + ins_mode t + return + if t.type is TYPE_EOF + ins_mode t + return + in_foreign_content t + return + # 8.2.5.1 # http://www.w3.org/TR/html5/syntax.html#creating-and-inserting-nodes # http://www.w3.org/TR/html5/syntax.html#appropriate-place-for-inserting-a-node @@ -1253,7 +1340,7 @@ parse_html = (txt, parse_error_cb = null) -> # Anything else #fixfull (iframe, quirks) ins_mode = ins_mode_before_html - ins_mode t # reprocess the token + process_token t return # 8.2.5.4.2 http://www.w3.org/TR/html5/syntax.html#the-before-html-insertion-mode @@ -1286,7 +1373,7 @@ parse_html = (txt, parse_error_cb = null) -> open_els.unshift el # ?fixfull browsing context ins_mode = ins_mode_before_head - ins_mode t + process_token t return # 8.2.5.4.3 http://www.w3.org/TR/html5/syntax.html#the-before-head-insertion-mode @@ -1317,13 +1404,13 @@ parse_html = (txt, parse_error_cb = null) -> el = insert_html_element head_tok head_element_pointer = el ins_mode = ins_mode_in_head - ins_mode t # reprocess current token + process_token t # 8.2.5.4.4 http://www.w3.org/TR/html5/syntax.html#parsing-main-inhead ins_mode_in_head_else = (t) -> # factored out for same-as-spec flow control open_els.shift() # spec says this will be a 'head' node ins_mode = ins_mode_after_head - ins_mode t + process_token t ins_mode_in_head = (t) -> if t.type is TYPE_TEXT and (t.text is "\t" or t.text is "\n" or t.text is "\u000c" or t.text is ' ') insert_character t @@ -1408,7 +1495,7 @@ parse_html = (txt, parse_error_cb = null) -> parse_error() open_els.shift() ins_mode = ins_mode_in_head - ins_mode t + process_token t ins_mode_in_head_noscript = (t) -> if t.type is TYPE_DOCTYPE parse_error() @@ -1440,7 +1527,7 @@ parse_html = (txt, parse_error_cb = null) -> body_tok = new_open_tag 'body' insert_html_element body_tok ins_mode = ins_mode_in_body - ins_mode t # reprocess token + process_token t return ins_mode_after_head = (t) -> if is_space_tok t @@ -1609,7 +1696,7 @@ parse_html = (txt, parse_error_cb = null) -> parse_error() break ins_mode = ins_mode_after_body - ins_mode t + process_token t return if t.type is TYPE_START_TAG and (t.name is 'address' or t.name is 'article' or t.name is 'aside' or t.name is 'blockquote' or t.name is 'center' or t.name is 'details' or t.name is 'dialog' or t.name is 'dir' or t.name is 'div' or t.name is 'dl' or t.name is 'fieldset' or t.name is 'figcaption' or t.name is 'figure' or t.name is 'footer' or t.name is 'header' or t.name is 'hgroup' or t.name is 'main' or t.name is 'nav' or t.name is 'ol' or t.name is 'p' or t.name is 'section' or t.name is 'summary' or t.name is 'ul') close_p_if_in_button_scope() @@ -1889,7 +1976,7 @@ parse_html = (txt, parse_error_cb = null) -> if t.type is TYPE_START_TAG and t.name is 'image' parse_error() t.name = 'img' - ins_mode t + process_token t return if t.type is TYPE_START_TAG and t.name is 'isindex' parse_error() @@ -1920,7 +2007,7 @@ parse_html = (txt, parse_error_cb = null) -> input_el.attrs_a.push ['name', 'isindex'] # fixfull this next bit is in english... internationalize? prompt ?= "This is a searchable index. Enter search keywords: " - insert_character prompt # fixfull split + insert_character new_character_token prompt # fixfull split # TODO submit typo "balue" in spec insert_html_element input_el open_els.shift() @@ -2037,7 +2124,7 @@ parse_html = (txt, parse_error_cb = null) -> open_els[0].flag 'already started', true open_els.shift() ins_mode = original_ins_mode - ins_mode t + process_token t return if t.type is TYPE_END_TAG and t.name is 'script' open_els.shift() @@ -2061,7 +2148,7 @@ parse_html = (txt, parse_error_cb = null) -> if can_in_table[t.name] original_ins_mode = ins_mode ins_mode = ins_mode_in_table_text - ins_mode t + process_token t else ins_mode_in_table_else t when TYPE_COMMENT @@ -2083,7 +2170,7 @@ parse_html = (txt, parse_error_cb = null) -> clear_stack_to_table_context() insert_html_element new_open_tag 'colgroup' ins_mode = ins_mode_in_column_group - ins_mode t + process_token t when 'tbody', 'tfoot', 'thead' clear_stack_to_table_context() insert_html_element t @@ -2092,7 +2179,7 @@ parse_html = (txt, parse_error_cb = null) -> clear_stack_to_table_context() insert_html_element new_open_tag 'tbody' ins_mode = ins_mode_in_table_body - ins_mode t + process_token t when 'table' parse_error() if is_in_table_scope 'table' @@ -2101,7 +2188,7 @@ parse_html = (txt, parse_error_cb = null) -> if el.name is 'table' break reset_ins_mode() - ins_mode t + process_token t when 'style', 'script', 'template' ins_mode_in_head t when 'input' @@ -2168,7 +2255,7 @@ parse_html = (txt, parse_error_cb = null) -> ins_mode_table_else old pending_table_character_tokens = [] # FIXME test (spec doesn't say this) ins_mode = original_ins_mode - ins_mode t + process_token t # 8.2.5.4.11 http://www.w3.org/TR/html5/syntax.html#parsing-main-incaption ins_mode_in_caption = (t) -> @@ -2196,7 +2283,7 @@ parse_html = (txt, parse_error_cb = null) -> break clear_afe_to_marker() ins_mode = ins_mode_in_table - ins_mode t + process_token t # else fragment case return if t.type is TYPE_END_TAG and (t.name is 'body' or t.name is 'col' or t.name is 'colgroup' or t.name is 'html' or t.name is 'tbody' or t.name is 'td' or t.name is 'tfoot' or t.name is 'th' or t.name is 'thead' or t.name is 'tr') @@ -2246,7 +2333,7 @@ parse_html = (txt, parse_error_cb = null) -> return open_els.shift() ins_mode = ins_mode_in_table - ins_mode t + process_token t return # 8.2.5.4.13 http://www.w3.org/TR/html5/syntax.html#parsing-main-intbody @@ -2261,7 +2348,7 @@ parse_html = (txt, parse_error_cb = null) -> clear_stack_to_table_body_context() insert_html_element new_open_tag 'tr' ins_mode = ins_mode_in_row - ins_mode t + process_token t return if t.type is TYPE_END_TAG and (t.name is 'tbody' or t.name is 'tfoot' or t.name is 'thead') unless is_in_table_scope t.name # fixfull check namespace @@ -2285,7 +2372,7 @@ parse_html = (txt, parse_error_cb = null) -> clear_stack_to_table_body_context() open_els.shift() ins_mode = ins_mode_in_table - ins_mode t + process_token t return if t.type is TYPE_END_TAG and (t.name is 'body' or t.name is 'caption' or t.name is 'col' or t.name is 'colgroup' or t.name is 'html' or t.name is 'td' or t.name is 'th' or t.name is 'tr') parse_error() @@ -2314,7 +2401,7 @@ parse_html = (txt, parse_error_cb = null) -> clear_stack_to_table_row_context() open_els.shift() ins_mode = ins_mode_in_table_body - ins_mode t + process_token t else parse_error() return @@ -2324,7 +2411,7 @@ parse_html = (txt, parse_error_cb = null) -> clear_stack_to_table_row_context() open_els.shift() ins_mode = ins_mode_in_table_body - ins_mode t + process_token t else parse_error() return @@ -2374,7 +2461,7 @@ parse_html = (txt, parse_error_cb = null) -> parse_error() return close_the_cell() - ins_mode t + process_token t return if t.type is TYPE_END_TAG and (t.name is 'body' or t.name is 'caption' or t.name is 'col' or t.name is 'colgroup' or t.name is 'html') parse_error() @@ -2382,7 +2469,7 @@ parse_html = (txt, parse_error_cb = null) -> if t.type is TYPE_END_TAG and (t.name is 'table' or t.name is 'tbody' or t.name is 'tfoot' or t.name is 'thead' or t.name is 'tr') if is_in_table_scope t.name # fixfull namespace close_the_cell() - ins_mode t + process_token t else parse_error() return @@ -2461,7 +2548,7 @@ parse_html = (txt, parse_error_cb = null) -> if el.name is 'select' break reset_ins_mode() - ins_mode t + process_token t return if t.type is TYPE_START_TAG and (t.name is 'script' or t.name is 'template') ins_mode_in_head t @@ -2482,7 +2569,7 @@ parse_html = (txt, parse_error_cb = null) -> if el.name is 'select' break reset_ins_mode() - ins_mode t + process_token t return if t.type is TYPE_END_TAG and (t.name is 'caption' or t.name is 'table' or t.name is 'tbody' or t.name is 'tfoot' or t.name is 'thead' or t.name is 'tr' or t.name is 'td' or t.name is 'th') parse_error() @@ -2493,7 +2580,7 @@ parse_html = (txt, parse_error_cb = null) -> if el.name is 'select' break reset_ins_mode() - ins_mode t + process_token t return # Anything else ins_mode_in_select t @@ -2511,31 +2598,31 @@ parse_html = (txt, parse_error_cb = null) -> template_ins_modes.shift() template_ins_modes.unshift ins_mode_in_table ins_mode = ins_mode_in_table - ins_mode t + process_token t return if t.type is TYPE_START_TAG and t.name is 'col' template_ins_modes.shift() template_ins_modes.unshift ins_mode_in_column_group ins_mode = ins_mode_in_column_group - ins_mode t + process_token t return if t.type is TYPE_START_TAG and t.name is 'tr' template_ins_modes.shift() template_ins_modes.unshift ins_mode_in_table_body ins_mode = ins_mode_in_table_body - ins_mode t + process_token t return if t.type is TYPE_START_TAG and (t.name is 'td' or t.name is 'th') template_ins_modes.shift() template_ins_modes.unshift ins_mode_in_row ins_mode = ins_mode_in_row - ins_mode t + process_token t return if t.type is TYPE_START_TAG template_ins_modes.shift() template_ins_modes.unshift ins_mode_in_body ins_mode = ins_mode_in_body - ins_mode t + process_token t return if t.type is TYPE_END_TAG parse_error() @@ -2552,7 +2639,7 @@ parse_html = (txt, parse_error_cb = null) -> clear_afe_to_marker() template_ins_modes.shift() reset_ins_mode() - ins_mode t + process_token t # 8.2.5.4.19 http://www.w3.org/TR/html5/syntax.html#parsing-main-afterbody ins_mode_after_body = (t) -> @@ -2578,7 +2665,7 @@ parse_html = (txt, parse_error_cb = null) -> # Anything ELse parse_error() ins_mode = ins_mode_in_body - ins_mode t + process_token t # 8.2.5.4.20 http://www.w3.org/TR/html5/syntax.html#parsing-main-inframeset ins_mode_in_frameset = (t) -> @@ -2685,8 +2772,84 @@ parse_html = (txt, parse_error_cb = null) -> parse_error() return - - + # 8.2.5.5 http://www.w3.org/TR/html5/syntax.html#parsing-main-inforeign + has_color_face_or_size = (t) -> + for a in t.attrs_a + if a[0] is 'color' or a[0] is 'face' or a[0] is 'size' + return true + return false + in_foreign_content_end_script = -> + open_els.shift() + # fixfull + return + in_foreign_content_other_start = (t) -> + acn = adjusted_current_node() + if acn.namespace is NS_MATHML + adjust_mathml_attributes t + if acn.namespace is NS_SVG and svg_name_fixes[t.name]? + t.name = svg_name_fixes[t.name] + if acn.namespace is NS_SVG + adjust_svg_attributes t + adjust_foreign_attributes t + insert_foreign_element t, acn.namespace + if t.flag 'self-closing' + if t.name is 'script' + t.acknowledge_self_closing() + in_foreign_content_end_script() + else + open_els.shift() + t.acknowledge_self_closing() + return + in_foreign_content = (t) -> + if t.type is TYPE_TEXT and t.text is "\u0000" + parse_error() + insert_character new_character_token "\ufffd" + return + if is_space_tok t + insert_character t + return + if t.type is TYPE_TEXT + flag_frameset_ok = false + insert_character t + return + if t.type is TYPE_COMMENT + insert_comment t + return + if t.type is TYPE_DOCTYPE + parse_error() + return + if t.type is TYPE_START_TAG and (t.name is 'b' or t.name is 'big' or t.name is 'blockquote' or t.name is 'body' or t.name is 'br' or t.name is 'center' or t.name is 'code' or t.name is 'dd' or t.name is 'div' or t.name is 'dl' or t.name is 'dt' or t.name is 'em' or t.name is 'embed' or t.name is 'h1' or t.name is 'h2' or t.name is 'h3' or t.name is 'h4' or t.name is 'h5' or t.name is 'h6' or t.name is 'head' or t.name is 'hr' or t.name is 'i' or t.name is 'img' or t.name is 'li' or t.name is 'listing' or t.name is 'main' or t.name is 'meta' or t.name is 'nobr' or t.name is 'ol' or t.name is 'p' or t.name is 'pre' or t.name is 'ruby' or t.name is 's' or t.name is 'small' or t.name is 'span' or t.name is 'strong' or t.name is 'strike' or t.name is 'sub' or t.name is 'sup' or t.name is 'table' or t.name is 'tt' or t.name is 'u' or t.name is 'ul' or t.name is 'var' or (t.name is 'font' and has_color_face_or_size(t))) + parse_error() + if flag_fragment_parsing + in_foreign_content_other_start t + return + loop # is this safe? + open_els.shift() + cn = open_els[0] + if is_mathml_text_integration_point(cn) or is_html_integration(cn) or cn.namespace is NS_HTML + break + process_token t + return + if t.type is TYPE_START_TAG + in_foreign_content_other_start t + return + if t.type is TYPE_END_TAG and t.name is 'script' and open_els[0].name is 'script' and open_els[0].namespace is NS_SVG + in_foreign_content_end_script() + return + if t.type is TYPE_END_TAG + if open_els[0].name.toLowerCase() isnt t.name + parse_error() + for node in open_els + if node is open_els[open_els.length - 1] + return + if node.name.toLowerCase() is t.name + loop + el = open_els.shift() + if el is node + return + if node.namespace is NS_HTML + break + ins_mode t # explicitly call HTML insertion mode # 8.2.4.1 http://www.w3.org/TR/html5/syntax.html#data-state @@ -4233,10 +4396,11 @@ parse_html = (txt, parse_error_cb = null) -> tok_state = tok_state_data # proccess input + # http://www.w3.org/TR/html5/syntax.html#tree-construction while flag_parsing t = tok_state() if t? - ins_mode t + process_token t # fixfull parse error if has self-closing flag, but it wasn't acknolwedged return doc.children -- 1.7.10.4