Chrome Extensions(機能拡張)の開発でちょっとハマった部分です。あるWebサイト上のデータを取得して、それをlocalStorageに保存して、それをChrome Extensions上で取得して利用しようと思ったのですが、localStorageの仕様上別のスコープとなりそれが無理だということがわかりました。調べてみると、Chrome ExtensionsのAPIを使ってbackground.js上でlocalStorageを使うことで解決できることがわかりました。今回は、その辺りのことをまとめておきます。
目次
localStorageとは
まず最初にlocalStorageについて説明します。すでにlocalStoregaについて把握されている方は読み飛ばしてください。
ブラウザは、Web Storageというクライアントサイドのストレージ(記憶装置)を持っています。localStorageは、その「Web Storage」を実装しているブラウザのwindowオブジェクトのプロパティで、Web Storageに対してデータの読み書きなどを行う機能を提供しています。
localStorageには以下のような特徴があります。
- 保存されたデータは永続的。Webアプリケーションがデータを削除するか、ユーザーがブラウザにデータを削除するように指示するまでデータは保存され続けます。
- ドキュメントの出身ごとにスコープを持つ。下で詳しく述べますが、localStorageは、プロトコル、ホスト名、ポート番号によってスコープが異なります。スコープが異なると、データの取得、上書きができません。
- 文字列keyと文字列valueにマッピングするkey-value型の連想配列。扱いが簡単です。
- 大容量。ブラウザによっても異なりますが、保存容量は5MB〜10MBとなります。
localStorageの詳細は以下をご参照ください。
localStorage APIについて
localStorageは、通常のJavaScriptオブジェクトと同じように扱うことができます。プロパティを設定してデータを保存し、プロパティを読み出してデータを取得します。
上記の方法でもlocalStorageを扱えますが、以下のような公式のAPIも定義されています。
プロパティ
- localStorage.length
- localStorageオブジェクトに保存されているデータの数(integer)を返します。
メソッド
- localStorage.key(n)
- 引数に数字「n(number)」が指定された時に、localStorageのn番目のkeyの名前を返します。
- localStorage.getItem(key)
- 引数にkeyの名前が指定された時に、そのkeyの値を返します。
- localStorage.setItem(key, value)
- 引数にkeyの名前と値が指定された時に、localStorageにkeyとそのvalue(値)を追加します。すでに存在していた場合は、そのkeyのvalue(値)を更新します。
- localStorage.removeItem(key)
- 引数にkeyの名前が指定された時に、localStorageからそのkeyを削除します。
- localStorage.clear()
- localStorageのすべてのkeyを削除します。
APIの使用例
以下はlocalStorage APIの使用例となります。
[参考]
ちなみにlocalStorageに保存したデータはDevelopertoolsの「Resources」の部分で確認できます。データの追加、更新、削除などもここで行うことが可能です。
Chrome ExtensionsでlocalStorageを使う際の問題点
localStorageの話がだいぶ長くなりましたが、これから本題となります。上記の通り、localStorageの特徴を考慮すると、ちょっとしたWebのアプリケーションを作るのであれば、保存領域として使うのにlocalStorageで十分なケースが多いんじゃないかと思います。特にブラウザ上で動くChrome Extensionsであれば、かなり親和性も高いです。Chrome Extensionsの開発でlocalStorageを使という話はよく聞きます。
ただここで注意が必要です。上記でも述べましたが、localStorageは「ドキュメントの出身によってスコープが異なる」ということです。Chrome Extensions内で完結するものであれば、特に問題はないと思いますが、ブラウザで表示しているWebサイトから取得したデータをlocalStoregeに保存して、Chrome Extensionsでそのデータを取得したり、またその逆でChrome Extensionsで保存したデータをブラウザで表示しているWebサイト上で取得したりすることができません。
localStorageのスコープについて、ここで少し詳しく説明しておきます。localStorageはドキュメントの出身(プロトコル、ホスト名、ポート番号)によってスコープが異なります。ドキュメントの出身が異なると、データを取得したり、上書きしたりすることができません。例えば、以下のURLはすべてドキュメントの出身が異なることになります。
- http://www.example.com
- https://www.example.com
- http://hoge.example.com
- http://www.example.com:8080
つまり、Chrome ExtensionsとWebサイト間でのデータのやり取りがそのままではできない仕様となっています。
データの橋渡し用にbackground.jsを使う
上記のスコープの問題を解決する方法として、Chrome Extensionsに用意されているbackground.jsを使ってChrome ExtensionsとWebサイト間のデータの橋渡しをさせるようにします。
background.jsはその名の通り、Chrome Extensionsのバックグラウンドで実行されるスクリプトファイルとなります。
background.jsを使う場合は、manifest.jsonに以下のように記載します。
ちなみに、上記のmanifest.jsonの例では、browser_actionとして‘popup.html’、content_scriptsとして‘https://example.com/*’と指定しています。つまりこのpopup.htmlとhttps://example.com/間でlocalStorateのデータのやり取りを行うのにbackground.jsを使うということになります。
肝心のbackground.jsをどのように使うかというと、Webサイト上(content_scripts上)で行いたいlocalStorageへの命令を、すべてこのbackground.jsで行うようにします。localStorageのスコープはバックグラウンドページとして形成されるので、 browser_action(popup.html)からは直接localStorageを扱えるようになります。Webサイト(content_scripts)とbackground.js間のやり取りはChrome Extensionsのchrome.runtime APIを使って行うようにします。
chrome.runtime APIでメッセージのやり取りを実装
chrome.runtime APIはbackgroundページを検索したり、manifestの詳細な情報を返したり、Webアプリやエクステンションのライフサイクルの中でのイベントの購読や応答を行うために使うAPIです。
ここでのchrome.runtime APIの用途は以下のとおりとなります。
Webサイト上(content_scripts上)からメッセージを送信
↓
backgroundページでそのメッセージを受け取りレスポンスを返す
↓
Webサイト上(content_scripts上)でレスポンスを受け取りコールバックを実行
background.jsにおけるchrome.runtime APIの実装
まずbackground.jsから処理を書いていきます。Webサイト上(content_scripts上)から受け取るメッセージごとに実行する処理を登録しておくイメージとなります。Webサイト上(content_scripts上)から送られたメッセージを受け取るには、chrome.runtime.onMessage.addListener(callback)メソッドを使います。メッセージが送られるとcallback関数を実行します。
上記で説明したlocalStorageのAPIのプロパティやメソッドを実行するために、以下のようにそれぞれ名前をつけて登録しておきます。
chrome.runtime.onMessage.addListener()メソッドの引数’request’に送られたメッセージが格納されます。つまりメッセージには、background.jsで実行すべき処理の名前と、その処理に必要な値を含めておく必要があります。それから引数’sendResponse’で、content_scriptsにデータを返すようになっています。background.jsで処理した値を名前をつけて’sendResponse’にセットするようにします。
content_scriptsにおけるchrome.runtime APIの実装
content_scripts(Webサイト)では、background.jsに登録した処理名とその処理に必要な値をchrome.runtime.sendMessage()メソッドの引数にセットして、localStorageに処理を加えたい箇所で実行するようにします。
background.jsでの処理の結果は、chrome.runtime.sendMessage()メソッドのコールバック関数の引数で受け取ることができます。受け取った値をコールバック関数内で処理することになります。
browser_actionからlocalStorageを扱う場合
browser_action(default_popupに指定したhtmlファイル)からは、直接localStorageにアクセスすることができるので、chrome.runtime APIを使う必要はありません。普通にlocalStorage APIを使ってデータの取得、更新、削除を行ってください。
まとめ
以上、特に難しいことをしないのであれば、chrome.runtime.onMessage.addListener()メソッドとchrome.runtime.sendMessage()メソッドを使った処理をそれぞれbackground.jsとcontent_scripts(Webサイト)に書くだけでChrome ExtensionsでlocalStorageを使えるようになります。background.jsというファイルを用意したり、一見複雑になりそうな感じもしますが、実際に実装してみると意外とこんなものかというくらいあっさり実装できてしまうと思います。
上でも述べましたが、Chrome ExtensionsとlocalStorageは親和性がとても高いです。Chrome Extensionsの開発でlocalStorageを使えるようになったら、開発できるプロダクトの幅も広がってくるので、ぜひここは抑えておきたいところだと思います。ぜひ参考にしてもらえらばと思います。
以下の通称サイ本は若干古いですが、localStorageについてもしっかり書かれていて参考になります。最後に紹介しておきます。
コメント