株式会社クリアコード > ククログ

ククログ

タグ:

Firefoxの先読み機能の無効化とその確認手順

※この記事の情報はFirefox ESR52を対象としています。これより新しいバージョンではこれらの情報は当てはまらない場合がありますので、ご注意下さい。

Firefoxの先読み機能の種類と無効化の方法

FirefoxにはWebサイトの閲覧時の体感的な快適さを向上するため、実際にリクエストが行われる前から接続やデータのダウンロードを行っておくという「先読み」の機能があります。ただし、無差別に全てのリンクを辿るのではなく、いくつかの条件が揃った時に初めて先読みが発動するようになっています。具体的には以下の要領です。

ユーザーから見れば快適さが増して嬉しい機能ですが、反面、ネットワークのトラフィックやセッション数は増大する事になります。弊社のMozillaサポート事業のお客様からも、契約回線の帯域が限られているなどの理由で、情報システム管理担当者の意向として「先読み機能を無効化したい」というご相談を頂く事があります。また、「既に担当者側でそのような設定を行ったつもりだが、実際にその指定が機能しているかどうかを確認したい」というご相談もあります。

前述の先読みに類する機能を全て無効化する場合、指定は以下のようになります。

  • 全般的な制御
    • network.predictor.enabledfalse(真偽型)
    • network.predictor.enable-hover-on-sslfalse(真偽型)
    • network.predictor.enable-prefetchfalse(真偽型)
  • 次のページを示すリンクの先読み
    • network.prefetch-nextfalse(真偽型)
  • DNSプリフェッチ
    • network.dns.disablePrefetchtrue(真偽型)
    • network.dns.disablePrefetchFromHTTPStrue(真偽型):TLSを使用したページでの挙動の制御。
  • リンクの上にポインタが載った時の投機的接続の抑止
    • network.http.speculative-parallel-limit0(整数型)

では、これらの設定が期待通りに反映されている事をどのように確認すればよいのでしょうか。これがこの記事の本題です。

設定が反映されているかどうかの確認

設定の書き間違いのようなケアレスミスや、それ以外にも何らかの理由から、設定したはずの値が反映されないままになっているという事は度々あります。そのため、この手のカスタマイズはなるべく、実際の挙動から設定の反映状況を確認する事が望ましいです。

データのダウンロードまでも行う場合の確認

HTTP接続からデータのダウンロードまでを行うフルスペックの「先読み」については、先読み対象となるHTTPリクエストが実際に処理されたかどうかを見るのが確実です。例えばこのブログの2016年5月10日の記事同年5月18日の記事へのリンクを含んでいますが、このリンクにはrel="next"という属性が指定され次のページへのリンクである事が示されているため、先読みの対象となります。

このような先読みは、ブラウザコンソールで動作を確認できます。 キーボードショートカットの「Ctrl-Shift-J」を使うかパネルメニューの「開発ツール」(またはメニューバーの「Web開発」)で「ブラウザコンソール」をクリックするとブラウザコンソールのウィンドウが開かれますので、そのウィンドウ上部の「ネットワーク」がハイライトされている状態でこのブログの2016年5月10日の記事を開いてみて下さい。先読みが機能していればGET http://www.clear-code.com/blog/2016/5/18.htmlというメッセージがコンソールに出力され、機能していなければこのメッセージは出力されません。

コンソールに出力される出力が多すぎて見分けが難しいといった場合には、低レベルのログでも動作を確認できます。この方法については後述します。

DNSの名前解決のみ行われる場合の確認

先読み機能には、DNSでの名前解決のみを先行して行う機能(DNSプリフェッチ)もあります。例えば、ページ内に<link rel="dns-prefetch" href="http://dns-prefetch.example.com">のような記述が含まれていると、FirefoxはHTTPでのリクエストではなく、dns-prefetch.example.comの名前解決の問い合わせのみを単独で行います。

この機能が動作しているかどうかを確認するためは、DNSへの問い合わせを行うモジュールが内部的に発行するイベントを監視する必要があります。この確認にもブラウザコンソールを使います。

そのためには、about:configで以下のように設定します。

  • devtools.chrome.enabledtrue(真偽型):スクリプトの実行を可能にする。
  • network.dns.notifyResolutiontrue(真偽型):DNSでの名前解決時に内部的なイベントを発行するようにする。

上記の設定を反映した状態でブラウザコンソールを開くと、コンソール下部に入力欄が出現します。この入力欄に Services.obs.addObserver(function(aSubject, aTopic, aData) { console.log(aTopic+': '+aData); }, 'dns-resolution-request', false); と入力してEnterキーを押すと、この内部的なイベントを捉えてコンソールにメッセージとして出力する事ができます。DNSプリフェッチのための指定が含まれているWebページ(テストケース)を開いた時にdns-resolution-request: dns-prefetch.example.comのようなメッセージがブラウザコンソールに出力されれば、DNSプリフェッチのための指定が処理されたと分かりますし、逆に、そのようなWebページを開いてもこのメッセージがコンソールに出力されなければ、DNSプリフェッチは無効化されていると判断できます。

TCP接続のみ行われる場合の確認

Firefoxの先読み機能の中には、データの読み込みは行わないまでも、ソケット接続のみ確立しておくという機能もあります。リンクの上にポインタが載った時点でまずソケット接続だけ先行して行っておき、クリックされた時にすぐにHTTPのリクエストを送出できるようにするというもので、この機能は投機的接続と呼ばれます。

ソケット接続を確立しただけの段階ではコンソールには何も出力されず、また、DNSプリフェッチのような内部的なイベントも発行されないため、投機的接続が行われているかどうかは、低レベルのログを解析して調べる必要があります。

Firefox 52ではログ収集対象のモジュールやログファイルの出力先は環境変数ではなく設定値で指定します。about:configで以下の設定を作成して下さい。

  • logging.nsHttp5(整数型)
  • logging.NetworkPredictor5(整数型)
  • logging.config.synctrue(真偽型)
  • logging.config.add_timestamptrue(真偽型)
  • logging.config.clear_on_startupfalse(真偽型)
  • logging.config.LOG_FILEC:\Users\Public\predict.log(文字列型)

この状態でFirefoxを再起動すると、logging.config.LOG_FILEで指定したパスの位置にpredict.log-main.123のような名前でログファイルが出力され始めます。(mainは親プロセスのログであることを示します。ファイル名の末尾の数字はプロセスIDです。)投機的接続の機能は通常のWebブラウズの中で暗黙的に行われるため、この状態のまましばらくWebページを2〜3画面分ほど見て回り、十分な量のログを溜めて下さい。

ログが溜まったら、上記のログ出力用の設定をリセットしてFirefoxを再起動し、出力されたログファイルをブラウザで開きます。この時、ログは以下のような内容になっているはずです。

2017-06-02 09:51:57.565000 UTC - [Main Thread]: I/Logger Flushing old log files
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp Creating nsHttpHandler [this=9fe1400].
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::Init
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::PrefsChanged [pref=(null)]
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::PrefsChanged Security Pref Changed (null)
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::MakeNewRequestTokenBucket this=9fe1400 child=0
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpAuthCache::Init
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpAuthCache::Init
2017-06-02 09:51:57.659000 UTC - [Main Thread]: V/nsHttp Creating nsHttpConnectionMgr @b5a1420
...

続けて、キーボードショートカットの「Ctrl-Shift-K」を使うかパネルメニューの「開発ツール」(またはメニューバーの「Web開発」)で「Webコンソール」をクリックし、ログファイルを読み込んだタブに対してコンソールを表示します。コンソール下部の入力欄に以下のログ解析用のスクリプトをコピー&ペーストし、Enterキーを押して実行します。

var body = document.body.textContent;
var requested = body.match(/nsHttpConnectionMgr::OnMsgSpeculativeConnect/g) || [];
var skipped = body.match(/Transport not created due to existing connection count/g) || [];
!requested.length ? 'not working' : requested.length == skipped.length ? 'all skipped' : 'preconnected';

すると、以下の3つのいずれかの解析結果がコンソールに出力されます。

  • preconnected:投機的接続が行われた。
  • all skipped:投機的接続が無効化されている(もしくは、同時接続数が最大値に達しているため投機的接続が行われなかった)。
  • not working:ネットワーク環境の都合等で投機的接続が行われていない。

3番目の「投機的接続が行われていない」というケースは、そのクライアントが接続しているネットワーク環境によって発生し得ます。 Firefoxは投機的接続を開始する前に接続先ホストがプライベートなネットワークに属しているかどうかを見ており、DNSで名前解決した結果が192.168.1.1010.21.0.100といったプライベートネットワーク上のIPアドレスだった場合には投機的接続の処理を中断するようになっています。 これは、投機的接続は元々、遠隔地にあるホストとのソケット接続の確立に時間がかかるためにフライングで接続しておくという趣旨の機能なので、ネットワーク的に近いホストに対しては行う意義が薄いからです。

