文字列から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の仕様が変更され例外がスローされるようになったのだと推察します。
kawasemi :
kawasemi :