Macで〜.appを実行する

ちょっと作ってみた。

Mac用のインターフェースでnsILocalFileMacというものが実はある。これをnsIFileに対して、QueryInterfaceするとisPackageというメソッドが使用可能になり、実行可能なパッケージであるか判定できる。

実装 - 1 : Info.plistファイルから実行ファイルを割り出す

パッケージのディレクトリ内、package/Contents/Info.plistというXMLファイルにそのパッケージの情報が書かれていて、<key>CFBundleExecutable</key>に対応する string 要素に実行ファイル名が書かれている。それを使って実際に実行するファイルを割り出す方法。

// 実行例
execute("/Applications/Google Chrome.app", ["--", "http://example.com"]);

var isMac = true; // 初期化時にMacかどうかの判定を行っておく。
function execute (path, args) {
  args = [].concat(args);
  var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  file.initWithPath(path);

  if (isMac) {
    file.QueryInterface(Ci.nsILocalFileMac);
    if (file.isPackage) {
      file = getExecuteFileFromPackage(file);
    }
  }
  var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
  process.init(file);
  process.run(false, args, args.length);
}

function getExecuteFileFromPackage (aFile) {
  var infoPlistFile = aFile.clone().QueryInterface(Ci.nsILocalFile);
  infoPlistFile.appendRelativePath("Contents/Info.plist");
  var ifstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
  var domparser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
  ifstream.init(infoPlistFile, -1, 0, 0);
  var doc = domparser.parseFromStream(ifstream, "UTF-8", infoPlistFile.fileSize, "application/xml");
  ifstream.close();
  var keys = doc.getElementsByTagName("key");
  var exeFileName;
  for (var i = 0, key; key = keys[i]; i++){
    if (key.textContent == "CFBundleExecutable") {
      exeFileName = key.nextElementSibling.textContent;
      break;
    }
  }
  var exeFile = aFile.clone().QueryInterface(Ci.nsILocalFile);
  exeFile.appendRelativePath("Contents/MacOS/" + exeFileName);
  return exeFile;
}

getExecutableFileFromPackage関数でInfo.plistファイルをパースして実行ファイルを取得している。(初めてDOMParserのparseFromStreamを使ったw)

ちょい面倒やね〜

実装 - 2 : /usr/bin/open に渡す

こちらは、実行パッケージだったら/usr/bin/openを実行ファイル、第一引数にパッケージのパスとなるように設定しなおす方法。

// 実行例
execute("/Applications/Google Chrome.app", ["--", "http://example.com"]);

var isMac = true; // 初期化時にMacかどうかの判定を行っておく。
function execute (path, args) {
  args = [].concat(args);
  var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  file.initWithPath(path);

  if (isMac) {
    file.QueryInterface(Ci.nsILocalFileMac);
    if (file.isPackage) {
      args = [file.path, "--args"].concat(args);
    }
    file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
    file.initWithPath("/usr/bin/open");
  }
  var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
  process.init(file);
  process.run(false, args, args.length);
}

こっちの方がシンプルで良さそうな感じですね〜。