Hatena::Groupmoz-addon

Ci.nsIZIGOROu

2008-02-08イケスカナイ

WebBrowser Controlには後からConnectObject()出来ない

| 03:41 |  WebBrowser Controlには後からConnectObject()出来ない - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  WebBrowser Controlには後からConnectObject()出来ない - Ci.nsIZIGOROu

IEってひょっとして生成前から仕込まないとダメなのかな?

var browser = WScript.CreateObject("InternetExplorer.Application");
WScript.ConnectObject(browser, "browser_");

これは接続出来ないって言われる。

WebBrowser Control, MSHTMLのイベント発火とJS側の状態

| 02:49 |  WebBrowser Control, MSHTMLのイベント発火とJS側の状態 - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  WebBrowser Control, MSHTMLのイベント発火とJS側の状態 - Ci.nsIZIGOROu

どの時点でIEのスクリプトに横槍出来るか試してみた。

はじめに

実験はIE7でやってます。IE6は捨ての方向で。

またWSHでやってます。ソース見れば分かると思うけど。

ソース


var browser = WScript.CreateObject("InternetExplorer.Application", "browser_");

var console = {
  log: function(msg) {
    WScript.StdErr.Write(msg + "\n");
  }
};

function browser_BeforeNavigate2(browser, url, flags, target, postdata, headers, cancel) {
  console.log("[event] BeforeNavigate2");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  with({ window: browser.Document.parentWindow }) {
    console.log("window -> " + typeof window);
    console.log("eval -> " + typeof window.eval);
  }
}

function browser_DownloadBegin() {
  console.log("[event] DownloadBegin");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  if (browser.ReadyState > 0 && typeof browser.Document == "object") {
    with({ window: browser.Document.parentWindow }) {
      console.log("window -> " + typeof window);
      console.log("eval -> " + typeof window.eval);
    }
  }
}

function browser_DownloadComplete() {
  console.log("[event] DownloadComplete");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  with({ window: browser.Document.parentWindow }) {
    console.log("window -> " + typeof window);
    console.log("eval -> " + typeof window.eval);

    if (browser.ReadyState == 1 && typeof window == "object") {
      window.console = console;

      window.document.attachEvent("onreadystatechange", function(evt) {
        console.log("[event] onreadystatechange");
        console.log("ReadyState : " + browser.ReadyState);
        console.log("document -> " + typeof browser.Document);
        console.log("window -> " + typeof window);
        console.log("eval -> " + typeof window.eval);
      });

      window.attachEvent("onload", function(evt) {
        console.log("[event] onload");
        console.log("ReadyState : " + browser.ReadyState);
        console.log("document -> " + typeof browser.Document);
        console.log("window -> " + typeof window);
        console.log("eval -> " + typeof window.eval);
      });
    }
  }
}

function browser_WindowStateChanged(flags, validFlags) {
  console.log("[event] WindowStateChanged : " + flags);
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);
  
  if (browser.ReadyState > 0 && typeof browser.Document == "object") {
    with({ window: browser.Document.parentWindow }) {
      console.log("window -> " + typeof window);
      console.log("eval -> " + typeof window.eval);
    }
  }
}

function browser_NavigateComplete2(ie, url) {
  console.log("[event] NavigateComplete2");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  with({ window: browser.Document.parentWindow }) {
    console.log("window -> " + typeof window);
    console.log("eval -> " + typeof window.eval);
  }
}

function browser_DocumentComplete(ie, url) {
  console.log("[event] DocumentComplete");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  with({ window: browser.Document.parentWindow }) {
    console.log("window -> " + typeof window);
    console.log("eval -> " + typeof window.eval);
  }
}

function browser_OnQuit() {
  console.log("[event] OnQuit");
  browser = null;
}

browser.Visible = true;
browser.Navigate2("http://sample.art-code.org/ie.html");

while (true) {
  if (!browser) {
    break;
  }
  WScript.Sleep(1000);
}

console.log("End");

結果

実際の実行結果は貼り付けると縦長になるので、表組みにしてみた。

object event ReadyState Document window eval
WebBrowser DownloadBegin 1 unknown -- --
WebBrowser WindowStateChanged 1 unknown -- --
WebBrowser DownloadComplete 1 object object undefined
WebBrowser DownloadBegin 1 object object undefined
WebBrowser NavigateComplete2 1 object object undefined
WebBrowser WindowStateChanged 1 object object undefined
HTMLDocument onreadystatechange 3 object object undefined
WebBrowser DownloadComplate 3 object object object
HTMLDocument onreadystatechange 4 object object object
WebBrowser DocumentComplate 4 object object object
HTMLWindow onload 4 object object object

こんな感じの結果となる。見分けやすいように変更点があるところをボールドにしてみた。

IE7の場合は最も早くwindowオブジェクトが生成されるのが、

  • DownloadComplateイベントの発火時
  • ReadyStateが1の時

が成り立つ時のようだ。

それ以前はdocumentオブジェクト相当がunknownとなりアクセス出来ない。またevalは当分実行出来なくて、やはりDownloadCompleteイベントの発火時で、ReadyStateが3の時に実行出来る。これじゃ全然意味が無い。*1

まとめ

  • とりあえずInternetExplorer.Applicationをダイレクトで生成すればイベントに接続出来る
  • 肝はDownloadCompleteイベント
  • evalは当分使えない
  • Document, ReadyStateの値が状態をはかるのに参考となる

って所かな。

ちなみにこれが出来るとGreasemonkey的な枠組みを作ろうと思えば出来る*2し、予め任意のオブジェクトを用意しておくことも可能です。

但しvar宣言はeval相当*3が使えないとやれないので、d:id:amachangdocumentオブジェクトを差し替えるhackを予め仕込むとかは出来ない。

但し、DownloadComplete, ReadyState = 3のときに差し込めば、実際のDOMアクセス処理が走る前には適用出来ると思いますけど。

これは画面遷移が挟まったとしても有効です。

*1:但しsetTimeoutの時間指定を0とかにしたら実行出来るかもしれない

*2:但し、WSHの権限をもろに使えるようになるのでやることによっては相当危険

*3:setTimeout, execScriptも同等の事が出来るけど