2009-07-07

webkitはHTML5のdocument.titleを正しくサポートしていない

ものすごく細かいことだけど、実際、間違っているので、書いてみる。

HTML5では、documentにはtitle属性が規定されており、以下のようになっている。

  1. If the root element is an svg element in the "http://www.w3.org/2000/svg" namespace, and the user agent supports SVG, then return the value that would have been returned by the DOM attribute of the same name on the SVGDocument interface. [SVG]

  2. Otherwise, let value be a concatenation of the data of all the child text nodes of the title element, in tree order, or the empty string if the title element is null.

  3. Replace any sequence of two or more consecutive space characters in value with a single U+0020 SPACE character.

  4. Remove any leading or trailing space characters in value.

  5. Return value.

document.title自体は、デファクトスタンダードといってもいいぐらい、主要なブラウザはサポートしている。しかし、この通りに実装しているとは限らない。

ある(X)HTMLに関して考える。ドキュメントには以下のtitle要素があるものとする。

<title>
 foo  bar 
</title>

ここで、HTMLコード中の改行は重要である。なぜなら、これが問題になるからだ。

SVGではないので、1.は当てはまらない。

2.は、すべての子孫のテキストノードを結合するという意味である。というのも、title要素の中には、テキストノード以外のものが含まれる可能性があるからである。上記の場合、ひとつのテキストノードしかないので、文字列は、分かりやすいように、空白文字をunicodeの値で表すと、"[U+0A][U+20]foo[U+20][U+20]bar[U+20][U+0A]" となる。

3.は、二つ以上の連続した空白文字(スペース、改行、タブ等)を、スペース(U+20)一つに置き換えるものである。

4.は、文字列の前後の空白文字をすべて取り除くものである。

5. で返される文字列は、"foo[U+20]bar" となる。

document.titleは、通常のユーザーがブラウザ上で見るのと同じ文字列が得られるはずなのだ。つまり、複数の空白文字が、スペースひとつに置き換えられ、前後の空白文字が除去されるというおなじみの挙動だ。

ところが、webkitはこれを正しく実装していない。webkitでは、子孫のテキストノードの結合は行うが、空白文字は、HTMLコードそのままの状態である。

最初、Chromeで問題を発見したので、chromeにバグリポートを送ろうかと思ったが、その前に調べた所、Safariも同様の挙動をするので、おそらくはwebkitの問題だと思われる。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html >
<html xmlns="http://www.w3.org/1999/xhtml" >

<head>

<title>
 foo bar 
</title>

<script type="application/javascript" >
<![CDATA[

var listener = 
{
    handleEvent : function ()
    {
        var log = document.getElementById("log") ;

        if ( !document.title )
        {
            log.innerHTML = "not supported at all!" ;
            return ;
        }

        var title = document.title ;

        if ( title.search("\n") !== -1 || title.search(" ") !== -1 )
            log.innerHTML += "replace fail<br />" ;

        if ( title.search(" foo") !== -1 )
            log.innerHTML += "remove fail" ;
    }
} ;

window.addEventListener( "load", listener, false ) ;


]]>
</script>

</head>
<body>

<p id="log">
</p>

</body>
</html>

No comments: