Home > Latest topics

Latest topics > Firefoxで独自プロトコルを定義する方法

宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

Firefoxで独自プロトコルを定義する方法 - Mar 26, 2007

独自のプロトコルというか独自のスキーマを使えるようにしたくてやり方を調べてみたら、あちこちでAdding a New Protocol to Mozillaという文書が紹介されてたんだけど、日本語訳が無かったのでとりあえず気合いと勘で訳してみた。誤訳があったらゴメンナサイ。

2004年の資料で、しかもMozilla Suiteを対象とした物なので情報が古い。Firefoxでのちゃんとしたやり方が分かり次第追記するつもり。試したらちゃんと動いたので、訳注としてFirefox用拡張機能でのやり方を追記した。

Mozillaに新しいプロトコルを追加する

Doron Rosenberg, IBM Corporation.

この文書は、Mozillaに新しいプロトコルを追加する方法の例を示しています。プロトコルの実装にはJavaScriptとXPCOMを利用します。また、以下の内容はすでにインストールされているMozillaに新しいプロトコルを追加する方法の解説も含んでいます。

はじめに

MozillaはHTTPやFTPといった一般的なWebのプロトコルをサポートしています。新しいプロトコルに対応させるために、すでにインストールされているMozillaを変更することは、時に有用です。これによって、Mozillaは他のアプリケーションと、よりスムーズに連携することができます。おそらく、ある人はMozillaに対して、ユーザがあるリンクをクリックしたときに、インスタントメッセンジャーのクライアントを起動してほしいと思うでしょう。あるいは、アプリケーションがMozillaを起動する時に、最初にページが読み込まれる前に何らかの処理をさせたいと思う場合もあるでしょう。

新しいプロトコルを追加するには、XPCOMコンポーネントを実装する必要があります。XPCOMは様々な言語でのプログラミングを許容しており、現在Mozillaにおいては、XPCOMコンポーネントはC++またはJavaScriptで実装することができます。この文書では、コンパイルする必要がないJavaScriptを使用します。

この文書で作成する例では、ユーザのデフォルトの検索エンジンで検索を行う「search:」プロトコルを追加します。

XPCOMシェル

図1(以下を参照)は、新しいプロトコルを実装するのに必要なJavaScriptによるXPCOMシェルの基本的な構造を示しています。

NSGetModule() は TestModule オブジェクトを返却するもので、XPCOMが私たちのコンポーネントを検索するときに呼び出されます。TestModule は、新しいコンポーネントを登録するためのものや、ProtocolFactoryオブジェクトを取得するための、XPCOMが呼び出すいくつかのメソッドを実装しています。ProtocolFactoryはMozillaの要求に応じて、Protocolオブジェクトを生成することを許可します。Protocolは最終的にはプロトコルの実装となる物で、XPCOMが呼び出すためのいくつかのメソッドを持ちます。newChannelと呼ばれるメソッドは、Mozillaがそのプロトコルを解決する必要が生じた際に実行されるコードを含んでいます。

新しいプロトコルを作るためにコードが変更されなければならない時を除いて、XPCOMシェルのコードの変更はすぐに適用されます。

図1:XPCOMシェルの基本的な構造


// XPCOMの定数の定義

// Protocolの定義

// ProtocolFactoryの定義

// TestModuleの定義

function NSGetModule(){
  return TestModule;
}

XPCOMの定数は、新しいコンポーネントのためのいくつかの定数と、それに加えて、私たちが利用したい一般的なXPCOMコンポーネントのためのものから成り立ちます。kPROTOCOL_CIDはこの特定のプロトコルのための一意な識別子で、新しいプロトコルの各々は、新しく生成されたUUIDをそれぞれ個別に持つべきです。kSCHEMEはそのプロトコルを呼び出すための方法を定義します。この場合は「x-search」がそうで、これによって、Webサイトからこのプロトコルを呼び出すときには、「x-search:検索語句」という書式を使うことになります。

