UserScript

### 概要
ブックマークレットはブックマーク一覧からユーザが「開く」ことでスクリプトを実行する一方、ユーザスクリプトはあるWebページのロード時に自動でスクリプトが実行されるように（URLフィルタなどで）設定することができる。

ユーザスクリプトの作成にあたって、バージョン情報やURLフィルタを埋め込むヘッダ（メタデータ）を付ける必要があるものの、それ以外では特別な記法などは特に要求されず、通常の（Webページ上で動作する）JavaScriptと同じように記述すればいいらしい。

さて、ユーザスクリプトの実行にはこれを実現するための拡張機能の導入が必要な場合がある。Chromeでは標準機能に含まれており、`chrome://extensions`にローカルのjsファイルをD&Dすることで通常の拡張機能と並べてユーザスクリプトを扱える。一方、Firefoxでは（今のところ）別途拡張機能の導入が必要らしい。

（2021/03/19 追記）Google Chromeでローカルのjsファイルをインストールする際、拡張子を`.user.js`にする必要があるみたい。`.js`だとブラウザのタブで開いてしまう（Linux版 Google Chrome 89）。

- https://www.chromium.org/developers/design-documents/user-scripts

代表例っぽいものを例示する。むかし同様のコンセプトの[ScriptAutoRunner](https://chrome.google.com/webstore/detail/scriptautorunner/gpgjofmpmjjopcogjgdldidobhmjmdbm?hl=ja)（[GitHub](https://github.com/nakajmg/ScriptAutoRunner)）というのを見かけたが、（これは更新されていないし）ちまたでは*monkeyというものが使われているらしい。


- Firefox
  - [Greasemonkey – 🦊 Firefox (ja) 向け拡張機能を入手](https://addons.mozilla.org/ja/firefox/addon/greasemonkey/)
  - [Tampermonkey – 🦊 Firefox (ja) 向け拡張機能を入手](https://addons.mozilla.org/ja/firefox/addon/tampermonkey/)
- Chrome
  - Greasemonkey（標準機能として統合）
  - [Tampermonkey - Chrome ウェブストア](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ja)

拡張機能のGreasemonkey、TampermonkeyにはどちらもGUIが存在し、GUI上でユーザスクリプトの追加、URLフィルタの設定（スクリプト外で別途指定する場合）、有効・無効化を行う。Greasemonkeyは管理はpopup上のみでエディタ用のpageがあり、Tampermonkeyはpopupもpageも管理に使えて、こちらもエディタ用のpageありっぽい。

これらは特殊API（`@grant`）以外の点では互換性がありそう。

### 環境
- Google Chrome 80
- Firefox 74
  - Greasemonkey 4.9
  - Tampermonkey 4.10

### Greasemonkey（Firefox拡張機能）
新規作成時テンプレート

```js
// ==UserScript==
// @name     Unnamed Script 000000
// @version  1
// @grant    none
// ==/UserScript==
```

すべてのWebページ（http, https）

```js
// ==UserScript==
// @name     Unnamed Script
// @include  http*
// @version  1
// @grant    none
// ==/UserScript==

console.log("Hello!");
```

すべてのページ

```js
// ==UserScript==
// @name     Unnamed Script
// @include  *
// @version  1
// @grant    none
// ==/UserScript==

console.log("Hello!");
```

example.com

```js
// ==UserScript==
// @name     Unnamed Script
// @match    http://example.com/*
// @version  1
// @grant    none
// ==/UserScript==

console.log("Hello example.com!");
```

### Tampermonkey（Firefox拡張機能）
新規作成時テンプレート

※ `@match`には作成時に開いていたページのURLが自動入力される

```js
// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        http://example.com/
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Your code here...
})();
```

example.com

```js
// ==UserScript==
// @name         New Userscript
// @namespace    http://example.com
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        http://example.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    console.log("Hello example.com!");
})();
```

### メタデータの指定

|キー|値|
|:--|:--|
|name|スクリプト名|
|namespace|名前空間。作成者のWebページ等を指定し、ユーザスクリプトリポジトリが同名のユーザスクリプトを区別できるようにする|
|version|バージョン|
|description|説明文|
|author|作成者|
|match|URLフィルタ（推奨）。[厳格なルール](https://developer.chrome.com/extensions/match_patterns)に基づいてパターンマッチングする（実行範囲の面でより安全）。複数指定可（[仕様](https://wiki.greasespot.net/Metadata_Block#.40match)）|
|include|URLフィルタ。マッチするURLを含める。複数指定可（[仕様](https://wiki.greasespot.net/Include_and_exclude_rules)）|
|exclude|URLフィルタ。マッチするURLを除外する。複数指定可（[仕様](https://wiki.greasespot.net/Include_and_exclude_rules)）|
|grant|ユーザスクリプト用の特殊APIへのアクセス設定。複数指定可|
|require|URLから外部js（ライブラリ）を呼び出し。複数指定可|

必要なものを指定する。

- [Metadata Block - GreaseSpot Wiki](https://wiki.greasespot.net/Metadata_Block)
- [metadata - What is the Greasemonkey namespace needed for? - Stack Overflow](https://stackoverflow.com/questions/386612/what-is-the-greasemonkey-namespace-needed-for)

#### 特殊API（grant）
- [@grant - GreaseSpot Wiki](https://wiki.greasespot.net/@grant)
- [#_grant @ Tampermonkey • Documentation](https://www.tampermonkey.net/documentation.php#_grant)

Greasemonkeyの場合、grantを省略すると`none`扱い。Tampermonkeyの場合、省略するとスクリプト中で使用されているAPIが自動で設定される（らしい）。

Greasemonkey：`GM.setValue`、`GM.getValue`

```js
// @grant GM.setValue
// @grant GM.getValue
```

Tampermonkey：`GM_setValue`/`GMApi.GM_setValue`、`GM_getValue`/`GMApi.GM_getValue`

```js
// @grant GM_setValue
// @grant GM_getValue
```

これらは*monkeyで実行されるスクリプト間で共通のストレージの値を読み書きするらしい（[GM.setValue - GreaseSpot Wiki](https://wiki.greasespot.net/GM.setValue)）。

#### ライブラリ・外部js呼び出し（require）

```js
// @require /js/example.js
// @require ../js/example.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js
```

- [#require @ Metadata Block - GreaseSpot Wiki](https://wiki.greasespot.net/Metadata_Block#.40require)
- [greasemonkey内でjQueryを使う - $shibayu36->blog;](https://blog.shibayu36.org/entry/2012/12/06/222435)

### CORS（Same-origin policy）
ブックマークレットのオリジンは表示中のWebページになる。ユーザスクリプトのオリジンはどうか。

```js
// 比較用ブックマークレット
javascript:console.log(location.origin);
```

```js
// ==UserScript==
// @name         Check origin
// @namespace    http://example.com
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        http://example.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    console.log(location.origin);
})();
```

Chrome標準機能、Firefox Greasemonkey、Firefox Tampermonkeyで`http://example.com`。

- [オリジン間リソース共有 (CORS) - HTTP | MDN](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)


### セキュリティ
セッションID（クッキー）でもフォームに入力されたパスワードでもなんでも読み出し/書き換え/送受信可能なので、任意ユーザの作ったスクリプトを使用する際は（他のプログラム同様）セキュリティに気をつけましょう..。

### その他参考
- [UserScriptの書き方メモ - Miuran Business Systems](http://site.m-bsys.com/code/userscript-memo)
- [UserScriptで業務改善](https://kenchan0130.github.io/post/2018-05-21-1)