Safari のウィンドウとドキュメント

Safari がタブに対応したのはいつでしたでしょうか?タブに対応したのはともかく、AppleScript からタブを操作できるようになったのはいつからでしたでしょうか。こういう時に参考になるのが AS Hole(AppleScriptの穴)。ここを見ると AppleScript でタブがまともに利用できるのは Mac OS X 10.5 以降、Safari 3 以降のようです(Safariの最前面のウィンドウ内のタブを順々に表示)。

何で、今更タブのことなんか?

Safari の AppleScript 対応度というのは Finder などに比べると貧弱なものです。それでも Safari の自動化に何らししょうがないのは AppleScript 内から Safari に JavaScript が実行させることができるからです。

それは、アプリケーションに AppleScript をどのように対応させるか?といった問題へのシンプルな答えでもあります。「ウェブブラウザの自動化や処理がどういう時に必要なのか?」といったことを考えた時、JavaScript を実行できることが何よりも問題の解決に役立ってくれるでしょう。

AppleScript への対応というのは、このようにキモとなる部分さえ外していなければ、Safari の do JavaScript 命令の実装だけで解決してしまうものなのです。これは他のアプリケーションでも同様です。そのため用語辞書を見るとアプリケーション作成者の AppleScript の理解度が分かったりできて面白かったりします。

閑話休題。

ようするに do JavaScript 命令は古くは document オブジェクトを引数に指定していたのですが、タブを操作できるようになると、tab オブジェクトを引数に渡すように変更されたのです。そして、tab オブジェクトは window の要素なのです。

この変更により、古いスクリプトは修正が必要になりました。

また、少し話は変わりますが、Safariで開いているページの一時保存・リストアで紹介されているスクリプト。重宝しています。

重宝しているのだけど、時々エラーになってしまうことがあるようです。どういう時にエラーになるかというと、ダウンロードしている項目を表示する「ダウンロード」というウィンドウや環境設定のウィンドウなどが前面にある時です。要するに document を持たないウィンドウが前面にある時です。

現在の Mac のアプリケーションの多くは Cocoa/Objective-C で作成されています。これらのアプリケーションは多くの window を利用しますが、ドキュメントベースのアプリケーション(書類の編集などができるアプリケーション)は window の中に document というオブジェクトを持っています。細かいことは省きますが、document を持っている window と持っていない window(設定パネルやダイアログ等)の 2 種類があるのです。

AppleScript で document というと、多くの場合この document を持った window のことを指します。例えば、TextEdit などで Book Review.rtf という書類を開いており、最前面に環境設定を開いていたとします。

tell application "TextEdit"
    name of window 1
    --> 環境設定
    name of document 1
    --> "Book Review.rtf"
    name of windows
    --> {"Book Review.rtf", "環境設定"}
end tell

windows として複数の window オブジェクトを取得すると全てのウィンドウが取得できます(document も含む)。そして、上記のスクリプトでは Book Review.rtf を最前面にすると window 1 は Book Review.rtf になります。また、document を持たない環境設定などの window は閉じても AppleScript から取得できます(これらの window の属性 visible が false になるだけで、存在自体が消滅するわけではない)。

これらは AppleScript で window と document を操作する時の注意点ですが...何が問題かというと、Safari の場合、JavaScript を実行させるためにタブを持ったウィンドウを取得しなければならないのだけど、最前面の document が必ずしも最前面の window だとは限らない、ということです。

document を持った window で最前面のものを取得する必要があります。

こういうことを知ってかどうかは分かりませんが、window の属性に document というものがあります。少し前に追加されたものだと思うのですが。また、以前はきちんと動作しなかったような...。この属性を利用して document を持った最前面の window を取得することができます。

Script Editor で開く

tell application "Safari"
    set front_window to my get_front_window()
    if front_window is missing value then return

    tell current tab of front_window
        do JavaScript "new Date();" in it
    end tell
end tell

on get_front_window()
    tell application "Safari"
        if not (front document exists) then return missing value
        set the_document to front document
        set window_ref to a reference to (windows whose document of it is not missing value)
        repeat with this_window in window_ref
            if document of this_window is the_document then return contents of this_window
        end repeat
        return missing value
    end tell
end get_front_window

tab オブジェクトが document の要素だったら話は簡単なんだけど、そうはいかないんでしょうね。この window の document 属性を使って前面の document オブジェクトを正確に指定する...という方法は、他のドキュメントベースのアプリケーションでも利用できるものなのですが、Script Editor(現在では AppleScript Editor)では、用語辞書を表示する window も document を持っている window オブジェクトなので注意が必要だったりします(つまり、document 1 という参照で用語辞書を表示している window を指してしまうこともある)。

Script Editor で開く

tell application id "com.apple.ScriptEditor2"
    name of windows whose document of it is not missing value
    --> {"名称未設定", "StandardAdditions.sdef"}
end tell

document を持っている window はアプリケーションにより異なる...と。System Events を使って GUI Scripting を利用すれば、各アプリケーションで共通して利用できるハンドラも作成できると思うのですが、System Events はどうも処理が遅くて...乗り気になれなかったり(必要があればもちろん使います)。

0 件のコメント :

コメントを投稿