他の先読み機能が働いているかどうかもデバッグ用ログから確認する

なお、前項で取得したログを解析すると、データの読み込みを伴う先読みが行われたかどうかも判別できます。今度は以下の解析用スクリプトをWebコンソールで実行します。

var body = document.body.textContent;
var success = body.match(/Predictor::Predict.*\n.*called on parent process.*\n.*not enabled/g) || [];
var all = body.match(/Predictor::Predict.*\n.*called on parent process/g) || [];
(success.length == all.length) ? 'all canceled' ? 'some canceled';

出力結果は以下の2つのうちいずれかです。

  • all canceled:先読み処理が無効化されている。
  • some canceled:先読み処理が有効である。

まとめ

以上、Firefox ESR52において先読みに類する機能の無効化の方法と、その検証方法をご案内しました。

ここで解説している情報の中には、まとまった情報源が無く、お客様のご要望を受けて調査した結果判明したという情報も含まれています。クリアコードではOSSのドキュメント化されていない仕様を調査して明らかにする業務も行っておりますので、導入をお考えのOSSが想定通りの動作をするかどうかに不安がある場合や、既にお使いのOSSが期待通りに動作せずお困りの場合などには、お気軽にお問い合わせ下さい

タグ: Mozilla
2017-06-16

Firefox 52以降でのルート証明書の自動インポート機能でできること、できないこと

Firefox ESR52以降のバージョンでは、隠し設定のsecurity.enterprise_roots.enabledtrueに設定することで、Windowsの証明書ストアに登録されたルート証明書をFirefox側で認識して使えるようになりました。

ただ、この機能の背景や使い方についてユーザー側の期待と実際の挙動との間に若干の齟齬が見られるため、ここで改めて状況を整理してみます。

この機能を有効にすると、以下の事を実現できます。

  • 組織内ネットワークに設置したSSLプロキシやSSLロガーの使用に必要な専用のルート証明書をFirefoxで認識させる。
  • 組織内ネットワークで運用しているサーバーの証明書専用のルート証明書をFirefoxで認識させる。

それに対し、以下のことは依然としてできません

  • Windowsの証明書ストアに元から含まれている証明書、例えば政府認証基盤のルート証明書をFirefoxで認識させる。 (「Firefoxでは日本の官公庁関係のWebサイトの一部が証明書エラーで見られない」という問題を解消する。)
  • 管理者でないユーザーがWindowsの証明書ストアに追加した証明書をFirefoxでも認識させる。

何故こうなっているのかは、機能の背景を知ることで理解できます。

この機能の導入経緯

上記の設定が導入されたBugを見ると、これは「エンタープライズでFirefoxを使いやすくする」という文脈に基づく機能だということが読み取れます。

「エンタープライズ」とは、従業員規模が千人や万人といった単位に達するような大規模な組織での使用ということです。このような規模の組織では組織内専用のルート証明書が必要になる事がままあり、それをFirefoxで使うためには証明書のインポート機能を提供するアドオンを使うか、集中管理の仕組みの実装の裏をかいて強引にインポートさせるかしかありませんでした。ところが、現在Firefoxは古い基盤技術からの脱却を進めているため、これらの裏技的なやり方は早晩使えなくなる見込みが立っています。そこで、裏技ではなくきちんとした正当な機能としてルート証明書をインポートする方法を設ける必要があった、というのがこれらのBugの背景にある事情です。

実際の動作

「Windowsの証明書ストア」は、実際にはそれ専用のデータベースがあるわけではありません。Windowsのレジストリ内には以下のようなレジストリキーの配下に証明書の情報が分散して格納されており、それらをマージして一覧表示した物を「証明書ストア」として見せているということになります。

  • HKEY_CURRENT_USER\Software\Microsoft\SystemCertificates
  • HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificates
  • HKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates
  • HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\SystemCertificates

エンタープライズ運用における「ルート証明書の追加」とは、Active Directoryなどを使って、これらの中で特に以下の位置に証明書を登録する事を指しています。

  • HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificates\Root\Certificates
  • HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\SystemCertificates\Root\Certificates
  • HKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates\Root\Certificates

このことから、Firefoxは前述の隠し設定が有効な場合、起動時に上記のレジストリキー配下を走査して、追加された証明書があればそれらをインポートするという設計となっています。

ただし、この機能によってインポートされた証明書は、Firefox自身の証明書ストアに永続的に保存されるわけではありません。複数のレジストリキーをマージした結果がWindowsの証明書ストアとして扱われるのと同様に、Firefoxでも「Firefox自身の元々の証明書ストアの内容」+「この機能によって認識されたWindowsの証明書ストア内の証明書」が、実際の認証処理における証明書ストアとして使われるという形となります。(そのため、不要になった証明書はWindowsの証明書ストアから削除するだけで、Firefoxからも認識されなくなります。Firefoxの証明書マネージャで証明書を削除する、という事をする必要はありません。)

動作確認の方法

この機能でインポートされた証明書はFirefoxの証明書マネージャには表示されないため、期待通りに証明書がインポートされているかどうかは、デバッグ用の詳細なログを見て判断する必要があります。

MCD用設定ファイルを使う場合には、例えば以下のようにします。

// 証明書のインポート機能を有効化する設定
lockPref('security.enterprise_roots.enabled', true);

// NSS(Firefoxのセキュリティモジュール)のログを出力するための設定
lockPref("logging.pipnss", 5);
lockPref("logging.config.LOG_FILE", "C:\\Users\\Public\\certlog.txt");
lockPref("logging.config.add_timestamp", true);
lockPref("logging.config.clear_on_startup", false);
lockPref("logging.config.sync", true);

この設定を反映した上で、実験用のダミーの証明書をHKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates\Root\Certificates配下に加えるレジストリファイルを使って「example.com」という名前の証明書をインポートした状態でFirefoxを起動すると、C:\Users\Public\certlog.txtに出力されたログに以下のような箇所が含まれるようになります。

...
2017-05-09 10:21:23.017000 UTC - [Main Thread]: D/pipnss certificate is trust anchor for TLS server auth
2017-05-09 10:21:23.017000 UTC - [Main Thread]: D/pipnss Imported 'example.com'
2017-05-09 10:21:23.017000 UTC - [Main Thread]: D/pipnss imported 1 roots
...

このD/pipnss Imported '(証明書の一般名)'というログが出ていれば、その証明書はFirefoxから見えています。逆に、そのようなログが現れていない場合、証明書は無視されているということになります。

ユーザーが自分で操作してWindowsの証明書ストアにインポートした証明書をFirefoxが認識しない理由

Windowsでルート証明書のファイルをダウンロードしてダブルクリックすると、その証明書を証明書ストアにインポートすることができます。しかし、この方法でインポートされた証明書は上記のFirefoxの機能では認識されません。何故でしょうか。

これは、証明書が保存されるレジストリ上の位置に理由があります。この方法で手動でインポートした証明書は、HKEY_CURRENT_USER配下の以下の位置に保存されます。

  • HKEY_CURRENT_USER\Software\Microsoft\SystemCertificates

それに対し、Firefoxの証明書インポート機能はHKEY_LOCAL_MACHINE配下の以下のキーのみを走査します。エンタープライズ向けの機能としてはそれで正解で、個々のユーザーがWindowsの証明書ストアに追加した物まで認識するのはお門違いだからです。

  • HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificates\Root\Certificates
  • HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\SystemCertificates\Root\Certificates
  • HKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates\Root\Certificates

政府認証基盤の証明書など、「Firefoxの証明書ストアには無いがWindowsの証明書ストアには含まれている」証明書がインポートされない理由と、その対処

2017年6月1日現在、政府認証基盤(GPKI)のルート証明書はFirefoxの証明書ストアには含まれていません。要望は上がっているのですが、政府側の対応がMozillaの定めるルート証明書の登録基準を満たしていないために作業が滞っているという状態です。

Windowsの証明書ストアには政府認証基盤の証明書も含まれているため、「Windowsの証明書ストアのルート証明書をFirefoxでインポートできるようになったのなら、政府認証基盤の証明書もインポートされて、証明書のエラーに悩まされることもなくなるのでは?」と期待される方もいらっしゃることでしょう。

ですが残念ながら、この機能では政府認証基盤のルート証明書はインポートされません。これは、Firefoxの証明書インポート機能の処理対象があくまで「管理者によってWindowsの証明書ストアに追加された証明書」に限られているからです。

