Re: 表單的 maxlength 屬性跟 non-BMP 字符支援(原: Re: JavaScript 的非基本多文種平面(non-BMP))

(11/06/17 16:18), Kang-Hao (Kenny) Lu wrote:
> 花了不少時間多做了一點研究,分成兩篇,這篇講現象與實作。
>
> 我也要對以下這個表達震驚:
>
>     HTML5 規範裡表單的 maxlength 屬性有考慮到這個東西(請參考代碼點長
> 度(code-point length)的定義),而且 webkit 也實作出來了。
>

之前討論這個[1]問題講的時候著重在實作的部份,其實規範上有一點含糊的地
方,我也有回報這個部份[2],因此來跟各位交流一下(順便炫耀一下 目前 HTML5
規範的翻譯進度 xd)。

[1] http://lists.w3.org/Archives/Public/public-html-ig-zh/2011Jun/0050
[2] http://www.w3.org/Bugs/Public/show_bug.cgi?id=13676

首先 maxlength 屬性的定義[3]那邊有提到「元素取值的代碼點(code point)長
度大於元素的最大容許取值長度」是它不過驗證的條件,而代碼點長度的定義[4]
是「字串有的 Unicode 代碼點的個數」,但是一個字串的代碼點長度其實在某些
情形下定義是很模糊的。

[3] http://www.w3.org/html/ig/zh/wiki/HTML5#attr-fe-maxlength
[4] http://www.w3.org/html/ig/zh/wiki/HTML5#code-point-length

用幾個例子來討論:

1. <input maxlength="2" value="林𠂇" />(假人名)

HTML5 用粗體(<dfn>)定義了很多內部使用的變數,很多地方沒有講這些變數是
32 位元還是 16 位元,不過在描述解析的部份似乎都假設各個字串變數是 32 位
元字串。所以上述的 HTML 不管原來的編碼是什麼,input 的「取值」[5]都是 32
位元的兩個字符/Unicode 代碼點,所以上述片段根據規範敘述應該符合驗證條件。

[5] http://www.w3.org/html/ig/zh/wiki/HTML5#concept-fe-value

2. input.maxLength = 2 的時候又有 input.value = "林𠂇"

這時候就麻煩了,因為基本上 ECMAScript 裡面的描述很少用到代碼點來定義行
為,而直接用 16 位元代碼單元(code unit)來定義 String.prototype.length
什麼的,事實各個 JS API(那些 IDL 定義框)裡面用的 DOMString 在
WebIDL(描述 DOM API 的語言,待翻 xd)下的定義[6]就是 16 位元的字串。所
以對於 .value 這個 IDL 屬性在設置[7]的時候,從 DOMString 到 input 元素的
「取值」其實有一個 16 位元字串到 32 位元字串的隱藏轉換(理論上的,因為事
實上實作全是用 16 位元),而這個隱藏轉換應該明確寫在規範裡。

[6] http://dev.w3.org/2006/webapi/WebIDL/#idl-DOMString
[7]
http://www.whatwg.org/specs/web-apps/current-work/multipage/common-input-element-attributes.html#dom-input-value-value
(到這邊就沒翻譯了 T__T)

另一種方法是定義元素的「取值」為 16 位元字串,這雖然在應付 1. 不是很難
(解析串流的 32 位元字串要轉 16 位元字串就比較明顯是用 UTF-16),但是要
定義一個 16 位元字串的 Unicode 代碼點長度,就要確保以下這種畸形情形也有
明確定義:

3. input.maxLength = 2 的時候 input.value = "\ud840𠂇"(\ud840 是一個代
理代碼點(surrogate code point))

而目前的規範沒有很明白的說「\ud840」究竟是算 1 個還是 0 個,其實我覺得連
「𠂇」都不能確定他的代碼點長度是算 1 而不是 2,畢竟代碼點是一個抽象概
念,不像代碼單位那麼具體。

所以我在[2]裡面提到了兩個規範的修正方法:

修正方法一:定義代碼點長度的句子「字串有的 Unicode 代碼點的個數」,應該
像 Java 的 String.codePointCount 的定義[8]一樣加上「1 個未配對的代理點算
1 個 Unicode 代碼點」(unpaired surrogates count as one Unicde code
point each)

[8] (似乎須翻牆)http://download.oracle.com/javase/1,5.0/docs/api/java
/lang /String.html#codePointCount%28int,%20int%29

修正方法二:不要管「代碼點長度」是不是「代碼點」的「長度」,總之定義「代
碼點長度」為先透過[9]的演算法轉成不含代理點的 Unicode 字符串列,再計算它
的長度,這基本上也是把前述的「16 位元字串到 32 位元字串的隱藏轉換」寫下來。

[9] http://dev.w3.org/2006/webapi/WebIDL/#dfn-obtain-unicode

總得來說,我覺得規範最好把每個內用變數是 32 位元還是 16 位元講清楚比較
好......


此致

呂 康豪(Kenny), 中文興趣小組W3C連絡人
Google+: https://plus.google.com/112088462407783855918/posts
新浪微博: http://t.sina.com.cn/1950042164

Received on Tuesday, 20 September 2011 09:40:50 UTC