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

const nsISupports           = Components.interfaces.nsISupports;

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

const nsIContentPolicy      = Components.interfaces.nsIContentPolicy;
const nsIURI                = Components.interfaces.nsIURI;
const nsIWindowMediator     = Components.interfaces.nsIWindowMediator;
const nsIXULAppInfo          = Components.interfaces.nsIXULAppInfo;

/**
 * 本体
 * mht から参照されているファイルのチェック、変換を行う
 *   Inherits From: nsIContentPolicy
 */
function arUnMHTContentPolicy () {
  this._init ();
}
arUnMHTContentPolicy.prototype = {
  /* nsIContentPolicy の定数 */
  ACCEPT           : nsIContentPolicy.ACCEPT,
  REJECT_OTHER     : nsIContentPolicy.REJECT_OTHER,
  
  _old : false, /* Boolean  旧バージョンかどうか */
    
  /**
   * インターフェースの要求
   *   nsISupports.QueryInterface
   *
   * @param  nsIIDRef iid
   *         インターフェース ID
   * @throws Components.results.NS_NOINTERFACE
   * @return nsIContentPolicy
   *         this
   */
  QueryInterface : function (iid) {
    if (iid.equals (nsISupports)
        || iid.equals (nsIContentPolicy)) {
      return this;
    }
        
    throw Components.results.NS_ERROR_NO_INTERFACE;
  },
    
  /**
   * 初期化処理
   */
  _init : function () {
    /* 旧バージョンのチェック */
    this._old = "OTHER" in Components.interfaces.nsIContentPolicy;
    if (this._old) {
      /* 旧バージョンの場合、定数を取得し直す */
      this.ACCEPT = true;
      this.REJECT_OTHER = false;
    }
  },
    
  /**
   * ロードするかどうかのチェック
   *   nsIContentPolicy.shouldLoad
   *
   * @param  Number contentType
   *         コンテントの種類
   * @param  nsIURI contentLocation
   *         対象の URI
   * @param  nsIURI requestOrigin
   *         呼び出し元の URI
   * @param  Browser/HTMLElement context
   *         ロード先
   * @param  String mimeTypeGuess
   *         予想される MIME-Type
   * @param  nsISupports extra
   *         不明
   *
   * 旧バージョンでは
   * @param  Number contentType
   *         コンテントの種類
   * @param  nsIURI contentLocation
   *         対象の URI
   * @param  HTMLElement requestOrigin
   *         ロード先
   * @param  Window context
   *         対象のウィンドウ
   */
  shouldLoad : function (contentType, contentLocation,
                         requestOrigin, context,
                         mimeTypeGuess, extra) {
    if (contentLocation.scheme == "chrome"
        || contentLocation.scheme == "data") {
      /* chrome, data の場合許可する */
      return this.ACCEPT;
    }
        
    if (this._old) {
      /* 旧バージョンの場合引数が違うので入れ替える */
            
      var targetWindow = context;
      context = requestOrigin;
            
      requestOrigin
      = Components.classes ["@mozilla.org/network/standard-url;1"]
      .createInstance (nsIURI);
      if (targetWindow.document.location) {
        try {
          requestOrigin.spec = targetWindow.document.location.href;
        }
        catch (e) {
          /* 古い Mozilla Suite の場合、許可する */
          return this.ACCEPT;
        }
      }
      else {
        /* 古い Mozilla Suite でのアンカーによる移動の場合、許可する */
        return this.ACCEPT;
      }
    }
        
    if (!requestOrigin) {
      /* 呼び出し元が無い場合許可する */
      return this.ACCEPT;
    }
    if (contentLocation.scheme == "unmht") {
      if (requestOrigin.scheme == "http"
          || requestOrigin.scheme == "https"
          || requestOrigin.scheme == "ftp") {
        /* http、https、ftp からの unmht への呼び出し */
                
        var reject = false;
        if (contentLocation.spec.indexOf ("unmht:///file.") == 0) {
          /* ローカルならば拒否する */
          reject = true;
        }
                
        if (reject) {
          /* 拒否する */
          return this.REJECT_OTHER;
        }
      }
    }
        
    if (requestOrigin.scheme != "unmht") {
      /* unmht 以外の場合許可する */
      return this.ACCEPT;
    }
        
    var mediator
    = Components.classes ["@mozilla.org/appshell/window-mediator;1"]
    .getService (nsIWindowMediator);
    var chromeWindow = mediator.getMostRecentWindow ("navigator:browser");
        
    if (!chromeWindow.UnMHT.shouldLoad (contentType, contentLocation,
                                        requestOrigin, context)) {
      return this.REJECT_OTHER;
    }
    
    return this.ACCEPT;
  },
    
  /**
   * 処理するかどうかのチェック
   *   nsIContentPolicy.shouldProcess
   *
   * @param  Number contentType
   *         コンテントの種類
   * @param  nsIURI contentLocation
   *         対象の URI
   * @param  nsIURI requestOrigin
   *         呼び出し元の URI
   * @param  Browser/HTMLElement context
   *         ロード先
   * @param  String mimeType
   *         MIME-Type
   * @param  nsISupports extra
   *         不明
   *
   * 旧バージョンでは
   * @param  Number contentType
   *         コンテントの種類
   * @param  nsIURI contentLocation
   *         対象の URI
   * @param  HTMLElement requestOrigin
   *         ロード先
   * @param  Window context
   *         対象のウィンドウ
   */
  shouldProcess : function (contentType, contentLocation,
                            requestOrigin, context,
                            mimeTypeGuess, extra) {
    return this.ACCEPT;
  }
};

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

/**
 * XPCOM のモジュール
 *   Inherits From: nsIModule
 */
var arUnMHTContentPolicyModule = {
  /* 本体に関する情報 */
  CONTRACTID: "@unmht.org/unmht-content-policy;1",
  CID: Components.ID ("{5c21c8ee-2d1e-46c1-9d6f-bc4876c13890}"),
  CNAME: "UnMHT Content Policy 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) {}
        
    compMgr = compMgr.QueryInterface (nsIComponentRegistrar);
    compMgr.registerFactoryLocation (this.CID,
                                     this.CNAME,
                                     this.CONTRACTID,
                                     fileSpec, location, type);
        
    var catman
    = Components.classes ["@mozilla.org/categorymanager;1"]
    .getService (nsICategoryManager);
    catman.addCategoryEntry ("content-policy",
                             this.CONTRACTID,
                             this.CONTRACTID, true, true);
  },
    
  /**
   * 登録解除処理
   *   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);
        
    var catman
    = Components.classes ["@mozilla.org/categorymanager;1"]
    .getService (nsICategoryManager);
    catman.deleteCategoryEntry ("content-policy",
                                this.CONTRACTID, true);
  },
    
  /**
   * ファクトリーオブジェクトを取得する
   *   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 arUnMHTContentPolicyFactory
   *         本体のファクトリー
   */
  getClassObject : function (compMgr, cid, iid) {
    if (cid.equals (this.CID)) {
      return arUnMHTContentPolicyFactory;
    }
        
    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 arUnMHTContentPolicyModule
 *         モジュール
 */
function NSGetModule (compMgr, fileSpec) {
  return arUnMHTContentPolicyModule;
}
