summaryrefslogtreecommitdiff
path: root/libgo/go/html
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-01-25 20:56:26 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-01-25 20:56:26 +0000
commit422eaae5fe0038ad189b8fd28cfd6a7094d67ae1 (patch)
treec68d6b2a9f5b82a23171b0a488a4b7e5c63ad860 /libgo/go/html
parente0f3ea3ed4b9d0bce9f4c14762e4257ba62c8fba (diff)
downloadgcc-422eaae5fe0038ad189b8fd28cfd6a7094d67ae1.tar.gz
libgo: Update to weekly.2012-01-15.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@183539 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/html')
-rw-r--r--libgo/go/html/foreign.go78
-rw-r--r--libgo/go/html/node.go4
-rw-r--r--libgo/go/html/parse.go309
-rw-r--r--libgo/go/html/parse_test.go19
-rw-r--r--libgo/go/html/render.go8
-rw-r--r--libgo/go/html/template/escape_test.go4
-rw-r--r--libgo/go/html/token.go9
7 files changed, 316 insertions, 115 deletions
diff --git a/libgo/go/html/foreign.go b/libgo/go/html/foreign.go
index 0f9b4ad560d..3ba81ce4d6f 100644
--- a/libgo/go/html/foreign.go
+++ b/libgo/go/html/foreign.go
@@ -4,6 +4,42 @@
package html
+import (
+ "strings"
+)
+
+func adjustForeignAttributes(aa []Attribute) {
+ for i, a := range aa {
+ if a.Key == "" || a.Key[0] != 'x' {
+ continue
+ }
+ switch a.Key {
+ case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show",
+ "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink":
+ j := strings.Index(a.Key, ":")
+ aa[i].Namespace = a.Key[:j]
+ aa[i].Key = a.Key[j+1:]
+ }
+ }
+}
+
+func htmlIntegrationPoint(n *Node) bool {
+ if n.Type != ElementNode {
+ return false
+ }
+ switch n.Namespace {
+ case "math":
+ // TODO: annotation-xml elements whose start tags have "text/html" or
+ // "application/xhtml+xml" encodings.
+ case "svg":
+ switch n.Data {
+ case "desc", "foreignObject", "title":
+ return true
+ }
+ }
+ return false
+}
+
// Section 12.2.5.5.
var breakout = map[string]bool{
"b": true,
@@ -53,4 +89,44 @@ var breakout = map[string]bool{
"var": true,
}
-// TODO: add look-up tables for MathML and SVG adjustments.
+// Section 12.2.5.5.
+var svgTagNameAdjustments = map[string]string{
+ "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",
+ "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",
+}
+
+// TODO: add look-up tables for MathML and SVG attribute adjustments.
diff --git a/libgo/go/html/node.go b/libgo/go/html/node.go
index 4ba3f5fb627..83f17308b18 100644
--- a/libgo/go/html/node.go
+++ b/libgo/go/html/node.go
@@ -26,6 +26,10 @@ var scopeMarker = Node{Type: scopeMarkerNode}
// content for text) and are part of a tree of Nodes. Element nodes may also
// have a Namespace and contain a slice of Attributes. Data is unescaped, so
// that it looks like "a<b" rather than "a&lt;b".
+//
+// An empty Namespace implies a "http://www.w3.org/1999/xhtml" namespace.
+// Similarly, "math" is short for "http://www.w3.org/1998/Math/MathML", and
+// "svg" is short for "http://www.w3.org/2000/svg".
type Node struct {
Parent *Node
Child []*Node
diff --git a/libgo/go/html/parse.go b/libgo/go/html/parse.go
index 6962e643932..43c04727ab8 100644
--- a/libgo/go/html/parse.go
+++ b/libgo/go/html/parse.go
@@ -51,58 +51,87 @@ func (p *parser) top() *Node {
return p.doc
}
-// stopTags for use in popUntil. These come from section 12.2.3.2.
+// Stop tags for use in popUntil. These come from section 12.2.3.2.
var (
- defaultScopeStopTags = []string{"applet", "caption", "html", "table", "td", "th", "marquee", "object"}
- listItemScopeStopTags = []string{"applet", "caption", "html", "table", "td", "th", "marquee", "object", "ol", "ul"}
- buttonScopeStopTags = []string{"applet", "caption", "html", "table", "td", "th", "marquee", "object", "button"}
- tableScopeStopTags = []string{"html", "table"}
+ defaultScopeStopTags = map[string][]string{
+ "": {"applet", "caption", "html", "table", "td", "th", "marquee", "object"},
+ "math": {"annotation-xml", "mi", "mn", "mo", "ms", "mtext"},
+ "svg": {"desc", "foreignObject", "title"},
+ }
)
-// stopTags for use in clearStackToContext.
-var (
- tableRowContextStopTags = []string{"tr", "html"}
+type scope int
+
+const (
+ defaultScope scope = iota
+ listItemScope
+ buttonScope
+ tableScope
+ tableRowScope
)
// popUntil pops the stack of open elements at the highest element whose tag
-// is in matchTags, provided there is no higher element in stopTags. It returns
-// whether or not there was such an element. If there was not, popUntil leaves
-// the stack unchanged.
+// is in matchTags, provided there is no higher element in the scope's stop
+// tags (as defined in section 12.2.3.2). It returns whether or not there was
+// such an element. If there was not, popUntil leaves the stack unchanged.
//
-// For example, if the stack was:
+// For example, the set of stop tags for table scope is: "html", "table". If
+// the stack was:
// ["html", "body", "font", "table", "b", "i", "u"]
-// then popUntil([]string{"html, "table"}, "font") would return false, but
-// popUntil([]string{"html, "table"}, "i") would return true and the resultant
-// stack would be:
+// then popUntil(tableScope, "font") would return false, but
+// popUntil(tableScope, "i") would return true and the stack would become:
// ["html", "body", "font", "table", "b"]
//
-// If an element's tag is in both stopTags and matchTags, then the stack will
-// be popped and the function returns true (provided, of course, there was no
-// higher element in the stack that was also in stopTags). For example,
-// popUntil([]string{"html, "table"}, "table") would return true and leave:
+// If an element's tag is in both the stop tags and matchTags, then the stack
+// will be popped and the function returns true (provided, of course, there was
+// no higher element in the stack that was also in the stop tags). For example,
+// popUntil(tableScope, "table") returns true and leaves:
// ["html", "body", "font"]
-func (p *parser) popUntil(stopTags []string, matchTags ...string) bool {
- if i := p.indexOfElementInScope(stopTags, matchTags...); i != -1 {
+func (p *parser) popUntil(s scope, matchTags ...string) bool {
+ if i := p.indexOfElementInScope(s, matchTags...); i != -1 {
p.oe = p.oe[:i]
return true
}
return false
}
-// indexOfElementInScope returns the index in p.oe of the highest element
-// whose tag is in matchTags that is in scope according to stopTags.
-// If no matching element is in scope, it returns -1.
-func (p *parser) indexOfElementInScope(stopTags []string, matchTags ...string) int {
+// indexOfElementInScope returns the index in p.oe of the highest element whose
+// tag is in matchTags that is in scope. If no matching element is in scope, it
+// returns -1.
+func (p *parser) indexOfElementInScope(s scope, matchTags ...string) int {
for i := len(p.oe) - 1; i >= 0; i-- {
tag := p.oe[i].Data
- for _, t := range matchTags {
- if t == tag {
- return i
+ if p.oe[i].Namespace == "" {
+ for _, t := range matchTags {
+ if t == tag {
+ return i
+ }
+ }
+ switch s {
+ case defaultScope:
+ // No-op.
+ case listItemScope:
+ if tag == "ol" || tag == "ul" {
+ return -1
+ }
+ case buttonScope:
+ if tag == "button" {
+ return -1
+ }
+ case tableScope:
+ if tag == "html" || tag == "table" {
+ return -1
+ }
+ default:
+ panic("unreachable")
}
}
- for _, t := range stopTags {
- if t == tag {
- return -1
+ switch s {
+ case defaultScope, listItemScope, buttonScope:
+ for _, t := range defaultScopeStopTags[p.oe[i].Namespace] {
+ if t == tag {
+ return -1
+ }
}
}
}
@@ -111,8 +140,30 @@ func (p *parser) indexOfElementInScope(stopTags []string, matchTags ...string) i
// elementInScope is like popUntil, except that it doesn't modify the stack of
// open elements.
-func (p *parser) elementInScope(stopTags []string, matchTags ...string) bool {
- return p.indexOfElementInScope(stopTags, matchTags...) != -1
+func (p *parser) elementInScope(s scope, matchTags ...string) bool {
+ return p.indexOfElementInScope(s, matchTags...) != -1
+}
+
+// clearStackToContext pops elements off the stack of open elements until a
+// scope-defined element is found.
+func (p *parser) clearStackToContext(s scope) {
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ tag := p.oe[i].Data
+ switch s {
+ case tableScope:
+ if tag == "html" || tag == "table" {
+ p.oe = p.oe[:i+1]
+ return
+ }
+ case tableRowScope:
+ if tag == "html" || tag == "tr" {
+ p.oe = p.oe[:i+1]
+ return
+ }
+ default:
+ panic("unreachable")
+ }
+ }
}
// addChild adds a child node n to the top element, and pushes n onto the stack
@@ -192,10 +243,9 @@ func (p *parser) addText(text string) {
// addElement calls addChild with an element node.
func (p *parser) addElement(tag string, attr []Attribute) {
p.addChild(&Node{
- Type: ElementNode,
- Data: tag,
- Namespace: p.top().Namespace,
- Attr: attr,
+ Type: ElementNode,
+ Data: tag,
+ Attr: attr,
})
}
@@ -624,10 +674,10 @@ func inBodyIM(p *parser) bool {
case "html":
copyAttributes(p.oe[0], p.tok)
case "address", "article", "aside", "blockquote", "center", "details", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "menu", "nav", "ol", "p", "section", "summary", "ul":
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
p.addElement(p.tok.Data, p.tok.Attr)
case "h1", "h2", "h3", "h4", "h5", "h6":
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
switch n := p.top(); n.Data {
case "h1", "h2", "h3", "h4", "h5", "h6":
p.oe.pop()
@@ -649,7 +699,7 @@ func inBodyIM(p *parser) bool {
p.addFormattingElement(p.tok.Data, p.tok.Attr)
case "nobr":
p.reconstructActiveFormattingElements()
- if p.elementInScope(defaultScopeStopTags, "nobr") {
+ if p.elementInScope(defaultScope, "nobr") {
p.inBodyEndTagFormatting("nobr")
p.reconstructActiveFormattingElements()
}
@@ -667,14 +717,14 @@ func inBodyIM(p *parser) bool {
p.framesetOK = false
case "table":
if !p.quirks {
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
}
p.addElement(p.tok.Data, p.tok.Attr)
p.framesetOK = false
p.im = inTableIM
return true
case "hr":
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
p.addElement(p.tok.Data, p.tok.Attr)
p.oe.pop()
p.acknowledgeSelfClosingTag()
@@ -683,12 +733,11 @@ func inBodyIM(p *parser) bool {
p.reconstructActiveFormattingElements()
p.addElement(p.tok.Data, p.tok.Attr)
p.framesetOK = false
- // TODO: detect <select> inside a table.
p.im = inSelectIM
return true
case "form":
if p.form == nil {
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
p.addElement(p.tok.Data, p.tok.Attr)
p.form = p.top()
}
@@ -698,7 +747,7 @@ func inBodyIM(p *parser) bool {
node := p.oe[i]
switch node.Data {
case "li":
- p.popUntil(listItemScopeStopTags, "li")
+ p.popUntil(listItemScope, "li")
case "address", "div", "p":
continue
default:
@@ -708,7 +757,7 @@ func inBodyIM(p *parser) bool {
}
break
}
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
p.addElement(p.tok.Data, p.tok.Attr)
case "dd", "dt":
p.framesetOK = false
@@ -726,13 +775,13 @@ func inBodyIM(p *parser) bool {
}
break
}
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
p.addElement(p.tok.Data, p.tok.Attr)
case "plaintext":
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
p.addElement(p.tok.Data, p.tok.Attr)
case "button":
- p.popUntil(defaultScopeStopTags, "button")
+ p.popUntil(defaultScope, "button")
p.reconstructActiveFormattingElements()
p.addElement(p.tok.Data, p.tok.Attr)
p.framesetOK = false
@@ -750,6 +799,19 @@ func inBodyIM(p *parser) bool {
copyAttributes(body, p.tok)
}
}
+ case "frameset":
+ if !p.framesetOK || len(p.oe) < 2 || p.oe[1].Data != "body" {
+ // Ignore the token.
+ return true
+ }
+ body := p.oe[1]
+ if body.Parent != nil {
+ body.Parent.Remove(body)
+ }
+ p.oe = p.oe[:1]
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.im = inFramesetIM
+ return true
case "base", "basefont", "bgsound", "command", "link", "meta", "noframes", "script", "style", "title":
return inHeadIM(p)
case "image":
@@ -776,7 +838,7 @@ func inBodyIM(p *parser) bool {
}
}
p.acknowledgeSelfClosingTag()
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
p.addElement("form", nil)
p.form = p.top()
if action != "" {
@@ -794,23 +856,20 @@ func inBodyIM(p *parser) bool {
p.oe.pop()
p.form = nil
case "xmp":
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
p.reconstructActiveFormattingElements()
p.framesetOK = false
p.addElement(p.tok.Data, p.tok.Attr)
case "math", "svg":
p.reconstructActiveFormattingElements()
- namespace := ""
if p.tok.Data == "math" {
// TODO: adjust MathML attributes.
- namespace = "mathml"
} else {
// TODO: adjust SVG attributes.
- namespace = "svg"
}
- // TODO: adjust foreign attributes.
+ adjustForeignAttributes(p.tok.Attr)
p.addElement(p.tok.Data, p.tok.Attr)
- p.top().Namespace = namespace
+ p.top().Namespace = p.tok.Data
return true
case "caption", "col", "colgroup", "frame", "head", "tbody", "td", "tfoot", "th", "thead", "tr":
// Ignore the token.
@@ -825,16 +884,16 @@ func inBodyIM(p *parser) bool {
p.im = afterBodyIM
return true
case "p":
- if !p.elementInScope(buttonScopeStopTags, "p") {
+ if !p.elementInScope(buttonScope, "p") {
p.addElement("p", nil)
}
- p.popUntil(buttonScopeStopTags, "p")
+ p.popUntil(buttonScope, "p")
case "a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u":
p.inBodyEndTagFormatting(p.tok.Data)
case "address", "article", "aside", "blockquote", "button", "center", "details", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "listing", "menu", "nav", "ol", "pre", "section", "summary", "ul":
- p.popUntil(defaultScopeStopTags, p.tok.Data)
+ p.popUntil(defaultScope, p.tok.Data)
case "applet", "marquee", "object":
- if p.popUntil(defaultScopeStopTags, p.tok.Data) {
+ if p.popUntil(defaultScope, p.tok.Data) {
p.clearActiveFormattingElements()
}
case "br":
@@ -883,7 +942,7 @@ func (p *parser) inBodyEndTagFormatting(tag string) {
p.afe.remove(formattingElement)
return
}
- if !p.elementInScope(defaultScopeStopTags, tag) {
+ if !p.elementInScope(defaultScope, tag) {
// Ignore the tag.
return
}
@@ -1017,45 +1076,56 @@ func inTableIM(p *parser) bool {
case StartTagToken:
switch p.tok.Data {
case "caption":
- p.clearStackToContext(tableScopeStopTags)
+ p.clearStackToContext(tableScope)
p.afe = append(p.afe, &scopeMarker)
p.addElement(p.tok.Data, p.tok.Attr)
p.im = inCaptionIM
return true
case "tbody", "tfoot", "thead":
- p.clearStackToContext(tableScopeStopTags)
+ p.clearStackToContext(tableScope)
p.addElement(p.tok.Data, p.tok.Attr)
p.im = inTableBodyIM
return true
case "td", "th", "tr":
- p.clearStackToContext(tableScopeStopTags)
+ p.clearStackToContext(tableScope)
p.addElement("tbody", nil)
p.im = inTableBodyIM
return false
case "table":
- if p.popUntil(tableScopeStopTags, "table") {
+ if p.popUntil(tableScope, "table") {
p.resetInsertionMode()
return false
}
// Ignore the token.
return true
case "colgroup":
- p.clearStackToContext(tableScopeStopTags)
+ p.clearStackToContext(tableScope)
p.addElement(p.tok.Data, p.tok.Attr)
p.im = inColumnGroupIM
return true
case "col":
- p.clearStackToContext(tableScopeStopTags)
+ p.clearStackToContext(tableScope)
p.addElement("colgroup", p.tok.Attr)
p.im = inColumnGroupIM
return false
+ case "select":
+ p.reconstructActiveFormattingElements()
+ switch p.top().Data {
+ case "table", "tbody", "tfoot", "thead", "tr":
+ p.fosterParenting = true
+ }
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.fosterParenting = false
+ p.framesetOK = false
+ p.im = inSelectInTableIM
+ return true
default:
// TODO.
}
case EndTagToken:
switch p.tok.Data {
case "table":
- if p.popUntil(tableScopeStopTags, "table") {
+ if p.popUntil(tableScope, "table") {
p.resetInsertionMode()
return true
}
@@ -1082,26 +1152,13 @@ func inTableIM(p *parser) bool {
return inBodyIM(p)
}
-// clearStackToContext pops elements off the stack of open elements
-// until an element listed in stopTags is found.
-func (p *parser) clearStackToContext(stopTags []string) {
- for i := len(p.oe) - 1; i >= 0; i-- {
- for _, tag := range stopTags {
- if p.oe[i].Data == tag {
- p.oe = p.oe[:i+1]
- return
- }
- }
- }
-}
-
// Section 12.2.5.4.11.
func inCaptionIM(p *parser) bool {
switch p.tok.Type {
case StartTagToken:
switch p.tok.Data {
case "caption", "col", "colgroup", "tbody", "td", "tfoot", "thead", "tr":
- if p.popUntil(tableScopeStopTags, "caption") {
+ if p.popUntil(tableScope, "caption") {
p.clearActiveFormattingElements()
p.im = inTableIM
return false
@@ -1109,17 +1166,23 @@ func inCaptionIM(p *parser) bool {
// Ignore the token.
return true
}
+ case "select":
+ p.reconstructActiveFormattingElements()
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.framesetOK = false
+ p.im = inSelectInTableIM
+ return true
}
case EndTagToken:
switch p.tok.Data {
case "caption":
- if p.popUntil(tableScopeStopTags, "caption") {
+ if p.popUntil(tableScope, "caption") {
p.clearActiveFormattingElements()
p.im = inTableIM
}
return true
case "table":
- if p.popUntil(tableScopeStopTags, "caption") {
+ if p.popUntil(tableScope, "caption") {
p.clearActiveFormattingElements()
p.im = inTableIM
return false
@@ -1203,7 +1266,7 @@ func inTableBodyIM(p *parser) bool {
data = "tr"
consumed = false
case "caption", "col", "colgroup", "tbody", "tfoot", "thead":
- if !p.popUntil(tableScopeStopTags, "tbody", "thead", "tfoot") {
+ if !p.popUntil(tableScope, "tbody", "thead", "tfoot") {
// Ignore the token.
return true
}
@@ -1215,7 +1278,7 @@ func inTableBodyIM(p *parser) bool {
case EndTagToken:
switch p.tok.Data {
case "table":
- if p.popUntil(tableScopeStopTags, "tbody", "thead", "tfoot") {
+ if p.popUntil(tableScope, "tbody", "thead", "tfoot") {
p.im = inTableIM
return false
}
@@ -1251,13 +1314,13 @@ func inRowIM(p *parser) bool {
case StartTagToken:
switch p.tok.Data {
case "td", "th":
- p.clearStackToContext(tableRowContextStopTags)
+ p.clearStackToContext(tableRowScope)
p.addElement(p.tok.Data, p.tok.Attr)
p.afe = append(p.afe, &scopeMarker)
p.im = inCellIM
return true
case "caption", "col", "colgroup", "tbody", "tfoot", "thead", "tr":
- if p.popUntil(tableScopeStopTags, "tr") {
+ if p.popUntil(tableScope, "tr") {
p.im = inTableBodyIM
return false
}
@@ -1269,14 +1332,14 @@ func inRowIM(p *parser) bool {
case EndTagToken:
switch p.tok.Data {
case "tr":
- if p.popUntil(tableScopeStopTags, "tr") {
+ if p.popUntil(tableScope, "tr") {
p.im = inTableBodyIM
return true
}
// Ignore the token.
return true
case "table":
- if p.popUntil(tableScopeStopTags, "tr") {
+ if p.popUntil(tableScope, "tr") {
p.im = inTableBodyIM
return false
}
@@ -1311,11 +1374,17 @@ func inCellIM(p *parser) bool {
case "caption", "col", "colgroup", "tbody", "td", "tfoot", "th", "thead", "tr":
// TODO: check for "td" or "th" in table scope.
closeTheCellAndReprocess = true
+ case "select":
+ p.reconstructActiveFormattingElements()
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.framesetOK = false
+ p.im = inSelectInTableIM
+ return true
}
case EndTagToken:
switch p.tok.Data {
case "td", "th":
- if !p.popUntil(tableScopeStopTags, p.tok.Data) {
+ if !p.popUntil(tableScope, p.tok.Data) {
// Ignore the token.
return true
}
@@ -1336,7 +1405,7 @@ func inCellIM(p *parser) bool {
return true
}
if closeTheCellAndReprocess {
- if p.popUntil(tableScopeStopTags, "td") || p.popUntil(tableScopeStopTags, "th") {
+ if p.popUntil(tableScope, "td") || p.popUntil(tableScope, "th") {
p.clearActiveFormattingElements()
p.im = inRowIM
return false
@@ -1405,21 +1474,40 @@ func inSelectIM(p *parser) bool {
})
}
if endSelect {
- for i := len(p.oe) - 1; i >= 0; i-- {
- switch p.oe[i].Data {
- case "select":
- p.oe = p.oe[:i]
- p.resetInsertionMode()
- return true
- case "option", "optgroup":
- continue
- default:
+ p.endSelect()
+ }
+ return true
+}
+
+// Section 12.2.5.4.17.
+func inSelectInTableIM(p *parser) bool {
+ switch p.tok.Type {
+ case StartTagToken, EndTagToken:
+ switch p.tok.Data {
+ case "caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th":
+ if p.tok.Type == StartTagToken || p.elementInScope(tableScope, p.tok.Data) {
+ p.endSelect()
+ return false
+ } else {
// Ignore the token.
return true
}
}
}
- return true
+ return inSelectIM(p)
+}
+
+func (p *parser) endSelect() {
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ switch p.oe[i].Data {
+ case "option", "optgroup":
+ continue
+ case "select":
+ p.oe = p.oe[:i]
+ p.resetInsertionMode()
+ }
+ return
+ }
}
// Section 12.2.5.4.18.
@@ -1618,6 +1706,11 @@ func parseForeignContent(p *parser) bool {
Data: p.tok.Data,
})
case StartTagToken:
+ if htmlIntegrationPoint(p.top()) {
+ inBodyIM(p)
+ p.resetInsertionMode()
+ return true
+ }
if breakout[p.tok.Data] {
for i := len(p.oe) - 1; i >= 0; i-- {
// TODO: HTML, MathML integration points.
@@ -1629,16 +1722,22 @@ func parseForeignContent(p *parser) bool {
return false
}
switch p.top().Namespace {
- case "mathml":
+ case "math":
// TODO: adjust MathML attributes.
case "svg":
- // TODO: adjust SVG tag names.
+ // Adjust SVG tag names. The tokenizer lower-cases tag names, but
+ // SVG wants e.g. "foreignObject" with a capital second "O".
+ if x := svgTagNameAdjustments[p.tok.Data]; x != "" {
+ p.tok.Data = x
+ }
// TODO: adjust SVG attributes.
default:
panic("html: bad parser state: unexpected namespace")
}
- // TODO: adjust foreign attributes.
+ adjustForeignAttributes(p.tok.Attr)
+ namespace := p.top().Namespace
p.addElement(p.tok.Data, p.tok.Attr)
+ p.top().Namespace = namespace
case EndTagToken:
for i := len(p.oe) - 1; i >= 0; i-- {
if p.oe[i].Namespace == "" {
diff --git a/libgo/go/html/parse_test.go b/libgo/go/html/parse_test.go
index 015b5838f0b..c929c257727 100644
--- a/libgo/go/html/parse_test.go
+++ b/libgo/go/html/parse_test.go
@@ -103,10 +103,21 @@ func dumpLevel(w io.Writer, n *Node, level int) error {
} else {
fmt.Fprintf(w, "<%s>", n.Data)
}
- for _, a := range n.Attr {
+ attr := n.Attr
+ if len(attr) == 2 && attr[0].Namespace == "xml" && attr[1].Namespace == "xlink" {
+ // Some of the test cases in tests10.dat change the order of adjusted
+ // foreign attributes, but that behavior is not in the spec, and could
+ // simply be an implementation detail of html5lib's python map ordering.
+ attr[0], attr[1] = attr[1], attr[0]
+ }
+ for _, a := range attr {
io.WriteString(w, "\n")
dumpIndent(w, level+1)
- fmt.Fprintf(w, `%s="%s"`, a.Key, a.Val)
+ if a.Namespace != "" {
+ fmt.Fprintf(w, `%s %s="%s"`, a.Namespace, a.Key, a.Val)
+ } else {
+ fmt.Fprintf(w, `%s="%s"`, a.Key, a.Val)
+ }
}
case TextNode:
fmt.Fprintf(w, `"%s"`, n.Data)
@@ -172,8 +183,8 @@ func TestParser(t *testing.T) {
{"tests3.dat", -1},
{"tests4.dat", -1},
{"tests5.dat", -1},
- {"tests6.dat", 47},
- {"tests10.dat", 16},
+ {"tests6.dat", -1},
+ {"tests10.dat", 33},
}
for _, tf := range testFiles {
f, err := os.Open("testdata/webkit/" + tf.filename)
diff --git a/libgo/go/html/render.go b/libgo/go/html/render.go
index 20751938d9d..07859faa7dd 100644
--- a/libgo/go/html/render.go
+++ b/libgo/go/html/render.go
@@ -149,6 +149,14 @@ func render1(w writer, n *Node) error {
if err := w.WriteByte(' '); err != nil {
return err
}
+ if a.Namespace != "" {
+ if _, err := w.WriteString(a.Namespace); err != nil {
+ return err
+ }
+ if err := w.WriteByte(':'); err != nil {
+ return err
+ }
+ }
if _, err := w.WriteString(a.Key); err != nil {
return err
}
diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go
index a57f9826b5b..2ce1fb566a5 100644
--- a/libgo/go/html/template/escape_test.go
+++ b/libgo/go/html/template/escape_test.go
@@ -302,7 +302,7 @@ func TestEscape(t *testing.T) {
},
{
"styleObfuscatedExpressionBlocked",
- `<p style="width: {{" e\78preS\0Sio/**/n(alert(1337))"}}">`,
+ `<p style="width: {{" e\\78preS\x00Sio/**/n(alert(1337))"}}">`,
`<p style="width: ZgotmplZ">`,
},
{
@@ -312,7 +312,7 @@ func TestEscape(t *testing.T) {
},
{
"styleObfuscatedMozBindingBlocked",
- `<p style="{{" -mo\7a-B\0I/**/nding(alert(1337))"}}: ...">`,
+ `<p style="{{" -mo\\7a-B\x00I/**/nding(alert(1337))"}}: ...">`,
`<p style="ZgotmplZ: ...">`,
},
{
diff --git a/libgo/go/html/token.go b/libgo/go/html/token.go
index 69af96840c2..5a385a1b5c5 100644
--- a/libgo/go/html/token.go
+++ b/libgo/go/html/token.go
@@ -52,11 +52,14 @@ func (t TokenType) String() string {
return "Invalid(" + strconv.Itoa(int(t)) + ")"
}
-// An Attribute is an attribute key-value pair. Key is alphabetic (and hence
+// An Attribute is an attribute namespace-key-value triple. Namespace is
+// non-empty for foreign attributes like xlink, Key is alphabetic (and hence
// does not contain escapable characters like '&', '<' or '>'), and Val is
// unescaped (it looks like "a<b" rather than "a&lt;b").
+//
+// Namespace is only used by the parser, not the tokenizer.
type Attribute struct {
- Key, Val string
+ Namespace, Key, Val string
}
// A Token consists of a TokenType and some Data (tag name for start and end
@@ -756,7 +759,7 @@ func (z *Tokenizer) Token() Token {
for moreAttr {
var key, val []byte
key, val, moreAttr = z.TagAttr()
- attr = append(attr, Attribute{string(key), string(val)})
+ attr = append(attr, Attribute{"", string(key), string(val)})
}
t.Data = string(name)
t.Attr = attr