/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

const nsISupports           = Components.interfaces.nsISupports;

const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
const nsIFactory            = Components.interfaces.nsIFactory;
const nsIModule             = Components.interfaces.nsIModule;
const nsIProtocolHandler    = Components.interfaces.nsIProtocolHandler;

const nsIIOService          = Components.interfaces.nsIIOService;
const nsIURI                = Components.interfaces.nsIURI;
const nsIXULAppInfo          = Components.interfaces.nsIXULAppInfo;

/**
 * 本体
 * mhtml プロトコルを通常の URI に置き換える
 *   Inherits From: nsIProtocolHandler
 */
function arUnMHTMhtmlProtocolHandler () {
}
arUnMHTMhtmlProtocolHandler.prototype = {
  scheme : "mhtml", /* String  プロトコルスキーム */
  defaultPort : -1, /* Number  ポート番号
                     *   ネットワークベースではないので -1 */
    
  protocolFlags: nsIProtocolHandler.URI_NOAUTH, /* Number プロトコルの詳細
                                                 *   file と同様のプロトコル*/
    
  /**
   * インターフェースの要求
   *   nsISupports.QueryInterface
   *
   * @param  nsIIDRef iid
   *         インターフェース ID
   * @throws Components.results.NS_NOINTERFACE
   * @return nsIProtocolHandler
   *         this
   */
  QueryInterface : function (iid) {
    if (iid.equals (nsISupports)
        || iid.equals (nsIProtocolHandler)) {
      return this;
    }
        
    throw Components.results.NS_ERROR_NO_INTERFACE;
  },
    
  /**
   * ブラックリストのポートを上書きするか
   *   nsIProtocolHandler.allowPort
   *
   * @param  Number port
   *         ポート番号
   * @param  String scheme
   *         プロトコルスキーム
   * @return Boolean
   *         上書きするか
   */
  allowPort: function (port, scheme) {
    return false;
  },
    
  /**
   * URI を作成する
   *   nsIProtocolHandler.newURI
   *
   * @param  String spec
   *         対象の URI
   * @param  String charset
   *         対象の文字コード
   * @param  nsIURI baseURI
   *         読み込み元の URI
   * @return nsIURI
   *         作成した URI
   */
  newURI : function (spec, charset, baseURI) {
    /* mhtml:http://hoge
     * mhtml://http://hoge
     *   to
     * http://hoge
     */
    if (spec.match (/^mhtml:(\/\/)?([^\/]+):\/\//)) {
      var uri
      = Components.classes ["@mozilla.org/network/standard-url;1"]
      .createInstance (nsIURI);
      uri.spec = spec.replace (/^mhtml:(\/\/)?([^\/]+):\/\//, "$2://");
      return uri;
    }
    else {
      var uri
      = Components.classes ["@mozilla.org/network/simple-uri;1"]
      .createInstance (nsIURI);
      uri.spec = "about:blank";
      return uri;
    }
  },
    
  /**
   * チャネルを作成する
   *   nsIProtocolHandler.newChannel
   *
   * @param  nsIURI uri
   *         対象の URI
   * @return nsIChannel
   *         作成したチャネル
   */
  newChannel : function (uri) {
    var ios
    = Components.classes ["@mozilla.org/network/io-service;1"]
    .getService (nsIIOService);
    return ios.newChannel ("about:blank", null, null);
  }
};

/**
 * 本体のファクトリー
 *   Inherits From: nsIFactory
 */
var arUnMHTMhtmlProtocolHandlerFactory = {
  /**
   * 本体を生成する
   *   nsIFactory.createInstance
   *
   * @param  nsISupport outer
   *          統合する対象
   * @param  nsIIDRef iid
   *         生成する対象のインターフェース ID
   * @return arUnMHTMhtmlProtocolHandler
   *         本体
   */
  createInstance : function (outer, iid) {
    if (outer != null) {
      /* 統合する対象がある場合はエラー */
      throw Components.results.NS_ERROR_NO_AGGREGATION;
    }
        
    var handler;
    handler = new arUnMHTMhtmlProtocolHandler ();
    if ("URI_LOADABLE_BY_ANYONE" in nsIProtocolHandler) {
      handler.protocolFlags |= nsIProtocolHandler.URI_LOADABLE_BY_ANYONE;
    }
        
    return handler.QueryInterface (iid);
  }
};

/**
 * XPCOM のモジュール
 *   Inherits From: nsIModule
 */
var arUnMHTMhtmlProtocolHandlerModule = {
  /* 本体に関する情報 */
  CONTRACTID: "@mozilla.org/network/protocol;1?name=mhtml",
  CID: Components.ID ("{6a0b6c61-7f15-4b42-af62-da7cfcdc04a8}"),
  CNAME: "UnMHT Mhtml Protocol Handler JS Component",
    
  /**
   * インターフェースの要求
   *   nsISupports.QueryInterface
   *
   * @param  nsIIDRef iid
   *         インターフェースID
   * @throws Components.results.NS_NOINTERFACE
   * @return nsIModule
   *         this
   */
  QueryInterface : function (iid) {
    if (iid.equals (nsISupports)
        || iid.equals (nsIModule)) {
      return this;
    }
        
    throw Components.results.NS_ERROR_NO_INTERFACE;
  },
    
  /**
   * 登録処理
   *   nsIModule.registerSelf
   *
   * @param  nsIComponentManager compMgr
   * @param  nsIFile fileSpec
   *         モジュールのファイル
   *           reference と他のソースコード中で並びが違う
   * @param  String location
   *         不明
   *           reference と他のソースコード中で並びが違う
   * @param  String type
   *         ローダの種類
   */
  registerSelf : function (compMgr, fileSpec, location, type) {
    try {
      var id
      = Components.classes ["@mozilla.org/xre/app-info;1"]
      .getService (nsIXULAppInfo).ID;
      if (id == "{3550f703-e582-4d05-9a08-453d09bdfdc6}") {
        /* Thunderbird */
            
        return;
      }
    } catch (e) {}
        
    if ("URI_LOADABLE_BY_ANYONE" in nsIProtocolHandler) {
      arUnMHTMhtmlProtocolHandler.prototype.protocolFlags
      |= nsIProtocolHandler.URI_LOADABLE_BY_ANYONE;
    }
        
    compMgr = compMgr.QueryInterface (nsIComponentRegistrar);
    compMgr.registerFactoryLocation (this.CID,
                                     this.CNAME,
                                     this.CONTRACTID,
                                     fileSpec, location, type);
  },
    
  /**
   * 登録解除処理
   *   nsIModule.unregisterSelf
   *
   * @param  nsIComponentManager compMgr
   * @param  nsIFile fileSpec
   *         モジュールのファイル
   *           reference と他のソースコード中で並びが違う
   * @param  String location
   *         不明
   *           reference と他のソースコード中で並びが違う
   */
  unregisterSelf : function (compMgr, fileSpec, location) {
    try {
      var id
      = Components.classes ["@mozilla.org/xre/app-info;1"]
      .getService (nsIXULAppInfo).ID;
      if (id == "{3550f703-e582-4d05-9a08-453d09bdfdc6}") {
        /* Thunderbird */
            
        return;
      }
    } catch (e) {}
        
    compMgr = compMgr.QueryInterface (nsIComponentRegistrar);
    compMgr.unregisterFactoryLocation (this.CID, fileSpec);
  },
    
  /**
   * ファクトリーオブジェクトを取得する
   *   nsIModule.getClassObject
   *
   * @param  nsIComponentManager compMgr
   * @param  nsCIDRef cid
   *         取得対象のクラス ID
   * @param  nsIIDRef iid
   *         取得対象のインターフェース ID
   * @throws Components.results.NS_ERROR_NOT_IMPLEMENTED
   *         Components.results.NS_ERROR_NO_INTERFACE
   * @return arUnMHTMhtmlProtocolHandlerFactory
   *         本体のファクトリー
   */
  getClassObject : function (compMgr, cid, iid) {
    if (cid.equals (this.CID)) {
      return arUnMHTMhtmlProtocolHandlerFactory;
    }
        
    if (!iid.equals (nsIFactory)) {
      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    }
        
    throw Components.results.NS_ERROR_NO_INTERFACE;
  },
    
  /**
   * 終了できるかどうか
   *   nsIModule.canUnload
   *
   * @param  nsIComponentManager compMgr
   * @return Boolean
   *         終了できるかどうか
   */
  canUnload : function (compMgr) {
    return true;
  }
};

/**
 * モジュールを取得する
 * @param  nsIComponentManager compMgr
 * @param  nsIFile fileSpec
 *         モジュールのファイル
 * @return arUnMHTMhtmlProtocolHandlerModule
 *         モジュール
 */
function NSGetModule (compMgr, fileSpec) {
  return arUnMHTMhtmlProtocolHandlerModule;
}
