モバイル端末環境でのjavascript injectionをするためには

この記事はニフティグループ20日目の記事です．

ハッカソンに参加した時に壁となったことについて書きます．技術録というよりかは四苦八苦録です．

#TL;DR
モバイル環境でjavascript injectionするためには次の対応が可能

1. android appのwebviewでjs injection
2. firefox web extension

* Chrome extensionはモバイル環境で利用不可能．


# はじめに

　yahoo主催のhackday2018に参加してきました．
https://hackday.jp/

私のチームはモバイル環境でスクレイピングするという事を目標としてプログラムを作ることにしました．
調べると何個かはモバイル環境でスクレイピングするというアプリはあるようですが，今回は初めてのハッカソンのメンバーが多いという事もあり，ある程度の工数で出来そうなこのテーマを選びました．
#### ゴール : モバイル端末だけでスクレイピングするサイトの登録を行い，対象サイトのアップデート履歴を確認できるようにする．

ディスカッション中にこのゴールが決まりました．次にどうやって実装するかについてです．

1. モバイル端末でchrome開発者ツールの要素セレクターのように要素を選択でき，domのidを特定する．
2. idやclass名からcssセレクターを用意し，サーバーに送信する
3. サーバーではcssセレクタを使って，対象サイトにアクセスし，情報を取得してくる．
4. 情報をモバイル端末で見れるようにする．

このような流れを考えました．２,4はhttpリクエストを投げれば良いですし，3はNode.js+express+puppeteerを使えばそれなりに簡単にできそうです．
１がハッカソンが始まった時点でどのように実装するか検討つきませんでした．ただ，Chromeの開発者ツールもjavascriptで動いていること，PC版のchrome　extensionではDOM選択ツールがある事から，不可能ではない事がなんと無くわかっていました．なので，次のことを検討しました．

1. iframeを使って対象サイトを部分表示し，js injectionを行い選択された部分を検出する
    * この方法はクロスドメイン制約によってアクセス制限がされ，対象となるサイトが表示されませんでした．
    * なので，iframeでは出来ない事がわかりました．これで出来たらすごく楽でよかったのですが，もうすこし考える必要がある事がわかりました．
2. Chrome extension(日本語でアドオン)によるjs injection
    * モバイル版ではChromeのextensionは開発予定自体ないので出来ない．
    * https://androidlover.net/googleapps/chrome-browser/android-chrome-not-planed-adding-extension.html
3. Android　app+ webview　w/ js injection
    * 割とめんどくさいのですが可能のようです．参加メンバーの中にアプリ開発者がいたためこの構成の開発経験がありました．
4. Firefox web extension
    * こっちも可能のようですが...メンバーの中で誰もFirefox　extensionの開発経験がありませんでした．
    
### ハッカソンでの決断
#### 3.Android　app+ webview　w/ js injection
とする事になりました．開発中につまずいて完成しないということは避けたかったため，経験者がいるこの方法をとりました．


#今回のテーマ
###　ハッカソンで取らなかった方法のfirefox extensionの方法を試してみようと思います．


MDNのチュートリアル通り進めればそれほど難なく js injectionできると思います．
https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension


## 作業フォルダを作ります．
```
mkdir borderify
cd borderify
```
## マニフェストを書きます

```manifest.json
{

  "manifest_version": 2,
  "name": "Borderify",
  "version": "1.0",
 
  "description": "Adds a solid red border to all webpages matching mozilla.org.",
  
  "icons": {
    "48": "icons/border-48.png"
  },

  "content_scripts": [
    {
      "matches": [
          "http://*/*",
          "https://*/*"
      ],
      "js": ["borderify.js"]
    }
  ]

}
```
チュートリアルからはmachesのURLを全てのサイトに対応してできるよう変更しました．

## icons/border-48.pngを用意します．
extension用のアイコン画像を用意します．画像をフォルダ内にいれるだけなので，特に特筆しません．

## インジェクションするコードを書きます．
```borderify.js
document.addEventListener("selectionchange", function (e) {
  if(window.getSelection().toString()==""){return};
    e.preventDefault();
    var selectionObject = window.getSelection();
    console.log(selectionObject.toString())
    window.confirm(selectionObject.toString())
    var selectionDOM = selectionObject.getRangeAt(0).startContainer.parentNode;
    console.log(selectionDOM)
  }, false);
```

`console.log()`に関しては開発者ツールを起動していないと見えないため，意味はありませんがデバッグのために仕込んでいます．

selectionchangeイベントはwindowではなくdocumentに紐付くイベントになります．
マウスのドラッグやスマホのロングタップによって選択されたタイミングでハッカします．
https://developer.mozilla.org/ja/docs/Web/Events/selectionchange

次に`window.getSelection()`で選択されたobjectを取得します．
`selectionObject.getRangeAt(0).startContainer.parentNode;`で選択されたDOMを取得出来ま
この時点でElementからcssパスを取得できます．

## 署名をしてxpiファイルを作成

このURLからxpiファイルを作成します．（要Firefoxアカウント：簡単に作れます）
https://addons.mozilla.org/ja/developers/addon/submit/distribution

## xpiファイルをスマートフォンに転送&インストール

これでやっとモバイル端末でjs injection出来ました．このextensionを有効化すると文字を選択したところのダイアログボックスが表示されると思います．
そういえば`window.confirm()`はPCからは呼び出せないため，console.logから確認するしかないです．
![image.png](https://qiita-image-store.s3.amazonaws.com/0/162708/7fb09dd7-7201-b6db-111e-0633f8ae6450.png)


#　最後に
今回はハッカソンで採用しなかったfirefox web extensionを使ったjs injectionをやってみました．正直なところアプリを一本書くよりもfirefox extensionを書いた方が早かったです．

良いクリスマスを