サードパーティの JavaScript を読み込む

Arthur Evans

コードを最適化してもサイトの読み込みが遅い場合は、サードパーティ スクリプトが原因である可能性があります。

サードパーティ スクリプトには、ウェブをより動的でインタラクティブにし、相互接続できるようにするさまざまな便利な機能が用意されています。中には、ウェブサイトの機能や収益源に不可欠なものもあります。ただし、使用にはリスクがあります。

  • サイトのパフォーマンスが低下する可能性があります。
  • プライバシーセキュリティの問題を引き起こす可能性があります。
  • 予測できない動作や、意図しない結果が生じる可能性があります。

サードパーティ スクリプトがサイトのクリティカル レンダリング パスに影響していないことを確認することをおすすめします。このガイドでは、サードパーティの JavaScript の読み込みに関連する問題を特定して修正し、ユーザーへのリスクを最小限に抑える方法について説明します。

サードパーティ スクリプトとは

サードパーティの JavaScript は、サードパーティ ベンダーから直接任意のサイトに埋め込むことができるスクリプトを指します。次に例を示します。

  • ソーシャル メディアの共有ボタン(Facebook、X、LinkedIn、Mastodon)

  • 動画プレーヤーの埋め込み(YouTube、Vimeo)

  • 広告 iframe

  • アナリティクスと指標のスクリプト

  • テスト用の A/B テスト スクリプト

  • ヘルパー ライブラリ(日付のフォーマット設定、アニメーション、関数ライブラリなど)

YouTube 動画の埋め込みの例
次のコードを使用してページに追加できる YouTube 動画埋め込みの例。
<iframe
  width="560"
  height="315"
  src="https://www.youtube.com/embed/mo8thg5XGV0"
  frameborder="0"
  allow="autoplay; encrypted-media"
  allowfullscreen
>
</iframe>

残念ながら、サードパーティ スクリプトを埋め込むと、多くの場合、そのスクリプトを頼りにページの速度低下を防いでいます。サードパーティ スクリプトは、サイト所有者の管理範囲外のリソースが原因でパフォーマンスが低下する一般的な原因です。たとえば、次のような問題があります。

  • 複数のサーバーに対してネットワーク リクエストが多すぎる。サイトが行うリクエストが多いほど、読み込みに時間がかかります。

  • JavaScript を送りすぎて、メインスレッドがビジー状態になる。JavaScript が多すぎると、DOM の構築がブロックされ、ページのレンダリングが遅れる可能性があります。CPU 使用率の高いスクリプトの解析と実行により、ユーザー操作が遅れ、バッテリーの消耗が早まる可能性があります。

  • サイズの大きな最適化されていない画像ファイルや動画を送信すると、データ量が増加し、ユーザーの費用が増加する可能性があります。

  • ページでスクリプトを慎重に読み込まないと、単一障害点(SPOF)として機能するセキュリティの問題。

  • HTTP キャッシュが不十分で、ブラウザがリソースを取得するためにネットワーク リクエストをさらに送信しなければならない。

  • 十分なサーバー圧縮がないと、リソースの読み込みが遅くなります。

  • 処理が完了するまでコンテンツの表示をブロックします。これは、非同期 A/B テスト スクリプトにも当てはまります。

  • document.write() など、ユーザー エクスペリエンスに悪影響を及ぼすことが知られているレガシー API の使用。

  • 過剰な DOM 要素や高価な CSS セレクタ。

  • 複数のサードパーティ埋め込みを追加すると、複数のフレームワークとライブラリが複数回読み込まれ、リソースが浪費され、既存のパフォーマンスの問題が悪化する可能性があります。

  • サードパーティ スクリプトでは、埋め込み手法が使用されることが多く、埋め込みで非同期または遅延を使用している場合でも、サーバーの応答が遅いと window.onload がブロックされる可能性があります。

サードパーティ スクリプトの問題を修正できるかどうかは、サイトと、サードパーティ コードの読み込み方法を構成できるかどうかによって異なります。幸い、サードパーティ リソースに関する問題を特定して修正するためのソリューションやツールは多数あります。

ページ上のサードパーティ スクリプトを特定するにはどうすればよいですか?

サイト上のサードパーティ スクリプトを特定し、パフォーマンスへの影響を判断することが、最適化への第一歩です。負荷の高いスクリプトを見つけるには、Chrome DevToolsPageSpeed InsightsWebPageTest などの無料のウェブ速度テストツールを使用することをおすすめします。これらのツールには、サイトで使用しているサードパーティ スクリプトの数や、実行に最も時間がかかっているスクリプトなど、豊富な診断情報が表示されます。

WebPageTest のウォーターフォール ビューでは、サードパーティ スクリプトの使用量が多い場合にその影響をハイライト表示できます。Tags Gone Wild の次の画像は、トラッキング スクリプトとマーケティング スクリプトではなく、サイトのメイン コンテンツを読み込むために必要なネットワーク リクエストの例を示しています。

ウェブページテストのウォーターフォール ビュー。実際のウェブサイトとトラッキング スクリプトの読み込みに要した時間が表示されています
このページのスクリプトの読み込みには、ページ自体よりも時間がかかります。

WebPageTest のドメインの内訳は、サードパーティ オリジンから取得されるコンテンツの量を可視化するためにも役立ちます。合計バイトとリクエスト数の両方で分類されます。

ドメイン別のコンテンツの内訳(初回視聴)。サードパーティごとのリクエストとバイトの割合が表示されます。
ドメイン別の内訳グラフは、ページのコンテンツのうちサードパーティから提供されているものの割合を示します。

サードパーティ スクリプトがページに及ぼす影響を測定する方法

スクリプトが問題を引き起こしている場合は、スクリプトの内容を確認し、サイトの機能に必要かどうかを判断します。必要に応じて A/B テストを実施し、ユーザーの認識価値と、主要なユーザー エンゲージメントやパフォーマンス指標への影響を比較します。

Lighthouse の起動時間の監査

Lighthouse の JavaScript 起動時間の監査では、スクリプトの解析、コンパイル、評価に時間がかかっているスクリプトがハイライト表示されます。これにより、CPU 使用率の高いサードパーティ スクリプトを特定できます。

スクリプトの評価と解析のサポートを示す Lighthouse
起動時間の監査では、読み込みに最も時間がかかるスクリプトを確認できます。

Lighthouse ネットワーク ペイロード監査

Lighthouse のネットワーク ペイロード 監査では、ページの読み込み時間を遅らせ、ユーザーがモバイルデータに予想以上の費用を費やす原因となるサードパーティのネットワーク リクエストなどのネットワーク リクエストを特定できます。

大規模なネットワーク ペイロードのサポートを示す Lighthouse
ネットワーク ペイロード監査では、最も時間がかかり、最も多くのデータを取得するネットワーク リクエストを確認できます。

Chrome DevTools のネットワーク リクエストのブロック

Chrome DevTools を使用すると、指定したスクリプト、スタイルシート、その他のリソースが使用できない場合にページがどのように動作するかを確認できます。これは、ネットワーク リクエストのブロックによって行われます。この機能は、ページから個々のサードパーティ リソースを削除した場合の影響を測定するのに役立ちます。

リクエストのブロックを有効にするには、[ネットワーク] パネルで任意のリクエストを右クリックし、[リクエスト URL をブロック] を選択します。すると、DevTools の引き出しにリクエストのブロックタブが表示され、ブロックされたリクエストを管理できるようになります。

DevTools のネットワーク パネルからリクエスト URL をブロックする
個々のネットワーク リクエストをブロックして、リクエストなしでページがどのように動作するかを確認します。

Chrome DevTools の [パフォーマンス] パネル

Chrome DevTools の [パフォーマンス] パネルを使用すると、ページのウェブ パフォーマンスに関する問題を特定できます。

  1. [録音] をクリックします。
  2. ページを読み込みます。DevTools には、サイトの読み込み時間の使用状況を示すウォーターフォール グラフが表示されます。
  3. [パフォーマンス] パネルの下部にある [ボトムアップ] に移動します。
  4. [プロダクト別にグループ化] をクリックして、ページのサードパーティ スクリプトを読み込み時間で並べ替えます。
(サードパーティ)プロダクト別にグループ化されたボトムアップ ビューを示す DevTools の [パフォーマンス] パネル
サードパーティ スクリプトがプロダクト別に、読み込み時間が長い順に並べられています。

Chrome DevTools を使用してページの読み込みパフォーマンスを分析する方法については、ランタイム パフォーマンスの分析を始めるをご覧ください。

サードパーティ スクリプトの影響の測定に推奨されるワークフローは次のとおりです。

  1. [Network] パネルを使用して、ページの読み込みにかかる時間を測定します。
    • 実際の状況をエミュレートするには、ネットワーク スロットリングCPU スロットリングを有効にすることをおすすめします。ユーザーは、ラボ環境下で高価なスクリプトの影響が軽減されるような高速なネットワーク接続とデスクトップ ハードウェアを備えている可能性は低いです。
  2. 問題と思われるサードパーティ スクリプトに対応する URL またはドメインをブロックします(コストの高いスクリプトの特定に関するガイダンスについては、Chrome DevTools のパフォーマンス パネルをご覧ください)。
  3. ページを再読み込みして、読み込み時間を再度測定します。
    • より正確なデータを取得するには、読み込み時間を少なくとも 3 回測定することをおすすめします。これは、一部のサードパーティ スクリプトがページの読み込みごとに異なるリソースを取得することを考慮しています。そのため、DevTools のパフォーマンス パネルでは複数の録画がサポートされています。

WebPageTest でサードパーティ スクリプトの影響を確認する

WebPageTest では、[詳細設定] > [ブロック] で、個々のリクエストの読み込みをブロックしてその影響を測定できます。この機能を使用すると、広告ドメインなど、ブロックするドメインのリストを指定できます。

WebPageTest の詳細設定 < ブロック。ブロックするドメインを指定するテキスト領域が表示されます。
このパネルで、ブロックするドメインをリストします。

この機能を使用するには、次のワークフローをおすすめします。

  1. サードパーティをブロックせずにページをテストする。
  2. 一部のサードパーティをブロックしてテストを繰り返します。
  3. [テスト履歴] から 2 つの結果を選択します。
  4. [比較] をクリックします。
2 つのレポートを比較できる比較オプションが表示された WebPageTest
比較する負荷テストの結果を選択します。

次の画像は、アクティブなサードパーティ リソースがあるページとないページの読み込みシーケンスを比較した、WebPageTest のフィルムストリップ機能のものです。個々のサードパーティ オリジンのテストでは、この値を確認して、ページのパフォーマンスに最も影響を与えているドメインを特定することをおすすめします。

サードパーティを無効にしたときと有効にしたときのサイトの読み込みの影響を示す WebPageTest フィルムストリップ
サードパーティ リソースのブロックの影響(Andy Davies 著の Using WebPageTest To Measure The Impact Of Third-Party Tags より)。

WebPageTest は、DNS レベルで動作し、ドメインをブロックする次の 2 つのコマンドもサポートしています。

  • blockDomains は、ブロックするドメインのリストを受け取ります。
  • blockDomainsExcept は、ドメインのリストを取得し、リストにないものをすべてブロックします。

WebPageTest には、単一障害点(SPOF)タブもあり、タイムアウトやリソースの読み込みの完全な失敗をシミュレートできます。ドメイン ブロッキングとは異なり、SPOF は徐々にタイムアウトするため、サードパーティ サービスに負荷がかかっている場合や一時的に使用できない場合にページの動作をテストするのに役立ちます。

WebPageTest の詳細設定 > SPOF > 失敗するホスト
SPOF テスト機能を使用して、指定したドメインの障害をシミュレートします。

Long Tasks を使用して負荷の高い iframe を検出する

サードパーティの iframe 内のスクリプトの実行に時間がかかると、メインスレッドがブロックされ、他のタスクが遅延する可能性があります。このような長いタスクにより、イベント ハンドラの動作が遅くなったり、フレームがドロップされたりして、ユーザー エクスペリエンスが低下する可能性があります。

リアルユーザー モニタリング(RUM)で長時間実行タスクを検出するには、JavaScript の PerformanceObserver API を使用して longtask エントリをモニタリングします。これらのエントリには、処理に時間がかかる原因となっているフレーム コンテキストを特定するために使用できる attribution プロパティが含まれています。

次のコードは、longtask エントリをコンソールにログに記録します。これには、「高負荷」の iframe のエントリも含まれます。

<script>
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Attribution entry including "containerSrc":"https://example.com"
      console.log(JSON.stringify(entry.attribution));
    }
  });

  observer.observe({entryTypes: ['longtask']});
</script>

<!-- Imagine this is an iframe with expensive long tasks -->
<iframe src="https://example.com"></iframe>

長時間タスクのモニタリングの詳細については、ユーザー中心のパフォーマンス指標をご覧ください。

サードパーティ スクリプトを効率的に読み込むにはどうすればよいですか?

サードパーティ スクリプトによってページの読み込み速度が遅い場合は、パフォーマンスを改善するためのいくつかの方法があります。

  • ドキュメントの解析をブロックしないように、async 属性または defer 属性を使用してスクリプトを読み込みます。
  • サードパーティ サーバーが遅い場合は、スクリプトをセルフホスティングすることを検討してください。
  • スクリプトがサイトに明確な価値をもたらさない場合は、削除します。
  • <link rel=preconnect><link rel=dns-prefetch> などのリソース ヒントを使用して、サードパーティ スクリプトをホストするドメインの DNS ルックアップを実行します。

async または defer を使用します。

JavaScript の実行はパーサー ブロックです。ブラウザがスクリプトに遭遇すると、DOM 構築を一時停止し、スクリプトを JavaScript エンジンに渡して、スクリプトの実行を許可してから DOM 構築を続行する必要があります。

async 属性と defer 属性は、この動作を次のように変更します。

  • async を使用すると、ブラウザは HTML ドキュメントの解析を続行しながら、スクリプトを非同期でダウンロードします。スクリプトのダウンロードが完了すると、スクリプトの実行中に解析がブロックされます。

  • defer を使用すると、ブラウザは HTML ドキュメントの解析を続行しながらスクリプトを非同期でダウンロードし、ドキュメントの解析が完了するまでスクリプトの実行を待機します。

スクリプトがクリティカル レンダリング パスに必要な場合を除き、サードパーティ スクリプトには常に async または defer を使用してください。一部のアナリティクス スクリプトなど、読み込みプロセスの早い段階でスクリプトを実行することが重要な場合は、async を使用します。defer は、ユーザーが最初に表示するよりもページの下部にレンダリングされる動画など、重要度の低いリソースに使用します。

パフォーマンスが主な懸念事項である場合は、ページの重要なコンテンツが読み込まれるまで非同期スクリプトを追加しないことをおすすめします。jQuery などの重要なライブラリに async を使用することはおすすめしません。

一部のスクリプト(特にサイトの重要な部分を構成するスクリプト)は、async または defer なしで読み込む必要があります。たとえば、サイトが機能するために必要な UI ライブラリやコンテンツ配信ネットワーク(CDN)フレームワークなどです。

他のスクリプトは、非同期で読み込まれると機能しません。使用しているスクリプトのドキュメントを確認し、非同期で読み込めないスクリプトを、読み込める代替スクリプトに置き換えます。サードパーティによっては、スクリプトが非同期で同じように機能する場合でも、同期で実行することを推奨している場合があります。

async ですべての問題が解決するわけではありません。広告目的のトラッキング スクリプトなど、ページに多数のスクリプトがある場合、それらを非同期で読み込んでも、ページの読み込みが遅くなることはありません。

リソースヒントを使用して接続のセットアップ時間を短縮する

ネットワーク リクエストには DNS ルックアップやリダイレクトなど、複数の複雑なコンポーネントが含まれているため、サードパーティのオリジンへの接続の確立には時間がかかります。特に、低速ネットワークでは時間がかかります。 などのリソース ヒントを使用すると、ページ読み込みプロセスの早い段階でサードパーティ スクリプトをホストするドメインの DNS ルックアップを実行できるため、残りのネットワーク リクエストを後でより迅速に処理できます。

<link rel="dns-prefetch" href="http://example.com" />

接続先のサードパーティ ドメインが HTTPS を使用している場合は、 を使用することもできます。これは、DNS ルックアップを実行し、TCP ラウンドトリップを解決して TLS ネゴシエーションを処理します。これらの他のステップは SSL 証明書の検証が含まれるため、非常に時間がかかり、事前接続を行うと読み込み時間が大幅に短縮されます。

<link rel="preconnect" href="https://cdn.example.com" />

iframe を使用した「サンドボックス」スクリプト

サードパーティ スクリプトを iframe に直接読み込むと、メインページの実行はブロックされません。AMP では、この方法を使用して JavaScript をクリティカル パスから除外します。このアプローチでは onload イベントがブロックされるため、重要な機能を onload に関連付けないようにしてください。