Bugzilla上のコメント等で度々言及されていますが、Mozillaはユーザーの安全を守るために、どのルート証明書を登録するかを独自に判断しており、他社の判断を鵜呑みにはしないというポリシーで証明書ストアを管理しています。FirefoxがWindowsの全ての証明書を無条件で信頼してしまっていては、このポリシーが無意味になり、ユーザーの安全やプライバシーを保護するための判断を全てMicrosoftに委ねることになってしまいます。ですので、Mozillaとしてはあくまでユーザー(エンタープライズの文脈では、組織のシステム管理者)により追加された証明書のみをインポート対象にするというのが、この機能の趣旨となっています。

なお、以上のような趣旨のため、政府認証基盤(GPKI)のルート証明書を改めて「システム管理者が追加した証明書」としてWindowsの証明書ストアに登録すれば、これはFirefoxのインポート対象となります。

まとめ

以上、Firefox 52以降で使えるWindowsの証明書ストアからのルート証明書のインポート機能について、詳細と検証手順をご案内しました。

弊社で取り扱うFirefoxの導入・サポート案件においても、ルート証明書のインポートについては度々ご相談を頂いています。ニーズの高い機能でありながら今まで対応が進んでこなかったのには、アドオンや裏技的な方法で強引に解決してしまえるからという言い訳が立っていたからという側面は否定できないでしょう。従来からあるアドオンが切り捨てられるという方針の転換には批判の声も多く挙がっていますが、本体で対応される事が望ましい機能が本来あるべき形で実装されるきっかけとなったという事で、ここは素直に喜んでおきたいところです。

ちなみに、Firefox 52の段階ではこの機能はWindows限定の物となっています。macOSやLinuxでは設定を有効化しても効果を得られませんのでご注意下さい。

タグ: Mozilla
2017-06-01

GeckoエンジンにmacOSでprintToFileの機能を実装してみた話

はじめに

Geckoエンジンはクロスプラットフォームを標榜して作成されています。また、できるだけそのプラットフォームの特長を生かすため、そのプラットフォーム特有のAPIを使用するように作られている箇所もあります。印刷に関連するGeckoのコードももちろんプラットフォームごとに異なるAPIを呼ぶようになっており、通常使用する限りにおいてはどのプラットフォームも一様に印刷の機能を使用することができます。

今回の記事は nsIWebBrowserPrint に紐づいているprintという印刷のためのAPIにmacOSではPDFを印刷するのに必要な情報が渡っていなかった問題を解決した、という題材について書きます。

Geckoでの印刷の流れ

Geckoでは印刷をするときにプラットフォーム固有のAPIを使用する箇所とプラットフォーム非依存のAPIの箇所があります。

プラットフォームに依存するモジュールはサービス化されており、実行時に適切なContract ID(CID)を持ったモジュールがロードされる仕組みとなっています。このCIDはJavaScript側からも見え、アドオンやXULアプリケーション上でもこのCIDを用いて適宜必要なモジュールをロードしていきます。

印刷の流れを追うと以下のようになります。

nsDocumentViewer::Print(...)
  -> nsPrintEngine::Print(...) 
    -> nsPrintEngine::DoCommonPrint(...) 
      -> printDeviceContext->InitForPrinting(aDevice, ...) // プラットフォームごとに別々のDeviceContextが作成されるのでそれを用いる
        -> aDevice->MakePrintTarget(..) 
    -> nsPrintEngine::DoCommonPrint(...) //戻ってきたら印刷プレビューか印刷ジョブを行う

macOSのCocoaのAPIでの実装はwidget/cocoa以下に配置されています。

macOSでprintToFileが動かない問題のBug

macOSではnsIPrintSettingsServiceの関数のprintToFileへtrueを渡してもPDFヘ出力されないというBugが長年に渡って未解決です。詳細はMozillaのBugzillaのprintToFile is busted on Mac | Mozilla Bugzillaを参照してください。

macOSでGeckoのレンダリング結果をPDFへ出力できなかったのはなぜか

macOSでPDFへの出力をさせるのに必要な情報が上記Bugのパッチがコミットされる前のコードで設定されているかどうかを見ます。

macOSでCocoaのAPIからPDF印刷を行うには | ククログ にあるように、NSPrintSaveJobNSPrintJobSavingURL のような値が設定されているかを探します。

ここで、macOSでPDF印刷ができないのはnsIPrintSettingsServiceのCIDを持つ https://hg.mozilla.org/mozilla-central/file/0ca553b86af3/widget/cocoa/nsPrintSettingsX.mm にこれらの値がないかどうかを調べれば原因が分かります。

探すと見事にそのようなことをしている箇所はありませんでした。 前の記事を元にGeckoに向けたパッチを書く必要があります。

GeckoのXPCOMで生成されたクラスのAPIの振る舞いをプラットフォーム固有にする

GeckoはXPCOMの技術を用いており、インターフェースはidlファイルで定義されています。 ここではnsIPrintSettingsのidlは https://dxr.mozilla.org/mozilla-central/source/widget/nsIPrintSettings.idl です。

GeckoはC++で書かれているため、nsPrintSettings クラスに定義されているメソッドで必要なものをoverrideしてやれば目的は達成できます。

そのため、SetToFileName(const char16_t *aToFileName) をoverrideします。

diff --git a/widget/cocoa/nsPrintSettingsX.h b/widget/cocoa/nsPrintSettingsX.h
--- a/widget/cocoa/nsPrintSettingsX.h
+++ b/widget/cocoa/nsPrintSettingsX.h
@@ -51,8 +51,10 @@ public:
   void SetInchesScale(float aWidthScale, float aHeightScale);
   void GetInchesScale(float *aWidthScale, float *aHeightScale);

+  NS_IMETHOD SetToFileName(const char16_t *aToFileName) override;
+
   void SetAdjustedPaperSize(double aWidth, double aHeight);
   void GetAdjustedPaperSize(double *aWidth, double *aHeight);

diff --git a/widget/cocoa/nsPrintSettingsX.mm b/widget/cocoa/nsPrintSettingsX.mm
--- a/widget/cocoa/nsPrintSettingsX.mm
+++ b/widget/cocoa/nsPrintSettingsX.mm
@@ -270,3 +275,30 @@ void nsPrintSettingsX::GetAdjustedPaperS
   *aWidth = mAdjustedPaperWidth;
   *aHeight = mAdjustedPaperHeight;
 }
+
+NS_IMETHODIMP
+nsPrintSettingsX::SetToFileName(const char16_t *aToFileName)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
+  nsString filename = nsDependentString(aToFileName);
+
+  NSURL* jobSavingURL =
+      [NSURL fileURLWithPath: nsCocoaUtils::ToNSString(filename)];
+  if (jobSavingURL) {
+    [printInfoDict setObject: NSPrintSaveJob forKey: NSPrintJobDisposition];
+    [printInfoDict setObject: jobSavingURL forKey: NSPrintJobSavingURL];
+  }
+  NSPrintInfo* newPrintInfo =
+      [[NSPrintInfo alloc] initWithDictionary: printInfoDict];
+  if (NS_WARN_IF(!newPrintInfo)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  SetCocoaPrintInfo(newPrintInfo);
+  [newPrintInfo release];
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}

ここまででPDFに出力するための関数の実装です。

まだmacOS向けにはoverrideする必要がある関数が残っています:

  • 用紙の単位
    • NS_IMETHOD SetPaperSizeUnit(int16_t aPaperSizeUnit) override;
  • 拡大縮小倍率
    • NS_IMETHOD SetScaling(double aScaling) override;
  • 縦横
    • NS_IMETHOD GetOrientation(int32_t *aOrientation) override;
    • NS_IMETHOD SetOrientation(int32_t aOrientation) override;
  • 余白(GeckoではMerginに加えてUnwritaebleMerginという設定値が存在する)
    • NS_IMETHOD SetUnwriteableMarginTop(double aUnwriteableMarginTop) override;
    • NS_IMETHOD SetUnwriteableMarginLeft(double aUnwriteableMarginLeft) override;
    • NS_IMETHOD SetUnwriteableMarginBottom(double aUnwriteableMarginBottom) override;
    • NS_IMETHOD SetUnwriteableMarginRight(double aUnwriteableMarginRight) override;

これらと、印刷用紙の単位変換を行なう処理を追加したものがこちらです。

このmozilla-centralのパッチではGeckoが印刷に用いている二つの単位系の対応も入っています。

  • インチ
    • kPaperSizeInches
  • ミリメーター
    • kPaperSizeMillimeters

の両方の単位系がGeckoでの印刷ではサポートされています。

