文字列からHTMLDocumentを作成するコード

必要に迫られて引数で渡された文字列から HTMLDocument を動的に作成するコードを書いたので紹介します。このコードはchromeコンテキスト上で、つまり拡張上で動作させることを前提としていますのでご注意ください。
下記コードを書く際にmodestで紹介されているnanot_viさんのサイトを参考にしました。またrange.createContextualFragment()実行時のセキュリティ上の課題を克服するためにkazさんのblogを参照させていただきました。

let getDOMHtmlDocument=function(str){
  let doc;
  let range;

  if(document.implementation.createHTMLDocument){
    // Firefox 4.0から
    doc=document.implementation.createHTMLDocument('');
    range=doc.createRange();
    range.selectNodeContents(doc.documentElement);
    range.deleteContents();
    doc.documentElement.appendChild(range.createContextualFragment(str));
  }else{
    // Firefox 3.6.xまで
    let doctype=document.implementation.createDocumentType(
      'html',
      '-//W3C//DTD HTML 4.01 Transitional//EN',
      'http://www.w3.org/TR/html4/loose.dtd'
    );
    doc=document.implementation.createDocument(null,'html',doctype);
    range=doc.createRange();
    range.selectNodeContents(doc.documentElement);
    let content=doc.adoptNode(range.createContextualFragment(str));
    doc.documentElement.appendChild(content);
  }
  return doc;
};

上記コードの5行目から11行目までのif文のブロックはcreateHTMLDocumentメソッドに対応しているFirefox 4.0以降用です。createHTMLDocumentメソッドに対応していないFirefox 3.6.xまでで動作するのは12行目から23行目までのelseブロックのコードとなります。

私はnanot_viさんのサイトを元にこのコードを書きはじめたのですが、createContextualFragmentメソッドの呼び出しのタイミングで必ず例外がスローされるという状況で行き詰まってしまいました。
HTMLのテキストが正規化されていないためパースできないのか、と最初は考えていたのですがcreateHTMLDocumentメソッドを使っているコードでも例外がスローされることに変りはないので数日悩むことになりました。
そしてたまたまkazさんのblogの記事を拝見することとなり、セキュリティ的に鑑みてselectNodeContentsメソッドの引数にブラウザ由来のdocument.documentElementではなくユーザが作成したdocumentオブジェクト由来のdoc.documentElementを渡すべきということを知り、そのような修正を加えたところ例外が発生しなくなり、例外発生の問題は解決されました。もちろん引数で渡された文字列も問題なくDOM Objectとなりました。

ちなみにブラウザ由来のdocumnetオブジェクトを使うとどのようなセキュリティ上のリスクがあるのかはkazさんのblogを参照してください。ブラウザ由来のdocumentオブジェクトは、chromeコンテキスト上に存在することがポイントです。
私は上記コードの開発をFirefox 3.6.xとFirefox 4.0pre ( Minefield ) で行っていました。しかしnanot_viさんのサイトにはFirefoxでの動作報告も含まれています。これはつまるところセキュリティ上の理由により、どこかのタイミングでFirefoxの仕様が変更され例外がスローされるようになったのだと推察します。

2 件のコメント

  1. kawasemi :

    はじめまして。かわせみと申します。私もHTMLドキュメントを返すDOMの方法について学んでいますので、doc.documentElementの使用する方法については、まだ少し理解が足りませんが、非常に参考になります。ありがとうございます。

    私なりに作ってみたものを、(セキュリティのことを考えないで作っていますが)、とりあえずIE8とFirefox4.0.1で動いてHTMLドキュメントも生成できましたので、こういった方法もあるということで、ひとつご紹介させていただきます。ちなみにWindowsXPのHome、バージョンは2002,Service Pack3です。

    DomSample

    <!–
    function create(){
    var divObj = document.getElementById("book");
    var tNode = document.createTextNode("taitorukomidasihonbun”);
    divObj.appendChild(tNode);
    }
    // –>

    クリックすると、HTMLの文書定義とタグを返します。HTMLの雛形が作れます。

  2. kawasemi :

    申し訳御座いません。先ほどの投稿でhtmlと文書定義を書いたのですが、はじかれているため補足させていただきます。

    スクリプトの三行目のcreateTextNodeにはHTMLの文書定義と表示させたいhtmlが入ります。
    あとはjavascriptを抜けたhtmlで表示させています。
    具体的にはbodyタグのフォームのbuttonでonClick=”create()”を使用して、その下のdivタグのidにbookを指定して表示しています。