Chrome は、権限ポリシー(旧称: 機能ポリシー)もサポートしています。これは、デベロッパーが特定のブラウザ機能へのアクセスを個別に無効にできる一連のポリシーです。これにより、サードパーティのコンテンツがサイトに望ましくない動作を導入するのを防ぐことができます。

サードパーティ スクリプトをセルフホストする

DNS 時間を短縮したり、HTTP キャッシュ ヘッダーを改善したりなど、重要なスクリプトの読み込み方法をより細かく制御したい場合は、自分でホストすることもできます。

ただし、セルフホスティングには、特にスクリプトの更新に関して独自の問題があります。セルフホスト スクリプトには、API の変更やセキュリティ修正の自動更新は行われません。そのため、スクリプトを手動で更新するまで、収益の損失やセキュリティの問題が発生する可能性があります。

別の方法として、サービス ワーカーを使用してサードパーティ スクリプトをキャッシュに保存し、ネットワークからスクリプトを取得する頻度をより細かく制御することもできます。また、サービス ワーカーを使用して、ページが重要なユーザー モーメントに達するまで、不要なサードパーティ リクエストをスロットリングする読み込み戦略を作成することもできます。

少数のユーザーを対象に A/B テストを実施する

A/B テスト(または分割テスト)は、ページの 2 つのバージョンをテストして、ユーザー エクスペリエンスとユーザー行動を分析する手法です。ページのバージョンをウェブサイト トラフィックのさまざまなサンプルで使用できるようにし、コンバージョン率が高いバージョンをアナリティクスから特定します。

ただし、A/B テストは、どのテストを有効にするかを判断するためにレンダリングを遅らせるように設計されています。JavaScript は、ユーザーが A/B テストに属しているかどうかを確認し、正しいバリエーションを有効にするためによく使用されます。このプロセスにより、テストに参加していないユーザーでもエクスペリエンスが低下する可能性があります。

ページのレンダリングを高速化するには、A/B テスト スクリプトをユーザーベースの小さなサンプルに送信し、表示するページのバージョンを決定するコードをサーバーサイドで実行することをおすすめします。

サードパーティ リソースを遅延読み込みする

広告や動画などの埋め込みサードパーティ リソースは、適切に作成されていないと、ページの読み込み速度の低下を招く可能性があります。遅延読み込みを使用すると、埋め込みリソースを必要なときにのみ読み込むことができます。たとえば、ユーザーがスクロールして広告が表示されるまで、ページの下部に広告を配信しないようにします。サードパーティのコンテンツは、メインページのコンテンツの読み込み後、ユーザーがページを操作する前に遅延読み込みすることもできます。

折り返しのコンテンツに不可欠なアセットと、それほど重要ではなく遅延読み込みできるアセットを示すイラスト。
ページの読み込み時にユーザーにすぐに表示されないアセットを遅延読み込みできます。

リソースを遅延読み込みする際は注意が必要です。多くの場合、不安定なネットワーク接続の影響を受ける可能性がある JavaScript コードが関与します。

DoubleClick の公式ドキュメントでは、広告を遅延読み込みする方法について説明しています。

Intersection Observer による効率的な遅延読み込み

従来、要素がレイジー読み込みのためにビューポートに表示されているかどうかを検出する方法はエラーが発生しやすく、ブラウザの速度が低下することが多々ありました。これらの非効率的な方法では、多くの場合、スクロール イベントまたはサイズ変更イベントをリッスンし、getBoundingClientRect() などの DOM API を使用して、要素がビューポートに対してどこにあるかを計算します。

IntersectionObserver は、ページ所有者が、監視対象の要素がブラウザのビューポートに移動したときやビューポートから移動したときに効率的に検出できるブラウザ API です。LazySizes には、IntersectionObserver のオプション サポートもあります。

遅延読み込みの分析

アナリティクス スクリプトの読み込みを長時間遅らせると、貴重なアナリティクス データを失う可能性があります。幸い、ページの初期読み込みデータを保持しながら、アナリティクスを遅延初期化する戦略があります。

Phil Walton のブログ投稿「The Google Analytics Setup I Use on Every Site I Build」では、Google アナリティクスのこのような戦略の 1 つについて説明しています。