そのため、

  • Inches -> Twips -> Pixels
  • Millimeters -> Twips -> Pixels

の両方を取り扱う必要があります。これは

diff --git a/widget/cocoa/nsPrintSettingsX.mm b/widget/cocoa/nsPrintSettingsX.mm
--- a/widget/cocoa/nsPrintSettingsX.mm
+++ b/widget/cocoa/nsPrintSettingsX.mm
@@ -254,8 +254,13 @@ NS_IMETHODIMP nsPrintSettingsX::SetPaper
 NS_IMETHODIMP
 nsPrintSettingsX::GetEffectivePageSize(double *aWidth, double *aHeight)
 {
-  *aWidth  = NS_INCHES_TO_TWIPS(mAdjustedPaperWidth / mWidthScale);
-  *aHeight = NS_INCHES_TO_TWIPS(mAdjustedPaperHeight / mHeightScale);
+  if (kPaperSizeInches == GetCocoaUnit(mPaperSizeUnit)) {
+    *aWidth  = NS_INCHES_TO_TWIPS(mAdjustedPaperWidth / mWidthScale);
+    *aHeight = NS_INCHES_TO_TWIPS(mAdjustedPaperHeight / mHeightScale);
+  } else {
+    *aWidth  = NS_MILLIMETERS_TO_TWIPS(mAdjustedPaperWidth / mWidthScale);
+    *aHeight = NS_MILLIMETERS_TO_TWIPS(mAdjustedPaperHeight / mHeightScale);
+  }
   return NS_OK;
 }

diff --git a/widget/cocoa/nsDeviceContextSpecX.mm b/widget/cocoa/nsDeviceContextSpecX.mm
--- a/widget/cocoa/nsDeviceContextSpecX.mm
+++ b/widget/cocoa/nsDeviceContextSpecX.mm
@@ -49,6 +49,19 @@ NS_IMETHODIMP nsDeviceContextSpecX::Init
   if (!settings)
     return NS_ERROR_NO_INTERFACE;

+  bool toFile;
+  settings->GetPrintToFile(&toFile);
+
+  bool toPrinter = !toFile && !aIsPrintPreview;
+  if (!toPrinter) {
+    double width, height;
+    settings->GetEffectivePageSize(&width, &height);
+    width /= TWIPS_PER_POINT_FLOAT;
+    height /= TWIPS_PER_POINT_FLOAT;
+
+    settings->SetCocoaPaperSize(width, height);
+  }
+
   mPrintSession = settings->GetPMPrintSession();
   ::PMRetain(mPrintSession);
   mPageFormat = settings->GetPMPageFormat();

のコードにより取り扱うことができます。

ここではGetCocoaUnitkPaperSizeMillimeterskPaperSizeInches を返すprotectedメソッドです。また、SetCocoaPaperSizeは印刷用紙の大きさを設定するメソッドです。

まとめ

macOSでもXULアプリケーションなどから printToFile = true を設定したときにPDFへ出力するパッチについて解説しました。 このBugのレポート日時は 2011-08-01 12:35 PDT なので、このパッチで5年半越しの問題が解決されました。 mozilla-centralの該当コミットを見るとBug番号がまだ6桁であり、周辺のコミットに紐づいたBug番号は7桁なので感慨深いですね。

つづき: 2017-03-10
タグ: Mozilla
2017-02-22

Firefox 45ESRとFirefox 47以降で、法人利用に影響のある変更が入ります

Bug 1267567が修正され、Firefox 45ESRの次のリリースとFirefox 47以降のバージョンにおいて、法人利用に影響を及ぼし得る変更が入りました。

現在Firefox 45ESR、Firefox 46、およびThunderbird 45に対してMCDの集中管理用設定ファイルを使用している環境では、同じ設定ファイルを次以降のリリースのFirefoxまたはThunderbirdでそのまま使うと、期待通りの結果を得られない可能性があります。 説明を参考に、設定ファイルの修正を行ってください。

以下、詳細と対応方法について詳しくご説明します。

影響範囲

先に「次以降のリリースのFirefoxまたはThunderbirdで」と述べましたが、実際の所、Bug 1267567はFirefox 40からFirefox 45にかけて発生していた後退バグに関する物です。 つまり正確に言うと、以下に列挙したバージョンのFirefoxには、集中管理用設定ファイルの取り扱いに問題があるということになります。

  • 40.0
  • 40.0.1
  • 40.0.2
  • 41.0
  • 41.0.1
  • 41.0.2
  • 42.0
  • 43.0
  • 43.0.1
  • 43.0.2
  • 43.0.3
  • 43.0.4
  • 44.0
  • 44.0.1
  • 44.0.2
  • 45.0 / 45.0ESR
  • 45.0.1 / 45.0.1ESR
  • 45.0.2 / 45.0.2ESR
  • 45.1.0 / 45.1.0ESR
  • 45.1.1 / 45.1.1ESR
  • 46.0
  • 46.0.1

ここに列挙したいずれかのバージョンのFirefox、およびバージョン番号が一致しているThunderbird向けに作成されたMCDの集中管理用設定ファイルは、これら以外のバージョンでは正常に読み込まれない可能性があります。

問題の概要

集中管理用設定ファイルは、日本語の文字を含める場合はUTF-8でエンコーディングする必要がありました。 しかしながら上記のバージョンでは、MCD用設定ファイル全体をUTF-8でエンコーディングしていても、文字列型の設定値に非ASCII文字(例えば、ひらがな・カタカナ・漢字など)が含まれていると、値が正常に読み込まれず空文字になってしまうという問題が起こります。

これは、Bug 1137799の修正の結果、ファイルの読み込み後の内容がUTF-8バイト列そのままで扱われず、Unicode文字列に変換されるようになったためです。

Firefoxの文字列型設定を読み書きする内部APIは、Unicode文字列ではなくUTF-8バイト列を入出力に使用する設計になっています。 Firefox 38ESRおよびFirefox 39までのバージョンでは、設定ファイル全体がUTF-8バイト列として読み込まれたまま解釈されていたため、ファイル内にUTF-8バイト列として記述された文字列がそのまま内部APIに渡される形となり、結果として文字列型の設定値が正しく読み書きされるという状況でした。

ところが、Bug 1137799の修正によりファイル全体が一旦Unicode文字列に変換されるようになった結果、文字列型設定を読み書きする内部APIに対してUTF-8バイト列ではなくUnicode文字列が渡されるようになってしまいました。 その結果、上記のバージョンでは文字列値が期待通りに読み書きされなくなってしまっていました。

この問題を回避する方法として、設定ファイル内で文字列値をlockPref()などの関数に渡す前に、unescape(encodeURIComponent("文字列"));と変換する、という方法があります。 例えば以下の要領です。

function lockStringPref(aKey, aValue) {
  aValue = unescape(encodeURIComponent(aValue));
  lockPref(aKey, aValue);
}

lockStringPref("_test.string.non-ASCII", "日本語");

これにより、Unicode文字列をUTF-8バイト列にしてから文字列型設定値を保存するAPIに引き渡す事になるため、非ASCII文字の設定値も期待通りに認識されるようになります。

しかしながら、Bug 1267567の修正により、文字列型の設定値については、Firefox自身が自動的にUnicode文字列からUTF-8バイト列への変換を行うようになりました(上記の回避策と同等の事をFirefox自身が行うようになりました)。

その結果、集中管理用設定ファイル内で上記の回避策を実施している場合には、回避策が二重に実施されるのと同じ結果となり、却って動作がおかしくなってしまうという状況が発生します。

設定ファイルの修正方法

以上のような経緯のため、今後リリースされるFirefoxでは、上記の例のような回避策が不要となります。 日本語の値であっても、設定はすべてlockPref()などの関数の値に直接記述できるようになり、結果として、Firefox 38ESRおよびFirefox 39までの挙動と同じに戻りました。 上記の回避策に類する変換を行っていた場合、変換を行なわないように設定ファイルを修正してください。 それにより、引き続き同じ設定ファイルをFirefox 47以降・Firefox 45ESR以降で使い続けられます。

まとめ

Firefox 45ESR、Firefox 47、およびThunderbird 45の今後のリリースで行われた変更のうち、法人利用への影響が懸念される変更点について、概要と対策をご紹介しました。 影響を受けるバージョンを使われる際には、上記の点にくれぐれもご注意下さい。

タグ: Mozilla
2016-05-10

FirefoxアドオンのWebExtensions移行についてMozilla Blogに寄稿しました

弊社メンバーが執筆した、従来からあるFirefox用アドオンを「WebExtensions」ベースに移行させた際の知見を解説した記事がMozilla Add-ons Blogに掲載されました。

