X-0024 nsIFileURLのインスタンスを作る

nsIURIのインスタンスには2種類ある

XULを扱っていると、nsIURIというインターフェースを使う必要のある場面が頻繁に出てきます。大概のメソッドでは、Web上のリソースもローカルのファイルも位置情報をこのnsIURIの形で渡してやれば万事OKなのですが、希に処理に失敗する場合があります。僕は、バックアップしておいた情報からブラウザのヒストリを復元する処理でこの問題に引っかかりました。

これは、「nsIURIのインスタンス」として生成したインスタンスはそのままではnsIFileURLとして扱えないからというのが原因でした。詳しく言うと、nsIURIのインスタンスは Components.classes['@mozilla.org/network/standard-url;1'].createInstance(Components.interfaces.nsIURI) で生成することができますが、こうして生成したインスタンスはQueryInterface()を使ってもnsIFileURLのインターフェースで扱うことが出来ないのです。nsIFileURLとしても扱えるnsIURIのインスタンスを生成するには、これとは別の手順を踏まなければなりません。

nsIFileURLとして扱えるnsIURIのインスタンスの作り方

nsIFileURLのインスタンスは、Mozillaの古いソースコードでは Components.classes['@mozilla.org/network/standard-url;1'].createInstance(Components.interfaces.nsIFileURL) という風にして生成するような書き方になっていますが、これは実際には動作しません。

一番簡単な生成方法は、ファイルパスをURLに変換する場合などによく使う、nsIIOServiceのnewFileURI()メソッドを使うやり方でしょう。というか僕はこの方法しか知りません。以下がその例です。

var url = 'file:///c:/test.txt';
var IOService = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService);

// newFileURI()はnsIFileのインスタンスからURIを生成するメソッドだが、
// nsIFileのインスタンスをURLから生成する方法はMozillaのバージョンに
// よって異なる。
// 複数バージョンを対象にしないなら、どれか一つに決め打ちして良い。
var tempLocalFile;
try { // Mozilla 1.2 以降
  var fileHandler = IOService.getProtocolHandler('file').QueryInterface(Components.interfaces.nsIFileProtocolHandler);
  tempLocalFile = fileHandler.getFileFromURLSpec(url);
}
catch(ex) { // Mozilla 1.1 以前
  try {
    tempLocalFile = IOService.getFileFromURLSpec(url);
  }
  catch(ex) { // Mozilla 1.0.x 以前
    tempLocalFile = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
    IOService.initFileFromURLSpec(tempLocalFile, url);
  }
}

// nsIURIのインスタンスを生成
var newURI = IOService.newFileURI(tempLocalFile);

こうして生成したnsIURIのインスタンスなら、nsIFileURLとして扱えます。実際の使用に当たっては以下のように、渡したURIの内容を見て自動で処理を振り分ける関数を作っておくと便利でしょう。

function makeURIFromSpec(aURI)
{
  try {
    var newURI;
    var IOService = Components.classes['@mozilla.org/network/io-service;1']
                              .getService(Components.interfaces.nsIIOService);
    aURI = aURI || '';
    if (aURI && aURI.match(/^file:/)) {
      var tempLocalFile;
      try {
        var fileHandler = IOService.getProtocolHandler('file')
                                   .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
        tempLocalFile = fileHandler.getFileFromURLSpec(aURI);
      }
      catch(ex) {
        try {
          tempLocalFile = IOService.getFileFromURLSpec(aURI);
        }
        catch(ex) { // [[interchangeability for Mozilla 1.0.x]]
          tempLocalFile = Components.classes['@mozilla.org/file/local;1']
                                    .createInstance(Components.interfaces.nsILocalFile);
          IOService.initFileFromURLSpec(tempLocalFile, aURI);
        }
      }
      newURI = IOService.newFileURI(tempLocalFile);
    }
    else {
      newURI = IOService.newURI(aURI, null, null); // 通常のURI
    }

    return newURI;
  }
  catch(e){
  }
  return null;
}