サードパーティオリジンの LocalStorage の挙動を調べてみた

こんにちは。花金だ! きたけーです。

サードパーティオリジンの LocalStorage の挙動をChromeSafariで調べてみたのでメモ。

やりたいこと

iframeで別オリジンのページを読み込み、元のページからiframeのwindowに値を送ってLocalStorageに値を保存したい。

検証用のコード

元のページ

  • ロード時に LocalStorage に指定されたキーの値が存在するか調べ、なければ値(日付と乱数)を生成して、LocalStorageに格納、画面に表示する
  • ボタンを押すと、iframeで読み込んだページに ↑で生成、格納した値をPostMessageで送る
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>parent</title>
  <script>
    var key = "test_item";
    var value = "";

    window.onload = function () {
      var resultElm = document.getElementById('result');
      value = localStorage.getItem(key);
      if (!value) {
        value = [(new Date()).toString(), Math.floor(Math.random() * 10e12)].join('\t');
        localStorage.setItem(key, value);
      }

      resultElm.innerHTML = value;
    };
    var sendValue = function () {
      var iframeElm = document.getElementsByTagName('iframe')[0];
      iframeElm.contentWindow.postMessage(value, 'http://bar.dev:3000');
    };
  </script>
</head>
<body>
  <div id="result"></div>
  <iframe src="http://bar.dev:3000/child.html" frameborder="0"></iframe>
  <button onclick="sendValue()">送る</button>
</body>
</html>

iframeで読むこむページ

  • PostMessageで送られた値をLocalStorageに指定されたキーで格納、画面に表示する
  • ロード時に LocalStorage に指定されたキーで格納された値を表示する
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>parent</title>
  <script>
    var key = "test_item";

    window.onload = function () {
      var resultElm = document.getElementById('result');
      resultElm.innerHTML = localStorage.getItem(key);
    }

    window.addEventListener("message", function (event) {
      var resultElm = document.getElementById('result');
      if (event.origin !== "http://foo.dev:3000") {
        return;
      }
      localStorage.setItem(key, event.data);
      resultElm.innerHTML = event.data;
    });
  </script>
</head>
<body>
  <div id="result"></div>
</body>
</html>

Chromeの場合

バージョンは 38.0.2125.111

表示はこんなかんじ。 PostMessageで値を送ってLocalStorageに保存した後に、「リロード」、「新しくウィンドウやタブを開いてページにアクセスする」、「ブラウザを起動し直す」などをしても、LocalStorageに格納された値は消えないことが分かる。

f:id:kitak:20141107094347p:plain

Safariの場合

バージョンは 7.0.6 (9537.78.2)

これがSafari7以降だと、おもしろい挙動になる。PostMessageで値を送って、LocalStorageに保存する処理が走った後にも関わらず、WebインスペクタでLocalStorageの内容が空っぽになっている。

f:id:kitak:20141107095007p:plain

リロードした場合は、iframeのほうで値を表示されるが、ウィンドウやタブを新しく作成してアクセスすると、iframeのほうでは値が表示されない。

どうやら、Safari7.0以降(また、iOS7 の Safari)では、サードパーティオリジンの WebStorage (LocalStorage, SessionStorage) に対するポリシーが変更されたらしく、LocalStorageはSessionStorageになるらしい。 (refs: webkit ではサードパーティドメインの localStorage が sessionStorage になる - Please Sleep)

じゃあ、SessionStorageに値が格納されているのか、と思って、調べてみたら空っぽだった。「SessionStorageになる」というよりは「格納期間の単位がセッションになる」というほうが正確かもしれない。

f:id:kitak:20141107094952p:plain

ちなみに、この挙動はSafariの「Cookie とその他の Web サイトのデータをブロック」の設定によるものでデフォルトの「知らないサイトや広告のみ」だと↑で説明した挙動、「しない」にすると↑のChromeでの挙動と同じになる。Chromeには「サードパーティCookie とサイト データをブロックする」(デフォルト オフ)があり、Chromeサードパーティオリジンの LocalStorageの挙動もこの設定のオン・オフで変わるかもしれない(ちょっと時間がないので今回はこれまで)。