これは、筆者がアドオンのWebExtensions移行のための調査を進める中で、アドオンを1つ移行できた事についてMozillaのアドオン開発者向けメーリングリスト上に投稿した所、他のアドオン開発者にその時の知見を共有するゲスト記事の執筆を勧められたために書いた物です。 草稿段階では「WebExtensionsへの移行」に直接は関係していない前段階の準備にあたる話が多く含まれていたため、全文は筆者個人のブログで公開することにして、Mozilla Add-ons Blogには前段階の話を省いた内容が抜粋して掲載されています。

Mozilla Add-ons Blogは英語のブログである上に、記事自体もいくつかの前提が端折られています。 ここでは、WebExtensionsそのものの背景事情も含めて上記の記事の内容を紹介します。

WebExtensionsとは

WebExtensionsは、Firefox用アドオンを開発するためのAPIであると同時に、アドオンの新しい形式そのものの名前でもあります。 現在はGoogle Chromeの拡張機能用のAPI群をFirefox上に再現する形で開発が進められており、未実装の機能の充実を急いでいる段階です。 また、Google ChromeのAPI群には含まれていない・Firefoxで独自に拡張したAPI群も追加される見込みで、既にそのようになっている部分もあります。

Firefoxアドオン向けのAPIを整備する試みは「FUEL」や「Add-on SDK(旧Jetpack)」など過去にもありました。 それらとWebExtensionsとの最大の違いは、「WebExtensionsの導入」と「既存のアドオンの仕組みの廃止」が強く紐付いているという点です。

Mozilla Firefoxはアドオンによって様々なカスタマイズができることが特徴です。 しかし、アドオンを開発するための基盤や互換性維持のための仕組みの整備が遅れていた事から、

  • Firefoxの更新に追従できず、動かなくなるアドオンが出てくる。
  • お気に入りのアドオンが動かなくなるので、ユーザーはFirefoxを更新したくなくなる。あるいは、お気に入りのアドオンが動かないということでFirefoxを使う動機が薄れ、Firefoxを使わなくなる。
  • ユーザー離れを防ぐために、アドオンに大きな影響を与える変更をFirefoxに対して入れにくくなる。Firefoxの根本的な設計を改良できない状態が続いてしまう。

といった具合に、Firefox自体の進歩にとってもユーザーにとっても望ましいとは言えない状態が発生してしまっています。

Add-on SDKはこのような状況の改善を目標の1つとしていましたが、現在は「アドオンを開発するための新しい推奨される方法」という位置付けに留まっており、すべてのアドオンをAdd-on SDKに移行させるには至りませんでした。 WebExtensionsではその反省もあってか、当初から「すべてのアドオンのWebExtensionsへの移行」と「現在のアドオンの仕組みの将来的な廃止」を前面に打ち出しています。 またそれと同時に、どのようなAPIが必要なのかをアドオン作者にヒアリングするという事も進められています。 これらの事から、過去の類似の取り組みとは腰の据え方が違うという印象を受けます。

従来型のFirefoxアドオンをWebExtensionsに移行する際のポイント

冒頭に挙げた記事では、Popup ALT AttributeというアドオンのWebExtensions移行の顛末を解説しています。

従来型のアドオンをWebExtensionsに移行する時の最も重要な点は、「今までのアドオン開発のノウハウは一旦すべて忘れて、ゼロベースで考えること」の一言に尽きます。

従来型のFirefox用アドオンは、本質的には「Firefox本体に動的に反映されるパッチ」と言うことができます。 そのため、機能性やメンテナンス性を強く意識して開発していくと、「Firefox本体の処理の中で期待通りの結果になっていない部分をピンポイントで書き換える」という、その時点でのFirefox本体の設計に強く依存した設計になってしまうことがあります。

Add-on SDKではいくつかの「抜け道」があり、それを使って「SDK以前の、今までのアドオン開発のノウハウ」を一部取り入れた開発」が可能となっていました。 それに対して、WebExtensionsにはそのような抜け道は一切用意されず、厳密にすべての要素がWebExtensionsの枠組みの中で実装される必要があります。 移行元のアドオンがFirefox本体の設計に強く依存した設計である場合、WebExtensionsへの移行は「同じ結果を得るアドオンを新規に再開発する」のに近くなります。 Popup ALT Attributesは、初出が2002年(※Firefox 1.0は2004年にリリースされたので、Firefoxより前からある事になる)と非常に歴史の古いアドオンなので、その典型的な例です。

実際の所、Popup ALT AttributeのWebExtensions移行は以下の3段階を経て実現されました。

  1. 再起動が必要なアドオンから、再起動不要なアドオン(Bootstrapped Extensions)への移行(2011年)
  2. シングルプロセス前提の設計から、マルチプロセス(e10s)対応の設計への移行(2016年2月)
  3. 従来のアドオンの枠組みから、WebExtensionsへの移行(2016年4月)

Bootstrapped Extensionsへの移行では、「動的な関数の書き換え」や「XULオーバーレイ」を廃止しました。 マルチプロセス(e10s)前提の設計への移行では、「Firefoxの内部的な処理に割り込んで任意の処理を行う設計」から、「Firefox内部の処理とは別の所で完結する設計」へ変更しました。 冒頭に挙げたMozilla Add-ons Blogに掲載された縮約版は、その後の最後のステップである、「アドオンを従来の形式からWebExtensionsの形式にパッケージングし直す」という工程にフォーカスした内容になっています。

ここでは3つの段階として書いていますが、実際には、1から3までが必ずこの順で行われる必要はありません。 例えば筆者が開発した別のアドオンでは、「1(再起動不要なアドオンへの移行)はできないが2(マルチプロセス対応)はできている」という物がいくつかあります。 既存のアドオンをWebExtensionsに移行できるかどうかは、1と2を両方とも実現できていて、且つ、必要なAPIがWebExtensionsで既に提供されていることが鍵となります。 Popup ALT Attributesはそのすべての条件を満たしていたため、つつがなくWebExtensionsに移行することができました。

まとめ

筆者が行ったFirefox用アドオンのWebExtensionsへの移行の顛末を記したブログ記事が、Mozilla Add-ons Blogに掲載されました。

いくつかの条件を満たしているアドオンであれば、WebExtensionsへの移行はそう困難ではありません。 しかし、現状のWebExtensionsにAPIが不足している事は事実です。

Mozilla自身がレガシーなアドオンの廃止とWebExtensionsへの移行の推進に本気で取り組もうとしている今のタイミングであれば、必要なAPIのWebExtensionsへの追加提案も不可能ではないと考えられます。 アドオン作者の人はぜひ自作のアドオンのWebExtensions移行を試みて、躓きやすい箇所・APIが不足している箇所を早めに明らかにし、dev-addons MLBugzilla上でコンポーネントが「WebExtensions」と明示されているBugなどを通じてMozillaにフィードバックしてみて下さい。

つづき: 2017-01-05
タグ: Mozilla
2016-05-02

第1回 法人向けFirefox導入セミナーで発表を行いました

2016年3月16日、Mozilla Japan主催の法人向けFirefox導入セミナーでFirefoxの法人利用についての紹介を行いました。

発表資料はGitHubの発表資料のリポジトリにてPDFを公開しています。 また、PDF版ではリンクになっていない参考情報などについてはMarkdown形式の元ファイルをご参照下さい。

Firefoxの法人向けカスタマイズメニューについて

発表の中で、法人利用で頻出の設定項目を一覧にしたカスタマイズメニューをご紹介しました。

Firefoxはカスタマイズ性の高さが特長の製品ですが、「なんでもできます」と言われても普通は途方に暮れてしまいますし、実際に法人で利用する場面では、行いたいカスタマイズの内容はある程度パターン化されています。 そこでクリアコードでは、業務の効率化を兼ねて、カテゴリ別にカスタマイズ項目とその実現方法を整理したカスタマイズメニューを作成・利用しています。

こちらも発表資料と同様にGitHubで公開しており、全設定項目の一覧はOpenDocument Formatのファイルとしてダウンロードしていただけます。 また、発表では触れませんでしたが、各カスタマイズ項目の検証手順のドキュメント(※現状では未完成)や、検証用のテストケース群もあります。

設定項目の一覧のODSファイルには、以下の3つのシートが含まれています。

  • 全般的な設定項目の一覧(メタインストーラの設定を含む)
  • キーボードショートカットやメニュー項目のON/OFFに関する設定の一覧
  • その他のUI項目の非表示化に関する設定の一覧

