IE11 を IE7 または IE8 モードで動かした時、特定条件下で ActiveXObject の typeof がおかしい

code

<!DOCTYPE html>
<html>
<head>
<script src="test.js"></script>
</head>
<body>
</body>
</html>
var isFunction = function(object) {
    // only with 'typeof'
    return typeof object == 'function';
};
 
// run in one thread. piled up karma
var result = '';
for ( var i=1; i<12; i++ ) { // the number varies. I'm not sure why. It might be the defference between 32bit and 64bit, or among zones,  or something
    result += isFunction() + '\n';
}
 
// then check in another thread, bang.
setTimeout(function(){
    var dom = document.getElementsByTagName('body')[0];
    alert(isFunction(dom)); // true!, ActiveXObject
    alert(result);          // all false
 
    alert(isFunction(XMLHttpRequest)); // true, another ActiveXObject
    // NOTICE: the type of XMLHttpRequest is 'function' on standard-mode, 'object' on emulateIE7 & emulateIE8 mode
 
    alert(isFunction({}));  // false, pure JS Object
},0);
        location /ie11-emulateie7/ {
                alias /home/oogatta/ie11/;
                add_header 'X-UA-Compatible' 'IE=EmulateIE7';
                expires epoch;
                add_header Cache-Control private;
                add_header Cache-Control must-revalidate;
                add_header Cache-Control no-store;
                add_header Pragma no-cache;
        }
 
        location /ie11-emulateie8/ {
                alias /home/oogatta/ie11/;
                add_header 'X-UA-Compatible' 'IE=EmulateIE8';
                expires epoch;
                add_header Cache-Control private;
                add_header Cache-Control must-revalidate;
                add_header Cache-Control no-store;
                add_header Pragma no-cache;
        }
 
        location /ie11-emulateie9/ {
                alias /home/oogatta/ie11/;
                add_header 'X-UA-Compatible' 'IE=EmulateIE9';
                expires epoch;
                add_header Cache-Control private;
                add_header Cache-Control must-revalidate;
                add_header Cache-Control no-store;
                add_header Pragma no-cache;
        }
 
        location /ie11-emulateie10/ {
                alias /home/oogatta/ie11/;
                add_header 'X-UA-Compatible' 'IE=EmulateIE10';
                expires epoch;
                add_header Cache-Control private;
                add_header Cache-Control must-revalidate;
                add_header Cache-Control no-store;
                add_header Pragma no-cache;
        }

試した環境

  • BrowserStack 上の Win8.1 IE11 desktop
  • 自宅の Win8.1 IE11 desktop / metro
  • modern.IE 配布の Win8.1 IE11 preview desktop / metro
  • 会社の VMWin8.1 IE11 desktop

期待

IE10 で以下の URL を開いた時と同じ結果を IE11 にも期待する。

alert(isFunction(dom)); false
alert(result); false x 11
alert(isFunction(XMLHttpRequest)); false
alert(isFunction({})); false

実際

実際は、

alert(isFunction(dom)); true
alert(result); false x 11
alert(isFunction(XMLHttpRequest)); true
alert(isFunction({})); false

となる

面白いところ

for ( var i=1; i<12; i++ ) {
    result += isFunction() + '\n';
}

こんな感じで、まず1スレッド内でカルマを貯める必要がある。この 12 は、環境によって 16 または 32 になる。

さらにスレッドを切った後に確かめに行くと、おかしな結果となる。

setTimeout(function(){
    var dom = document.getElementsByTagName('body')[0];
    alert(isFunction(dom)); // true
},0);

スレッドの切り方は、例えば HTML に書くとかでも同じ。ただし別 JS だと発生しない(理由全く不明)。スレッドを切らずに実行すると結果は正しくても引き続きカルマを貯める結果となる。

判定関数を再定義すると、カルマがリセットされてまた規定回数は正しい結果を返す。

DOM だけでなく XMLHttpRequest も同じ

IE9 モード以降

IE9 モード以降は問題ない。 HTMLElement 及び XMLHttpRequest がネイティブ JS オブジェクトになるためと思われる。

結論

IE11 の互換モードは deprecated なので、 MS さんあんまテストしてないぽい。期待しない。使わない。きっと他にも細かいバグあるよ。

IE feedback には report 済

ところでなんでこれ重要なわけ?

http://106.186.119.112/ie11-emulateie7/prototype.html

古い prototype.js 使ってて EmulateIE7 付けてそのまんま放置のサイトが IE11 で初めて壊れる。どこのサイトかって?。そんなの訊くなよ!。

原因が「実行回数」だってところに行き着くのに1日半かかってるので「調べる意味あったんですか?」とか言わないで下さい。