JXAでSafari, Chrome, Firefoxを操作する際の違い

# まとめ

Safari: Apple謹製だけど機能少ない
Chrome: なぜか異常に充実
Firefox: ほぼ何もできない

----

表

気が向いたときにちょっとずつ更新中

||Safari|Chrome|Firefox|
|:--|:--|:--|:--|
|参照|Application( **"Safari"** )|Application( **"Chrome"** )|Application( **"Firefox"** )|
|ウィンドウ<br>(全て)|app.windows()|app.windows()|app.windows()|
|ウィンドウ<br>(最前面)|app.windows[0]|app.windows[0]|app.windows[0]|
|ウィンドウ<br>作成|app **.Document**().make()|app **.Window**().make()|-|
|タブ<br>(全て)|win.tabs()|win.tabs()|-|
|タブ<br>(表示中)|win **.currentTab**()|win **.activeTab**()|-|
|タブ<br>切り替え|win **.currentTab = tab**|win **.activeTabIndex = index**|-|
|URL移動|tab.url = "http://qiita.com"|tab.url = "http://qiita.com"|-|
|JS実行|app **.doJavaScript**("alert('hi.'),{in: tab})|app **.execute**(tab, {javascript:"alert('hi.')"})|-|
|ページ更新|tab.url = tab.url()|tab.reload()|-|
|読込中止|-|tab.stop()|-|
|戻る & 進む|(やろうと思えばdoJavaScriptで)|tab.goBack();<br>tab.goForward();|-|
|ブックマーク|-|参照可(長いので省略)|-|
|履歴|-|-|-|

Chromeの新規ウィンドウ作成は戻り値でそのままWindowを得られる。
SafariはDocumentが返ってくるからURL移動したいときとか困る。
(`app.Window().make()`できればいいのに・・・)

Windowを得る場合は以下のようにするといいのかもしれない・・・

```js
var Safari = Application("Safari");
var doc = Safari.Document().make();
var win = Safari.windows[doc.name()];

//URL移動
win.currentTab.url = "http://qiita.com"
```

# 使ってみる

以下の記事と同じことをするコードを書いてみました。

- [Safariで自動でタブ切り替え・更新をする - Qiita](http://qiita.com/okuramasafumi/items/90320141b3439133ccd5)

JXAを知らない方はこちら。

- [知らないうちにMacがシステム標準でJavaScriptで操作できるようになってた (JXA) - Qiita](http://qiita.com/zakuroishikuro/items/1b02378bf9e940602d87)

## やること

下準備

- 待ち秒数を指定 (DELAY)
- アプリケーションの参照を取得
- 最前面のウィンドウを取得

-------------------------
ループ

- タブを切り替えてDELAY秒だけ待つ
- ページを更新してDELAY秒だけ待つ

------------------------

試すには30秒は長いので3秒にしました。

# Safari

```js:autoreload_safari.js
const DELAY = 3;

var Safari = Application("Safari");
var window = Safari.windows[0]; //最前面のウィンドウを取得

while(window.exists()) { //無限ループ (ウィンドウが開いてなければ実行しない)
  window.tabs().forEach(function(tab){

    // タブ切り替え
    window.currentTab = tab;
    delay(DELAY);

    // 更新
    tab.url = tab.url();
    delay(DELAY);
  });
};
```

ウィンドウから全タブを取得できます。
タブの切り替えは現在のタブ(`currentTab`)に任意のタブをセットすることで行います。

Safariには表示中のページを更新する関数がありません。
ページの更新は表示中のurlをタブの`url`プロパティに再セットすることで可能です。

または`doJavaScript`によってページ内でJavaScriptを実行して更新します。

```js:JavaScriptで更新(doJavaScript関数)
Safari.doJavaScript("location.reload()", {in:tab});
```
`doJavaScript`はページ内での実行結果を戻り値として返します。
ページから簡単に情報を得たい場合に便利かもです。
同じJavaScriptで書けますし。

メモ:

ブックマークにアクセスすることが出来ません。
ブックマークを整理するスクリプト書きたかったのに無理でした・・・。

リーディングリストに追加する機能はあるのですが、その一覧を取得する機能も無いです。
ビビりました。

# Chrome

```js:autoreload_chrome.js
const DELAY = 3;

var Chrome = Application("Google Chrome");
var window = Chrome.windows[0]; //最前面のウィンドウを取得

while(window.exists()){ //無限ループ (ウィンドウが開いてなければ実行しない)
  window.tabs().forEach(function(tab, i){

    // タブ切り替え
    window.activeTabIndex = i + 1;
    delay(DELAY);
  
    // 更新
    tab.reload();
    delay(DELAY);
  });
};
```

ウィンドウから全タブを取得できます。
タブの切り替えはウィンドウの`activeTabIndex`にインデックスをセットすることで行います。
インデックスは1から始まることに注意。

タブからはインデックスを取得できません。
そのためforEachのカウンタを利用する必要があります。

アクティブなタブを取得する関数は`.activeTab()`です。
これは読み取り専用なのでSafariのように他のタブをセットすることはできません。

Chromeには表示中のページを更新する機能(`.reload()`)があります。
更新キャンセルや「進む」「戻る」の機能もあります。

もちろんページ内でJavaScriptを実行することも可能です。

```js:JavaScriptで更新(execute関数)
Chrome.execute(tab, {javascript:"location.reload()"});
```
Safariと同じように`execute`もページ内での実行結果が戻り値になります。

メモ:

今回のブラウザの中で、スクリプト向けに用意された機能はChromeがダントツで充実してました！

ブックマークにアクセスすることもできます。
ブックマークを整理するスクリプトを書くことも可能でしょう。
やってませんが。

# Firefox

```js:autoreload_firefox.js
const DELAY = 3;

var Firefox = Application("Firefox");
var window = Firefox.windows[0]; //最前面のウィンドウを取得

SystemEvents = Application("System Events");

while(window.exists()){ //無限ループ (ウィンドウが開いてなければ実行しない)

  // タブ切り替え
  Firefox.activate();
  delay(0.3); //アクティブになるまで少し待つ
  SystemEvents.keystroke("]", {using:["command down", "shift down"]});
  delay(DELAY);
  
  // 更新
  Firefox.activate();
  delay(0.3); //アクティブになるまで少し待つ
  SystemEvents.keystroke("r", {using:"command down"});
  delay(DELAY);
};
```

ウィンドウからタブを取得できません。
ってことは`Application.documents()`でページの内容を見れるのかな？

```text
>> Firefox = Application("Firefox")
=> Application("Firefox")
>> Firefox.windows.length
=> 10
>> Firefox.documents()
=> []
>> 
```

！？
空の配列が返ってきました。
どうやら用語説明はテンプレそのまま使っていて、機能は何もなさそうです。多分。
ウィンドウを取得する以外、ほぼ何も出来なさそうです。

こういう場合は`System Events`からキーボードを押したりして操作するしかありません。
キーボードやマウス操作をする方法はこちらに書きました。

- [Macのキーボード入力、マウスクリックをJavaScriptで (JXA) - Qiita](http://qiita.com/zakuroishikuro/items/afab0e33ad2030ba2f92)

`Application("Firefox")`の部分を変えるだけでSafariでもChromeでも動きます。
いちいち`.activate()`する必要があるので他の作業中に動作させるのは無理です。

メモ:

ページの内容すら取得できないんじゃ・・・？
スクリプトで操作するなら別のブラウザを使いましょう。

**追記:**
windowにもdocumentプロパティがあることに気付きましたが、nullでした。

```js
varFirefox = Application("Firefox");

Firefox.windows[0].document();
//=> undefined

Firefox.windows[0].properties();
//=> {"document":null, "closeable":true, "zoomed":true, "class":"window", "index":1, "visible":true, "name":"Yahoo! JAPAN", "modal":false, "miniaturizable":true, "titled":true, "id":6188, "miniaturized":false, "floating":false, "resizable":true, "bounds":{"x":0, "y":23, "width":1280, "height":709}, "zoomable":true}
```

ウィンドウからページのタイトルだけ取得できるようです。
上の例だとYahooを開いていることが`name`プロパティで分かります。
ページ内容を取得する方法が存在するなら、知ってる方教えてください。

# 蛇足

ぜんぜん関係ないけど、Terminalでシェルスクリプトを実行する`doScript`もSafariの`doJavaScript`と同じ構文
ただし戻り値はタブの参照です。タブを指定しない場合は新しいウィンドウを開くので。

```js:Terminal
var Terminal = Application("Terminal");
var tab = Terminal.windows[0].selectedTab(); // SafariはcurrentTab。名前統一してくれ

Terminal.doScript("echo 'hi.'", {in:tab});
var newTab = Terminal.doScript("echo 'hi new window.'");
```

コマンド実行の戻り値を得たい場合は`doShellScript`を使います
これはTerminalじゃなくても使えます

```js:doShellScript
var app = Application.currentApplication();
app.includeStandardAdditions = true;

var date = app.doShellScript("date");
```