それぞれの設定項目には、設定項目の識別用のIDと共に、簡単な説明と、各選択肢の具体的な反映方法を記してあります。 ここに書かれている内容に従ってMCD用の集中管理用設定ファイルglobalChrome.css用のスタイルシートファイルを作成すれば、それらのカスタマイズが反映された状態のFirefoxを迷い無く作る事ができます。

リポジトリにあるファイルは一般的な内容になっており、実際の業務では、ここからお客様ごとのニーズに合った選択肢を選び出して使用しています。 現在はちょうどFirefox 38ESRからFirefox 45ESRへの更新が発生している時期なので、その過程で得られた知見を随時フィードバックしつつ、各項目や対応する検証手順をアップデートしているという段階です。

検証が完了していない箇所については、内容に間違いや見落としがある可能性がありますので、その場合はもし宜しければissueでの指摘やプルリクエストを頂けましたら幸いです。

カスタマイズのデモンストレーション用メタインストーラについて

発表の中で、実際のメタインストーラを実行してカスタマイズ済みのFirefoxをインストールする様子をご紹介しました。 こちらの実際のファイルもGitHubリポジトリのリリースの一部として公開しています

ただし、Firefoxのインストーラそのものの再配布はできないため、実際には「メタインストーラ作成キット一式」の状態の物をアップロードしています。 こちらのZIP形式のファイルを展開すると「FxDemoInstaller-source」というフォルダが取り出されます。この中の「resources」フォルダにFirefox 45ESRのインストーラの実行ファイルをコピーして、「FxDemoInstaller.bat」というバッチファイルを実行する事で、最終的なインストーラの実行ファイル「FxDemoInstaller-45.0.exe」が作成されます。 この「FxDemoInstaller-45.0.exe」をWindowsクライアントにコピーして実行することで、カスタマイズ済みのFirefoxが実際にインストールされます(それ以外のファイルはコピーしなくても大丈夫です)。

このデモ用メタインストーラで行われているカスタマイズ内容は、上記のカスタマイズメニューからいくつか項目をピックアップして作成しています。 カスタマイズの適用方法の例として参考にして下さい。

カスタマイズメニューとメタインストーラのご利用上の注意

上記のカスタマイズメニューとメタインストーラは、自社内での展開用などにそのままお使いいただいて問題ありません。 ただし、内容については無保証となります。

期待通りの結果を得られないなどのトラブルへの対応、個別の環境に合わせるための調査・修正、既存の項目でカバーされていないカスタマイズのご相談などについては、クリアコードのMozillaサポートサービス(有償)のご利用をご検討下さい。

次回のセミナーについて

この法人向けセミナーは毎月第3水曜日の定期開催となっており、次回は4月20日を予定しています。 参加登録はDoorkeeperのMozillaグループから行うことができ、開催会によっては事前登録時のアンケートで、あらかじめセミナーで聞きたい内容をリクエストすることもできるようになる予定です。 Firefoxの法人利用についてピンポイントで不明な点がある場合には、その旨をお伝えいただければ幸いです。

また、次回開催時には15時から16時までと17時から18時までを個別相談の時間として会場を開放し、セミナーは16時から17時の開催とする予定です。 セミナーの質疑応答で大々的には質問できないことでお困りの場合には、個別相談の時間にご相談頂くこともご検討ください。

以上、Mozilla Japanの第1回法人向けFirefox導入セミナーの発表内容の補足と、次回セミナーのご案内でした。

つづき: 2017-01-05
タグ: Mozilla
2016-03-17

Firefox・Thunderbirdを、検証用にクリーンな環境で起動する方法

FirefoxやThunderbirdはアドオンや設定によるカスタマイズが可能ですが、アドオンを多数インストールして様々な設定を変更した状態だと、何か問題が起こった場合にその原因がどこにあるのかを特定しにくくなります。

とりあえずの対応方法としては、「カスタマイズ内容をすべてリセットしてまっさらの状態に戻す」ことによって問題の解消を図るという方法があります。 FirefoxでもThunderbirdでも、「ヘルプ」メニューから「アドオンを無効化して再起動」を選択したり、Firefoxであれば「トラブルシューティング情報」のタブから「Firefoxをリセット」という操作を行ったりすると、アドオンがインストールされていない状態にすることができます。 ただ、問題の原因究明には繋がりませんし、普段使いの環境を失う事になるため、問題の深刻度によっては「そこまでしなくてもよい」と思う場合もあるでしょう。

このような場合には、一時的な空のプロファイルを使って、普段使いの環境を残したままでクリーンな状態のFirefoxやThunderbirdを並行して動かすことができます。 これを使って問題の原因を調査し、本質的な解決を図った上で改めて普段使いの環境にフィードバックすれば、うまくいけば何も失わずに問題を解消できます。

以下はFirefoxの場合の解説ですが、Thunderbirdの場合も同様ですので、適宜読み替えて読んでいただければ幸いです。

プロファイルとは?

Firefoxのユーザー設定は、インストール先とは別の「ユーザープロファイル」という場所にまとめて保存されています。 これは、WindowsであればC:\Users\(ユーザー名)\Application Data\Roaming配下に保存されています。 「トラブルシューティング情報」の「プロファイルフォルダ」欄で「ディレクトリを開く」ボタンを押すと、実際に今起動しているFirefoxのプロファイルフォルダを開くこともできます。

ユーザープロファイルは通常は上記の位置にある物が使われますが、フォルダのパスをコマンドラインオプションの-profileの値として与えると、そのフォルダをユーザープロファイルの置き場所としてFirefoxを起動することができます。 例えばF:\data\firefoxの内容をユーザープロファイルとしてFirefoxを起動する場合は、以下のようにします。

C:\> cd "C:\Program Files (x86)\Mozilla Firefox"
C:\> firefox.exe -profile "F:\data\firefox"

OS X版のFirefoxも、ターミナルからオプションを指定して起動すれば同様のことができます。

$ cd /Applications/Firefox.app/Contents/MacOS
$ ./firefox-bin -profile "/path/to/blank/directory"

この方法で空のフォルダを指定して起動すると、普段使いの環境とは全く別に、アドオンがインストールされておらず、履歴や設定も何も保存されていない、クリーンな状態なFirefoxを起動することができます。 この状態であれば、使い込まれた状態の環境では容易に試すことができないアドオンのインストールやアンインストール、複数を組み合わせた状態での動作検証などを容易に行えます。

検証を行う機会が多いのであれば、firefox.exeへのショートカットを作成し、プロパティを開いて「リンク先」の欄に以下のような形でコマンドラインオプションを追記しておくことで、コマンドプロンプトを開かずにそのプロファイルでFirefoxを起動することもできます。

"C:\Program Files (x86)\Mozilla Firefox\firefox.exe" -profile "F:\data\firefox"

ユーザープロファイルのパス自体に空白文字が含まれる場合、それ自体を一続きの文字列として示すために二重引用符で囲う必要があることに注意して下さい。

なお、Windows版とOS X版のFirefox 38およびThunderbird 38以降のバージョンでは、-profileで指定するパスの位置に実際にフォルダが存在しない場合は、フォルダが自動的に作成されるようになっています。 Linux版のFirefoxやThunderbirdでは、フォルダが存在しないとエラーになります(1136620 – -profile option does not create a new directory on linux if the directory does not exist)。

普段使いの環境のFirefoxと、新規プロファイルのFirefoxを並行して動作させる

すでにFirefoxが起動している状態で上記のコマンドを実行しても、既に起動しているFirefoxの別ウィンドウが開かれるだけだったり、タブが1つ開かれるだけだったりという結果になります。 これは、コマンドラインから起動されたFirefoxが既に起動しているFirefoxを認識して、既に起動しているFirefoxに処理を委譲し(新しいタブを開く、または新しいウィンドウを開く)、新たに起動された方のFirefox自身はすぐに終了してしまうために起こる現象です。

普段使いの環境でインターネット上の情報を検索しながら、クリーンな環境で検証を進める、というような使い方をしたい場合には-no-remoteオプションを追加で指定する必要があります。

C:\> cd "C:\Program Files (x86)\Mozilla Firefox"
C:\> firefox.exe -profile "F:\data\firefox" -no-remote

このオプションが指定されていると、新たに起動されたFirefoxは既に起動しているFirefoxのことを認識せず、両者を並行して起動できるようになります。 このオプションは、firefox.exeへのショートカットのリンク先にも指定できます。

普段使いの環境のFirefoxと、違うバージョンのFirefoxを並行して動作させる

上記の-profileオプションと-no-remoteオプションの組み合わせを使うと、普段使いのFirefoxとは別のバージョンのFirefoxを併用することも可能となります。

