localStorageだとオプションでunlimitedStorageを指定していても5MB以上は保存できないみたいで、App cacheやWebSQLを使う必要があるとか

調べてるとchrome.storageでもunlimitedStorageは使えるようなのでこれを使うことにしました
データ形式はlocalStorage同様に単純ですが非同期で実行されるので扱いがめんどうです

chrome.storageはchromeの拡張機能用のデータを保存する機能です
データ保存用の機能はlocalStorage, sessionStoage, webSQL, IndexedDBなどがありますがlocalStorageに近いです

英語のマニュアル


localStorageとの違い

○keyとvalueを保存できてlocalStorage自体を1つのオブジェクトのように扱えましたが、保存できるvalueは文字列のみで、保存時にJSON.stringify、読み出し時にJSON.parseをかける必要がありました
chrome.storageではこれを自動でやってくれるのでオブジェクトなど好きな型で保存できます

○ページ内に埋め込んで実行するcontent_scriptsでは、localStorageは拡張機能ではなくページ内のlocalStorageのデータを読み込みます
拡張機能のlocalStorageからデータを読み出す場合はバックグラウンドページを通す必要があります
chrome.storageだと、content_scriptsでも拡張機能で保存したデータを読み出せます

○シークレットモードを使う時にspanningとsplitのモードがあって、プロセス間でデータ共有できるかみたいな違いがあるようです(詳しく知らない)
chrome.storageだとどっちのモードでもシークレットモードじゃない時と同じようにデータにアクセスできます

保存や読み込みの関数が非同期

○localStorageではunlimitedStorageを指定していても5MBまでしか保存できません
chrome.storage.localではunlimitedStorageで5MB以上のデータを保存できます


良いところはオブジェクトのまま保存できるところ、良くないところは非同期になるところ

下はaというキーの値を持ってきてanyprocess関数を実行してその結果をbに保存する例です

localStorage
var a = JSON.parse(localStorage["a"]);
var b = anyprocess(a);
localStorage["b"] = JSON.stringify(b);
// 次の処理 

chrome.storage
chrome.storage.local.get("a",function(a){
    var b = anyprocess(a);
    chrome.storage.local.set({b:b}, function(){
        // 次の処理
    }
})

localStorageだと上から順にみていけばいいので読みやすいですが、chrome.storageだとどんどん関数の中に入っていくので見づらいです

これだけだとまだシンプルですが長くなってくるとコールバック関数だらけで自分でも流れの把握が難しいです
クライアントJavaScriptなのにnodejs書いてる気分…

自分の読みやすさだけなら慣れればまだなんとか、なのですが非同期実行だとchromeのエラー表示まで不親切になります
どこでエラーがあったかがすごくわかりにくいです
このせいでlocalStorageで書いていたコードをchrome.storageに直すのにかなり苦労しました

あと、最適化されてるのかもしれませんが、毎回関数を実行してるのって遅い気がして気になります
CだとO3オプションとか高速化のために関数をinline化してるようですし


chrome.storageの種類

chrome.storageには3種類あります

chrome.storage.sync
chrome.storage.managed
chrome.storage.local

sync
syncはchromeの同期で一緒にデータも同期してくれます
ネット上に送っても大丈夫なデータだけにしたほうが良さそうです

同期する分、データサイズや書き込み回数に制限があるので保存するのはオプションくらいにしておいてコンテンツ系はあまり保存しないほうが良いです
データサイズは現時点では全部で100KiB1つあたりは最大4KiBアイテム数は最大512個です

managed
chrome33から入った新しい機能で、書き込めるのはドメイン管理者だけで、拡張機能では読み込みだけできる保存場所です

ドメイン管理者はどうやってここに書き込むのか や 使用例はどんなところ なのかはよくわかりません
新しい分まだあまり活用されてないのかな

local
ローカルに保存する用
普段は5MiBですがunlimitedStorageオプションで無制限にできます


使い方

機能の数は少なめです
基本chrome.storage.localと書きますがsyncならchrome.storage.syncです

メソッド

get

chrome.storage.local.get("aaa", function(result){
    console.log(aaa, result.aaa);
});
結果は、2つ目の引数に渡す関数の1つ目の引数にオブジェクト型では入ります
valueだけじゃないので、アクセスするには result.aaa のように一手間必要です
aaaがなければresult.aaaはundefinedです

複数のkeyのvalueが欲しい時は1つ目の引数を配列にします
chrome.storage.local.get(["aaa","bbb","ccc"],function(result){
    console.log(result.aaa, result.bbb, result.ccc);
});
resultにはkeyの分だけ要素があります

getの結果で、必要なキーのデータだけあるlocalStorageが返ってくるような感じです

また、1つ目の検索クエリをオブジェクト形式で渡すとデータが無い場合のデフォルト値を設定できます
chrome.storage.local.get({aaa:1,bbb:2}, function(result){
    console.log(result.aaa, result.bbb);
});
aaaにデータがあればその値、なければ1がセットされたオブジェクトがresultに入っています

設定のデフォルト値を設定するのに向いてます
バックグラウンドのページの起動時に、すでにデータがあればそのまま、なければ値を設定する例です
chrome.storage.local.get({opt1: true, opt2:false, opt3: 4}, function(result){
    chrome.storage.local.set(result);
});
opt1のデフォルト値がtrue、opt2のデフォルト値がfalse、opt3のデフォルト値が4の場合です
すでに他の値が入っていればその値になるのでsetで変更されるのはデータがないところだけになります

1つ目の引数を省略すれば全部のデータが返って来ます
chrome.storage.local.get(function(allresult){
console.log(allresult);
});

set

chrome.storage.local.set({a:1, b:2}, function(){
    // ここにset終わったあとの処理
});
setは1つ目の引数で渡したオブジェクトのkeyとvalueをストレージに追加します
渡したもので置き換えるのではないです

getBytesInUse

chrome.storage.local.getBytesInUse(["aaa","bbb"], function(bytes){
    console.log(bytes+"バイト使用してます");
});
1つ目の引数にkeyの配列かkeyの文字列を渡します
コールバック関数にはgetで取ってきたデータのサイズが入ります
配列で複数指定してると合計値になります
デフォルト値に意味は無いのでオブジェクトは渡せません

syncを使う時にはサイズ制限あるので有用かも

remove

chrome.storage.local.remove(["aaa","bbb"], function(){
    // 削除後にしたい処理
});
これもkeyか配列を渡してkeyが一致したデータを削除します

clear

全部のデータを削除します
全部消すのでremoveの1つ目の引数がいらない版です


setSchemaというメソッドも有りましたが、特に使い道はなさそうです
ソースコードがJavaScriptで読めたので見てみましたが、chrome.storage.localなどの中にあるfunctionSchemasを書き換えるものでした
functionSchemasはgetやclearなどのメソッドの引数の型や数などを書いてあるだけでここを書き換えても特に良いことがあるようにも思えません


イベント

イベントは「変更があった時」だけです
localとかsyncごとに書くんじゃなくて全体で設定します
引数は関数1つだけで、その関数の2番目の引数にlocalとかが入ってるのでそこで判定します
chrome.storage.onChanged.addListener(function(changes,ns){
    // nsにsyncやlocalが入ってる
    // changesは変更があったキーのオブジェクト
});
changesはこんな形式です
{
    aaa: {
        oldValue: 100,
        newValue: 200
    },
    bbb: {
        newValue: 10
    }
}
何も入ってないところにデータを入れるとoldValueがなかったりしますが、アクセスするとundeifnedが帰ってくるので問題無く使えます


感想

コールバック関数の山がつらいです
他の関数と同じように結果を代入するように書いたら、非同期処理が終わって結果が代入されてからその後の処理を続けるようにして欲しいんですけどー


ecmascript6ではyield使って関数を途中から実行できるから普段通りの書き方に近い方法で書けるらしいとどこかでみたけどいつになったらecmascript6がchromeでつかえるようになるんだろう