» Mozilla Hacks ブログ翻訳

Developer Edition 46 – メモリツールと @media サイドバーの改良。そして

この記事は 2016 年 2 月 2 日に投稿された “Developer Edition 46 – More memory tooling, improved @media sidebar and more” の抄訳です。

Firefox Developer Edition 46 がリリースされました!このバージョンでは、メモリ使用状況のプロファイリングに有用な新機能を追加しました。また既存の機能も改良されています。この記事では、それらの中のうち大きなものを取り上げて紹介します。

メモリツール:ドミネータビュー

アプリのメモリ使用に関するデバッグとプロファイリングを支援するツール。それがメモリツールです。そのツールに、今回のリリースで新しいビューが追加されました。追加されたドミネータビューはオブジェクトのメモリアロケーションの分析に役立ちます。シャローサイズの列には表示されるオブジェクトの大きさが、保持サイズの列には正しくアロケートされた被参照オブジェクトのサイズが、それぞれ累積値と共に表示されます。これを見ることで、それぞれのオブジェクトの全体のメモリ使用量に対する影響度を計れます。またこのビューには、特定のオブジェクトを生成している箇所の素早い特定を助ける機能も持っています。ドミネイタービューに関する詳しい説明はこちらをごらんください。

ドミネータビューのスクリーンショット:
ドミネイタービュー

パフォーマンスツールと GC に対するプロファイリング

パフォーマンスツールも機能が追加され、パフォーマンス記録時にメモリ割り当てを記録できるようになりました。この機能は GC の実行頻度を減らし、アプリの応答性向上に役立ちます。設定の歯車アイコンをクリックし、「メモリ割り当てを記録」にチェックを入れることで、この機能を利用できます。

メモリ割り当てビューのスクリーンショット:
メモリ割り当てビュー

Emscripten 関数名を復元する昨日

Emscripten を用いてネイティブコードをコンパイルすると、コンパイラによって関数名が元のものから変更されてしまいます。今回のリリースで、プロファイラのコールツリーから C 言語での関数名を確認できるようになりました。この機能を使えば、簡単に元のソースコードとの対応をとることができるようになります(開発記録)。

スタイルエディタとメディアクエリ

Firefox の開発ツールには、レスポンシブデザインビューと呼ばれる機能があります。画面サイズの変化に対応できるレスポンシブなサイトの開発を助ける機能です。この機能をメディアクエリサイドバーと一緒に利用すると、現在の画面サイズで適用されているメディアルールを確認できます。今回のリリースで、レスポンシブデザインビューの画面サイズの変更にあわせて、このメディアクエリサイドバーの内容が更新されるようになりました。この機能の開発記録はこちらでご覧になれます。

メディアクエリサイドバーの動作例:
メディアクエリサイドバーが、レスポンシブデザインビューの画面の変化に合わせて更新されている

常にデバッガを起動

開発ツールを開いた状態で debugger 文を実行すると、デバッガが起動し、該当箇所で処理を停止します。これはデバッガパネルが非アクティブの状態でも動作します。詳細は James Long のブログポストをご覧ください。

その他の改善点

