Polymerでローカルストレージによる複数タブの同期を行いたい場合に注意すること

## Polymer でローカルストレージを使う

[Polymer](https://www.polymer-project.org/) でローカルストレージを利用したい場合、[app-storage](https://www.webcomponents.org/element/PolymerElements/app-storage) の `app-localstorage-document` タグを利用します。

:warning: [iron-localstorage](https://www.webcomponents.org/element/PolymerElements/iron-localstorage)は現時点で `deprecated` になっています。

`app-localstorage-document` タグはローカルストレージに変更があったとき `data-changed` というイベントを発火します。
このイベントがきたら、 `event.detail.value` もしくは、タグの `data` 属性から値を取得することができます。

例えば以下のように記述します。

```html
<dom-module id="demo">
  <template>
    <app-localstorage-document id="storage" key="hoge"></app-localstorage-document>
  </template>

  <script>
    Polymer({
      is: 'demo',
      listeners: {
        'data-changed': '_dataChanged',
      },
      setValue: function(value) {
        this.$.storage.set('data', value);
      },
      _dataChanged: function(event) {
        console.log(event.detail.value);
      },
    });
  </script>
</dom-module>
```

`demo` エレメントを使ってブラウザを複数のタブで開くと、`setValue` したタブの値が、他のタブではコンソールに出力されます。

## 現状

この実装を様々なブラウザ（最新版）で試してみたところ、下記のような状況でした。

| Chrome | Firefox | Safari | IE11 | Edge |
|:------:|:-------:|:------:|:----:|:----:|
| OK     | OK      | OK     | NG   | NG   |

[app-localstorage-document タグの実装](https://github.com/PolymerElements/app-storage/blob/master/app-localstorage/app-localstorage-document.html)を確認してみると、以下のようになっています。

```js:app-localstorage-document.html#L82
      attached: function() {
        this.listen(window, 'storage', '__onStorage');
        this.listen(
            window.top,
            'app-local-storage-changed',
            '__onAppLocalStorageChanged');
      },
```

`window.onstorage` イベントでローカルストレージの変更を受け取って、`__onStorage` を呼び出しています。

```js:app-localstorage-document.html#L182
      __onStorage: function(event) {
        if (event.key !== this.key ||
            event.storageArea !== this.storage) {
          return;
        }
        this.syncToMemory(function() {
          this.set('data', this.__parseValueFromStorage());
        });
      },
```

`__onStorage` では、`__parseValueFromStorage` を呼び出しています。

```js:app-localstorage-document.html#L204
      __parseValueFromStorage: function() {
        try {
          return JSON.parse(this.storage.getItem(this.key));
        } catch(e) {
          console.error('Failed to parse value from storage for', this.key);
        }
      },
```

`__parseValueFromStorage` では、 `this.storage` で実際のローカルストレージから値を取得しています。

MS系のブラウザでは、`window.onstorage` イベントのタイミングでは、ストレージの内容は書き換わっていないようです。
（実際に開発者ツールを使って確認すると、イベント受信時はローカルストレージの値は古いままだったのですが、イベント処理が終わったあとで確認すると、新しい値になっていました。）

## 対応

https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onstorage
のサイトを確認すると、イベントオブジェクトには `newValue` と `oldValue` が設定されていることがわかりました。

実際にその値をチェックすると、IE11やEdgeでも変更された値が入っていることがわかりました。
なので、先ほどの例を簡単に書き換えると、以下のようになります。

```html
<dom-module id="demo">
  <template>
    <app-localstorage-document id="storage" key="hoge"></app-localstorage-document>
  </template>

  <script>
    Polymer({
      is: 'demo',
      setValue: function(value) {
        this.$.storage.set('data', value);
      },
      _dataChanged: function(event) {
        console.log(event.newValue);
      },
      ready: function() {
        this.listen(window, 'storage', '_dataChanged');
      }
    });
  </script>
</dom-module>
```
これでIE11やEdgeでも値の同期を行うことができるようになります。
ただし、このコードには問題があって、ローカルストレージのキーのチェックなどはしていないので、前述の `__onStorage` のようにチェックは必要になると思います。

## issue を書いた

上記内容を issue にして上げておいたので、修正されると良いなーと思っています。
（時間があったらPRしたいとも思っている）

https://github.com/PolymerElements/app-storage/issues/110