例えば、普段はリリース版のFirefox 43を使っていて、それとは別にFirefox 45のベータ版の動作を検証したくなることがあるでしょう。 このような場合、Firefox 45のベータ版の方を普段のインストール先とは別の場所にインストールして、これらのオプションを指定して起動すれば、普段使いのFirefox 43で調べ物をしながらFirefox 45のベータ版を試すことができます。

C:\> cd "C:\Program Files (x86)\Mozilla Firefox 45Beta"
C:\> firefox.exe -profile "F:\data\firefox-beta" -no-remote

まとめ

FirefoxやThunderbirdについて、普段使いの環境には影響を与えずに、クリーンな環境や別のバージョンを並行して起動させる方法をご紹介しました。

様々なカスタマイズを行った状態の普段使いの環境で不具合と思われる現象に遭遇して、それをBugzillaやアドオンの作者に報告しても、Firefoxの開発者やアドオンの開発者の環境では問題が再現しないという場合には、原因究明が難しかったり、時には解決を諦めないといけなかったりという事になる場合もあります。

問題の原因をきちんと切り分けて、再現条件を特定するためにも、不具合に遭遇した時はこの記事で紹介している手順でクリーンな環境のFirefoxを起動して、そこから問題が再現する状態を整えるまでの最短の手順を探るようにしましょう。

つづき: 2016-01-06
タグ: Mozilla
2015-12-25

Cert Importer 1.4の再公開と、Firefox ESR版での署名要求の無効化設定について

先日、Firefoxアドオンの署名義務化に伴っていくつかのアドオンのMozilla Add-onsのWebサイト上で配布しないように切り替えた旨をお伝えしましたが、その際、Cert Importer(証明書インポータ)だけは、その特性上サイドローディング形式でのインストールが可能な権限を設定して貰えない(本審査が棄却されていた)ために、Firefox 44以降のバージョンでは一切利用できない状態のままとなっておりました。

この度、本件について進展がありましたので、この場にてご報告いたします。

Cert Importer 1.4のリリース

Firefoxに新たに認証局証明書をインポートする際には、その証明書を信頼するかどうかの確認が表示されます。 Cert Importer 1.3までのバージョンは、この確認をスキップして証明書を完全に自動的にインポートする設計となっていましたが、この「確認をスキップする」という点が問題視されていたがために、本アドオンは本審査が棄却される結果となっていました。

これを受けて、Cert Importer 1.4では当該機能を削除し、証明書のインポート時にはユーザ自身の手による明示的な許可の操作を必要とするように仕様を変更しました。 既に、署名済みのXPIパッケージはCert ImporterのGitHubリポジトリのリリース一覧のページからダウンロードできるようになっています。

利便性の点では後退する結果となりましたが、悪意ある攻撃者が攻撃用の証明書を完全に自動でインストールするというような悪用のされ方を防ぐためには避けられない変更ですので、何卒ご容赦下さい。

Firefox 45ESRでの署名要求無効化について

Firefox 44以降のバージョンではサイドローディングであるかどうかに関わらず、アドオンにはMozillaによる署名が必須となります。 アドオンのXPIパッケージへの署名申請を自動化する仕組みは既に用意されており、大抵の場合はこれにより署名申請の手間を軽減できるため、大きな混乱は無いと予想されますが、それでも解決不可能な問題が残ります。 それは、社外秘の情報を含むアドオンの取り扱いについてです。

というのも、現在アドオンに有効な署名を施すことができるのはMozilla Corporationのみであるため、署名を施してもらうためにはアドオンのインストール用パッケージをMozilla Corporationに引き渡す必要があります。 社内の取り決めや他社とのNDAの都合などからファイルを社外に持ち出せないアドオンの場合、署名を施せないため、やはりFirefox 44以降のバージョンでは利用できないという事態が発生してしまいます。

現在リリースされているFirefox 43までのバージョンや、開発者向けの検証用という位置付けの「Aurora」および「Nightly」では、about:configなどを使って隠し設定のxpinstall.signatures.requiredの値をfalseに変更することで、署名要求を無効化することができます。 Firefox 44以降のリリース版およびベータ版ではこの設定自体が機能しなくなり、署名要求を無効化できなくなることが予告されています。

これについて、まだオフィシャルな場所では情報が公開されていませんが、企業利用向けの長期サポート版であるESR版(Firefox 45ESRおよびそれ以降のバージョン)では、署名要求を無効化する隠し設定を引き続き利用できる状態とする方針である旨、Mozilla Japanから連絡を頂いております。 社外に持ち出せないアドオンを必要とする場合には、この設定を伴ってESR版Firefoxを使うという運用を選択するようにして下さい。

ただしこの措置は、ESR版の運用は技術的な知識のあるシステム管理者がアドオンの安全性を確認した上で行う事が想定されるためということになります。 一般のユーザがESR版でこの設定を使用し未署名のアドオンを使用することは推奨されませんので、ご注意下さい。

タグ: Mozilla
2015-12-22

Firefox用アドオンのデジタル署名の自動化

このククログでも既に何度か触れていますが、Firefox 44以降ではアドオンのインストール用パッケージ(XPIパッケージ)について、Mozillaによるデジタル署名が施されていない物はインストールできないようになります。

この変更は、一般の利用者にとっては使い勝手の面であまり変化はありませんが、アドオンを開発・公開する側にとっては大きな影響があります。 その1つとして、アドオンをMozilla Add-ons以外の方法で頒布する場合に必要なステップが1つ増えるという事が挙げられます。

というのも、Mozillaによるデジタル署名が施された状態のXPIパッケージを入手するには、Mozilla Add-onsのWebサイトにXPIパッケージをアップロードし、場合によってはエディターによる承認を得た上で、署名済みのXPIパッケージを改めてダウンロードしなくてはなりません。 この作業自体は簡単なのですが、「Webサイトを訪問して、ログインして、フォームを使ってファイルを送信して、署名済みのファイルをダウンロードする」という手作業が必要でした。 このような定型的な作業を毎回人の手で行うのは非効率であるのみならず、ミスの発生原因になりうるので、可能であれば自動化したいところです。

このような状況を受けて、Mozilla Add-onsのWebサイト側にSigning APIという物が実装され、上記の作業のある程度の自動化が可能になりました。

上記リンク先の記事では具体的な手順が案内されていますが、「このようにすればコマンド一発で可能」というものではないため、自分でやるには若干ハードルが高いです。 そのため、アドオンSDKに含まれるコマンドラインツールのjpmに署名APIを使ってXPIに署名するためのサブコマンドが追加されました。 SDKを使ってアドオンを開発する場合は、これを使うことで署名の申請までを自動化することができます。

それとは別のものとして、SDKを使わないでアドオンを開発する場合向けに、XPIパッケージの署名申請を支援するBashスクリプトを作成しました。 この記事では、jpmではなくこのスクリプトを用いてXPIパッケージへのデジタル署名を申請する手順を解説します。

※2015年12月21日追記:上記のjpmコマンドは、SDKを導入しなくてもjpmというNPMパッケージ単体の導入により利用可能になるとのことです。 よって、基本的にはアドオンのXPIパッケージへの署名にはjpmのサブコマンドsignを使うことをお薦めします。 以下で紹介するスクリプトは、jpm signではカバーできない範囲のニーズを満たす必要がある場合にのみ使うようにして下さい。

スクリプトの動作準備

署名申請の支援スクリプトは、Ubuntu 14.04LTS上で動作を確認済みです。 スクリプトそのものはBashスクリプトですが、APIへのアクセスにcurlを使用します。 また、Node.js用のJWTライブラリを使うため、nodejsコマンドおよびnpmコマンドが利用可能になっている必要があります。

以下の説明では、例としてUbuntuでの操作手順を示します。 他の環境で使う場合は、環境依存の記述を適宜読み替えて下さい。

署名申請にの支援スクリプトは複数に分かれているため、スクリプトは単体でダウンロードするのではなく、スクリプト群一式を作業ディレクトリに保存するようにして下さい。 例えば以下の要領です。

$ mkdir -p /tmp/signxpi
$ cd /tmp/signxpi
$ git clone https://github.com/piroor/makexpi.git

APIキーの生成

署名の申請を自動化するにあたっては、JSON Web Token(JWT)用のAPIキーが必要です。 これは、Mozilla Add-onsの開発者センターのAPIキー管理画面で生成できます。 本記事執筆時点では用途別の複数APIキーの使い分けは考慮されていないようで、生成できるAPIキーは一種類のみとなっています。 既にAPIキーを生成済みの場合、APIキーを生成し直すと古いAPIキーは無効になる模様ですのでご注意下さい。

APIキーは、「JWT発行者(JWT issuer)」と「JWT秘密鍵(JWT secret)」の2つの文字列のペアとして生成されます。 APIキーを準備できたら、次のステップに進みます。

