このサイトの記事更新は2019年11月に終了されました。過去記事アーカイブを公開しています。
投稿されたすべてのトピック
WebExtensionを書いてみよう
この記事はhttps://hacks.mozilla.org/2015/09/lets_write_a_webextension/の翻訳です。
WebExtensionを書いてみよう
By Blake Winton
WebExtensionはFirefox extensionを書く新しい方法です。
このテクノロジーはクロスブラウザ互換性の為に開発されました: 膨大な量のAPIがGoogle ChromeやOperaがサポートしているextension API と互換性を保っています。 他のブラウザの為に作られたアドオンは大抵の場合、少しの変更を加えるだけで動きます。APIもまたmultiprocess Firefoxとほぼ互換性を保っています。
一つだけ付け加えると、MozillaはChrome and OperaのサポートしているおおよそのAPIを実装している間、私たちはそれらAPIだけに制限されるわけではありません。それが理にかなっている場合、私たちは新しく機能を追加して、他のブラウザベンダーとこの機能を同様にを実装するかどうかという話をします。最後に、WebExtension APIはまだ開発途中です。Firefox Nightlyを使用する場合、最新のものを得られるため標準準拠の動作になります。よってこのチュートリアルを行うにはたぶんFirefox Nightlyを使用するのがベストでしょう。しかし覚えておいてください、これはまだ実験的な技術です – もしかしたら、なかったことになるかもしれません(訳注:もう正式機能になりました)。
始めてみる
よし、じゃあまずはシンプルなアドオンから始めてみようか。ボタンを追加して、クリックされたとき、私の一番好きなサイトが新しいタブで開くようにしよう。
まず最初のファイルとしてFirefoxに私たちのアドオンを伝える manifest.json
が必要だ。
<code>{ "manifest_version": 2, "name": "Cat Gifs!", "version": "1.0", "applications": { "gecko": { "id": "catgifs@mozilla.org" } }, "browser_action": { "default_title": "Cat Gifs!" } }</code>
よし!これで完了!たぶんあなたのアドオンはこのようになったと思います。もちろん、私たちはまだそれが動くかどうかわからない。なのでFirefoxにインストールしてみよう(最新のFirefox Nightlyを使います)。ディレクトリからFirefoxに manifest.json
をドラッグしてみてください。しかし実際にはあなたが望んだとおりに動かなかったと思います。
インストール
Firefoxがあなたの拡張をアドオンとして認めるには拡張子を.xpiとしてzipファイルを与える必要があります。なので最初に7-Zipをインストールして(訳注:win7,win10以降は標準でzip圧縮できます。)、7z a catgifs.xpi manifest.json
と入力します。(もしあなたがMacやLinuxを使っているならzipコマンドが標準で入っています。なのでzip catgifs.xpi manifest.json
とタイピングするだけで大丈夫です)。そうしたら、 catgifs.xpi
をFirefoxにドラッグします。そうするとエラーが表示されます。なぜなら私たちのアドオンはまだ署名されていないからです。
about:config
に行き、xpinstall.signatures.required
と検索ボックスに入力し、エントリーをダブルクリックし、falseに設定し、タブを閉じます。その後、もう一度 catgifs.xpi
をFirefoxにドラッグしてみてください。新しいアドオンをインストールするかどうかの選択画面が現れるはずです!
ここで重要なことは、Firefox44(年内にリリース)で始める場合、Firefox Betaやリリースバージョンアドオンのブラウザにインストールするためには署名を要求します。なのでもしあなたが上の設定をセットした場合でも、このチュートリアルを行うためにはまだFirefox NightlyやDeveloper Editionが必要になります。
もちろん、私たちのアドオンはまだほとんど動きません。
なので修正してみましょう!
機能を加える
最初に、下のコードを manifest.jsonの
browser_action
:という記述が含まれている行の上に記述します。
<code> "background": { "scripts": ["background.js"], "persistent": false },</code>
もちろん現在、上に出てきたbackground.js
ファイルはまだ存在していませんので作成しましょう。下のjavascript をbackground.js
の中に貼り付けます。
<code>'use strict'; /*global chrome:false */ chrome.browserAction.setBadgeText({text: '(ツ)'}); chrome.browserAction.setBadgeBackgroundColor({color: '#eae'}); chrome.browserAction.onClicked.addListener(function(aTab) { chrome.tabs.create({'url': 'http://chilloutandwatchsomecatgifs.com/', 'active': true}); });</code>
そうするとこんな風になるはずです。 もう一度7z a catgifs.xpi manifest.json background.js
(もしくは zip catgifs.xpi manifest.json background.js
)とタイピングしてアドオンを再生成し, catgifs.xpi
をFirefoxに再度ドロップします。そしてボタンをクリックする新しいタブが開くようになりました!
Firefox: W^X JIT-code が有効になりました
jandemooij.nlで 2015 年 12 月 29 に公開された、W^X JIT-code enabled in Firefox の抄訳です。
6 月にさかのぼりますが、SpiderMonkey の JIT コードに W^X 保護を有効にするオプションを追加しました。過去数週間にわたって、パフォーマンスに関する問題の修正を行い、昨日すべてのプラットフォームの Nightly でこの機能を標準で有効にしました。これによって、JIT コードを持つメモリ上のページは、実行可能であるか、書き込み可能であるかのどちらか一方になります。両方のフラグが立つことはありません。
この変更をいれた理由
ほとんどすべての JIT コンパイラ、そして昨日までの Firefox は、コード用のメモリを RWX (read-write-execute) フラグをつけてアロケートします。JIT はその特質として、インラインキャッシュのように動いているコードを修正することがあります。あらかじめメモリを書き込み可能としておくことで、パフォーマンス低下を引き起こさずにコード修正を行えます。しかし RWX メモリは以下にあげるような問題の遠因となります:
- セキュリティ:RWX ページは、ある種のバグを利用した攻撃の難易度を下げてしまいます。そのため、モダンなオペレーティングシステムは実行コードが格納されているメモリ領域を、実行可能 / 書き込み不可能に設定します。一方データ用の領域は通常、実行不可能に設定されます。このあたりの詳細については W^X もしくは DEP を参照してください。RWX JIT コードは、このルールの例外となっており、そのため興味深い攻撃対象となっています。
- メモリ破壊:他の場所のメモリ破壊に起因する、JIT コード内でのクラッシュのメモリダンプを解析したことがあります。メモリ破壊を引き起こすバグはすべて深刻ですが、その原因がなんであれ、メモリ破壊が発生したら直ちにクラッシュするほうが良いとされているバグでもあります。
W^X 保護の動作
W^X が有効になっている場合、JIT コードを格納するすべてのページは標準で書き込み不可能となります。格納されている JIT コードを修正する場合には、RAII クラスである AutoWritableJitCode
を利用して、書き込み可能(RW)なページを作成します。Windows では VirtualProtect を、それ以外のプラットフォームでは mprotect を利用して実現しています。修正後、AutoWritableJitCode オブジェクトのデストラクタが、ページを RW から RX に戻します。
ちなみに、W^X に代わる手段としては、デュアルマッピングがあります。この方式では、ページはまず RW にマップされ、その後 RX にマップされます。2010 年に、何人かが TraceMonkey のために、この手法を実装し、パッチを作成しました。しかしパッチが取り込まれることはありませんでした。この方式は mprotect のオーバヘッドを回避できますが、安全性を保つために RW マッピングは異なるプロセスで実行される必要があります。複雑で、IPC によるオーバーヘッドもあるため、取り込まれませんでした。
性能
先週、W^X を動作させる際に行われていた暗黙的な割り込みチェックに関する問題を修正し、不要な mprotect の呼び出しを削除し、W^X の性能を低下させていたコードの最適化をおこないました。
この結果ベンチマークや、テストを行ったサイトで W^X による性能低下は極めて小さくなりました。Lraken や Octane ではオーバーヘッドは 1% 以下でした。昔の SunSpider でのオーバーヘッドはより大きくなりました。これはほとんどのテストが数ミリ秒で終わるためコンパイル時間による影響が大きいためです。それでも Windows と Linux では 3% 以下、OS X では 4% 以下となっています。OS X の方が遅いのは、mprotect が他のプラットフォームと比較して遅いためです。
W^X 保護が SpiderMonkey にうまく組み込めたのは、いくつかの理由があると考えています:
- SpiderMonkey はベースラインコンパイルを行う前に、バイトコードを解釈するインタプリタモードで動作します。Web の場合、ほとんどの関数の呼び出し回数は 10 回未満です。これらの関数は JIT コンパイルされることがないため、メモリ保護のためのオーバヘッドも存在しません。
- ベースライン JIT はほとんどの演算に IC スタブを利用します。これは間接的に呼ばれるため、スタブをアタッチする際にコード領域を書き込み可能にする必要はありません。またスタブはコードを共有するため、コンパイルを行うのは最初に特定のスタブをアタッチする時のみです。Ion IC スタブを利用するためにはメモリが書き込み可能でなければなりませんが、ベースラインコンパイル時に Ion はほとんど IC を利用しません。
- asm.js に関しては(そしてすぐに WebAssembly も!)、モジュール全体が AOT コンパイルされます。コンパイル後、コード領域の全てを RW から RX に変更するために 1 度だけ mprotect が呼ばれます。さらに低速の実行パスを通る時にのみコード変更が行われるため、基本的に asm.js / WebAssembly に関しては性能上のオーバヘッドはありません。
結論
Firefox Nightly で動作する JIT 用のコードで、W^X 保護を行うようになりました。バグや深刻な性能低下がないと思われるため、Firefox 46 に組み込まれる予定です。
最後に、私たちに先だって勇敢にも W^X 保護を導入したOpenBSD と HardenedBSD のチームに感謝します!
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
Brian Grinstead について
Firefox の開発ツールを開発するエンジニア。
- Web サイト:briangrinstead.com
- Twitter アカウント:@bgrins
メインスレッド以外での 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 コンテキストの作成から描画関数の呼び出しまでを別ファイルに分離します。
<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 を呼び、コンテキストを新しく作成したワーカスレッドに移譲している点に注目してください。transferControlToOffscreen
は HTMLCanvasElement ではなく、OffscreenCanvas のインスタンスを返します。HTMLCanvasElement と同様、offscreen.clientWidth
や offscreen.clientHeight
のような属性にはアクセスできませんが、offscreen.width
や offscreen.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.prototype
に commit メソッドを追加されます。このメソッドを呼ぶことで描画内容を、 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.drawArrays
や gl.commit
は非同期的に呼び出されます。このループはメインスレッドで実行されるため、結果として描画はメインスレッド中の GC によってブロックされることとなります。ただし、ワーカスレッドの実行自体はメインスレッド中の GC によってブロックされません(少なくとも Firefox の SpiderMonkey バーチャルマシンでは)。
この問題を解決するためには、ワーカスレッド中での処理をより賢いものに変更する必要があります。そしてその解決策は、requestAnimationFrame
を Worker コンテキストで呼び出せるようにすることでしょう。実装状況はこちらのトラッキングバグで確認できます。
まとめ
requestAnimationFrame
を Worker で利用できるようになるためには未だ開発が必要ですが、OffscreenCanvas API を利用することで、メインスレッドのブロックとは関係なく描画処理を行えるようになりました。既存の WebGL コードをワーカで実行するための変更は数分で済むことも示しました。animation.html と animation-worker.html(worker.js を利用)をご覧いただければ、変更前と変更後を比較できます。
Nick Desaulniers について
オープン Web のために Mozilla で戦うソフトウェアエンジニア。Firefox OS に対するサードパーティ開発者を助けるのではなく、WebGL を利用したグラフィックスプログラミング、オープンソースへの貢献、そして Emscripten を利用した C / C++ コードの JavaScript へのトランスコンパイルについての講演を行う。多くのプログラミング言語、JIT コンパイル、JavaScript の高速化、グラフィックスプログラミングに多くの知見を持つ。オープンソースソフトウェアと、全ての人がインターネットを利用できるようにすることに情熱を傾けている。
- Web サイト:https://nickdesaulniers.github.io/
- Twitter:@LostOracle
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 が推奨している内容は次の通りです。
- クリック時にユーザがページから移動しないよう、リンクに
return false
を追加する - ユーザがリンクをクリックした際には、トラッキングのためにクリック内容を GA のイベントキューに追加し、終了したらユーザを新しいページに移動させるよう GA に伝える
- 受け取ったイベントを GA が処理する
- コールバックを 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 つを意味します。
- GA が読み込まれる前にイベントをキューに追加できる(これなら早い段階で生じたイベントを簡単にトラッキングできますね!)
- これまでと同様
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 標準、高品質なコード、アクセシビリティ、そしてゲーム開発に興味がある。
- Web サイト:http://www.belenalbeza.com
- Twitter: @ladybenko
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 をビルドプロセスに組み込む方法はいく通りもあります。Grunt、Gulp、Broccoli、Webpack、Ember CLI、Handlebar といったツールには、すでにプラグインが用意されています。
実世界でのビルドプロセスを考慮して、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 Braun、Jonathan Kingston、Francois Marier そして Havi Hoffman に感謝します。
Justin Dorfman について
Justin は MaxCDN のディベロッパーリレーションズディレクターで、企業の持つ技術の普及とネットワークを利用する開発者の手助けの責任者。BootStrapCDN を 2012 に開始。Bootstrap、Font Awesome、Grunt、Ionic、jQuery Foundation、Twemoji、Nginx、Gnu Bash への貢献者で、 FOSS コミュニティにへ深く関わっている。
- Web サイト:https://www.justindorfman.com
- Twitter:@jdorfman
Joshua Mervine について
Heroku の SRE。開発とシステムエンジニアリングに 20 年以上の経験を持つ。2013 に BootstrapCDN の開発をリード。自分のプロジェクトと他のプロジェクトへの貢献を通じて、オープンソースとコミュニティに深く関わる。
- Web サイト:http://www.mervine.net
- Twitter:@mervinej
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 オブジェクトと同様に操作できます。操作するためのメソッドも持っていますし、イベントハンドラを登録することも可能です。メソッドの中で最も重要なものは start
とstop
で、イベントの中では 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 Audio や WebGL などを利用したストリームの操作・変更が可能です。これらは効率的かつ高速なデータ変換に向いているため、よく 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
に接続されているものは全て、ストリームとしてオーディオグラフから出力されます。そのストリームは、streamDestination
の stream
属性で参照できます。これを利用して、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 でコンタクト可能。
- Web サイト: soledadpenades.com
- Twitter: @supersole
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 を使った描画の限界を広げるべく活動を行う。
- Web サイト:gregtatum.com/
- Twitter:@TatumCreative
Greg Tatum によるその他の記事はこちら。
Firefox 47 アドオン互換性情報
[これは Mozilla Add-ons Blog の記事 Add-on Compatibility for Firefox 47 の翻訳です]
Firefox 47 が 6 月 7 日 [日本時間同日深夜] リリース となります。Firefox 47 の変更点でアドオンの互換性に影響を及ぼす可能性のあるものを以下にまとめました。Firefox 47 for Developers により詳しい情報が載っていますので、こちらも併せてご覧ください。
一般
- FUEL が削除されました。FUEL ライブラリ は Firefox 40 以降廃止予定となっていました。
about:customizing
の先読みハックが削除されました。UI カスタマイズパネルはabout:customizing
URL を使って開かれなくなりました。gDevTools.jsm
がcommonjs
モジュールへ移動されました。今後はCu.import
の代わりにrequire("devtools/client/framework/devtools-browser");
を使って読み込む必要があります。このモジュールは後方互換性のためまだ動作しますが、将来的に削除されます。- Web ページから
view-source:
URL へリンクを貼ることはできなくなりました。
タブ
TabOpen
とTabClose
イベントが、ウィンドウ間で移動されたタブに関してさらなる詳細を提供するようになりました。- タブの
visibleLabel
プロパティとTabLabelModified
イベントが廃止されました。これは、タブに異なるラベルを付けることを可能にした以前の変更を取り消す形となります。
XPCOM
CookieManager
はuserContextId
が一致する Cookie のみ削除するようになりました。これにより、nsICookieManager.remove
に新たな必須引数originAttributes
が追加されました。nsIX509CertDB
に様々な変更が行われました。該当バグは Bug 1064402、Bug 1241646、Bug 1241650 です。多くの関数が変更されたため、ここではすべて取り上げません。- PSM で
nsIEnumerator
の使用が中止されました。これにより、listTokens
、listSlots
両関数に変更が加えられ、いずれもnsIEnumerator
の代わりにnsISimpleEnumerator
を返すようになりました。 nsINavHistoryQuery
からuriIsPrefix
オプションが削除されました。removeVisitsTimeframe
がHistory.removeVisitsByFilter
に置き換えられ、廃止予定となりました。
新機能
- 自動的に閉じる XUL パネルのデバッグが難しい問題が解決しました。アドオン内の ポップアップパネルをデバッグ できるようになりました。
- デスクトップ版 Firefox が
mozbrowser
のようなフレームに対応しました。トップレベルウィンドウで読み込まれているかのようにコンテンツに認識させられる 新たなiframe
API が実装されました。
この一覧に載っていない変更点や間違いを見つけたらコメント欄でお知らせください。もしあなたのアドオンが Firefox 47 で動かなくなった場合は、筆者の方でも調査したいと思います。
AMO に登録されているアドオンの 自動互換性テストと対応バージョンの更新 は数週間以内に行われますので、AMO に Firefox 46 対応のアドオンを登録している方は後日メールをチェックしてみてください。