1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

iframe内でvuex-persistedstateを使ったページが動作しない場合がある件

Posted at

本記事はパロニム Advent Calendar 2021の13日目の記事です。

概要

webStorage (localStorage, sessionStorage) を使うページが、シークレットウィンドウ(プライベートウィンドウ)内のiframeで読み込まれた時に、以下のような例外が出てしまって上手く動作しない場合があります。

Chromeの場合

SecurityError Failed to read the 'sessionStorage' property from 'Window': Access is denied for this document.

Safariの場合

SecurityError The operation is insecure.

Vue + vuex + vuex-persistedstate で作っていたページでこの問題が起きていたので対応を考えます。

原因

いきなり結論ですが、ブラウザのプライバシー設定が関係しており、サードパーティのCookieをブロックする設定になっているときに発生します。

Chrome

設定 → プライバシーとセキュリティ → Cookieと他のサイトデータ
「サードパーティーのCookieをブロックする」 or 「シークレットモードでサードパーティのCookieをブロックする」 or 「すべてのCookieをブロックする」

Safari

設定 → プライバシーとセキュリティ
「すべてのCookieをブロック」

この設定が有効になっているとき、iframe内で window.localStorage を参照しただけで例外が出てしまいます。
( sessionStorage でも同様)

特にChromeでは「シークレットモードでサードパーティのCookieをブロックする」がデフォルト設定になっているため、
シークレットウィンドウ(プライベートウィンドウ)のiframe内で発生するパターンが大半です。

vuex-persistedstate では永続化のためのストレージとしてデフォルトで localStorage が内部で利用されるため、そのままだと動作しなくなってしまいます。
永続化は無理だとしてもせめて例外によって動作が止まるのはなんとかしたいところです。

対応案

window.localStorage を参照出来るかチェックし、出来ない場合は localStorage を利用しないようにします。

初期化時オプションの storage で利用ストレージを指定出来るのでここでlocalStorageが利用出来ない場合はMapを使ったダミーに差し替えます。
もちろん永続化は出来ないものの、止まることは無くなります。

import { createStore } from 'vuex'
import createPersistedState from 'vuex-persistedstate'
export default createStore({
  //...,
  plugins: [createPersistedState({
    storage: (() => {
      try {
        if (window.localStorage) {
          return window.localStorage
        }
      } catch (e) {
        console.log("localStorage is unavailable.")
      }

      const map = new Map();
      return {
        getItem: function (key) {
          const value = map.get(String(key))
          return typeof value !== 'undefined' ? value : null
        },
        setItem: function (key, value) {
          map.set(String(key), String(value))
        },
        removeItem: function (key) {
          map.delete(String(key))
        },
      }
    })()
  })]
})

ただ、localStorageが使える場合はシークレットウィンドウでもウィンドウを閉じるまでは書き込んだ内容が保持されるのに対し、このダミーはページ移動(リロード含む)で消えてしまうので全く同じとはならないので注意です。

要件上厳しいのであれば一番最初に明示的にユーザーに利用不可と伝えてエラーとしてしまうのも検討しましょう。

ここまで書いておいてなんですが、pluginsにcreatePersistedState()を渡さないようにするでも良いですね。

最後に

iframeでなければシークレットウィンドウでも問題が出なかったので気付くのが割と遅くなってしまいました、反省。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?