« 今週のお買い物 (2008.03.02~03.08) | トップページ | 03月09日のココロ日記(BlogPet) »
2008.03.09
WebBrowserコントロールで試行錯誤 ― userscript を実現する方法をいろいろ試してみる
「WebBrowserコントロールで試行錯誤 ― IEコンポーネント利用ブラウザによる Autopagerize の実現に向けて」の続きです。
とりあえず、userscript をサポートするには、Webサーバから取ってきた html を書き換えて、userscript を読み込んで実行するようにすればいいだけなので、割と簡単。書き換え後の html + Javascript を実行するのは WebBrowser コントロールへ丸投げすることで実現できるので、要するに、html を書き換えるところだけ、自前で作成すればいいってこと。
[観] WebBrowserコントロールで試行錯誤 ― IEコンポーネント利用ブラウザによる Autopagerize の実現に向けて
と書いたものの、そんなに簡単ではなさそうです。html を書き換える方法として
- CreateElement で script 要素をつくって、AppendChild で <script type="text/javascript" src="ローカルファイル名"></srcipt> を挿入する方法
HtmlDocument doc = webBrowser1.Document; HtmlElement scriptElem; string cwd = Environment.CurrentDirectory; scriptFile = "file:///" + cwd + "\\" + scriptName1; scriptElem = doc.CreateElement("script"); scriptElem.SetAttribute("type", "text/javascript"); scriptElem.SetAttribute("src", scriptFile); doc.Body.AppendChild(scriptElem); - CreateElement で script 要素をつくり、innerText で Javascript ソースコードを直接流し込み、AppendChild で元の html に挿入する方法
HtmlDocument doc = webBrowser1.Document; HtmlElement scriptElem; scriptElem = doc.CreateElement("script"); scriptElem.SetAttribute("type", "text/javascript"); scriptElem.InnerText = "prompt('','test!');"; doc.Body.AppendChild(scriptElem); - innerText ではなく、innerHtml で流し込む方法
- script 要素を直接 AppendChild せずに、script を innerText として保持する div 要素を AppendChild する方法
HtmlDocument doc = webBrowser1.Document; HtmlElement divElem; divElem = doc.CreateElement("div"); divElem.SetAttribute("class", "scriptTest"); divElem.InnerText = "<script type=\"text/javascript\">prompt('','test!!');</script>"; doc.Body.AppendChild(divElem); - script を innerText ではなく、innerHtml として保持する div 要素を AppendChild する方法
- html をいったんローカルに取り込んで書き換えたものを評価(再レンダリング)する方法
string url = webBrowser1.Url.ToString(); string doc0 = changeCode(webBrowser1.DocumentText); // (UTF-8 でなければ)UTF-8 にする doc0 = RegulizeDocument(doc0, url); // html ファイル内の 相対パス → 絶対パス 書き換え // script 挿入 l = doc0.LastIndexOf("</body>"); if (l > 0) { doc0 = doc0.Substring(0, l - 1); doc0 += "<script type=\"text/javascript\">"; doc0 += "prompt('','test!!');"; doc0 += "</script>"; doc0 += "</body></html>"; HtmlDocument doc1 = webBrowser1.Document.OpenNew(true); doc1.Write(doc0); // 書き換え済みの html で置き換えて、レンダリング実行 }
などを試してみました。
最初の方法は、まずセキュリティに引っ掛かって動かないのではないかと思いきや、よくわからないJavascriptのエラーで止まってしまいました。デバッガを使うモードにしている影響かもしれません。デバッガオフなら、セキュリティの警告ダイアログが出るのかも。
二番目から五番目の方法は全滅。
本来なら、CreateElementでHtmlElementを作ったら、それのInnerHtml(or InnerText)にスクリプトを記述すれば良いはずなのですが、なぜか出来ません、、、説明によればSCRIPTのHtmlElementは子要素が持てないからだそうです。
.NET 2.0 WebBrowserで表示しているページにJavascriptを挿入する|.NET Framework 2.0 と C#での開発ブログ
いま、これではまってて先に進めない。
はらへた
(1) 外部Javascript を取り込むのは問題なし
(2) ローカルJavascriptはなぜか、「1行目の2文字目でエラー」うんぬんというエラーダイアログが出る(セキュリティの警告は特に出ない)
(3) InnerText は全く使えない(scriptタグ自体が消えてしまう)、InnerHtml は に置換されてしまう
(4) 親を div エレメントにして、その子要素にInnerHtml で script を挿入するのも試してみたが、だめ。script と img は挿入不可らしい。他のもの(p とか)は問題なく挿入できる
ということで、userscript を実現するには、以下の方法を取るしかない?
(a) userscript をいったんどこかのサーバにアップロードして、外部JSとして読み込む
(b) サーバから取ってきた html をいったんローカルに保存し、 userscript を含む html に書き換えて、navigate する
で、六番目の方法(上記引用の (b) に相当)を試してみたところ、いくつか Javascript のエラーが出るものの、それを無視して実行を続けると、こちらで用意した Javascript が挿入されて、ちゃんと実行されることを確認。なぜ、Javascript のエラーが出るのか、考えられる理由は、以下の2つ。
- ローカルで実行されるので、元のhtmlが存在するサイトとは違う権限で実行されることになり、権限に依存する処理はエラーになる
- 元の html ファイルから参照しているスタイルシートやJavascriptファイルの中で、相対パス表記をしているものがあると、それらは参照するのに失敗し、エラーになる(ローカル環境にたまたまその相対パスで参照できるファイルがあれば、参照できてしまえるんでしょうけど)。
他にも理由があるのかもしれませんが、とりあえず、思いつくのは、この2つ。試しに、Javascript を全く使っていないサイトに対して、「六番目の方法」を試してみたら、Javascript のエラーが全く出ませんでした。ということで、上記2つの問題さえ解決できれば、Javascript を使っている Webページに対しても、「六番目の方法」が使えると思われます。
(b) の方法を試してみたら、当然といえば当然ですが、ローカルのhtmlをレンダリングすることになるので、中で参照している諸々が相対パス表記である場合、その html が置かれていたサイトからではなく、ローカルから取り込もうとするので、正常にレンダリングされません。相対パスを絶対パスに書き直す処理が必要。取り込んだファイル(スタイルシートとか)の中も相対パスだと、それも書き換えないといけない。これはちょっとコストが高い。
はらへた
私と似たようなことをやっていてすでに解決している人がいないか、Google を頼りに探してみたところ、「s.h.log: C#のWebBrowserコンポーネントからJavaScriptを実行」という記事を発見。 この記事では、「ブックマークレット形式の Javascript を Navigate で評価する」ことで、html をいじろうというアプローチが取られています。
この方法の利点は、2つ。
- いじる対象のWebページをローカルに保存して書き換えるという準備が不要なこと
(したがって、相対パスを絶対パスに書き換える必要もないし、実行権限の問題も発生しない) - 既存のブックマークレット資産をそのまま使えること
ただし、ブックマークレットは最大508文字までという大きな制約(参考: 「ブックマークレット 508文字」を Google で検索)があり、一般的に508文字以内におさえるのが難しい userscript を実行するのに応用するのは無理だと思います。結局、どこかのサーバに Javascript (userscript) を用意して、それをインクルードして利用するというのが、現実的という結論になってしまいそうです。
IE7 を入れていても、WebBrowser コントロールでは、相変わらず 508文字制限が効いてしまっているのが、悲しいです。
とりあえず、ここ数日あがいてみた結果(C# のソースコード)をさらしておきます。何かの参考にどうぞ。
Javascript を挿入して実行させようとするとエラーが出る (六番目の方法)
エラーを無視して、強引に実行。期待した通りの結果が得られた (六番目の方法)
本当に外部JS に頼るしかないのか、もうちょっとあがいてみます。
投稿者: tsupo 2008.03.09 午前 07:11
| 固定リンク
|
|
| ![]()
|
|
アマゾンわくわく探検隊
トラックバック
この記事のトラックバックURL:
この記事へのトラックバック一覧です: WebBrowserコントロールで試行錯誤 ― userscript を実現する方法をいろいろ試してみる:
» WebBrowserコントロールで試行錯誤 ― とりあえず、タブブラウザ化してみた from 観測気球
userscript をサポートする方向でのあれこれがだんだん煮詰まってきたので、気分転換をかねて、タブブラウザ化してみました。 続きを読む
コメント
はじめまして、ちょっと投稿から時間がたっていますが、私も以前同じ現象で悩んだ経験があります。
私はIEコンポーネントベースのアプリを作ろうとしていてabout:blankで初期化した画面にprototypeなどのスクリプトを流し込んで画面のレンダリングをさせる方法を検討してました。
スクリプトの始でエラーになるのは、.NETが内部コードをUNICODEで扱っているためのようで、初期化完了直後はHtmlDocumentのデータ先頭にごみが乗って(もしかしてBOM?0xFFとかだし)しまう為、後から差し込むスクリプトが文字化け状態で誤動作しているようです。
スクリプト自体をUNICODEにしたところで データ先頭に読めない文字が載っているため実行エラーになります。
IE7以降に潜むバグっぽいです、IE6だと起らなかったと思います。
回避策はブランクページを使わずローカルのどこかに空ファイル(もしくは初期化ファイル)を作成して、それを表示すると正常に読めるようです。
いずれにしろ、画像はローカルファイルかネット上のものを読む必要があるため、妥当な方法ではないかと思います。
私はアプリからアクセスする際のイベントフックにはまり中です・・・IEのリーク問題って解消してないのですね。
参考になれば幸いです。
投稿者: TATUO (2008.12.09 午後 11:16)