上述した以外にも、多くの点が改善されました。主だったものは以下の通りです:

  • メモリツールに取得したヒープのスナップショットを削除する機能が追加されました(開発記録
  • 色調が暗いページでインスペクタのインフォバーが見やすくなるように、コントラストを調整しました(開発記録
  • メモリツールのコールスタック中のテキストが選択できるようになりました(開発記録
  • 大量のログが非同期に追加された場合における console.log の性能が向上しました(開発記録
  • ストレージインスペクタで扱える要素数に上限がなくなりました(開発記録

Developer Edition のリリースに貢献いただいた皆様に感謝いたします。またインストールして、感想をお聞かせください。

Tim Nguyen について

Firefox の開発ツールのコントリビュータ。Web 開発と Web デザインに情熱を持つ。

Twitter アカウント:@therealntim

Tim Nguyen による他の記事

Brian Grinstead について

Firefox の開発ツールを開発するエンジニア。

Tim Nguyen による他の記事

メインスレッド以外での WebGL の利用方法

この記事は 2016 年 1 月 22 日にポストされた “WebGL Off the Main Thread” の抄訳です。

Firefox 44 以降で、WebGL を Web Workers で利用出来るようになりました!OffscreenCanvas API を利用することで、メインスレッド以外で利用可能な WebGL コンテキストを作成できるようになります。

以下の例を実行するためには、Firefox 44 以降が必要です。現在のところ Firefox Developer Edition もしくは Firefox Nightly が該当します(訳注:記事執筆当時。現在はリリース版の Firefox でも利用可能です)。また API は利用するためには about:config から gfx.offscreencanvas.enabled の値を true に設定する必要があります。なお、この機能は ANGLE がサポートされるまで Windows では利用できません。AlteredQualia が指摘するように、Windows の Firefox Nightly 46 ではこの機能が利用出来るようになりました。ああ、恥ずかしい。

ユースケース

これは、初めてユーザに表示されているものの変更をメインスレッド以外のものに許可する API です。描画はメインスレッドの状態とは関係なく行われます。詳細はワーキンググループで策定中の仕様をごらんください。

コードの変更点

私の WebGL に関する講演で利用した、基本的な WebGL アニメーションを例に解説します。このメインスレッドで描画を行っているコードを、Worker を利用するように変更してゆきましょう。

変更対象の WebGL コードによるアニメーション

最初に WEBGL コンテキストの作成から描画関数の呼び出しまでを別ファイルに分離します。

<script src="gl-matrix.js"></script>
<script>
  // main thread
  var canvas = document.getElementById('myCanvas');
  ...
  gl.useProgram(program);
  ...

上記のコードが以下のように変更されます:

// main thread
var canvas = document.getElementById('myCanvas');
if (!('transferControlToOffscreen' in canvas)) {
  throw new Error('webgl in worker unsupported');
}
var offscreen = canvas.transferControlToOffscreen();
var worker = new Worker('worker.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);
...

HTMLCanvasElement.prototype.transferControlToOffscreen を呼び、コンテキストを新しく作成したワーカスレッドに移譲している点に注目してください。transferControlToOffscreenHTMLCanvasElement ではなく、OffscreenCanvas のインスタンスを返します。HTMLCanvasElement と同様、offscreen.clientWidthoffscreen.clientHeight のような属性にはアクセスできませんが、offscreen.widthoffscreen.height のような属性にはアクセス可能です。postMessage の第 2 引数を通じて、変数の所有権を別スレッドに移します。

ここからはワーカスレッドでの作業になります。canvas 要素を引き渡すメインスレッドからのメッセージを受信するまで、WebGL コンテキストの作成できません。WebGL コンテキストの取得、バッファの作成と初期化、属性や uniform の設定と取得、そして描画といったコードの変更は必要ありません。

// worker thread
importScripts('gl-matrix.js');

onmessage = function (e) {
  if (e.data.canvas) {
    createContext(e.data.canvas);
  }
};

function createContext (canvas) {
  var gl = canvas.getContext('webgl');
  ...

OffScreenCanvas によって、WebGLRenderingContext.prototypecommit メソッドを追加されます。このメソッドを呼ぶことで描画内容を、 WebGL コンテキストによって利用されている OffscreenCanvas の生成元である canvas 要素に反映させられます。

アニメーションの同期

次にアニメーションについて取り上げます。requestAnimationFrame による同期を postMessage 経由でワーカスレッドに伝達します。

// main thread
(function tick (t) {
  worker.postMessage({ rAF: t });
  requestAnimationFrame(tick);
})(performance.now());

ワーカ側の onmessage 関数は以下のようになります:

// worker thread
onmessage = function (e) {
  if (e.data.rAF && render) {
    render(e.data.rAF);
  } else if (e.data.canvas) {
    createContext(e.data.canvas);
  }
};

requestAnimationFrame による繰り返しを設定する代わりに、gl.commit() メソッドを呼ぶよう、render 関数を変更します。

// main thread
function render (dt) {
  // update
  ...
  // render
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.drawArrays(gl.TRIANGLES, 0, n);
  requestAnimationFrame(render);
};

このコードを、以下のように変更します:

// worker thread
function render (dt) {
  // update
  ...
  // render
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.drawArrays(gl.TRIANGLES, 0, n);
  gl.commit(); // new for webgl in workers
};

この手法に対する制約

上記のコードでは、requestAnimationFrame によって渡される値を利用した速度を適切に考慮したアニメーションを行っていません。速度を考慮するならフレームレートとは独立にアニメーションが行われますが、この例はそうでないため、フレームレートに依存したアニメーションとなっています。その点ではまだ問題があるコードとなっています。

JavaScript のガベージコレクション (GC) に起因する処理の一時停止を避けるため、メインスレッドから描画ロジックの実行を別スレッドに動かすこともあると思います。GC はメインスレッドによる requestAnimation フレームの呼び出しを遅らせてしまいます。requestAnimationFrame ループ中で呼び出される postMessage が契機となって、gl.drawArraysgl.commit は非同期的に呼び出されます。このループはメインスレッドで実行されるため、結果として描画はメインスレッド中の GC によってブロックされることとなります。ただし、ワーカスレッドの実行自体はメインスレッド中の GC によってブロックされません(少なくとも Firefox の SpiderMonkey バーチャルマシンでは)。

この問題を解決するためには、ワーカスレッド中での処理をより賢いものに変更する必要があります。そしてその解決策は、requestAnimationFrameWorker コンテキストで呼び出せるようにすることでしょう。実装状況はこちらのトラッキングバグで確認できます。

まとめ

requestAnimationFrame を Worker で利用できるようになるためには未だ開発が必要ですが、OffscreenCanvas API を利用することで、メインスレッドのブロックとは関係なく描画処理を行えるようになりました。既存の WebGL コードをワーカで実行するための変更は数分で済むことも示しました。animation.htmlanimation-worker.htmlworker.js を利用)をご覧いただければ、変更前と変更後を比較できます。

Nick Desaulniers について

オープン Web のために Mozilla で戦うソフトウェアエンジニア。Firefox OS に対するサードパーティ開発者を助けるのではなく、WebGL を利用したグラフィックスプログラミング、オープンソースへの貢献、そして Emscripten を利用した C / C++ コードの JavaScript へのトランスコンパイルについての講演を行う。多くのプログラミング言語、JIT コンパイル、JavaScript の高速化、グラフィックスプログラミングに多くの知見を持つ。オープンソースソフトウェアと、全ての人がインターネットを利用できるようにすることに情熱を傾けている。


Nick Desaulniers によるその他の記事はこちら

Google アナリティクスとプライバシー・イベントトラッキング

原文: Google Analytics, Privacy, and Event Tracking on January 27, 2016 by Stephanie Hobson

自分の web サイトをユーザがどのように利用したか確認するのに、Google アナリティクス (GA) のようなサードパーティ製のサービスを利用することが多いかと思います。このようなサービスによって、ユーザの訪問時刻やクリックの内容を知ることができ、得られたデータはメンテナンス時間や機能の削除など、重要な意思決定の際に役立つことがあります。

これらのサービスは様々な web サイトで利用されているため、ユーザがインターネットを利用するほど、サービスを運用するサードパーティはユーザの情報を多く集めることができます。しかし、すべてのユーザがこの仕組みに満足しているわけではありません。不満を持つ人の中には Ghostery などの広告ブロッカーをインストールしたり、Firefox のトラッキング保護機能 を有効にしたりすることで、自分のプライバシーを守る取り組みを始めています。

私はとても良いことだと思います!

web サイトに支障が出ることも

web サイトのコードがサードパーティ製サービスに依存していたり、そのサービスにアクセスできない場合、web サイトの動作に支障が出ることがあります。

以降の例では、よくあるユースケースとして外部リンクのトラッキングを取り上げています。しかし、GA のスクリプトを読み込まなかったユーザが、私たちの web サイト内で移動できなくなってしまったため(特定のエッジケースであり、字面ほど深刻ではありません)、私はこの問題を調査することになりました。

また、今回はユニバーサルアナリティクスのトラッキングに用いるコード (analytics.js) を使用しています(旧来のコード ga.js ではありません)。そのため、私のコードと皆さんのコードとに異なる部分があるかもしれません。

何が問題なのか?

Google が 2016 年 1 月現在推奨している アウトバウンドリンクのトラッキング 方法は以下のようなものです。

<a href="http://www.example.com"
   onclick="trackOutboundLink('http://www.example.com'); return false;">
        Check out example.com
</a>
var trackOutboundLink = function(url) {
    ga('send', 'event', 'outbound', 'click', url, {
        'transport': 'beacon',
        'hitCallback': function(){document.location = url;}
    });
}

Google が推奨している内容は次の通りです。

  1. クリック時にユーザがページから移動しないよう、リンクに return false を追加する
  2. ユーザがリンクをクリックした際には、トラッキングのためにクリック内容を GA のイベントキューに追加し、終了したらユーザを新しいページに移動させるよう GA に伝える
  3. 受け取ったイベントを GA が処理する
  4. コールバックを GA が呼び出し、ユーザを新しいページへ移動させる

この仕組みにより、ユーザの行動を記録するまでユーザを確実に web サイトへ留めることができます。しかし、GA がイベントの処理に失敗した場合は… コールバックは決して呼び出されず、web サイト上のリンクがすべて壊れてしまいます。:(

ユーザが新しいページに移動できるかは GA 次第なのですが、もし GA のコードが決して実行されなかったとしたら?

どうやって問題を回避するか

現在推奨されている方法で GA を web サイトに追加している場合、以下のようなコードがすべてのページに含まれているはずです。

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-xxxxx-x', 'auto');
ga('send', 'pageview');

このスニペットが生成したグローバルオブジェクト ga には、同じページに読み込まれた任意の JavaScript からアクセスできます。

これは以下の 2 つを意味します。

  1. GA が読み込まれる前にイベントをキューに追加できる(これなら早い段階で生じたイベントを簡単にトラッキングできますね!)
  2. これまでと同様 ga オブジェクトは常に存在しているので、オブジェクトの存在によって GA が読み込まれたか否かは判断できない

ならば GA が読み込まれたかどうかを知る術はあるのでしょうか?ええ、賢い人であれば GA が初期化時にグローバルオブジェクト ga を何通りかの方法で差し替えている事実に気づくでしょう。ただし、このことは Google の公式ドキュメントには何も書かれていないため、いずれは動作しなくなる可能性があります。

Google の Andreas Veithen 氏は自身のブログ記事 Tracking outbound links with Google Analytics で、ga オブジェクトが q という配列を持っているか否かを確認するのが最も信頼性がある、と提案しています。ga.q のキューで待機しているイベントすべてを GA が初期化時に処理したのち、この配列は削除されます。ga.q は先程のスニペットで作成される配列であり、そこに変更を加えると web サイトに影響を与えかねないため、Google の立場としては大きな変更と捉えるでしょう。対して、ga.q を削除しないように Google がコードを変更することもあり得ますが、これが他の方法よりも信頼性があるかどうかは分かりません。

John Morton 氏は自身のブログ記事 Google Analytics, Ghostery, and Event Tracking で、ga オブジェクトの create プロパティで判断することを提案しています。この手法であれば長く使えそうですし、おそらく最も広く使われている方法です(例えば GitHub でも利用されているようです)。

Dom Sammut 氏は自身のブログ記事 Workaround for when the hitCallback function does not receive a response (analytics.js) で、ga オブジェクトの loaded プロパティで判断できると述べています。この手法は Google のドキュメントでも 非公式に使われている節があり、いくらか信頼性があるかもしれません。確認している対象が明白なため、個人的にはこの方法が好みです。

最後の選択肢は、Google が推奨しているコードを以下のように書き換える方法です。

var trackOutboundLink = function(url) {
    // GA オブジェクトが存在し、かつ初期化されていることを確認する
    if(window.ga && ga.loaded) { {
        // 真ならば、リンクによる遷移を GA に任せる
        ga('send', 'event', 'outbound', 'click', url, {
            'transport': 'beacon',
            'hitCallback': function(){document.location = url;}
        });
    } else {
        // 偽ならば、リンクによる遷移を自分で行う
        document.location = url;
    }
}

サードパーティ製のスクリプトを利用する際は、どのようなスクリプトでも 上のような確認を行うのがグッドプラクティスです。また、自分のドメイン以外(CDN など)から読み込んだスクリプトに、ローカルのフォールバックも用意すべきでしょう。

ユーザのプライバシーを尊重する

ユーザの多くは自身のプライバシーに関心を持っていますが、プライバシーを保護する手段は他にもあります。ユーザのデータをより慎重に扱うには、国の定める法律や企業のプライバシーポリシーによる許可が勤務先によっては必要かもしれません。

プライバシーを意識した Google アナリティクスの設定

解析のプラットフォームとして Google アナリティクスを選択した場合、プライバシーを向上させる方法は数多くあります

設定が簡単なものの 1 つに、ユーザの IP アドレスを秘匿化するよう要求する方法があります。本質的には、Google アナリティクスが IP アドレスを記録する前に、アドレスの末尾に位置するオクテットを 0 に変更するものです

これを適用するには、トラッキングのスニペットにある ga('send', 'pageview'); の後に以下の行を追加します。

ga('set' 'anonymizeIp', true);

もちろん、Google はユーザの IP アドレスを依然と取得します(それが web の仕組みです)。この設定は IP アドレスを保存しないよう Google を信頼することだけです。

Do Not Track (DNT) を利用する

また、ユーザが Do Not Track を有効にしているか否かを確認し、読み込むスクリプトを積極的に切り替えることも可能です。

Schalk Neethling 氏は自身のブログ記事 Respect User Choice ~ Do Not Track でその仕組みを解説しています。要約すると、GA の呼び出し自体をラップすることで、ユーザが DNT を有効にしているかどうかの確認を GA を呼び出す前に行うというものです。

function _dntEnabled() {
    // ヘルパー関数を読み込む: https://github.com/schalkneethling/dnt-helper/blob/master/js/dnt-helper.js

}
// GA を読み込む前に DNT の値を確認する
if (!_dntEnabled()) {
    // include the Google Analytics snippet
}

こうすることで、ga オブジェクトがページのグローバルスコープに追加されなくなりますが問題はありません。その場合でも上記のスニペットは上手く動作します。

オンライン上におけるユーザのプライバシーを尊重しつつ解析サービスを利用する方法・提案は他にありますか?あなたのご意見を是非お聞かせください。

ビデオシリーズ:HTML5 でのゲーム開発

この記事は “HTML 5 game development video series” の抄訳です。

ゲーム開発に興味ありますか?HTML5 でのゲーム開発を始めるのにピッタリなビデオをまとめてご紹介します。

なぜ HTML5 で開発するのか?

最初のビデオは、Web 向けにゲームを開発するる理由について解説しています。流通時の摩擦や、がっちりしたマーケットプレースからの解放、開発ツールにおける選択の自由、利用可能な API といったテーマを解説しています。

Why an HTML 5 game: https://youtu.be/XjtlsAP6Oic

ゲームエンジンとライブラリ

2つ目のビデオでは、現在のゲームエンジンとライブラリを概観します。Phaser のような JavaScript のライブラリだけでなく、Unity のような HTML5 へ出力できるマルチプラットフォームなエンジンもカバーしています。

HTML 5 game dev – Engines and Libraries: https://youtu.be/KgI5IMFCZgM

Firefox 開発ツール

次は Firefox Developer Edition で利用可能な開発ツールについてです。これを利用することでゲームのデバッグやプロファイリングが可能です。コードをその場で修正し、異なるスクリーンサイズへの対応を行い、デバッグをし…といったことがすべて解説されています。

HTML 5 game development – Developer tools: https://youtu.be/3SmEzjR3jgk

TIPS と game jam

最後のビデオは、game jam 参加者にとって有用です。週末をかけてゲームを開発する大会である game jam。私もなんどとなく参加しる大会ですが、難しくそしてチャレンジしがいのあるものであるということを骨身に染みて知っています。このビデオで解説されている TIPS が助けになることを祈っています。

HTML 5 game development – Tips for game jams: https://youtu.be/f1P6vPHhlyY

HTML5 でゲームを作った時は、ぜひおしらせください。よろこんで遊ばさせていただきます!

Belén Albeza について

Mozilla のディベロッパーリーレーションズとして働くエンジニア兼ゲーム開発者。Web 標準、高品質なコード、アクセシビリティ、そしてゲーム開発に興味がある。

Belén Albeza による他の記事はこちら

SRI をビルドプロセスへ組みこもう

この記事は “How to implement SRI in your build process” の抄訳です。

顧客から「おたくのサイトがマルウェアを配布している」との連絡があった場合を想像してみてください。心臓は止まり、冷汗をかき、Tweet が溢れ始めるでしょう。

「あれ?おかしいぞ」

システムは汚染されていませんでした。

実際にハッキングされたのはWeb サイトで利用しているスクリプトを配布している CDN のプロバイダで、そのスクリプトがマルウェア化していたのでした。そこで顧客に事実を報告しましたが、彼らは気にしません。製品の安全性と、顧客からの信頼は失われてしまいました。これが 2 年前に起きたとしたら、

「気の毒なことだったね」

と言ったでしょう。しかし今ならこう言うでしょう:

「SRI を使うべきだ」

Subresource Integrity (SRI) は比較的新しい Web アプリケーションのセキュリティ標準であり、上記のような状況から身を守るための W3C 仕様です。SRI は Web サイトのコンテントセキュリティにおける、セーフティネットや命綱のようなものです。script タグや link タグに integrity 属性を追加し、その値に SHA-2 ハッシュ値を指定するだけで利用できます:

<script src="http://code.jquery.com/jquery-2.2.3.min.js" 
    integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo=" 
    crossorigin="anonymous">
</script>

SRI を利用するためには CDN で CORS が有効になっていなければなりません。詳しくは SRI を紹介する Hacks ブログの記事をご覧ください。

対応状況

対応しているファイル種別

SRI の第 1 バージョンは CDN で配布される Cascading Styles Sheets (CSS) や JavaScript (JS) ファイルに対するサブリソースハイジャッキングによる大規模な攻撃への対応策として設計されました。今の所 Flash や画像、動画などはサポートされていませんが、将来の改定で使用に追加されることが期待されます。

ブラウザの対応状況

2016 年 4 月時点の CanIUse.com によると、モバイル、デスクトップを合わせて 52% 程度のブラウザが SRI に対応しています。デスクトップでは、Firefox (44 以降)、Chrome (47 以降)、Opera (36 以降)が対応しています。モバイルでは次にあげる Android 上でのブラウザのみが対応しています: Chrome(49 以降)、Android Browser(49 以降)、Opera(36 以降)、Firefox(45 以降)

付記:SRI は iOS 版 Firefox などのブラウザでは利用できません。これはブラウザやアプリ内での Web 閲覧を含め WebKit をブラウザエンジンとして利用することを、全てのアプリに対して Apple が要求しているためです。詳しくはこちらのステータスアップデートをご覧ください…

SRI をビルドプロセスに組みこもう

SRI をビルドプロセスに組み込む方法はいく通りもあります。GruntGulpBroccoliWebpackEmber CLIHandlebar といったツールには、すでにプラグインが用意されています。

実世界でのビルドプロセスを考慮して、Grunt プラグインである grunt-sri をハッシュ値生成に利用することにしました。

jQuery を利用する場合

code.jquery.com に対して SRI を適用する場合、 前述した実装よりもシンプルな手法を取れます。すでに Grunt を利用している場合、grunt-sri を利用するように設定します。grunt-sri は指定されたファイルを捜査して、SRI を実装するために必要なメタデータを含む JSON ファイルを生成します。このファイルはデータリソースとして、アプリケーションをビルドする際に簡単に利用できます。grunt-sri を利用する簡単な例は、次のようになります:

// Gruntfile.js
grunt.loadNpmTasks("grunt-sri");
grunt.initConfig({
    sri: {
        generate: {
            src: [ 'public/**/*.js', 'public/**/*.css' ],
            options: { algorithms: [ 'sha256' ] }
        }
    }
});

次に grunt sri:generate を実行します。./payload.json が出力され、アプリケーションや他の Grunt タスクから利用できるようになります。生成された SRI 用の SHA ハッシュ値は、次のように利用できます。これは grunt-sri のドキュメントにも記載されています。

// ES6 from https://github.com/neftaly/grunt-sri#javascript
var payload = require("./payload.json");
var sri = (id) => payload.payload[id];

var element = `<link
    href='https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/style.css?cache=${ sri("@cssfile1").hashes.sha256 }'
    integrity='${ sri("@cssfile1").integrity }'
    rel='stylesheet'>`;

実装に関する詳細は https://github.com/neftaly/grunt-sri 、もしくは我々が code.jquery.com に対して送った pull request、特にこの部分をご覧ください。

JavaScript / Node.js 以外の例

昔ながらの Makefile を利用されている場合でも、問題なく組み込めます。UNIX ライクな環境をお使いと仮定すると、以下のように node モジュールを利用する必要なく実現できます:

# Makefile
generate:
    cat FILENAME.js | openssl dgst -sha256 -binary \
        | openssl enc -base64 -A

こちらの Gist に様々なプラットフォームで SRI 向けに SHA ハッシュ値を生成する例を公開しています。

結論

Subresource Interity を利用すると、自分でコントロールできないサーバから配信される静的アセットの安全性を、とてもシンプルに保てます。ビルドプロセスに組み込むためのツールもいくつか提供されています。モダンな Web サイト / アプリケーション開発者なら SRI を実装するだけでなく、その利益を説明し議論することも忘れてはならないでしょう。

最後に、この記事をレビューを助けてくれた Frederik BraunJonathan KingstonFrancois Marier そして Havi Hoffman に感謝します。

Justin Dorfman について

Justin は MaxCDN のディベロッパーリレーションズディレクターで、企業の持つ技術の普及とネットワークを利用する開発者の手助けの責任者。BootStrapCDN を 2012 に開始。Bootstrap、Font Awesome、Grunt、Ionic、jQuery Foundation、Twemoji、Nginx、Gnu Bash への貢献者で、 FOSS コミュニティにへ深く関わっている。

Justin Dorfman による他の記事はこちら

Joshua Mervine について

Heroku の SRE。開発とシステムエンジニアリングに 20 年以上の経験を持つ。2013 に BootstrapCDN の開発をリード。自分のプロジェクトと他のプロジェクトへの貢献を通じて、オープンソースとコミュニティに深く関わる。

Joshua Mervine による他の記事はこちら

MediaRecorder を使ってブラウザの(ほぼ)全てを記録しよう

Record almost everything in the browser with MediaRecorder の抄訳です。

MediaRecorder API は、動画や音声のようなメディアストリームを保存するための API です。保存した結果は、例えば ogg ファイルに出力するといった形で、楽しむことが可能です。

ブラウザでは、ストリームを様々な形で取得できます。よく見る例には次のように MediaDevices インタフェースを利用して Web カメラからのストリーム取得があります。

navigator.mediaDevices.getUserMedia({
  audio: true
}).then(function (stream) {
  // do something with the stream
}

ストリームを取得したら、次に MediaRecorder を作成します。

var recorder = new MediaRecorder(stream);

このインスタンスは、他の JavaScript オブジェクトと同様に操作できます。操作するためのメソッドも持っていますし、イベントハンドラを登録することも可能です。メソッドの中で最も重要なものは startstop で、イベントの中では dataavailable が重要です。このイベントは保存したストリームのエンコードが終わり、データが利用可能になった際に送出されます。

以上の点を踏まえて記述した、録音用のコードは以下のようになります:

recorder.addEventListener('dataavailable', function(e) {
// e.data contains the audio data! let's associate it to an

// start recording here...
recorder.start();

// and eventually call this to stop the recording, perhaps on the press of a button
recorder.stop();

こちらでライブデモをご覧になれます。

かなり短いコードではありますが、プラグインを必要しないブラウザ内で音声エンコードを実現しています。

追加された新機能を使ってみよう

基本を押さえたところで、追加された新機能、ブラウザでの録画についてみることにしましょう。

動画はとても複雑なものですが、MediaRecorder は簡単に利用できる API を提供しています。前掲のコードを変更して、録画できるようにしていきましょう。

最初にストリームの取得部分のコードを変更します。次のように音声だけでなく、動画も取得するように設定します。

navigator.mediaDevices.getUserMedia({
  audio: true,
  video: true // <-- new!
})

再生時に利用する要素を audio から video に変更した点以外、その他の部分は本質的に変更がありません。こちらで動作例を確認いただけます

すごくないですか?外部のライブラリをロードする必要も、C / C++ で記述されたソースコードを Emscripten のようなツールを使って asm.js に変換する必要も、PNaCl のような移植性の低い方法を取る必要もありません。たった 62 行の JavaScript を書くだけで、外部に依存することなくブラウザの機能だけを使ったプログラムを実現できるんです。大事なことなのでもう 1 度言います。たった 62 行ですよ!しかもコメントをいれて!

このプログラムはプラグインなどを使う方法と比べて、帯域も CPU の電力消費も抑えられます。なぜならビデオエンコーディングはネイティブコードが担った方が効率的だからです。また他のプラットフォームにコードをそのまま持っていくことも可能です。やったね!

さらなる改良

MediaRecorder は、ストリームの種類やそのソースを区別せず扱います。この特徴のおかげで、MediaRecorder に渡す前に Web AudioWebGL などを利用したストリームの操作・変更が可能です。これらは効率的かつ高速なデータ変換に向いているため、よく MediaRecorder と組み合わせて使われます。

メディア保存のための新しい API 群(この中に Media Recorder API も含まれます)には、canvas 要素や audio 要素、video 要素の拡張が定められています。これによりこれらの要素からもストリームを取得できるようになります。また新規ストリームの作成や、トラックの追加 / 取得 / 削除といったストリーム操作も可能になっています。

ステップ・バイ・ステップで、詳しく見ていきましょう。

DOM 要素からのストリーム取得

canvas に描画されるアニメーションの録画を例に説明してゆくことにしましょう。次のように <canvas>captureStream() メソッドを呼ぶだけで、ストリームを取得できます。

var canvasStream = canvas.captureStream();

次に、上述したように取得したストリームを対象とする MediaRecorder を作成します。

var recorder = new MediaRecorder(canvasStream);

こちらの例では requestAnimationFrame を利用してcanvas にホワイトノイズを描画しています。

「外部入力のない canvas を録画してるだけじゃね?」

という声も聞こえてきそうなので、入力画像を操作し、ストリームにして保存する例を作成しました。

// set the stream as src for a video element
video.src = URL.createObjectURL(stream)

// periodically draw the video into a canvas
ctx.drawImage(video, 0, 0, width, height);

// get the canvas content as image data
var imageData = ctx.getImageData(0, 0, width, height);

// apply your pixel magic to this bitmap
var data = imageData.data; // data is an array of pixels in RGBA

for (var p = 0; p < data.length; p+=4) { 
  var average = (data[p] + data[p+1 ] + data[p+2]) / 3; data[p] = average >= 128 ? 255 : 0; // red
  data[p+1] = average >= 128 ? 255 : 0; // green
  data[p+2] = average >= 128 ? 255 : 0; // blue
  // note: p+3 is the alpha channel, we are skipping that one
}

この例では動画に対して <canvas> を利用してフィルタを適用しています。この <canvas> に対して captureStream() を呼べばストリームが取得でき、それを録画するのは簡単です。録画を実現している例はこちらでご覧になれます。

<canvas> でのピクセル操作は効率の良い手段とは言えません。WebGL を利用する方が効率の面では優れていますが、設定も複雑で、この例には不向きなため今回は利用を見合わせています。

付記: 仕様では audio 要素や video 要素は captureStream メソッドを持つこととなっています。しかしブラウザによっては未実装の場合があります。

AudioContextからストリームへ、そしてAudioContext へ

適切なノードを利用することにより、ストリームは Web Audio の入力にも、そして出力にもなりえます。つまり AudioContext にストリームを入力として与え、オーディオグラフによって処理をすることできます。また AudioContext から他のストリームへと出力することで、さらなる操作や利用が可能になります。

stream とaudioContext が既に用意されているとすると、次のように MediaStreamAudioSourceNode オブジェクトを作成することで、音声ストリームをオーディオコンテキスト内で利用できるようになります:

var sourceNode = audioContext.createMediaStreamSource(stream);

このノードを直接 audioContext.destination に接続することで、入力の音声を聞くことができます:

sourceNode.connect(audioContext.destination);

またフィルタをかけることもできます。次のようにフィルタに接続し、フィルタを audioContext.destination へ接続することで、元の音声ではなく、フィルタされた結果を聞くことができます:

var filter = audioContext.createBiquadFilter();
filter.connect(audioContext.destination);
sourceNode.connect(filter);

フィルタした方を出力したければ、MediaStreamAudioDestination ノードを作成し、audioContext.destination の代わりにフィルタと接続します。

var streamDestination = audioContext.createMediaStreamDestination();
filter.connect(streamDestination);

streamDestination に接続されているものは全て、ストリームとしてオーディオグラフから出力されます。そのストリームは、streamDestinationstream 属性で参照できます。これを利用して、MediaRecorder のインスタンスを作成し、フィルタされた音声を録音します:

var filteredRecorder = new MediaRecorder(streamDestination.stream);

入力された音声にフィルタを適用し、その結果を録音するデモはこちらでご覧になれます。

結合しよう

ここまで動画と音声の処理方法について個別に解説してきましたが、その 2 つを同時に行うことを求められる場合もあります。

これまでの解説の通り、動画と音声を並行して処理することもできます。この場合、動画は再生されていなくてはならず、処理前の音声を聞くことになってしまいます。音声トラックなしのビデオストリームを取得していたため、上述の例ではこの問題が発生しませんでした。

「それなら、video 要素をミュートにすればよいのでは?」

と思われるかもしれませんが、それでは処理対象であるストリームの音声もミュートされてしまいます。

この問題は、新しく 2 つのストリームを作成することで解決できます。動画像のみを扱うストリームと、音声のみを扱うストリームをそれぞれ作成後、それぞれを並行して処理し、最後に 1 つのストリームにまとめます。

まずは新しいストリームを作りましょう。MediaStream コンストラクタを呼ぶことで、新しいストリームを作成できます。

var videoStream = new MediaStream();

getVideoTracks() を呼ぶことで、video トラックのリストを取得できます。それぞれのトラックを videoStream に追加します:

var videoTracks = inputStream.getVideoTracks();
videoTracks.forEach(function(track) {
    videoStream.addTrack(track);
});

同様に audioStream も作成します:

var audioStream = new MediaStream();
var audioTracks = inputStream.getAudioTracks();
audioTracks.forEach(function(track) {
    audioStream.addTrack(track);
});

これで動画処理用と音声処理用のストリームをそれぞれ作成し、並行して処理できるようになりました。

// Manipulate videoStream into a canvas, as shown above
// [...]
// Then get result from canvas stream into videoOutputStream
var videoOutputStream = videoCanvas.captureStream();

// Manipulate audio with an audio context, as shown above
// [...]
// Then get result from audio destination node into audioOutputStream
var audioOutputStream = streamDestination.stream;

この時点で、videoOutputStream と audioOutputStream、2 つのストリームができています。これからこの 2 つを 1 つのストリームにまとめます。getTracks() メソッドを利用することで、コードをより一般的なものにしています:

var outputStream = new MediaStream();
[audioOutputStream, videoOutputStream].forEach(function(s) {
    s.getTracks().forEach(function(t) {
        outputStream.addTrack(t);
    });
});

いつものように outputStream を MediaRecorder コンストラクタの引数に与えます:

var finalRecorder = new MediaRecorder(outputStream);

Boo でここまで解説したテクニックを全て利用したデモを見られます。このビデオブースデモは、音声と動画の処理、そしてエンコードも含め、完全にクライアントサイドで動作しています。

ブラウザの対応状況

ブラウザの対応状況は、優れているとは言える状況にはありません。しかしすぐに良くなることでしょう。

Firefox Developer Edition 47 以降のデスクトップ版 Firefox は、動画と音声の保存も含め、これまで解説した全てのテクニックに対応しています。

Android 版 Firefox では MediaRecorder を利用できませんが、Firefox 48 で対応される予定です。モバイルでのパフォーマンスは良いとは呼べませんが、今年のできるだけ早いうちにハードウェアエンコーディングを動作させられるように開発が続いています。

Chrome 47 以降、Opera 36 以降では、録画は WebRTC ストリームからのみ行えます。captureStream() による canvas からの動画ストリームや、Web Audio からのストリームには対応していません。また利用するためには、chrome://flags もしくは opera://flags から試験運用版のウェブ プラットフォームの機能を有効にする必要があります。Chrome 49 ではこの機能と、録音は標準で有効になります。Microsoft Edge はある時点で実装が開始されるようです。実はスペック作成者の 1 人は Microsoft で働いています。

Web サイトの機能を追加で向上させるためなら、上記の機能を使っても良いでしょう。

つまらない理由でプログラムが正しく動作しなくなることや、動作しない録音ボタンを表示してしまうことを防ぐために、MediaRecorder に対応していないブラウザでアクセスされた場合と同様に、まずは互換性のチェックを行いましょう。追加の機能は、それが利用可能である場合にのみ示されるべきです。

例えば、次のように window オブジェクトが MediaRecorder 属性を持つかどうかを確認することで、対応状況の確認ができます。

if(window.MediaRecorder !== undefined) {
    // great! show recording UI
}

詳細と関連情報

このポストを読んで MediaRecorder と、それに関連した API に興味を持ち、より深く学びたいと思っていただければ幸いです。

オンラインのシンプルな利用例を見ることで、それぞれのテクニックを個別に学べます。全部入りの大きなデモを見るよりもわかりやすいでしょう。コードはレポジトリごとクローンできます。この例は mediaDevices.getUserMedia を利用しているため、対応していないブラウザのためのポリフィルも用意されています。単純化のためコードにはポリフィルは含まれていないので、利用には注意が必要です。

上でも触れた Boo は、動画と音声に効果を適用し短いビデオクリップとして出力する、完全にブラウザ上で動作するビデオブースです。このソースコードも同様に公開されています。

以前に MediaRecorder について解説した時からの進捗について知りたい方は、2014 年 6 月の Chris Mills によるポストをごらんください。とても大きな大きな進捗がありました。

ステキなものを作られた際には、ぜひご連絡ください!

Soledad Penadés について

Mozilla の Developer Relations チーム所属。Web 上でステキで、リアルタイムに動作するものを作る人を助けるために活動中。irc.mozilla.org の #devrel でコンタクト可能。

Soledad Penadés による他の記事はこちら

Firefox の開発ツールを使った JavaScript のパフォーマンス向上

この記事は Hacks ブログの記事、”Optimizing JavaScript Performance With Firefox Dev Tools
” の抄訳です。

Firefox の開発ツールに含まれるパフォーマンスツールを利用すると、パフォーマンスの低い JavaScript を調査して、一般的なレスポンスとレイアウトに関する性能を見ることが可能になります。

以下のスクリーンキャストでは、コードの中で性能を低下させている要因を調査し、それを修正する方法を解説しています。具体的には、かろうじて数秒に 1 度画面を更新できる程度に低速な 2D の canvas の描画デモを、パフォーマンス分析し、少しの修正を加えることで実用的なフレームレートで描画できるように改善しています。

スクリーンキャスト:https://youtu.be/VFZFpzQTG6M

関連情報

Greg Tatum について

Firefox の開発ツールに含まれる、パフォーマンスツールとメモリツールの開発者。
クリエイティブコーディングに対する情熱を持ち、Canvas 2D と WebGL を使った描画の限界を広げるべく活動を行う。

Greg Tatum によるその他の記事はこちら。

Firefox の新しいメモリツール

この記事は 2015 年 11 月 11 日にポストされた “Firefox’s New Memory Tool” の抄訳です。

Firefox Developer Edition 44 には、新しくメモリツールが追加されました。このツールを使うと、Web アプリケーションのメモリ使用状況について理解しやすくなります。特にリソースの制限の厳しいモバイル向けの Web サイトの開発者には有用でしょう。この強力なツールの使い方は、フリーランスのクリエイティブデザイナーであり、フランスの Mozilla コミュニティメンバーでもある Baptiste Kaenel の作成した動画をご覧ください:https://youtu.be/DJLoq5E5ww0

メモリツールはまずメモリのスナップショットをとります。その結果を様々な点でグループ分けし、ツリーや表形式で表示します。標準では “coarse type” でグルーピングしています。この設定では、メモリ上のものを次の 4 つに分類します。

  • オブジェクト:JavaScript のオブジェクト。[[Class]] 名によって、より細かくグループ分けされます
  • Scripts:Web アプリケーションによってロードされた JavaScript のソースコード。ここにはSpiderMonkey の JIT コンパイラである IonMonkey によって生成された実行可能なマシンコードも含まれます
  • String:Web アプリケーションが使用する、JavaScript の文字列です
  • その他:上記のいずれにも属さないものを表します

“coarse type” 以外には、”object class” と “internal type” でもグルーピング可能です。前者では JavaScript の [[Object]] クラスによってグルーピングを行い、後者ではそれらの C++ での型によって分類します。後者は特に Firefox のプラットフォーム開発を行う人にとって有用な機能でしょう。

また “allocation stack” というグルーピング方法も選べます。これはメモリパネルの上部にある「割り当てスタックを記録」にチェックを入れることで利用できます。これは割り当てスタックの記録を取ることによって、アプリケーションのパフォーマンスが低下するためです。その代わり、ヒープ領域上のものを JavaScript コード上の位置によってグループ化して確認できるようになります。他の設定とは異なり、JavaScript のソースコードとメモリ上の要素とを直接関連付けて確認できるようになります。

allocation stack によってグルーピングされている例

スナップショットはいくつも取ることができ、それぞれのスナップショットにはメモリの使用量が MB 単位で表示されます。これを見ることで、アプリケーションのメモリの使用量が時間に応じて増減する様子を確認できます。

メモリツールの詳細については、MDN のメモリツールのページをご覧ください。また私たちは皆さまからのフィードバックをおまちしています。ぜひ Firefox Developer Edition をダウンロードし、コメントを @FirefoxDevTools 宛ににツイートください。

Dan Callahan について

Mozilla の Developer Relation を担当するエンジニア。元 Mozilla Persona の開発者。

Dan Callahan による他の記事はこちら

Nick Fitzgerald について

Firefox の開発ツールと JavaScript エンジンである SpiderMonkey を担当。ソースマップの仕様策定者の一人で、ブログ fitzgeraldnick.com を不定期に更新中。IRC では fitzgen でコンタクト可能。

Nick Fitzgerald による他の記事はこちら

Developer Edition 47:ユーザエージェントの変更、ポップアップデバッグなど

Developer Edition 47 – User agent emulation, popup debugging and more の抄訳です。

今週 Firefox Developer Edition 47 がリリースされました。この記事に関連して Devtools reload add-on service worker tooling などのポストもあります。それぞれのトピックについては、そちらもぜひご参照下さい。この記事では、Developer Edition のその他の変更点についてまとめます。

ユーザエーエジェントの偽装

ユーザエージェントを変更できる機能が、レスポンシブデザインモードに追加されました。「Custom User Agent」にユーザエージェント名を入力するだけで、ユーザエージェントを変更できます。例えばモバイルブラウザのユーザエージェントを入力すれば、デスクトップでもスマートフォン向けサイトを見ることができます。

ユーザエージェントを変更している様子は、こちらのスクリーンキャストをご覧ください:

ユーザエージェントを変更するスクリーンキャスト

参照グラフの表示

前回のリリースで、ドミネータビューが追加されました。これはメモリを多く利用するアプリのデバッグを助けるツールでした。今回のリリースでは、これにパスパネルが追加されました。このパネルではノードがグラフとして表示され、その中の選択されたノードはガーベジコレクションの対象ではなくなります。この機能は、メモリリークのデバッグを行う際に有用です。詳細については、MDN の解説をご覧ください。

ドミンネータビューのパスパネル

コンソールでの複数行入力

コンソールでの複数行入力がよりやりやすくなりました。Enter キーを押すだけで、入力が終了しているかどうかをコンソールが自動判断します。継続入力があると判断した場合は、次の入力行が表示されます。複数行を入力するのか、1 行のみの入力なのかを機にすることなく、コマンドを入力できるようになりました。

ストレージインスペクタ

ストレージインスペクタが、キャッシュストレージをサポートしました。Service worker のデバッグ時に有用な機能です。Service Worker のデバッグに関しては Sole Penadés のブログポストをご覧ください。

またツールバーにある検索ボックスを利用して、表示するコンテンツをフィルタできるようにもなりました。フィルタしている様子は、こちらのスクリーンショットをご覧ください:
ストレージインスペクタで表示するコンテンツをフィルタしている様子

テーマの変更

ツールボックスの表示も改善しました。標準のタブを少し短くすることや、メモリツールに新しくアイコンを追加といった小さな変更に加えて、いくつかの大きな変更もあります。ライトテーマの一新がその代表例です。より見やすく、洗練されたルック & フィールとなりました。

新しいライトテーマを利用した例は次のスクリーンショットをご覧ください:

新しいライトテーマを利用している例

またデバッガの表示も更新しました。条件付きのブレークポイントは、スクリーンショットのようにオレンジ色で表示されるようになりました:

条件付きブレークポイントのハイライト表示例

ネットワークモニタのツールバーの位置も、画面上部に変更されました。これでよりアクセスしやくなり、他のツールとのデザイン上の一貫性も保たれるようになりました。

ネットワークモニタ。ツールバーが上部に移動している。

アドオン向け:ポップアップのデバッグ

WebExtensions のリリースに向けて、アドオンのデバッグを簡単にする機能を追加されつつあります。今回のリリースでは、ポップアップの調査を簡単に行うための機能が追加されました。この機能を利用すると、クリックしてもポップアップが消えないようにロックできるようになります。ブラウザツールボックスを起動し、右上にある 4 つの四角を持つアイコンをクリックすることで、ポップアップをロックできます。詳しくはこちらの記事をご覧ください。

また実際の利用例をこちらのスクリーンキャストでご覧いただけます:
Firefox 47: debugging popups in WebExtensions

その他の変更点

上記の変更点に加えて、ツールボックスに全般にわたって改善が行われています。特筆するべきものは、以下の 2 つです:

また、Firefox のマルチプロセス化の関係で、3D ビューが削除されました。この機能を利用されたい場合は、こちらのアドオンをご利用ください。

フォントインスペクタも、標準では無効化されるようになりました。将来的には再度有効化される予定ですが、現在のリリースで有効化するには、about:config で devtools.fontinspector.enabled の値を変更する必要があります。

Developer Edition のリリースに貢献いただいたみなさま、ありがとうございました!最新版をこちらからインストールして、フィードバックをお願いします。

Tim Nguyen について

Firefox の開発ツールのコントリビュータ。Web 開発と Web デザインに情熱を持つ。

Twitter アカウント

Tim Nguyen による他の記事

Firefox 開発ツールを使った Service Workers と Push のデバッグ

原文: Debugging Service Workers and Push with Firefox DevTools on March 11, 2016 by Soledad Penadés

Firefox 44 で Web Push が利用可能になった ことに引き続き、Firefox Developer Edition 47 の開発ツールでは Service Workers と Push Notifications のコードをデバッグできるようになりました。

この記事で採り上げる機能はこちらのスクリーンキャストで説明されています。New Service Worker debugging tools in Firefox Developer Edition 47

文字で読むほうが好きな方はこのままお読みください!

about:debugging

Service Workers の動作は通常の Web Workers と同じではなく、そのライフサイクルも両者で異なります。そのため、開発ツールの デバッガ タブに Service Workers のコードと通常のスクリプトを一緒に表示させることはできません。

そこで、登録された Service Workers と Shared Workers をすべて集めたダッシュボードを新しく追加しました。amongst other debuggable items such as Add-ons.

デバッグを始めるには、まず新しいタブに about:debugging と入力し、画面左の Workers タブをクリックします。

about-debugging

このダッシュボードを開く別の方法として、ツールWeb 開発Service Workers の順にメニューをたどる、またはツールバーのメニューから 開発ツールService Workers の順にクリックする方法もあります(開発ツール がない場合は、ツールバーメニューの カスタマイズ を利用してください)。

service-worker-menu

toolbar-menu

ダッシュボードのリアルタイム更新

このダッシュボードを初めて開いた場合、Service WorkerShared Worker のセクションに「まだありません。」と表示されているはずです。これらのセクションは Worker の登録に合わせて自動的に更新されます。表示されるボタンも Worker の状態に合わせて変わり、Worker が実行中であれば Pushデバッグ が表示され、登録された Worker がアクティブでなければ Start だけが表示されます。

試してみましょう!ウィンドウで about:debugging を開き、別のウィンドウでは 簡単な Service Worker のデモ を開いてください。Service Worker が登録され、Service Worker のセクションに表示されるはずです。ダッシュボードをリロードする必要はありません!

Service Worker のデバッグ

Service Worker をデバッグするには、その Worker を実行している必要があります。Debug ボタンをクリックするか、まだ実行中でなければ Start をクリックしてください(その Service Worker が登録済で about:debugging のダッシュボードに表示されていることが必要です)。

すると Service Worker のコードが書かれた新しいウィンドウが開きます。ここで 通常のデバッグと同じことはすべて、例えばブレークポイントを設定したり、ステップ実行や変数の確認などを行うことができます。

pop-up-debugger

Push Notifications

Web Push API を用いているコードもデバッグすることができ、Service Worker の push イベントリスナの中にブレークポイントを設定することで実現できます。プッシュ通知を受信すると、デバッガがブレークポイントで停止します。

pop-up-debugger-breakpoint

とても使いやすい機能ですが、ネットワークが一時的に接続できない場合など、手元で解決できない理由によってプッシュ通知の到着が遅れる場合があります。しかし嬉しいことに、その Worker に対して Push ボタンを押すことで、こういった状況でも push イベントに依存したコードをテストすることができます。

このボタンを押すと push payload が送信され、その後 push イベントが間髪入れずに発火します。これにより、サーバからプッシュ通知が届くまで待つ必要がなくなり、開発時間を短くする ことができます。

Shared Workers のデバッグ

Shared Workers のデバッグもサポートされています。とはいえ、Service Workers のデバッグと異なるのは about:debugging で表示される場所が違うことぐらいです。

(キャッシュされた)リクエストのデバッグ

通常のネットワークに向けたリクエストと、Worker によってキャッシュされたリクエストとを見分けることも可能です。キャッシュされたリクエストの場合、転送量 の欄に表示されるのがデータ量ではなく Service Worker と表示されます。

network-highlighted

fetch イベントリスナの中にブレークポイントを設定することで、Service Workers が発行するリクエストに割り込んでデバッグすることが可能です。

fetch-event

デバッガがブレークポイントで停止した際に、変数リストの中にある event オブジェクトを確認することで、リクエストされた url や HTTP ヘッダなどを調べることができます。

まとめ

私たちが現在取り組んでいる新機能について、おおまかな説明ができたことと思います。

about:debugging に関するリファレンス は MDN で参照できます。Service Workers についてより詳しく知りたい場合は Service Worker の利用について や、素晴らしいデモと使用例が載っている Service Workers cookbook を確認するとよいでしょう。