Chromeデベロッパーツール拡張で、Techサポートを効率化する

※ この記事は、PLAID Advent Calendar 2017 9日目です。

こんにちは、プレイドの @jumtech ([twitter: @jumpei_ikegami](https://twitter.com/jumpei_ikegami))です。

BizDevチームでエンジニアをしています。

趣味は[Tech系ポッドキャスト](https://shiganai.org/)の配信です。

Matzさんも聴いてます。(語弊)

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ポッドキャスト聴いてると、いきなり私個人向けに挨拶される驚き。 <a href="https://twitter.com/hashtag/%E3%81%97%E3%81%8C%E3%81%AA%E3%81%84%E3%83%A9%E3%82%B8%E3%82%AA?src=hash&amp;ref_src=twsrc%5Etfw">#しがないラジオ</a></p>&mdash; Yukihiro Matsumoto (@yukihiro_matz) <a href="https://twitter.com/yukihiro_matz/status/908996089914601472?ref_src=twsrc%5Etfw">2017年9月16日</a></blockquote>

私は社内では珍しく、ビジネス側のチームでエンジニアをしています。

そこで、技術的なサポート業務をChromeデベロッパーツール拡張で効率化した話を書きます。

# まとめ
- Chromeデベロッパーツールは簡単に拡張できる
- Chromeデベロッパーツール拡張を使って、自分好みのWebサイトインスペクタが作れる

# なぜ必要だったか
弊社では、Web接客プラットフォーム「[KARTE](https://karte.io/)」を開発しています。

ざっくり言うと、エンドユーザーの属性やリアルタイムの行動に応じて、KARTE導入サイト上で、動的にコンテンツを出し分けるツールです。

そのため、「なんかコンテンツ出ないんだけど？」みたいな問い合わせが、社内外を問わず飛んできます。

これまでは、ChromeデベロッパーツールのNetworkタブで弊社サーバーとの通信を探して、Preview画面でgzipされたJavaScriptを解読していました。

これはつらい、つらい。

技術の力で解決です。

# 作ったものが、こちら
![----------2017-08-14-5.30.51.png](https://qiita-image-store.s3.amazonaws.com/0/137932/6ed60558-9b12-bc28-1889-ba165225fc43.png)

toB向けなので、画像だけで失礼します。

なんと、Chromeデベロッパーツールを拡張すると、カスタムのタブを1つ追加し、専用のviewを追加することができます。

Networkタブに表示されるようなrequest/responseの内容も取得できるので、通信の中で気になる部分だけをpick-upして表示するようなことができます。

# Chrome拡張で\<title>の中身を表示する
世の中には、2種類のChrome拡張があります。

- ただのChrome拡張
- Chromeデベロッパーツール拡張

もう少し詳しく言うと、Chrome拡張は実行されるコンテキストの違う複数のscriptを組み合わせて作ることができ、それらscript毎にできることが違います。

[概念図](https://developer.chrome.com/extensions/devtools)
![devtools-extension (1).png](https://qiita-image-store.s3.amazonaws.com/0/137932/9d9b3121-456f-9813-0f34-44b6aaf8a8f2.png)

実は、ただのChrome拡張といえども、閲覧ページのDOMに触ることは可能です。

例えば、ポッドキャストのshow noteを書くとき、
__「リンクを貼るページの\<title>の中身だけをコピペしたい」__
みたいなことがあります。

デベロッパーツール拡張の前に、まずは試しに、それを作ります。

## ブラウザのコンソールにtitleタグの中身を出す
まず、適当なフォルダを作り、`manifest.json`(Chrome拡張としての設定ファイル)を作成します。

```manifest.json
{
  "name": "showTitle",
  "version": "1",
  "manifest_version": 2,
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]
}
```

仕様は[こちら](https://developer.chrome.com/extensions/manifest)。

`manifest_version`は、`manifest.json`のフォーマット自体のversionなので、最新である`2`を指定します。

`content_scripts`プロパティでは、URLパターン毎に、展開するJavaScriptやCSSを登録できます。

マッチパターンの書き方は、[こちら](https://developer.chrome.com/extensions/match_patterns)をどうぞ。

次に、閲覧ページと同じコンテキストで実行される`content.js`を作成します。

```content.js
const title = document.getElementsByTagName('title')[0].innerText;
console.log(title);
```

<img width="718" alt="スクリーンショット 2017-12-06 10.53.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/137932/7e77cafe-c414-a932-609f-589e0a81da78.png">


これで、拡張機能ができました。
はい、簡単です。

あとは、Chromeで`chrome://extensions/`にアクセスし、

- 右上の「デベロッパーモード」にチェック
- 左上の「パッケージ化されていない拡張機能を読み込む」

から、manifest.jsonが存在するフォルダを読み込みます。

<img width="718" alt="スクリーンショット 2017-12-06 10.51.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/137932/b99318ff-e953-cfce-853d-9f6d45a1ce02.png">

chromeの上部バーにアイコンが追加され、Chrome拡張として登録されたことがわかります。

試しに、[KARTE](karte.io)のサイトを開いてみると、

<img width="718" alt="スクリーンショット 2017-12-06 10.53.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/137932/278a0734-4154-e9cc-a64a-d2c8fd461d3f.png">

`content.js`が実行され、Consoleにtitleタグの中身が出力されました。

## Chrome拡張自体の表示領域が欲しい！
いちいちConsoleを開いてタイトルをコピペするのは面倒なので、Chrome拡張自体の表示領域を確保します。

そのために、Chrome拡張の[browserAction](https://developer.chrome.com/extensions/browserAction)にpopupを登録します。

```manifest.json
{
...
  "browser_action": {
    "default_popup": "popup.html"
  }
}
```

```popup.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <style>
  .title { width: 500px; }
  </style>
</head>
<body>
  <div id="title" class="title"></div>
  <script src="popup.js"></script>
</body>
</html>
```

html内に直接scriptを書くとなぜか動かないので、外部ファイルに切り出します。

```popup.js
document.getElementById('title').innerText = 'ここにタイトルを表示したい';
```

再度 `chrome://extensions/`にアクセスし、「リロード」ボタンを押すと、

<img width="522" alt="スクリーンショット 2017-12-06 11.13.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/137932/64652ac0-3333-38b8-0fd6-7010fd915a34.png">

アイコンを押したときに、popup.htmlを表示させることができます。
popup.htmlのコンテキストで要素の検証やconsoleを見たい場合は、アイコンを右クリックして、「ポップアップを検証」を押すと、popup用のデベロッパーツールが開きます。

## content.jsからpopup.jsにタイトルを受け渡す！

popup.jsが参照するDOMはpopup.htmlなので、直接ページ上のDOMを触ることができません。

そこで、閲覧ページのDOM操作はcontent.jsに任せて、その結果を[MessagePassing](https://developer.chrome.com/apps/messaging)で受け取ります。

content.js側では、メッセージを受け取った場合のイベントリスナを登録します。
閲覧ページのタイトルを含めて、レスポンスを返します。

```content.js
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  const title = document.getElementsByTagName('title')[0].innerText;
  sendResponse( {title: title} );
});
```

popup.js側では、メッセージの送信を行います。

```popup.js
chrome.tabs.query({currentWindow: true, active:true}, (tabs) => {
  chrome.tabs.sendMessage(tabs[0].id, {text:'titleちょうだい'}, (res) => {
    document.getElementById('title').innerText = res.title;
  });
});
```

ご存知のように、Chromeでは気付くと大量のウィンドウとタブが量産されているので、対象のタブを特定するために[query](https://developer.chrome.com/extensions/tabs#method-query)プロパティで対象タブの絞り込み条件を指定しています。

ここでは、現在のウィンドウでアクティブなタブを選択します。
タブが特定できたら、そのタブに対してメッセージと、レスポンスが帰ってきたときのコールバックを渡します。

そのコールバックの中で、content.jsから受け取ったtitleを、popup.html側のDOMに追加します。


再度Chrome拡張をリロードして右上のアイコンをクリックすると、問題なくページタイトルが表示されていることがわかります。

<img width="654" alt="スクリーンショット 2017-12-06 11.36.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/137932/b5020aa5-985c-23c6-fcb1-06e0a54ede4b.png">

## 完成！
以上で、実用的なChrome拡張ができました。

実際のソースコードは、以下をご覧ください。
https://github.com/jumtech/show-title-extension

# いざ、デベロッパーツール拡張へ！
ここまでが、「ただのChrome拡張」でできる最低限のことでした。

ここから、いよいよみんな大好きChromeデベロッパーツールの拡張に進みます。

と思ったのですが、なんとちょうどいいブログ記事を見つけたので、そちらをご覧ください。（宣伝）

[Chromeデベロッパーツールに独自機能を追加する6つのステップ | PLAID engineer blog](http://tech.plaid.co.jp/extending_chrome_devtools/)

この記事では、閲覧ページからTwitterサーバーへのリクエストを検知し、Twitter埋め込みを使っているかを一瞬で判別するツールを作る例を紹介しています。

また、類似の内容で[We Are JavaScripters!](https://wajs.connpass.com/)という勉強会に個人的に登壇した資料もあります。

[Chromeデベロッパーツールを自分色に染める // Speaker Deck](https://speakerdeck.com/jumtech/chromedeberotupaturuwozi-fen-se-niran-meru)

こちらでは、閲覧ページで使われているWeb解析系のツールを可視化するという同様の機能実装に加えて、デベロッパーツール内へのVue.jsの導入についても触れています。

なお、これらの執筆時点と現在で、重要な変更点が1点あります。

当時の持ちブキは「銀モデ」でしたが、現在は「黒ZAP」です。

上記に注意して、楽しいChrome拡張ライフとスプラトゥーンライフをお送りください！