署名申請の送信

署名申請を行うスクリプトは、リポジトリ内にあるsign_xpi.shです。 以下のようにオプションを指定して実行します。

$ makexpi/sign_xpi.sh -p "XPIファイルのパス" \
                      -o "署名済みファイルの保存先ディレクトリのパス" \
                      -k "JWT発行者" \
                      -s "JWT秘密鍵" \
                      -d

例えば、XPIファイルがカレントディレクトリにある「myaddon.xpi」で、署名済みのファイルを同じくカレントディレクトリに保存するのであれば、以下のようになります。

$ JWT_ISSUER=user:xxxxxx:xxx
$ JWT_SECRET=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
$ makexpi/sign_xpi.sh -p "./myaddon.xpi" \
                      -o "./" \
                      -k "$JWT_ISSUER" \
                      -s "$JWT_SECRET" \
                      -d
The file will be uploaded for signing.

The file will be uploaded for signing.という出力を得られたでしょうか? 実は、この時点ではまだファイルは送信されていません。 -dはいわゆるdry runを行うためのオプションで、このオプションが指定された場合、実際のファイル送信は行われないようになっています。 dry runに成功したら、改めて-dオプションを外し、実際にファイルを署名申請します。 (慣れてきたら、dry runでの確認はスキップして構いません。)

$ makexpi/sign_xpi.sh -p "./myaddon.xpi" \
                      -o "./" \
                      -k "$JWT_ISSUER" \
                      -s "$JWT_SECRET"

申請に成功し、署名済みのファイルが得られた場合、署名済みのファイルがカレントディレクトリに保存されます。

なお、署名APIは、「アドオンの新しいバージョンをアップロードするためのAPI」という形をとっているため、この時点で、署名申請を行ったバージョンは当該アドオンの新バージョンとして登録されることになります。 この時、当該バージョンは詳細情報が入力されていない状態になりますので、公開用の(listedな)アドオンの場合は、別途バージョン一覧から詳細情報を入力する必要があります。

後から署名済みのファイルをダウンロードする

レビュー待ちなどの理由で署名済みのファイルをすぐには入手できなかった場合、Not signed yet. You must retry downloading after signed.というメッセージが表示されてエラーとなります。 この場合、レビューが完了して署名済みファイルをダウンロードできるようになった段階で再挑戦する必要があります。 また、署名済みのファイルを紛失してしまった場合にも、再度ダウンロードしなくてはなりません。

このようなケースでは、download_signed_xpi.shという別のスクリプトを使います。

$ makexpi/download_signed_xpi.sh -i "アドオンの内部ID" \
                                 -v "署名済みファイルをダウンロードするバージョン" \
                                 -o "署名済みファイルの保存先ディレクトリのパス" \
                                 -k "JWT発行者" \
                                 -s "JWT秘密鍵"

例えば、アドオンのIDが「myaddon@example.com」で、「バージョン1.5」の署名済みのファイルをカレントディレクトリに保存するのであれば、以下のようになります。

$ JWT_ISSUER=user:xxxxxx:xxx
$ JWT_SECRET=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
$ makexpi/download_signed_xpi.sh -i "myaddon@example.com" \
                                 -v "1.5" \
                                 -o "./" \
                                 -k "$JWT_ISSUER" \
                                 -s "$JWT_SECRET"

まとめ

De-coupling Reviews from Signing Unlisted Add-ons | Mozilla Add-ons Blog私的翻訳)によると、非公開(unlisted)のアドオンについては、人力のレビューを経ずに即座に署名済みのXPIパッケージを入手できるようになっている模様です。

ただ、人力レビューが必要な場合であっても、この署名APIによって「新バージョンリリース時のファイルのアップロード」までは自動化できます。 署名義務化の発表以降、アドオンの開発や提供、検証にあたって手間が増えた部分はいくつかありますが、こういった部分でささやかながらリリースの自動化をしやすくなってくれたことは、素直に有り難いことだと言えるでしょう。

タグ: Mozilla
2015-12-17

Thunderbirdのエラーコンソールの出力結果を効率よく収集する方法

FirefoxやThunderbirdには、内部で発生したエラーの情報やデバッグ用の情報を表示するためのコンソールが内蔵されています。 FirefoxでもThunderbirdでも、「Ctrl-Shift-J」というキーボードショートカットでこのコンソールを開く事ができます。

FirefoxのエラーコンソールはWeb開発ツール由来の物となっており、表示された内容を簡単に選択してクリップボードにコピーできます。 しかしながら、Thunderbirdのエラーコンソールは設計上の都合から、1つ1つのログをコピーする事しかできず、また表示可能なログの最大件数も250件に制限されています。 大量のメッセージが発生する状況ではすぐにログが流れてしまって、有効な情報を収集することができません。

以下は、この制限を実証するための「300件のエラーを報告する」というサンプルコードです。

for (var i = 1; i <= 300; i++) { Components.utils.reportError('Error '+i); }; 

これをエラーコンソール内の「コード」欄にコピー&ペーストして「コードを評価」ボタンをクリックして実行すると、それまで表示されていたログや新規のログの300件のうち最初の50件までは消えてしまい、「Error 51」から「Error 300」までの250件だけが見えるという結果になることが分かります。 実際の運用上で問題が発生している時に、エラーコンソール内をすさまじい速度でログが流れていってしまうと、原因の切り分けのための調査自体もはかどりません。

エラーコンソールの最大表示ログ件数を増やす

幸い、Thunderbirdのエラーコンソールでは任意のコードを実行できます。 この機能を使えば、エラーコンソール自身の最大表示件数を変える事ができます。

以下のコードをエラーコンソール内の「コード」欄にコピー&ペーストし、「コードを評価」ボタンをクリックして実行すると、ログの最大件数が99999件に拡大されます。

Components.utils.import('resource://gre/modules/Services.jsm');var box = Services.wm.getMostRecentWindow('global:console').document.getElementById('ConsoleBox'); var descriptor = { value: 99999 }; Object.defineProperty(box, 'limit', descriptor); Object.defineProperty(box, 'fieldMaxLength', descriptor);

実際に、続けて以下のようなコードを実行してみると、それまでのログと併せてすべてのログが表示されることを確認できます。

for (var i = 1; i <= 300; i++) { Components.utils.reportError('New Error '+i); }; 

この方法は、エラーコンソールを開いた時点より後に出力されるログに対してしか利用できません。 (エラーコンソールを開く前に大量にログが出ている場合、それらは既に制限を超えて流れてしまっているために、この方法では見る事ができません。) とはいえ、エラーコンソールを開いた後の任意のタイミングで再現試験を行える状況であれば十分に有用でしょう。

なお、この変更は「エラーコンソール」のウィンドウを閉じるまでの間のみ有効で、エラーコンソールを開き直したりすると最初の状態(最大250件のみ表示)に戻ります。

エラーコンソールの内容をクリップボードにコピーする

また、以下のコードを実行すれば、その時点でコンソールに表示されているすべてのログをまとめてクリップボードにコピーできます。

Components.utils.import('resource://gre/modules/Services.jsm');var text=[];for (var row of Services.wm.getMostRecentWindow('global:console').document.getElementById('ConsoleBox').mConsoleRowBox.children) { if (row.boxObject.height > 0) { text.push(row.toString()); } }; Components.classes['@mozilla.org/widget/clipboardhelper;1'].getService(Components.interfaces.nsIClipboardHelper).copyString(text.join('\n---\n'));

これなら、ログの件数が多い場合でも容易にログを保存することができます。 アドオンのエラー情報を作者に伝える場合や、バグトラッキングシステムに問題を報告する時などにご活用下さい。

つづき: 2016-01-06
タグ: Mozilla
2015-12-15

2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|
タグ:
RubyKaigi 2015 sponsor RubyKaigi 2015 speaker RubyKaigi 2015 committer RubyKaigi 2014 official-sponsor RubyKaigi 2014 speaker RubyKaigi 2014 committer RubyKaigi 2013 OfficialSponsor RubyKaigi 2013 Speaker RubyKaigi 2013 Committer SapporoRubyKaigi 2012 OfficialSponsor SapporoRubyKaigi 2012 Speaker RubyKaigi2010 Sponsor RubyKaigi2010 Speaker RubyKaigi2010 Committer badge_speaker.gif RubyKaigi2010 Sponsor RubyKaigi2010 Speaker RubyKaigi2010 Committer
SapporoRubyKaigi02Sponsor
SapporoRubyKaigi02Speaker
RubyKaigi2009Sponsor
RubyKaigi2009Speaker
RubyKaigi2008Speaker