サードパーティのスクリプトを安全に読み込む

このセクションでは、サードパーティ スクリプトをできるだけ安全に読み込むためのガイダンスについて説明します。

document.write() を避ける

サードパーティ スクリプト(特に古いサービスの場合)は、document.write() を使用してスクリプトを挿入して読み込むことがあります。これは、document.write() の動作が不安定で、障害のデバッグが困難であるため問題です。

document.write() の問題を解決するには、使用しないことです。Chrome 53 以降では、document.write() の使用に問題がある場合、Chrome DevTools はコンソールに警告を記録します。

document.write() を使用したサードパーティ埋め込みの違反をハイライト表示する DevTools コンソールの警告
Chrome DevTools で document.write() の使用が報告される。

このエラーが表示された場合は、ブラウザに送信された HTTP ヘッダーを探して、サイトの document.write() の使用状況を確認できます。Lighthouse では、document.write() をまだ使用しているサードパーティ スクリプトもハイライト表示されます。

Lighthouse のベスト プラクティス監査で document.write() の使用が報告される
document.write() を使用しているスクリプトを示した Lighthouse レポート。

タグ マネージャーを慎重に使用する

タグとは、デジタル マーケティング チームがデータの収集、Cookie の設定、ソーシャル メディア ウィジェットなどのサードパーティ コンテンツのサイトへの統合を可能にするコード スニペットです。これらのタグは、ページにネットワーク リクエスト、JavaScript の依存関係、その他のリソースを追加するため、ページのパフォーマンスに影響する可能性があります。タグを追加するほど、ユーザーへの影響を最小限に抑えることは難しくなります。

ページの読み込みを高速に保つには、Google タグ マネージャー(GTM)などのタグ マネージャーを使用することをおすすめします。GTM では、タグを非同期でデプロイできるため、タグ同士が互いの読み込みをブロックしないようにできます。また、タグの実行にブラウザが行うネットワーク コールの数を減らし、データレイヤ UI でタグデータを収集できます。

タグ マネージャーの使用に伴うリスク

タグ マネージャーはページの読み込みを効率化するように設計されていますが、不用意に使用すると、次のような理由で読み込みが遅くなる可能性があります。

  • タグ マネージャーのタグと自動イベント リスナーの数が多すぎると、ブラウザが必要以上に多くのネットワーク リクエストを送信し、コードがイベントにすばやく応答できなくなります。
  • 認証情報とアクセス権を持つユーザーであれば誰でも、タグ マネージャーに JavaScript を追加できます。これにより、ページの読み込みに必要な費用のかかるネットワーク リクエストの数が増えるだけでなく、不要なスクリプトによるセキュリティ リスクやその他のパフォーマンスの問題が発生する可能性があります。こうしたリスクを軽減するため、タグ マネージャーへのアクセスを制限することをおすすめします。

グローバル スコープを汚染するスクリプトは使用しない

サードパーティ スクリプトは、予期せずページを破損させるさまざまな動作をする可能性があります。

  • JavaScript 依存関係を読み込むスクリプトによって、コードと悪影響を及ぼすコードがグローバル スコープに混入する可能性があります。
  • 予期しない更新により、互換性のない変更が発生する可能性があります。
  • サードパーティ コードは転送中に変更され、ページのテストとデプロイで動作が異なる場合があります。

不正行為をチェックするために、読み込むサードパーティ スクリプトの定期的な監査を行うことをおすすめします。自己テスト、サブリソースの完全性、サードパーティ コードの安全な送信を実装して、ページの安全を確保することもできます。

緩和戦略

サードパーティ スクリプトがサイトのパフォーマンスとセキュリティに与える影響を最小限に抑えるための大規模な戦略をいくつかご紹介します。

次の例は、CSP の script-src ディレクティブを使用して、ページで許可される JavaScript ソースを指定する方法を示しています。

// Given this CSP header Content-Security-Policy: script-src
https://example.com/ // The following third-party script will not be loaded or
executed

<script src="https://not-example.com/js/library.js"></script>

関連情報

サードパーティの JavaScript の最適化について詳しくは、以下をご覧ください。

レビューをいただいた Kenji Baheux、Jeremy Wagner、Pat Meenan、Philip Walton、Jeff Posnick、Cheney Tsai に感謝します。