Chrome拡張機能の実践:データ保存と活用


以前の記事で、Chrome拡張機能自体の作成についてを実践することができたので、続いてもう一歩踏み込んだところとして、chrome.storage.local/sync.set/get を用いた「データの保存」「(保存した)データの使用」について実践してみます

TL;DR

chrome.storage.local.set , chrome.storage.local.get の実践
実践を通じて自分でわかったこと

完成形

GitHubレポジトリに配置しました

要件定義

「データを保存」「(保存した)データの使用」が必要になるプロダクトを作る必要がある
そのため、以前から面倒だと思っていた点として、たまにやっていた「アンケートの個人情報入力」(ブラウザの機能だといまいち)について半自動化する、をついでにやってしまおうと考える
何かしらのページに対する「入力フォームに入力する個人情報の保存」(→options.htmlを使う)、「保存した情報を入力フォームに(半)自動入力」する拡張機能を最終形とする
セキュリティ面が不安である+自分のPCでしか使わないものであるため、chrome.storage.sync (Googleアカウントを用いて異なるデバイス間でデータを同期、別デバイスでも使用できるようになる)でなく、 chrome.storage.local (PC自体にデータを保存する、5MBまで)を用いる

設計

manifest.json

だいたい前のものと同じなので使いまわしです
ただし、今回はどのページでも動いてほしいので、 "content_scripts""matches"["http://*/*", "https://*/*"] とかにしておきます
また今回は(設定用に)オプションページが要るので、"options_page": "options.html" を設定、また chrome.storage を使うので "permissions": ["storage"] もつけておきます

options.html

特に工夫も何もなく、 input タグで入力欄を作っていくだけになります

入力欄一例
<div class="form">姓(漢字): <input type="text" class="inputForm narrowForm" id="lastNameJPKanji" value="">   名(漢字): <input type="text" class="inputForm narrowForm" id="firstNameJPKanji" value=""></div>

「念のため」を考えて、使うと使わないとに関わらず、名前(フル・姓・名/漢字・ひらがな・カタカナ)、電話番号(固定・携帯/ハイフン無し・ハイフンあり)など、さらに英語表記の場合など色々用意しました
後から考えればハイフンとか空白とかで区切るときは、出力時に split とか使えばいいだろ、とか、こんな沢山入力項目いらんだろ(実際、後のoptions.jsやcontents.jsまで含めて、作るの面倒だった)とかありましたが、まあ実践ということでいいか、と流します

挙動(保存・読み出し・クリア)の発火のために、ボタンも作ってだいたい完成です
(配置若干乱れてる箇所がありますが、自分用である+今回の主題ではないということで目をつぶります)

ボタン押した時の挙動は、options.htmlで動くための挙動ということでoptions.jsに書いていきます

options.js

ここで、まずデータの保存である chrome.storage.local.set の実践になります

