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

anti scroll

ブラウザと小説の新しい関係を模索する

Operaの拡張でもクロスドメイン通信

ChromeFirefoxと拡張を開発できたので、調子にのって普段まったく使わないOperaの拡張も作ってみました。

で、何ヶ月ぶりかで起動してみたら、案の定バージョンが偉く上がっていて、早速バージョンアップしてみたら、また一段と我が道を行くUIに変わってましたね……

Operaはバージョンアップするたびにボタンの位置とかメニューとかガラっと変わる印象があるんですけど、中の人の入れ替わりが激しいのか、あるいは単に開発者の気まぐれなのか?

それはさておき、全体的に情報が少なくて大変でしたが、なんとか目的のクロスドメイン通信ができたので載せておきます。

やりたいことは、開いたページのURLを取得して、外部のウェブサービスに投げて結果を受け取り、その結果を元に開いたページをちょこちょこと書き換える、というものです。

最初にバックグラウンドページ。ここにバックグラウンドページ処理のスクリプト(background.js)を配置。

<doctype html>
<html>
  <head>
    <title>Edit the Page</title>
    <script type="text/javascript" src="background.js"></script>
  </head>
</html>

そのバックグラウンド処理(background.js)の中身

// background.js

window.addEventListener("load", setupConnection, false);

function setupConnection(){

  // ページが開くと、content scriptがbackground page にAttachしてくる
  opera.extension.onconnect = function(event) {
    event.source.postMessage("echoURL"); // content scriptにURL下さい、とメッセージ
  }
  
  // content script から返答を待ち受ける
  opera.extension.onmessage = function(event){
    var url = event.data;

    // 外部APIを呼ぶ
    var xhr = new XMLHttpRequest()
    xhr.onreadystatechange = function(){
      if(this.readyState == 4){
	if(this.status == 200){
	  event.source.postMessage(xhr.responseText); // content scriptにAPI結果を渡す
	} else if(this.status == 400){
	  opera.postError(xhr.responseText);
	}
      }
    };
    xhr.open("GET", "http://api.someservice/somefunc.json?url=" + url, false);
    xhr.send();
  }
}

そして開いたページのコンテキストで動くスクリプト。includes以下に置くルール……だと思います。

// includes/content.js
opera.extension.onmessage = function(event){
  var message  = event.data;

  if(message == "echoURL"){
    event.source.postMessage(encodeURIComponent(document.URL)); // background.jsに返信
  } else {
     // XHRの結果を処理。window.$ に注意
     window.$("#some-id").html(message);
  }
};

jqueryなどは、includesディレクトリに一緒に放り込んでおけば、勝手にロードされて使えます。

ただグローバルスコープがwindowオブジェクトじゃないので、$を呼ぶにはwindow.$とか呼ばなきゃいけないのが少し面倒です。

あとはconfig.xmlにクロスドメインする先のURLを登録しておけばオッケーです。

<?xml version="1.0" encoding="utf-8"?>
<widget xmlns="http://www.w3.org/ns/widgets">
  <name>mytest</name>
  <description>mytest</description>
  <access origin="http://api.someservice" subdomains="true"/>
</widget>

拡張は、ファイル全部をzipでまとめて拡張子を「oex」にする、ということなので、こんなMakefileを作っておくと便利かもしれません。

all: mytest.oex

mytest.oex: background.js config.xml index.html includes/content.js
	rm -f $@
	zip $@ background.js
	zip $@ index.html
	zip $@ config.xml
	zip -r $@ includes