図2:定数の定義


const kSCHEME = "x-search";
const kPROTOCOL_NAME = "Search Protocol";
const kPROTOCOL_CONTRACTID = "@mozilla.org/network/protocol;1?name=" + kSCHEME;
const kPROTOCOL_CID = Components.ID("789409b9-2e3b-4682-a5d1-71ca80a76456");
/* 訳注:↑のUUID(GUID)はこのままコピペせず、
         実装したいプロトコルごとに新しく生成すること。 */

// Mozillaによって元々定義されている物
const kSIMPLEURI_CONTRACTID = "@mozilla.org/network/simple-uri;1";
const kIOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1";
const nsISupports = Components.interfaces.nsISupports;
const nsIIOService = Components.interfaces.nsIIOService;
const nsIProtocolHandler = Components.interfaces.nsIProtocolHandler;
const nsIURI = Components.interfaces.nsIURI;

以下はTextModuleのコードです。registerSelfは、このコンポーネントで定義されているいくつかの定数を使って、そのコンポーネントを登録します。

図3:TestModuleのコード


/**
 * JavaScriptによるXPCOMコンポーネントを登録するためのあれこれ
 *
 * 私たちはxpcom-startupカテゴリを監視するために、私たち自身をセットアップします。
 * これは私たちに出発点を提供します。
 */

var TestModule = new Object();

TestModule.registerSelf = function (compMgr, fileSpec, location, type)
{
  compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  compMgr.registerFactoryLocation(kPROTOCOL_CID,
                                  kPROTOCOL_NAME,
                                  kPROTOCOL_CONTRACTID,
                                  fileSpec, 
                                  location, 
                                  type);
}

TestModule.getClassObject = function (compMgr, cid, iid)
{
  if (!cid.equals(kPROTOCOL_CID))
    throw Components.results.NS_ERROR_NO_INTERFACE;

  if (!iid.equals(Components.interfaces.nsIFactory))
    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

  return ProtocolFactory;
}

TestModule.canUnload = function (compMgr)
{
  return true;
}

ProtocolFactoryのコードは、XPCOMに関するいくつかのエラーのチェックを行うためのcreateInstanceによってインスタンス化され、エラーがなければProtocolオブジェクトを返却します。

図4:ProtocolFactoryのコード


var ProtocolFactory = new Object();

ProtocolFactory.createInstance = function (outer, iid)
{
  if (outer != null)
    throw Components.results.NS_ERROR_NO_AGGREGATION;

  if (!iid.equals(nsIProtocolHandler) && !iid.equals(nsISupports))
    throw Components.results.NS_ERROR_NO_INTERFACE;

  return new Protocol();
}

最後に、Protocolオブジェクトでそのプロトコルの機能を実装します。newChannel()はそのプロトコルが利用されたときに実行されるべきコードが含まれているメソッドです。プロトコルはチャンネルとして機能するもので、そのコードはそれが動作するためにそれ自身を実装する必要があります。

図5:Protocolのコード


function Protocol()
{
}

Protocol.prototype =
{
  QueryInterface: function(iid)
  {
    if (!iid.equals(nsIProtocolHandler) &&
        !iid.equals(nsISupports))
      throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  },

  scheme: kSCHEME,
  defaultPort: -1,
  protocolFlags: nsIProtocolHandler.URI_NORELATIVE |
             nsIProtocolHandler.URI_NOAUTH,

  allowPort: function(port, scheme)
  {
    return false;
  },

  newURI: function(spec, charset, baseURI)
  {
    var uri = Components.classes[kSIMPLEURI_CONTRACTID].createInstance(nsIURI);
    uri.spec = spec;
    return uri;
  },

  newChannel: function(input_uri)
  {
    // そのプロトコルが使用されたときに実行されるべきコードをここに書く。
  }    
}

Protocolの実装