dataset一例
function dataSet () {
    chrome.storage.local.set(
  {
    "ProfilesJP": {
        "FullName": {
            "Kanji": document.getElementById("fullNameJPKanji").value,
            "Hiragana": document.getElementById("fullNameJPHira").value,
            "Katakana": document.getElementById("fullNameJPKana").value
        },
        "LastName": {
            "Kanji": document.getElementById("lastNameJPKanji").value,
            "Hiragana": document.getElementById("lastNameJPHira").value,
            "Katakana": document.getElementById("lastNameJPKana").value
        },
...
    });
}

基本的にはこんな漢字で、JSON型データ(オブジェクト)の各キーを定め、そのキーに対し : で区切って保存するデータ(今回はinputタグの入力フォーム内に書いたものなので、 getElementById で入力フォームを指定し .value で中身を指定する)を当てはめる形になります
この関数を「Save」ボタンを押した時に働かせたいので、「Save」ボタンに addEventListener clickdataSet が発火するように設定しておきます

続いて、保存したデータの読み出しのために chrome.storage.local.get の実践です
ここでは、保存したデータを読み出して各入力フォームに表示する(データの確認のため)挙動を作ることで学んでいきます

savedDataLoad一例
function savedDataLoad (){
    chrome.storage.local.get("ProfilesJP", function (value) {
        document.getElementById("fullNameJPKanji").value = value.ProfilesJP.FullName.Kanji;
        document.getElementById("fullNameJPHira").value = value.ProfilesJP.FullName.Hiragana;
        document.getElementById("fullNameJPKana").value = value.ProfilesJP.FullName.Katakana;

        document.getElementById("lastNameJPKanji").value = value.ProfilesJP.LastName.Kanji;
        document.getElementById("lastNameJPHira").value = value.ProfilesJP.LastName.Hiragana;
        document.getElementById("lastNameJPKana").value = value.ProfilesJP.LastName.Katakana;
...
    });
}

データロードを行なう chrome.storage.local.get 自体は上記のように、第一引数に読み込みたいデータのキー(上記だと "ProfilesJP" )を設定することになります(多分 set した時の一番上のキーのどれか?)
第二引数に function (value) (あるいは (value) => とアロー関数でも良さそう)を設定して、この関数の中でデータが使用できることになります
データ自体の読み出しとしては、 value (第二引数の関数の引数として設定したもの)からピリオドで区切り、保存したデータのキーを書いていくことでデータを指定できますが、この際 value の直後に来るものとして、 chrome.storage.local.get 自体の第一引数に指定したキーを入れないとちゃんと指定できません

読み込めなかった例
document.getElementById("fullNameJPKanji").value = value.FullName.Kanji;
読み込めた正答例
document.getElementById("fullNameJPKanji").value = value.ProfilesJP.FullName.Kanji;

ここで value だけで最初のキーの代わりのようになると誤解して、 value.FullName.Kanji のように書いたところ「そんなデータないぞ」とエラー出されて少し詰まりました

この関数についても、対応する「Load」ボタンを押した時に働かせたいので、 addEventListener clicksavedDataLoad が発火するように設定しておきます

上記のコードについて、一部のみ設定して、単体テストと結合テストの中間のようなことを実行します

ちゃんと入力したデータを保存した後、入力内容を消去してから読み出すと、入力内容が復活しているので動作確認完了です
すべてのデータに対して同じように設定し、options.jsを完成させます
(時折id名やキー名を間違える凡ミスをちょくちょくやらかし、名付けのてきとーさを後悔)

contents.js

ちゃんと挙動ができたということで、今度は適用先のページで動かすためのcontents.jsを用意しておきます
やること自体はoptinos.jsで行なったデータ読み込みと同じなので、先に作成した savedDataLoad と同じように設定するだけです
実際のページを確認して入力フォームのclass名やname名(今回の適用先と考えているページのフォームにはidが設定されてなかった)で指定し、そこに value.~ を設定していきます

このとき、発火については対象ページのみにしておきたいこと、各ページでフォームの形式やclass名などが統一されていないであろうこと(A社のアンケートフォームとB社のアンケートフォームで入力フォームの数や形式が全然違うなんてザラなので)を考えて、URLで入力フォームを判別し、その際に適した入力が行なわれるように、URLの中身で異なる入力用関数が動くようにします(今回は1つだけ)
また、場合によっては「やっぱ手で入力したい」となるであろうことも考えて「自動」で働くように、入力用関数の最初に window.confirm で自動入力するかどうかを選べるようにします

実際に挙動を確かめた結果がこのような形

「自動入力しますか?」にOKと応えることで、入力対象の情報が入っていることが確認できました

学び

  • chrome.storage.local.set( ) の中身にJSON型で記述することで、データを保存できる
  • chrome.storage.local.get("キー名", function (value) { }); の中身に value.キー名 と書くことで、データの読み出しができる
    • value.キー名 について、最初のキー名は一番上のキー名(chrome.storage.local.getの第一引数に入れたもの)でなければならない
  • window.confirm の実践

実用

目的の一つとして、対象アンケートフォームへの入力(半)自動化もあったので、早速使用してみたところ、しっかりフォームが埋められ、回答速度がだいぶ早くなりました
ただ、同じ会社のアンケートフォームの中でも、質問内容によってはフォームの数やname名が異なり適切に埋められないことも何度か
完全にするには事前にフォーム数を見るとか、フォームの手前に書いてある記述名とかで判別する必要がありそうです(今回は要件的にそこまでしなくていいのでやらない)

実用に向く拡張機能が作れるようになったことで、自分の作業も効率化できそうになってきました
似たような機能持つChrome拡張はすでにあると思いますが、学習も目的の一つなので車輪の再発明おおいに結構!
自分用に最適化できるようカスタマイズしていけそうです