読者です 読者をやめる 読者になる 読者になる

心魅 - cocoromi -

半角スペース時々全角

content scriptからクロスサイトなXHRするプロトタイプ

なおこのプロトタイプは完成しません。


AutoPagerize for Chromeのソースを参考にcontentスクリプトからクロスサイトなxhrをしてみた。

AutoPagerize for Chrome
http://d.hatena.ne.jp/swdyh/20090525/1243232130


あと個人的な都合でGM_xmlHttpRequestのラッパーを作ることを目指します。

先に言い訳

  • 設計?なにそれ
  • ラッパーと言いつつonerrorに対応してません
  • リスクの検証を全くしていませんのでどんな危険があるかわかりません。
  • まずいことがあったらツッコミして欲しいです
  • コードの利用は自己責任でお願いします

ContentScriptとextension本体との通信を行うAPI

chrome.extension.connectというAPIがありコレでちょいと面倒なことをすると本体をプロキシにしてクロスサイトなXHRが出来そう。
FlashでLocalConnectionとか使ったことある人にはなじみのあるコードじゃねーですかね。


Message Passing
http://code.google.com/chrome/extensions/messaging.html

良くわからない解説

  1. content scriptからbackgroundにコネクションを貼る
  2. content scriptでクロスサイトなxhrしたくなったら、backgroundにメッセージを送ってお願いする
  3. このときコールバック関数はcontent scriptで管理しておく
  4. またbackgroundに識別子(ID)を渡しておく
  5. backgroundは通信が終わったら識別子とともに結果をメッセージとして送る
  6. content scriptは識別子に対応するコールバックを呼び出す

使う物

マニフェストを見てもらえば判ると思いますが、backgroundページを利用します。

{
    〜〜中略〜〜
    "background_page": "background.html",
    "permissions": [
        〜〜content scriptがアクセスするホスト名の配列
    ],
    "content_scripts": [{
        "matches": [
            "http://*/"
        ],
        "js": [ "test.js" ]
    }]
}

permissionsの項目はスクリプトがアクセスするホスト名を列挙します。
若干語弊があるのですが、詳しく説明するとExtensionの作り方から説明せにゃなならんので以下を参照してください。
Formats: Manifest Files#permissions
http://code.google.com/chrome/extensions/manifest.html#permissions



んで、このHTMLに直接JSを書いても良いけど一応分離。
この辺は趣味。
background.html

<html>
    <head>
        <script src="background.js"></script>
    </head>
    <body>
    </body>
</html>

完全にAutoPagerizeのパクリです。
http://github.com/swdyh/autopagerize_for_chrome/blob/master/src/background.html

backgroundの実装

こちらもほぼパクリですが。

background.js

window.onload = init

//趣味
var CMD = {
    get : get 
} ;

function init() {
    chrome.extension.onConnect.addListener(function(port) {
        port.onMessage.addListener( function( message , con ) {
            message.args.push( con );
            //ここも趣味
            (CMD[ message.action ]||function(){}).apply( CMD , message.args ) ;
        });
    });
}

function get( opt , callbackid , con ) {
    var xhr = new XMLHttpRequest()
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) { 
            con.postMessage( { action : "xhrResponse" , args : [ callbackid , xhr ] } ); 
        }
    }
    xhr.open( opt.method , opt.url , true)
    xhr.send(null)
    return xhr
}

content scriptの実装

chrome判定なんかはAutoPagerizeからそのまま引用してます
test.js

//chromeで実行されているか判定する
function isChromeExtension() { return (typeof chrome == 'object') && (typeof chrome.extension == 'object') }

//グリモンのAPIchrome互換にする
function chromeCompatible() {

    //バックグラウンドとの通信開始
    var port = chrome.extension.connect() ;
    port.onMessage.addListener( function( res ) { 
            ( Connection[ res.action ] || function(){} ).apply( Connection , res.args );
    } ) ;
    
    
    //裏とのやりとりを引き受けるオブジェクト
    Connection = (function(){
        var callbackList = {} , 
            callbackId = 0 ;

        //裏からのレスポンスを受けてコールバック発動
        function xhrResponse( id , response ){
            callbackList[ id ]( response );
            delete callbackList[ id ];
        }

        //裏からのレスポンスに対応するコールバックを登録する
        function registXHRCallBack( callback ){
            callbackList[ ++callbackId ] = callback ;
            return callbackId ;
        }

        return {
            xhrResponse : xhrResponse ,
            registXHRCallBack : registXHRCallBack 
        };
    })();

    GM_xmlhttpRequest = function( opt ) {
        port.postMessage( { 
            action : "get" ,//裏にxhrを要求
            args : [ opt , Connection.registXHRCallBack( opt.onload ) ] //引数
        });
    }
}
if( isChromeExtension() ){ 
    chromeCompatible(); 
}

まとめ

  • connectionを使ってextension本体に実際の処理を任せる
  • 色々するとcontent scriptでもクロスサイトなxhrが出来る


って、そもそも本当にcontent scriptからクロスサイトなxhr出来ないの?