definePropertyとか__defineGetter__で設定するgetterです
Chromeの拡張機能のcontent_scriptsで使うと思ったとおりに動いてくれません
※getter関係なくChromeのcontent_scriptsのせいでした
Chromeの拡張機能のcontent_scriptsで使うと思ったとおりに動いてくれません
※getter関係なくChromeのcontent_scriptsのせいでした
今回、cookie使った拡張機能でも作ろうかなと思って参考にするためcookie扱う拡張機能を拾ってきてソースを読んでました
そこで明らかに無限ループになりそうなコードがあって、コンソールに貼り付けると案の定ループして
Uncaught RangeError: Maximum call stack size exceeded.
とエラーが出ました
なのに、拡張機能の方では起きてませんでした…
そのコードを必要なとこだけ取り出したのが↓です
コードの内容は、
defineGetter関数はdocumentオブジェクトのプロパティcookieに読み取るアクセスがあった場合にイベントCOOKIE-GETを発火させます
読んでみると、defineGetter関数は↓だけでいいのではと思うかもですが
getterを設定するのも変数同様content_scripts内で行うと消えるのを確認しました
下のイベントリスナは、COOKIE-GETイベントが発生した時にdocument.cookieを読み取り表示します
つまり、document.cookieにアクセスすると、getterで指定された関数が呼ばれ、それがCOOKIE-GETイベントを発生させて、COOKIE-GETのイベントリスナでdocument.cookieにアクセスするので
getter→イベントリスナ→getter→……
とループするはずです
しかし、Chrome拡張機能のcontent_scriptsから実行されると
イベントリスナの関数内でのdocument.cookieへのアクセスではgetterが機能せず普通にdocument.cookieの値を返しています(なんで?)
content_scripts以外でコンソールなどでこのコードを実行した場合は↓のようにループしてます
長いので途中略してます
推測ですが、content_scripts内で作った変数とか保存できないので、content_scriptsが動いてるところは実はページ内と同じ環境の別な場所であって、getter登録したのは本物のページの方だけでcontent_scriptsが動いてる方のdocumentにはgetter登録されてないから元々のdocument.cookieの値が返される…かな?
----
*追記*
推測が当たってました
content_scripts内のグローバルと、content_scriptsからscriptタグ追加による方法でグローバルにそれぞれ変数を作りました
そして、content_scripts内とコンソールの2つからグローバルに作った変数にアクセスできるか調べてみました
結果はこんな感じでcontent_scripts内は別のスコープが適用されてるみたいです
content_scriptsが終了してもcontent_scripts内の関数からはデータが見えるようにclosureとして扱われてるようです
だとすると、content_scripts内での変更をページの方でも引き継ぐ変更や、イベントリスナは受け取れるとかはどういうことなんだろう
やっぱりよくわからない…
そこで明らかに無限ループになりそうなコードがあって、コンソールに貼り付けると案の定ループして
Uncaught RangeError: Maximum call stack size exceeded.
とエラーが出ました
なのに、拡張機能の方では起きてませんでした…
そのコードを必要なとこだけ取り出したのが↓です
このコードをコンソールに貼り付けたり、jsファイルとして読み込むとスタックサイズを超えるとエラーがでますfunction defineGetter() {var script;script = "(" + function() {document.__defineGetter__("cookie",function(){var event = new CustomEvent("COOKIE-GET");console.log("getter-dispatch-start");document.dispatchEvent(event);console.log("getter-dispatch-end");return "getter-result";})} + ")()";var script_element = document.createElement("script");script_element.appendChild(document.createTextNode(script));document.documentElement.appendChild(script_element);}// define event listenerdocument.addEventListener("COOKIE-GET", function() {console.log("listener-start");var a = document.cookie;console.log('document.cookie is "'+a+'"');console.log("listener-end");});defineGetter();
コードの内容は、
defineGetter関数はdocumentオブジェクトのプロパティcookieに読み取るアクセスがあった場合にイベントCOOKIE-GETを発火させます
読んでみると、defineGetter関数は↓だけでいいのではと思うかもですが
関数を文字列化してscriptタグ作ってそれに入れて、ロードするという風にしているのは、content_scriptsとして実行した場合に作られた変数などはcontent_scriptsが終わると消えてしまうのでscriptタグを読み込みcontent_scriptsの外でgetterを設定するようにしていますfunction defineGetter() {document.__defineGetter__("cookie",function(){var event = new CustomEvent("COOKIE-GET");console.log("getter-dispatch-start");document.dispatchEvent(event);console.log("getter-dispatch-end");return "getter-result";})}
getterを設定するのも変数同様content_scripts内で行うと消えるのを確認しました
下のイベントリスナは、COOKIE-GETイベントが発生した時にdocument.cookieを読み取り表示します
つまり、document.cookieにアクセスすると、getterで指定された関数が呼ばれ、それがCOOKIE-GETイベントを発生させて、COOKIE-GETのイベントリスナでdocument.cookieにアクセスするので
getter→イベントリスナ→getter→……
とループするはずです
しかし、Chrome拡張機能のcontent_scriptsから実行されると
となってループしていません
イベントリスナの関数内でのdocument.cookieへのアクセスではgetterが機能せず普通にdocument.cookieの値を返しています(なんで?)
content_scripts以外でコンソールなどでこのコードを実行した場合は↓のようにループしてます
長いので途中略してます
Chromeのcontent_scriptsだけで起こるのでdatalistが動かないみたいにバグの一種かなとも思いましたが実際に使われてる拡張機能でこの方法を使ってるので裏ワザみたいなものかなとも思えますdocument.cookiegetter-dispatch-startlistener-startgetter-dispatch-startlistener-startgetter-dispatch-startlistener-start:::Uncaught RangeError: Maximum call stack size exceeded.getter-dispatch-enddocument.cookie is "getter-result"listener-endgetter-dispatch-enddocument.cookie is "getter-result"listener-endgetter-dispatch-enddocument.cookie is "getter-result"listener-endgetter-dispatch-end:::"getter-result"
推測ですが、content_scripts内で作った変数とか保存できないので、content_scriptsが動いてるところは実はページ内と同じ環境の別な場所であって、getter登録したのは本物のページの方だけでcontent_scriptsが動いてる方のdocumentにはgetter登録されてないから元々のdocument.cookieの値が返される…かな?
----
*追記*
推測が当たってました
content_scripts内のグローバルと、content_scriptsからscriptタグ追加による方法でグローバルにそれぞれ変数を作りました
そして、content_scripts内とコンソールの2つからグローバルに作った変数にアクセスできるか調べてみました
content_scripts内で 作成した変数 | scriptタグでグローバル に作成した変数 | |
content_scriptsからアクセス | ○ | × |
コンソールでアクセス | × | ○ |
結果はこんな感じでcontent_scripts内は別のスコープが適用されてるみたいです
content_scriptsが終了してもcontent_scripts内の関数からはデータが見えるようにclosureとして扱われてるようです
だとすると、content_scripts内での変更をページの方でも引き継ぐ変更や、イベントリスナは受け取れるとかはどういうことなんだろう
やっぱりよくわからない…
コメントする