最後のステップは、そのプロトコルが使用されたときに呼び出されるコードの実装です。newChannelはXPCOMのnsIURIの型の引数一つを伴って呼び出されます。URIの文字列を取得するには、specプロパティを使います。その文字列は、そのプロトコルが呼ばれたときのURIの全体を含んでいます。この場合では「x-search:(検索文字列)」です。最初にしなければいけないのは、「x-search:」の部分を除去する処理です。dump()はメッセージをコンソールに出力する物で、デバッグ用途に便利です。

図6:引数の受け取り


newChannel: function(aURI)
{
  // aURIはnsIURIのオブジェクトなので、.specから文字列を得ることができる
  var mySearchTerm = aURI.spec;

  // kSCHEME: の部分を除去する
  mySearchTerm = mySearchTerm.substring(mySearchTerm.indexOf(":") + 1, mySearchTerm.length);    
  mySearchTerm = encodeURI(mySearchTerm);

  dump("[mySearchTerm=" + mySearchTerm + "]\n");

  ...
},

次は、ユーザの検索エンジンを取得する処理です。以下のコードはMozillaのnavigator.jsファイルから引用された物です。最初に、フォールバック用のデフォルトの検索エンジンを取得するために設定が呼び出されます。その後に、ユーザの選択している検索エンジンを取得するために、インターネット検索機能のデータを指し示しているRDFデータソースを参照しています。

図7:検索エンジンを取得する


newChannel: function(input_uri)
{
  ...

  var finalURL = "";

  try{ 
    // 設定サービスを取得
    var prefService = Components.classes["@mozilla.org/preferences-service;1"]
                                .getService(Components.interfaces.nsIPrefService);

    var prefBranch = prefService.getBranch(null);

    defaultSearchURL = prefBranch.getComplexValue("browser.search.defaulturl",
                          Components.interfaces.nsIPrefLocalizedString).data;

    var searchDS = Components.classes["@mozilla.org/rdf/datasource;1?name=internetsearch"]
                             .getService(Components.interfaces.nsIInternetSearchService);

    var searchEngineURI = prefBranch.getCharPref("browser.search.defaultengine");
    if (searchEngineURI) {          
      searchURL = searchDS.GetInternetSearchURL(searchEngineURI, mySearchTerm, 0, 0, {value:0});
      if (searchURL)
        defaultSearchURL = searchURL;      
    }
    dump("[Search Protocolは成功しました: " + defaultSearchURL + "]")
  } catch (e){
    dump("[Search Protocolは検索の設定の取得に失敗しました: " + e + "]\n");
  }

  finalURL = defaultSearchURL + mySearchTerm;

  ...
},

前述したように、プロトコルはチャンネルとして機能しますので、newChannelはチャンネルを返さなくてはいけません。プロトコルの目的は検索を実行することなので、チャンネルはブラウザウィンドウの表示ページを検索エンジンへと変更するJavaScriptとともに返されます。これはIOServiceを取得して、新しいチャンネルを作成し、newChannelからそれを返却することによって成されます。

図8:チャンネルの生成


newChannel: function(input_uri)
{
  ...

  /* ダミーのnsIChannelのインスタンスを生成 */
  var ios = Components.classes[kIOSERVICE_CONTRACTID]
                      .getService(nsIIOService);

  return ios.newChannel("javascript:document.location.href='" + finalURL + "'", null, null);

},

最終的なnewChannelのコードを、以下の図9で示しています。プロトコルのためのコードの全体はここにあります。

図9:最終的なProtocolのコード


