Google Chrome 拡張機能の開発 - 4 - マウスカーソルにある位置のものをコピー

<font color="#0000ff"><b>やりたいこと：マウスカーソルがある位置で任意のコマンドを実行したら、その場所の要素をインテリジェントにコピーしたい</b></font>
<font color="#ff0000"><b>やれたこと：バックグラウンドによるコンテキストメニュー表示と、メッセージの送受信と、コンテンツスクリプトでのカーソル位置の要素の取出し</b></font>
[【ソースコードも一応GitHubへアップしてあります】](https://github.com/rrryutaro/RChromeExtension)（※でも、自分でダウンロードして使ってみたらうまく動かないよ・・・あるぇ～？）

# 前回の間違いを修正
前回、データ保存に`localStorage`を使用したが、Chrome拡張でのAPIを知ったので書きかえます。

```js:popup.js(旧)
//保存
localStorage.setItem(saveDataName, JSON.stringify(saveData));

//読込
const data = JSON.parse(localStorage.getItem(saveDataName));
```
```js:popup.js(新)
//保存
chrome.storage.sync.set({
  "CopyFormat": saveData
});

//読込
chrome.storage.sync.get(["CopyFormat"],items=>{
  const data = items.CopyFormat;
});
```

# コンテキストメニューを表示する
マニフェストにバックグラウンド用の定義を加えます。

```json:manifest.json
  "background": {
    "page": "background.html",
    "persistent": true
  },
```
```html:background.html
<!DOCTYPE html>
<html>
<head>
<title>R Chrome Extension</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="background.js"></script>
</head>
<body>
<textarea id="text" />
</body>
</html>
```
```js:background.js
chrome.contextMenus.create({title: "コピー", onclick: info=>{
  console.log("ほげ");
}});
```

こんな感じで、とりあえずコンテキストメニューにコマンドが追加されて、実行できることを確認しました。

# コンテンツスクリプトで表示中のページにスクリプトを加える
マニフェストにコンテンツスクリプト用の定義を加えます。（※今は適当にコピペしてきた形のものを使っていますので、各定義については十分理解できていません）

```json:manifest.json
  "content_scripts": [
    {
      "all_frames": true,
      "js": ["jquery-3.1.1.min.js", "contentscripts.js"],
      "matches": ["<all_urls>"],
      "run_at": "document_end"
    }
  ]  
```
```js:contentscripts.js
$("*").hover(e=>{
  console.log(e);
},e=>{});
```

マウスカーソルを動かすと、カーソルのある位置のオブジェクトが取得されていることがわかりました。

# バックグラウンドとコンテンツスクリプトの連携
いくつか方法があるようでしたが、`chrome.tabs.sendRequest`と`chrome.extension.onRequest`を使用する方法をとりました。

```js:background.js
chrome.contextMenus.create({title: "コピー", onclick: info=>{
  chrome.tabs.getSelected(null, tab=>{
    chrome.tabs.sendRequest(tab.id, {command: "Copy"}, response=>{
      console.log(response);
    });
  });
}});
```
```js:contentscripts.js
chrome.extension.onRequest.addListener((request, sender, sendResponse)=>{
  if (request.command==="Copy")
    sendResponse({"text": "hoge"});
});
```
コンテキストメニューからコマンドを選択して、値が戻ってくることが確認できました。

# カーソルの位置にある文字列をクリップボードへコピーする
ソースコードなどが表示されている場所で、カーソルを合わせてコピーで、そのソースコードをまるっとコピーすることを想定します。

現状、うまくできる場所とできない場所があり、ログ出力を確認すると、イベントが数回発生していたりと問題が多数ありますが、とりあえず、方法的にはこんな感じなのだろうといった段階です。
なので、あまり好ましいソースコードの状態ではありませんし、変なことをしているかもしれませんが、おいおい直していきます。

```json:manifest.json
{
  "name": "R Chrome Extension",
  "manifest_version": 2,
  "version": "0.0.4",
  "description": "コピー＆ペーストに関わる作業を効率化するための機能を盛り込んだツールです。（としていく予定）",
  "permissions": [
    "tabs", "clipboardWrite", "contextMenus", "storage"
  ],
  "browser_action": {
    "default_popup": "popup.html"
  },
  "background": {
    "page": "background.html",
    "persistent": true
  },
  "content_scripts": [
    {
      "all_frames": true,
      "js": ["jquery-3.1.1.min.js", "contentscripts.js"],
      "matches": ["<all_urls>"],
      "run_at": "document_end"
    }
  ]  
}
```
※前回までは`version`の指定が適当でしたが、試に公開してみた際、このバージョン設定でミスしました。ご注意ください。

```html:background.html
<!DOCTYPE html>
<html>
<head>
<title>R Chrome Extension</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="background.js"></script>
</head>
<body>
<textarea id="text" />
</body>
</html>
```
```js:background.js
chrome.contextMenus.create({title: "コピー", onclick: info=>{
  chrome.tabs.getSelected(null, tab=>{
    chrome.tabs.sendRequest(tab.id, {command: "Copy"}, response=>{
      console.log(response.text);
      const txt = $("#text");
      txt.val(response.text);
      txt.select();
      document.execCommand("copy");
    });
  });
}});
```
```js:contentscripts.js
let selectionTarget;
let selectionText;
$("*").hover(e=>{
  selectionTarget = e.target;
},e=>{});
$("*").bind("contextmenu",()=>{
  selectionText = selectionTarget.innerText;
});
chrome.extension.onRequest.addListener((request, sender, sendResponse)=>{
  if (request.command==="Copy")
    sendResponse({"text": selectionText});
});
```

# 動かしてみた感じ
![image](https://qiita-image-store.s3.amazonaws.com/0/156225/9fb25ba6-a94d-43f6-e465-bce4276cb116.png)
![image](https://qiita-image-store.s3.amazonaws.com/0/156225/5c09f278-20d9-3db0-6ba1-63189237f17e.png)
一応コピーできているなぁ・・・


# 今回参考にした記事
- [chrome拡張機能 > background.jsからcontentscript.jsにメッセージを送る方法 · For myself tomorrow](https://tigawa.github.io/blog/2013/10/13/chrome-extensions-message/)

以上

|前回|一覧|次回|
|:--|:--:|--:|
|[保存できるようにする](http://qiita.com/rrryutaro/items/4b996b4594e3fafcc77d)|[シリーズ一覧](http://qiita.com/rrryutaro/items/015b67568ddd947ec2b5)|[コピーした履歴をわかるようにする](http://qiita.com/rrryutaro/items/530d5a50dec8436ebb5c)|