2019年10月25日金曜日

Thunderbird 68.2.0とアドオンの対応状況

Thunderbird 68.2.0 がリリースされました。

このバージョンから自動アップデートが有効になっているので、まだバージョンアップしたくない場合は、うっかり更新しないように注意が必要です。

v68系に移行できない大きな理由に、アドオンの対応があると思いますが、以下のサイトで対応状況をまとめてくれています。

ThunderKdB: Extension Developer Resources

10/24現在では、125件が対応できているみたいです。
全部で1340件のうち、v60系対応ですら302件と、v60の時点で1000件以上がドロップアウトしてしまっていたんですね…

ThunderbirdはFirefoxとは違って、WebExtensions/MailExtensionsに移行するも、レガシーアドオンもサポートする方針を打ち出してきました。
実際、v60/v68ではアドオンの修正が必要なものの、レガシーアドオンも動いています。
ですが、方針転換し、次期ESRリリース(Thunderbird 78, 2020年6月リリース予定)からは、レガシーのサポートをやめる話も出てきています。

Intent to de-support: traditional add-ons

アドオン作者としては、WebExtensionsでの作り直しの労力や、レガシーアドオンの自由度の高さから、サポートを継続してほしいですが、
この対応状況を見ると、有名どころの機能を本体に組み込んでしまって、サポートを打ち切ろうという考えも分からなくもないです。

で、最後に、私のアドオンの対応状況ですが、v68.2.0でもう少しベータテストを継続してから、正式版をリリースしようと思います。
これから一気に移行が進んで、バグ連絡がくることも考えられるので。
今のところ、v68.2.0でも問題なさそうです。



2019年10月23日水曜日

Thunderbird 68に未対応アドオンをインストールする方法 (2)

「互換性がありません」と出て、インストールできない場合は、まず上記を参考にアドオンを修正してください。

インストールできたが動作しない場合(ほとんどがそうだと思いますが)、ソースコードの修正が必要になります。
基本的には、以下に記載された内容に対応していく必要があります。

ここでは、実際に私がやったことも踏まえて、修正方法をまとめていきたいと思います。


XUL/Javascriptの知識が必要ですが、HTML/他のプログラミング言語にふれたことがある方であれば、なんとなくわかると思います。

あわせて、エラーコンソール(Ctrl+Shift+Jで開きます)のエラーメッセージも確認しておいてください。どの修正をしたらいいかのヒントになります。

今回は、比較的どのアドオンにも共通するものを4つ書きます。UI回りなど、ほかにもありますが、それはまたの機会に。

0.前提

  • アドオンの解凍、再パッケージのやり方はわかっている方を想定しています。
    わからない方は、Thunderbird 68に未対応アドオンをインストールする方法 を確認してください。(といっても、zipで解凍/圧縮するだけです)
    また、今回の修正対象の*.xul, *.jsファイルは、解凍してできた「content」フォルダの中にあることが多いです。

1.グローバル変数の定義
以下のグローバル変数の定義がなくなりました。
エラーコンソールで、「nsIXXXXXの定義がない」と言われているときは、だいたいこれです。
ここに挙がっていないものもあるかもしれないですが、同様の対応でよいです。
実際、nsMsgViewCommandType は、オフィシャルには説明はありませんでしたが、定義がなくなっていました。

メインウィンドウ:
  • NsMsgFolderFlags
  • nsMsgViewCommandType

メール編集ウィンドウ:
  • nsIMsgCompDeliverMode
  • nsIMsgCompSendFormat
  • nsIMsgCompConvertible
  • nsIMsgCompType
  • nsIMsgCompFormat
  • nsIAbPreferMailFormat
  • nsIPlaintextEditorMail
  • nsISupportsString
  • mozISpellCheckingEngine

Components.interfaces.」を頭につけたものに全部置き換えるか、
Components.interfaces.NsMsgFolderFlags

自分で定義しなおします。
const NsMsgFolderFlags = Components.interfaces.NsMsgFolderFlags;


2.Importの変更
Javascriptモジュールのインポートの仕方が変わっています。
インポートでエラーになるか、「XXX is not a function」のようなエラーの場合は、この修正でなおる可能性があります。

import文によって、修正の仕方が変わります。
まずは、*.jsファイルのimport文を確認してください。

Case 0:
まず、
  • MailUtils.js
  • mailServices.js
をインポートしている場合、ファイル名が変わっているので、それぞれ
  • MailUtils.jsm
  • mailServices.jsm
に変更して、以下のCase 1/Case 2の対応をしてください。
(拡張子がjsmにかわっている)

また、MailUtils.js の MailUtils.getFolderForUri 関数は、MailUtils.getExistingFolder に変わっているので、使っている場合は修正してください。

Case 1:
以下の場合、
Components.utils.import("resource://foo/modules/Foo.jsm");
// または
ChromeUtils.import("resource://foo/modules/Foo.jsm");

以下のように修正します。
var {Foo, Bar} = ChromeUtils.import("resource://foo/modules/Foo.jsm");

「Foo」「Bar」は、使用する関数名を書きます。
よくわからない場合は、インポートファイルに定義してある全てを書いてしまえばOKです。

で、何が定義されているかはインポートファイルを確認します。
インポートファイルの確認の仕方は、以下の通り。
  1. Thunderbirdのインストールフォルダにある「omni.jar」をzipで解凍
  2. 解凍してできた「modules」フォルダの中から、ファイルを探す
  3. 見つけたファイルの冒頭にある「EXPORTED_SYMBOLS」を確認する

iteratorUtils.jsmを例にすると、EXPORTED_SYMBOLSは以下のようになっています。
this.EXPORTED_SYMBOLS = ["fixIterator", "toXPCOMArray", "toArray"];

なので、そのインポートは以下のようになります。(「"」は削除してください)
var {fixIterator, toXPCOMArray, toArray} = ChromeUtils.import("resource:///modules/iteratorUtils.jsm");

Case 2:
以下の場合、
ChromeUtils.import("resource://foo/modules/Foo.jsm", scope);

以下のように修正します。
var scope = ChromeUtils.import("resource://foo/modules/Foo.jsm");

単に2番目の引数に書いているものを、import関数の戻り値にもってくるだけです。


3.Stringbundleの修正
<stringbundleset> と <stringbundle> の削除対応です。
多言語化されているアドオンの場合は、対応が必要です。
ワークアラウンドが準備されているので、修正は簡単です。

<stringbundle>が定義されているxulファイルに、以下の定義を追加するだけです。

<script type="application/x-javascript" src="chrome://global/content/elements/stringbundle.js"/>


4.Dialogのondialogaccept/ondialogcancelイベントハンドラの修正

オプション画面などのダイアログが閉じれない/変更が反映されない場合は、この修正が必要かもしれません。

以下のように、ダイアログのOK/Cancelを押したときに呼び出す処理をXULファイルで指定している場合、
<dialog buttons="accept,cancel"
    ondialogaccept="return onAccept();"
    ondialogcancel="return onCancel();>

</dialog>

Javascriptでのイベントハンドラ登録に変更します。
document.addEventListener("dialogaccept", function(event) {
    let ret = onAccept();
    if (ret == -1) { //onAccept()がエラーを返したときに、ダイアログを閉じない場合
        event.preventDefault();
    }
});

document.addEventListener("dialogcancel", function(event) {
    let ret = onCancel();
    if (ret == -1) { //onCancel()がエラーを返したときに、ダイアログを閉じない場合
        event.preventDefault();
    }
});

人気の投稿(過去7日間)