逆引きChrome extension開発メモ

# Chrome extensionの基本的な概念
[Chrome拡張の開発方法まとめ　その1：概念編](https://qiita.com/k7a/items/26d7a22233ecdf48fed8)
[Chrome Extension の作り方 (その1: 3つの世界)](https://qiita.com/sakaimo/items/6b7b464de4bdcad18802)
[Chrome Extension の作り方 (その2: Contents Script)](https://qiita.com/sakaimo/items/6b7b464de4bdcad18802)
[Chrome Extension の作り方 (その3: Browser Action / Page Action)](https://qiita.com/sakaimo/items/129a785146e5873c42a6)
[Chrome Extension の作り方 (その4: Event Page / Background Page)](https://qiita.com/sakaimo/items/edd910a770b3d5ba83e3)
[Chrome Extension の作り方 (最終話: メッセージパッシング)](https://qiita.com/sakaimo/items/5e41d6b2ad8d7ee04b12)

Chrome extensionにおける基本的なことは上記のページでかなり勉強できる．お世話になりました． 

# 現在開いているタブの情報を取得する
ここで現在開いているタブとは，現在選択しているウィンドウで開いているタブのことを示す．

```:permissions
["tabs","activeTab"]
```

```javascript

chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
  active_url = tabs[0].url;
});
```
**<解説>**
[chrome.tabs.query(object queryInfo, function callback)](https://developer.chrome.com/extensions/tabs#method-query)を使う．
`active:true`で実行したウィンドウ内でのアクティブなタブを指定し，`currentWindow:true`で現在選択しているウィンドウを指定する．
`query`のcallbackはarray of Tabsであり，今取得しようとしているタブの情報は`tabs[0]`に格納されている．
ＵRL以外にも以下のような情報も含まれている．

- id  
タブのID.browser session内で固有の値を持つので，`sendMessage`などでメッセージパッシングを行う際にこれを指定することにより，任意のタブへメッセージを送ることができるようになる．
- windowId  
そのタブを含むウィンドウのID.
- title  
タイトル．
- pendingUrl

> The URL the tab is navigating to, before it has committed. This property is only present if the extension's manifest includes the "tabs" permission and there is a pending navigation.
https://developer.chrome.com/extensions/tabs#property-Tab-pendingUrl

参考:https://developer.chrome.com/extensions/tabs#type-Tab

# タブのURLを取得できない

```javascript

chrome.tabs.onCreated.addListener(function (tab) {
  alert(tab.url);//sometimes ""
  alert(tab.pendingUrl);
  if(tab.pendingUrl.match(/.*.pdf$/)){
    //URLが.pdfで終わるような場合にのみ実行する
  };
}
```

**<解説>**
`Tab`からURLを取得できない場合は，タブを開いたばかりだと`tab.url`を取得できないことがある(確定?)ので，`pendingUrl`を確認してみるとよい．



# 選択しているテキストを取得
**<解説>**

```javascript:contents.js
winodw.getSelection().toString()
```
`contents.js`ではこのように簡単に取得できる．一方，`ContextMenus`では以下のような方法でも取得できる．

```:permissions
["contextMenus"]
```
```javascript:background.js
chrome.contextMenus.create({
  title: "selectiontext>:%s",
  type: "normal",
  contexts: ["selection"],
  onclick: function (info) {
    alert(info.selectionText)
  },
});
```

`title`にある`%s`は選択中のテキストに置き換わる．
`info`に含まれる他の情報など詳細は[chrome.contextMenus](https://developer.chrome.com/apps/contextMenus#method-create)を参照されたい．
余談だが，PDF上の選択したテキストは`window.getSelection().toString()`で拾うのは難しい．
私は諦めて`onclick`の`info`から拾う実装にした．

# タブを開く
```javascript

chrome.tabs.create({
  url: "http://www.google.co.jp/search?hl=ja&source=hp&q=how_to_create_tabs",
  active: false,
});
```
**<解説>**
[chrome.tabs.create(object createProperties, function callback)](https://developer.chrome.com/extensions/tabs#method-create)を使う．
ここでは`active:false`としてタブを開いた際に現在開いているタブの選択状態が変わらないように指定した．`createProperties`には`windowId`なども指定できる(デフォルトは現在のウィンドウ)．



# タブを消す
```javascript

chrome.tabs.getSelected(null, function (tab) {
  chrome.tabs.remove((tabIds = tab.id));
});
```
**<解説>**
[chrome.tabs.remove(integer or array of integer tabIds, function callback)](https://developer.chrome.com/extensions/tabs#method-remove)を使用する．
現在選択しているタブのTab Objectsを`chrome.tabs.remove((tabIds))`により選択したIDを持つタブを削除する．tabIdsにはIDのリストを渡すこともできる．
参考:https://developer.chrome.com/extensions/tabs#method-remove

# popupのウィンドウを消す

```javascript:popup.js
window.close();
```
**<解説>**
cssで`display:none;`を指定しても完全には消えずに小さなウィンドウが残ってしまうので`close()`してしまえばよい．ただし，`sendMessage`で`background`にメッセージを送っていた場合は，`sendResponse()`から返ってくる前に消してしまうと正常に処理を終えられなくなることに注意したい．
あくまですべての処理を終えてから消すこと．


# background.jsにメッセージを送る
```javascript:contents.js
chrome.runtime.sendMessage({ message: "from_contents" }, function (res) {
  alert("Hello "+res);
});
```

```javascript:background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
  if (request.message == "from_contents") {
   sendResponse("World!")
  }
});
```
**<解説>**
`background.js`にメッセージを送るには[chrome.runtime.sendMessage(string extensionId, any message, object options, function responseCallback)](https://developer.chrome.com/extensions/runtime#method-sendMessage)を用いる．
`chrome.tabs.sendMessage(integer tabId, any message, object options, function responseCallback)`は`tabId`を指定できないので使用できない．
つまり，`background.js`に向けたメッセージはすべて`chrome.runtime.sendMessage()`を用いる．

# contents.jsからpopup.jsにメッセージを送る
```javascript:contents.js
chrome.runtime.sendMessage({ message: "from_contents" }, function (res) {
  alert("Hello "+res);
});
```
```javascript:popup.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
  if (request.message == "from_contents") {
   sendResponse("World!")
}
});
```
**<解説>**
`popup.js`はアイコンをクリックしたウィンドウ上に現れるので，一見するとそのタブのIDを持っていそうだが，実際は持っていない．
そのため，`chrome.runtime.sendMessage()`を用いる．この辺の使い分けを理解するのに私は時間がかかった...

# sendResponse()で複数の値を渡したい

```javascript:popup.js
chrome.runtime.sendMessage({ message: "test" }, function (resjson) {
  var obj = JSON.parse(resjson);
  alert(obj.count);
  alert(obj.result);
});
```
```javascript:background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
  if (request.message == "test") {
    var result=true;
    var count=42;
    sendResponse('{"result":result,"count":count}');
  }
});
```

**<解説>**
**`sendResponse()`の引数はJSON object**であるので，JSONの形に成形すれば複数引数の値を渡すことができる．
[公式ドキュメント](https://developer.chrome.com/extensions/runtime#method-sendMessage)にはしっかり記載されているが，外部解説サイトでは`sendResponse(result)`のように一つの変数の値しか渡していない例が多かったので，ここで強調して紹介しておきたい．


# 終わりに
chromeにデータを保存できる[chrome.storage](https://developer.chrome.com/apps/storage)や，Options Pageはまだ使用したことがないので勉強次第追記する．

jsやhtml周りを勉強する必要はありますが，Chrome拡張機能は思った以上に簡単に作れます．
はじめに紹介した分かりやすい解説記事や[公式サンプル](https://developer.chrome.com/extensions/samples)を参考に作ってみてはいかがでしょうか．