2018/09/05 10:03 pm
Prevent from executing an inline script
Uncategorized, , , ,

以前にも書いた気がするが、赤福プラスはふたばが返す html をまるごと変換し、上書きする。従って、元の html に記述されている画像や、スクリプトや、インラインフレームの読み込みはまったく不要で、ブロックする必要がある。ブロックした上で、変換後の html から改めて読み込まなければならない。

そんなわけで、Chrome では WebRequest API を用いてそれを実現していたのだが、ただ一つ html に直接記述された script 要素、つまりインラインスクリプトの実行は見逃していた。まあこれを見逃しても src 属性付きのスクリプトの読み込みはブロックしているので、大抵のインラインスクリプトはなんちゃらが定義されていませんエラーになって実害はないのだが、気にはなる。

しかし、Chrome のエクステンションの API を眺めてみてもインラインスクリプトの実行をブロックする機能は見つからない。いやあることはある。例えば WebRequest でレスポンスヘッダに CSP を忍ばせて、インラインスクリプトを除外するとか、あるいは ContentSettings でふたば上のみの javascript の実行を禁止するとか。が、リファレンスを読んでみるといずれも html を読み込んでから DOMContentLoaded までの短い期間だけスクリプトの実行を抑制するという要件にはちょっと合ってない。

いろいろ調べてみたところ、content script で MutationObserver を用いて script 要素が DOM ツリーに追加された瞬間をキャッチし、type を text/javascript とか application/javascript から、スクリプトとして実行されないものに書き換えるとそういう動作を実現できるようだ。で、用が済んだら、DOMContentLoaded ハンドラで disconnect() すればいい。完璧だ。

ただし Firefox ではこの type 書き換え法が効かないので、その代わり script 要素の beforescriptexecute イベントをリスンして適宜 preventDefault() する。つまり実行タイミングを start_at にした content script から

var observer = new MutationObserver(ms => {
.   function handleBeforeScriptExecute (e) {
.   .   e.target.removeEventListener(
.   .   .   'beforescriptexecute', handleBeforeScriptExecute, false);
.   .   e.preventDefault();
.   };
.   ms.forEach(m => {
.   .   m.addedNodes.forEach(node => {
.   .   .   if (node.nodeType != 1 || node.nodeName != 'SCRIPT') return;
.   .   .   node.type = 'text/plain';
.   .   .   node.addEventListener(
.   .   .   .   'beforescriptexecute', handleBeforeScriptExecute, false);
.   .   });
.   });
});
observer.observe(document.documentElement, {
.   childList: true,
.   subtree: true
});

とこんな感じのコードを走らせる。

ちなみに Presto Opera だとこんな長いコードを書かなくても、

window.opera.addEventListener('BeforeScript', function(e){e.source=''}, false);

だけで実現できる。オーパーツすぎる…。

そういえば忘れていたけど、次のリリースから赤福プラスは Presto Opera をもうサポートしない。

Leave a Reply

Archives