newChannel: function(input_uri)
{
  // aURIはnsIURIのオブジェクトなので、.specから文字列を得ることができる
  var mySearchTerm = aURI.spec;

  // kSCHEME: の部分を除去する
  mySearchTerm = mySearchTerm.substring(mySearchTerm.indexOf(":") + 1, mySearchTerm.length);    
  mySearchTerm = encodeURI(mySearchTerm);

  dump("[mySearchTerm=" + mySearchTerm + "]\n");
  var finalURL = "";

  try{ 
    // 設定サービスを取得
    var prefService = Components.classes["@mozilla.org/preferences-service;1"]
                                .getService(Components.interfaces.nsIPrefService);

    var prefBranch = prefService.getBranch(null);

    defaultSearchURL = prefBranch.getComplexValue("browser.search.defaulturl",
                          Components.interfaces.nsIPrefLocalizedString).data;

    var searchDS = Components.classes["@mozilla.org/rdf/datasource;1?name=internetsearch"]
                             .getService(Components.interfaces.nsIInternetSearchService);

    var searchEngineURI = prefBranch.getCharPref("browser.search.defaultengine");
    if (searchEngineURI) {          
      searchURL = searchDS.GetInternetSearchURL(searchEngineURI, mySearchTerm, 0, 0, {value:0});
      if (searchURL)
        defaultSearchURL = searchURL;      
    }
    dump("[Search Protocolは成功しました: " + defaultSearchURL + "]")
  } catch (e){
    dump("[Search Protocolは検索の設定の取得に失敗しました: " + e + "]\n");
  }

  finalURL = defaultSearchURL + mySearchTerm;

  /* ダミーのnsIChannelのインスタンスを生成 */
  var ios = Components.classes[kIOSERVICE_CONTRACTID]
                      .getService(nsIIOService);

  return ios.newChannel("javascript:document.location='" + finalURL + "'", null, null);

},

Protocolのインストール

訳注:ここまでで作成した独自のプロトコルの実装を拡張機能に含めるには、作成したJavaScriptのファイルを「nsTestProtocolHandler.js」のような名前で保存し、拡張機能のパッケージングの際にinstall.rdfと同じ階層に作成したcomponentsフォルダに入れておくだけでよい。以下、一応参考のために訳文を掲載しておくけれども、Firefox用拡張機能を作成する際には、以下の記述はあんまり意味がないので無視していい。

MozillaのXPCOMコンポーネントはMozillaのインストール先ディレクトリの中のcomponentsディレクトリにあります。searchプロトコルをインストールするには、ここにあるすべてのJavaScriptファイルをコピーしてください。また、そのコンポーネント(登録されたコンポーネントはcomponentsディレクトリの中のcompreg.datファイルの中に列挙されます)を登録するようにMozillaに指示するためには、.autoregと名付けられた空のファイルをMozillaのインストールディレクトリの、Mozillaの実行ファイルと同じ階層に置きます。

プロトコルがインストールされた後は、新しいコンポーネントを登録するために、Mozillaを再起動する必要があります。そのプロトコルは「x-search:mozilla」とURLバーに入力してEnterキーを押すことによって呼び出すことができます。

Copyright © 2004 IBM Corporation, all rights reserved.

分類:Mozilla > XUL, , , , , , 時刻:17:48 | Comments/Trackbacks (2) | Edit

Comments/Trackbacks

独自のプロトコルを追加する

以下のページで解説されている通りに JavaScript 製 XPCOM を登録してやることで、簡単に実現可能。
Adding a New Protocol to Mozilla
日本語訳
Latest topics > Firefoxで独自プロトコルを定義する方法 - o...

Trackback from SCRAPBLOG at 2007/03/26 (Mon) 23:50:41

[IT][Firefox]Firefoxで独自プロトコルをハンドリングするアドオンを作成する

最近Firefoxの自作アドオンを作成しようといろいろやってみてるので、その一部の内容をブログに記しておきます。 最終的に作りたいアドオンの内容はまだ公開できないのだけれど、 技術的にやりたい要素としては大体以下の通り。 独自プロトコルを定義して、そのプロトコルに

Trackback from 遙かへのスピードランナー at 2008/06/02 (Mon) 19:48:24

TrackBack ping me at


の末尾に2020年11月30日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2007-03-26_protocol.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。

Post a comment

writeback message: Ready to post a comment.

2020年11月30日時点の日本の首相のファミリーネーム(ひらがなで回答)

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のコメント

最近のつぶやき