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:]
+ }
+ }
+}
+
// Section 12.2.5.5.
var breakout = map[string]bool{
"b": true,
// 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
return true
default:
panic("html: bad parser state: unexpected namespace")
}
- // TODO: adjust foreign attributes.
+ adjustForeignAttributes(p.tok.Attr)
p.addElement(p.tok.Data, p.tok.Attr)
case EndTagToken:
for i := len(p.oe) - 1; i >= 0; i-- {
} 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)
{"tests4.dat", -1},
{"tests5.dat", -1},
{"tests6.dat", 47},
- {"tests10.dat", 22},
+ {"tests10.dat", 30},
}
for _, tf := range testFiles {
f, err := os.Open("testdata/webkit/" + tf.filename)
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
}
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<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
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