document.all.item()修正

document.all.item()の引数にはインデックス(数字)しか入らないと思い込んでいたが、改めて仕様を確認すると文字列も引数に取れるようだ。
というか、本家のHTMLCollection.itemでの定義は明確にunsigned longしか取らないとしている。つまりMSは「This method is defined in World Wide Web Consortium (W3C) Document Object Model (DOM) Level 1 」と言っているが完全準拠ではなく、item()の文字列引数はMSの拡張仕様だ。


ともあれ世の中で使われている以上W3C準拠でなくても対応しておかないと対処できない。実際某HPで document.all.item(id) というスクリプトが例外(スクリプトレベルの例外でなく、OSレベルの一般保護例外)になってしまっていた。


調べていくと、中身が文字列のJSVALを JS_ValueToInt32 に渡すと js32.dll 内部で例外(null参照)が発生してしまうようだ。

NTDLL! 7c941230()
Decompile(SprintStack * 0x02b5d5d4, unsigned char * 0x00fef897, int 3) line 1860 + 47 bytes
js_DecompileCode(JSPrinter * 0x0100b400, JSScript * 0x00fef850, unsigned char * 0x00fef897, unsigned int 3) line 2347 + 17 bytes
js_DecompileValueGenerator(JSContext * 0x01018dc0, int 1, long 48685956, JSString * 0x00000000) line 2669 + 27 bytes
js_ValueToInt32(JSContext * 0x01018dc0, long 48685956, long * 0x02b5d808) line 765 + 17 bytes
JS_ValueToInt32(JSContext * 0x01018dc0, long 48685956, long * 0x02b5d808) line 552 + 17 bytes
HTMLCollection_item(JSContext * 0x01018dc0, JSObject * 0x02e6cc88, unsigned int 1, long * 0x02f60108, long * 0x02b5d968) line 214 + 22 bytes
js_Invoke(JSContext * 0x01018dc0, unsigned int 1, unsigned int 0) line 843 + 26 bytes
js_Interpret(JSContext * 0x01018dc0, long * 0x02b5e7c4) line 2852 + 15 bytes
js_Invoke(JSContext * 0x01018dc0, unsigned int 0, unsigned int 2) line 860 + 13 bytes
js_InternalInvoke(JSContext * 0x01018dc0, JSObject * 0x02e85fa8, long 48685928, unsigned int 0, unsigned int 0, long * 0x02f60060, long * 0x02b5eb54) line 935 + 20 bytes
JS_CallFunctionValue(JSContext * 0x01018dc0, JSObject * 0x02e85fa8, long 48685928, unsigned int 0, long * 0x02f60060, long * 0x02b5eb54) line 3527 + 31 bytes
event_evaluateAll(JSContext * 0x01018dc0, JSObject * 0x02e6d160, unsigned int 0, long * 0x02f60060, long * 0x02b5eb54) line 945 + 30 bytes
js_Invoke(JSContext * 0x01018dc0, unsigned int 0, unsigned int 0) line 843 + 26 bytes
js_Interpret(JSContext * 0x01018dc0, long * 0x02b5fa58) line 2852 + 15 bytes
js_Execute(JSContext * 0x01018dc0, JSObject * 0x02e6b740, JSScript * 0x00fef4c0, JSStackFrame * 0x00000000, unsigned int 0, long * 0x02b5fa58) line 1055 + 13 bytes
JS_ExecuteScript(JSContext * 0x01018dc0, JSObject * 0x02e6b740, JSScript * 0x00fef4c0, long * 0x02b5fa58) line 3373 + 25 bytes
WRJavaScriptExecuteScript(_WRJSHANDLE_ * 0x02c7fb28, const char * 0x00491830 `string') line 260 + 24 bytes
WRJavaScriptEvaluateScript(_WRJSHANDLE_ * 0x02c7fb28, const char * 0x00491830 `string') line 303 + 13 bytes
EvaluateScriptSub(_WRJSHANDLE_ * 0x02c7fb28, const char * 0x00491830 `string', tagWRDOCOBJBASE * 0x02131ea4, tagWRURL * 0x020d30cc) line 599 + 13 bytes
EvaluateScript(_WRJSHANDLE_ * 0x00000000, tagWRANALYZEINFO * 0x02b5ff74, tagWRDOCOBJBASE * 0x02131ea4) line 872 + 25 bytes
__WRHtmlParse(tagWRANALYZEINFO * 0x02b5ff74, tagWRDOCOBJBASE * 0x02131ea4, unsigned long 4) line 195 + 15 bytes
AnalyzeThread(unsigned long 10) line 744 + 15 bytes
KERNEL32! 7c80b683()

ドキュメント上はJSVAL_STRINGも受け付けるとなってるんだがなー…。
JSVAL_STRINGを受け取ることはできるが、数値を表す文字列以外の場合が考慮されていないのだろうか。文字列が数値として有意かどうか判定してから渡すなんて面倒な使い方の訳ないと思うのでjs32のバグなのかもしれない。


document.all.item()の引数が文字列の場合にはHTMLCollection.namedItemに転送するよう変更。HTMLCollection.namedItemは文字列を引数に取り、引数と同じidまたはname属性を持つ要素を返す。


つまりMS仕様の document.all.item は W3C の HTMLCollection.item + HTMLCollection.namedItem の両方の仕様を併せ持つということになるらしい。
ただし、idまたはnameが一致する要素が複数あったときの挙動は異なる。
document.all.item は、該当する全ての要素の集合を返すのに対し、namedItemは、最初に見つかった要素1つだけしか返さないという違いはある。これも微妙にMS拡張風味だ。