質問スレPart23の261からの流れが興味深かったのでちょっと実験してみた.

前提

ユーザー JavaScript でできることによれば,.js なスクリプトはすべてのスクリプトの実行の前のとなっていて,一方 ユーザー JavaScript による制御のGreasemokeyスクリプトの節 によれば,.user.js にしたスクリプトは DOMContentLoade 以降 load 前になっているので,以下の順序で実行されることが予想される.

.js で登録した DOMContentLoaded と 文書内でインラインで登録した DOMContentLoaed との関係や,.js で登録した onloadbody.onload と*.user.js で登録した onload との関係などは特に規定されていない.また DOMContentLoadedbody.onload の間にはページ内の各画像類の onload が発生するはずだがその順序も特に規定されてない.さらに Extension との関係もイマイチ不明.

実験

Opera 11.01.1169 で

という構成にして実験してみたところ Dragonfly のエラータブで以下の出力が得られた.

console.log   1  in extension injected.js
console.log   2  in test.js
console.log   8  in extension injected.js BeforeScript listener; window.addEventListener('DOMContentLoaded', function(){ console.log( 'in html DOMContentLoaded listener' ); }, false );
console.log  10  in test.js BeforeScript listener; window.addEventListener('DOMContentLoaded', function(){ console.log( 'in html DOMContentLoaded listener' ); }, false );
console.log  11  in extension injected.js AfterScript listener; window.addEventListener('DOMContentLoaded', function(){ console.log( 'in html DOMContentLoaded listener' ); }, false );
console.log  13  in test.js AfterScript listener; window.addEventListener('DOMContentLoaded', function(){ console.log( 'in html DOMContentLoaded listener' ); }, false );
console.log   4  in extension injected.js BeforeExternalScript listener; <http://ashula.info/lab/opera/inhead.js>
console.log   6  in test.js BeforeExternalScript listener; <http://ashula.info/lab/opera/inhead.js>
console.log   8  in extension injected.js BeforeScript listener; console.log( 'in inhead.js via script.src' );
console.log  10  in test.js BeforeScript listener; console.log( 'in inhead.js via script.src' );
console.log   1  in inhead.js via script.src
console.log  11  in extension injected.js AfterScript listener; console.log( 'in inhead.js via script.src' );
console.log  13  in test.js AfterScript listener; console.log( 'in inhead.js via script.src' );
console.log   8  in extension injected.js BeforeScript listener; console.log('in html before image');
console.log  10  in test.js BeforeScript listener; console.log('in html before image');
console.log   1  in html before image
console.log  11  in extension injected.js AfterScript listener; console.log('in html before image');
console.log  13  in test.js AfterScript listener; console.log('in html before image');
console.log   8  in extension injected.js BeforeScript listener; console.log('in html after image');
console.log  10  in test.js BeforeScript listener; console.log('in html after image');
console.log   1  in html after image
console.log  11  in extension injected.js AfterScript listener; console.log('in html after image');
console.log  13  in test.js AfterScript listener; console.log('in html after image');
console.log   4  in extension injected.js BeforeExternalScript listener; <http://ashula.info/lab/opera/inbody.js>
console.log   6  in test.js BeforeExternalScript listener; <http://ashula.info/lab/opera/inbody.js>
console.log   1  in html img.onload
console.log   8  in extension injected.js BeforeScript listener; console.log( 'in inbody.js via script.src' );
console.log  10  in test.js BeforeScript listener; console.log( 'in inbody.js via script.src' );
console.log   1  in inbody.js via script.src
console.log  11  in extension injected.js AfterScript listener; console.log( 'in inbody.js via script.src' );
console.log  13  in test.js AfterScript listener; console.log( 'in inbody.js via script.src' );
console.log  17  in test.js DOMContentLoaded listener; 
console.log  15  in extension injected.js DOMContentLoaded listener; 
console.log   1  in html DOMContentLoaded listener
console.log   2  in test.user.js 
console.log  19  in extension injected.js load listener; 
console.log  21  in test.js load listener; 
console.log   1  in html body.onload
console.log   4  in test.user.js window.load listener

整理すると

となった.BeforeScript などの window.opera 固有のイベントは Extension,*.js と登録された順に発火している.DOMContentLoaded も Extension,*.js,インラインとなった.load に関しては,Extension,*.js,インライン,*.user.js 登録された順となった

load イベントの順序で body.onload が *.user.js より先に実行されているのは DOMContentLoaded 発火時すでに, body.onloadload に登録されているためと考えられる.

img.onloadDOMContentLoaded より先に発火しているが,外部のリソースを読み込む script 要素が直後にあるためと考えられる.

まとめ

Extension と 2 種の UserJS の実行タイミングを調査し Extension,*.js,*.user.js となっているのを確認した.

BeforefScript, BeforeExternalScript, AfterScriptwindow.opera 固有イベントは や DOMContentLoaded は EventListener が登録された順に実行されている事がわかった.

*.user.js で onload に登録すれば body.onload より後に実行できるので本当に最後に実行出来る事もわかった.

追加実験

この結果はDragonfly 起動時限定., opera.postError なら確かめられるのでは という指摘を頂いたので,ちょっと実験してみた.

console.log を使っていたところをすべて opera.postError に書き換えて,出力の最初に Date.now() を入れるようにし,Exstension の Injected Script に jQuery 1.4.4 (mini) を追記してみたところ,*.js な UserJS のほうが先に実行される様になった.Date.now() の出力値は同じだったが,イベントハンドラの実行順から extension のほうが後だと考えられる.