別タブで開いた画面にopenerを渡すことによって引き起こされるパフォーマンス問題について

投稿日: August 1, 2022

openerとブラウザプロセス

タイトルの通り、Webアプリケーションにおいて遷移先画面に opener (遷移元画面 Window への参照) を渡してしまうと、アプリケーションのパフォーマンス問題を招く可能性がある。JavaScriptの window.open における windowFeatures、あるいはHTMLのanchor要素における rel 属性の設定を考えたことのある人なら、おそらく一度はぶつかる話である。

今に始まった話ではないので、この件に関してWebで検索すれば日本語のものも含めいくつも情報を見つけることができる。その中の多くは以下の記事を元ネタにしている。

一部抄訳して引用すると、

ほとんどのブラウザは、Firefox を除いて(そして彼らはそれに取り組んでいます)、マルチプロセスです。各プロセスには、私たちがよく「メイン」スレッドと呼ぶものを含め、複数のスレッドがあります。ここでは、構文解析、スタイル計算、レイアウト、ペイント、および非ワーカー JavaScript が実行されます。つまり、あるドメインで動作するJavaScriptは、別のドメインで動作するウィンドウ/タブとは別のスレッドで実行されます。

しかし、DOM が window.opener を介して与える同期的なクロスウィンドウ アクセスにより、target="_blank" で起動したウィンドウは同じプロセスおよびスレッドで終了します。iframeやwindow.openで開かれたウィンドウも同様です。

最初、この記事が執筆されたのが2016年だったので、もしかすると今では状況も変わっているのではないかと期待したが、手元で検証してみたところ2022年の現在でも状況は変わっていなかった。

ということで、引き続きアプリケーションのパフォーマンスを考えるのであれば (セキュリティリスクを無視できるとしても) opener は渡すなというのが結論になるのであるが、今後いつ状況が変わるかわからない(あるいは永遠に変わらないかもしれない)ので、いつでもサクッと状況を確認できるように検証アプリを用意した。

検証アプリ

検証アプリは以下にデプロイしている。

https://yukiyokotani.github.io/performance-impact-of-passing-window-opener/

アプリの中には3つのボタンがある。

  • Open in New Tab
    新しいタブで画面を開く。このとき、新しいタブには遷移元から opener が渡るようになっている。
  • Exclusive thread for 10 seconds
    ループで10秒間スレッドを専有するようにしている。実行すると処理の間、自タブは勿論 opener を通じて Window への参照を共有している他タブもレンダリングがストップする。
  • +1
    カウンターのインクリメントボタン。レンダリングがストップしていることを確認するためにある。ループがスレッドを専有している間はクリックしても反応しない。

ソースコードは以下のリポジトリに上げている。

補足:なぜopenerを渡したいか

opener を渡さなければいけないケースというのは実際のところかなり少ないとは思うが、あえて渡すとしたら以下のようなケースが考えられる。

  • 別タブで開いた遷移先windowから遷移元windowの内容を参照したい
  • 別タブで開いた遷移先windowから遷移元windowへ window.postMessage() でメッセージを送りたい。

パフォーマンス問題が起きた場合、もし遷移元と遷移先が同一オリジンであれば BroadcastChannel API を利用したメッセージのやり取りで問題は回避できると思われる。ただ、遷移元と遷移先でオリジンが異なる場合、 opener を渡さずに (つまりパフォーマンス問題を回避して) 要件を達成する方法を自分は知らない。

補足:openerの渡し方

遷移先に opener を渡す方法、あるいは渡さない方法について簡単にまとめておく。

window.open

window.open で遷移する場合のopenerの指定は引数の windowFeature によって行う。デフォルトでは opener が渡るので、渡したい場合は特に何もしなくて良い。渡したくない場合は noopener もしくは noreferrer を設定する必要がある。この両者の違いは遷移先に referrer を渡すかどうかなので、それをもとに判断すればよく、両方設定する必要はない。

動作確認する際は、遷移先のコンソールで document.referrer を見てみればよい。ちなみに opener についてもコンソールで window.opener を見れば、opener が渡っているかを簡単に確認することができる。

https://html.spec.whatwg.org/multipage/window-object.html#apis-for-creating-and-navigating-browsing-contexts-by-name

anchor要素

target='_blanck' の場合が問題になる。Chromiumは target='_blanck' の場合にデフォルトで opener を渡さないようになっているので、渡したくない場合は何もしなくて良い。渡したい場合は rel=opener で明示的に設定する必要がある。

https://html.spec.whatwg.org/multipage/links.html#link-type-opener


share on...