Home > Latest topics

Latest topics 近況報告

たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。

萌えるふぉくす子さんだば子本制作プロジェクトの動向はもえじら組ブログで。

宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

Page 1/248: 1 2 3 4 5 6 7 8 9 »

Virtual DOMでなく生のDocumentFragmentを与えてDOMを差分更新したいって話 - Mar 09, 2020

この記事はQiitaとのクロスポストです。

概要

FirefoxのアドオンやChromeの拡張機能向けに、名前空間をまたいでDOMに変更を差分適用したい場面で使える、Virtual DOMでないReal DOMで差分適用する、webextensions-lib-dom-updaterという名前のライブラリをつくりました。

どう使うの?

クライアント側でタブの情報を取得して、サーバー側でそれをレンダリングする、という場面であれば以下のようになります。

クライアント側(制御担当):

// IDからタブのオブジェクトを得る(WebExtensionsのAPI)
const tab = await browser.tabs.get(tabId);
// プロセスをまたいで、レンダリングして欲しい内容を送る
browser.runtime.sendMessage(
  '受信側の識別子',
  // ↓テンプレート記法でHTMLのコード片をそのまま生成
  `
    <span id="tab"
          class="${tab.active ? 'active' : ''}">
      <span id="throbber"
            class="${tab.status}">
        <span id="throbber-image"
              class="${tab.status}"></span>
      </span>
      <img id="favicon"
           class="${tab.status}"
           src="${tab.favIconUrl}">
      <span id="label">${tab.title}</span>
    </span>
  `.trim()
);

サーバー側(画面描画担当):

import { DOMUpdater } = './dom-updater.js';

// 他のプロセスからのメッセージを待ち受ける(WebExtensionsのAPI)
browser.runtime.onMessageExternal(message => {
  // 反映先の要素
  const before = document.getElementById('container');

  // 反映する内容をDocumentFragmentにする
  const range = document.createRange();
  range.setStart(document.body, 0);
  const after = range.createContextualFragment(message);
  range.detach();

  // DocumentFragmentの内容でbeforeと異なる部分があれば、
  // それをbeforeに差分適用する
  DOMUpdater.update(before, after); // ←これを作った。
});

どの辺に新規性があるの?

Virtual DOMでなく生のReal DOMを更新内容として指定する(※例ではDocumentFragmentを使ってますが、普通のElementでも構いません。)ので、Virtual DOMの独自記法を覚えなくていいです。利点はそれだけです。

既に同じ事をするライブラリが世の中にはあったのかもしれませんが、自分には見つけられませんでした。どなたかご存じでしたら教えてください……

→と書いていたら、morphdomという似た趣旨のライブラリが既にあると教えて頂きました。今回実装したものとの比較を最後に追記しました。

なんで作ったの?

Tree Style TabというFirefox用アドオンで、「他のアドオンから指示して、タブの中に任意のUI要素を追加する」という事をやるために作りました。

(スクリーンショット:Tree Style Tabでの利用例) 見た目を元々のタブに合わせているのでちょっと分かりにくいですが、このスクリーンショットの左側で「Add-ons - Mozilla | MDN」というラベルを伴って表示されている「細いタブっぽい物」が、別のアドオンから指示された通りの内容を、このライブラリによる差分適用で埋め込んだ部分です。

アドオン間での通信ではJSONオブジェクト形式のメッセージしか扱えないため、こういう事をやろうとすると

  • 挿入するUI要素の内容を、どんな書式でどうやって指定させるか
  • 挿入されたUI要素を、どんな書式でどうやって指定して更新させるか

ということを決める必要があります。

Virtual DOM使えばいいやん

DOMの変更の差分適用といえば既存のVirtual DOM専用のライブラリは既にいくつもあって、

このあたりの記法をそのまま使えばいいといえばいい話です。

が、どれもべつに「スタンダード」というわけではないようなので、どれを選んでも後で文句を言われそうな気がします。宗教戦争がもしあるなら、そこに参戦したくはないですし、ただでさえ「Tree Style Tabが他のアドオン向けに提供する独自のAPI」というめちゃめちゃニッチな場面なので、こんな限定された場面のために新たに(もし普段から使っている物があるなら、それとは別のライブラリ由来の)独自の記法を覚えてもらうのは忍びないです。というか、自分がこれ以上覚えたくありません。

その点、HTMLのソースを文字列で指定してDOMの標準的な機能でNodeやDocumentFragmentにするという事にしておけば、多少冗長ではあるものの、「デジュールスタンダードなんで」と言ってしまえます。技術選択で悩まなくてもよくするためだけの選択というわけです。

続きを表示する ...

Information disclosure vulnerability of Tree Style Tab and Multiple Tab Handler - May 29, 2019

日本語版

As already announced at changelogs, an information disclosure vulnerability was found in Tree Style Tab 2.0-3.0.13 and Multiple Tab Handler 2.0-3.0.6.

  • The vulnerability was already fixed at TST 3.0.14 and MTH 3.0.7.
  • Please be careful if you deactivated auto-update of addons, or using old TST or MTH on old Firefox. I strongly recommend you to update or uninstall them for safety.

Technically detailed description is already published at a GitHub issue. It was a spec bug around APIs for communication with other addons. In short: the API design was not matched to the security model of WebExtensions API itself.

  • A WebExtensions based Firefox addon can read privacy data of tabs, only when such a risk was notified on its installation and you intentionally granted that. Title, URL, and "FavIcon" of tabs are such sensitive data.
  • TST and MTH are addons providing list-like UI for tabs, so they must require permissions to access such sensitive tab data. You might saw such notification on your initial installation.
  • TST and MTH provide API for other addons to notify information of specified tree or multiselected tabs. The data notified via those API were constructed from tabs.Tab objects got via WebExtensions API with their own permissions, and they contained full tab data from both regular and private windows, including title, URL, and FavIcon, without any confirmation. To be honest, I forgot that those properties are not accessible without tabs which is a non-default permission.
  • Their API is callable via a general WebExtensions API browser.runtime.sendMessage(), and any addon can call browser.runtime.sendMessage() with no special permission.
  • As the result, sensitive tab data were accessible from any other addon with no permission and no warning when TST or MTH was installed.

Currently there is no report about actual such an "attacker" addon. This vulnerability was found by a self-check.

However, it existed for a long time (from initial releases of their versions 2.0 at 2017), and might be known axiomatically with public API documents - attackers might find it out when he read the API spec carefully. If you found any actual attacker example, please tell them me.

I already contacted to community managers of Mozilla, so I will update this announcement if any progress.


Why I did such a terrible mistake? I think there are mainly two reasons.

First, TST and MTH were originally developed as XUL addons.

In old days, all XUL addons had same permissions: they were allowed to access any data on Firefox. Old API of these addons were also designed on the policy same to XUL addons themselves - there was no concept like permissions for each client addon.

On the other hand, there are some known concepts of WebExtensions: isolated namespaces and secure permission model based on declarations. So users just need to be careful about safety for each addon simply. And, there is one more self-evident (but I missed that) fact: my addons TST and MTH may be allowed to read sensitive data, but other API client addon may not.

This means that it was required that updating the concept of my APIs following to the security model of WebExtensions API itself. But regrettably I forgot to do that - I concentrated to migration of their features and missed this point. As the result, TST and MTH were became like a bigmouth: they got sensitive information out of Firefox and blabbed to others carelessly.

Second, I misunderstood the power of the tabs permission.

The permission name tabs looks like required to do anything around tabs, for example getting a list of tabs. So, because APIs of TST and MTH require id of tabs as their parameter, I thought that "client" addons must have the tabs permission and there was no problem to return full tab information.

But that was misunderstanding. Actually, many WebExtensions API around tabs are callable regardless the addon has no tabs permission, and the permission just appends extra sensitive properties to tab objects. I added tabs to required permissions on very early days of my WebExtensions addon development experience and it was left there for a long time, thus I had no chance to correct such a misunderstanding. Possibly I might specify tabs permission for other addons even if it is unnecessary.

Please remind my mistake as a lesson, if you plan to design callable API for other addons. You need to be careful to treat any data got via WebExtensions API as sensitive, as:

  • You should understand the exact effect of the permission you specified at permissions in manifest.json. Don't put needless permissions, and keep it mind that you should make effort to minimize the list of permissions anytime.
  • You should build response/notification messages for other addons from scratch, especially without recycling of objects got via WebExtensions API with your own permissions.

ツリー型タブとマルチプルタブハンドラに脆弱性がありました - May 29, 2019

English Version

既にTwitter等でお知らせしていますが、ツリー型タブのバージョン2.0~3.0.13とマルチプルタブハンドラのバージョン2.0~3.0.6に脆弱性がありました。

  • 問題自体はツリー型タブ 3.0.14以降およびマルチプルタブハンドラ 3.0.7以降で修正済みです。
  • 自動更新を停止していたり、古いバージョンのFirefox用に古いTST・MTHを使用していたりすると、脆弱性を突かれる恐れがあります。更新またはアンインストールを強くお勧めします。

詳細な説明はGitHubのissueに日本語でも記載していますが、端的に言うと「他のアドオンと連携するために独自に提供していたAPIの仕様がガバガバだった」という事になります。

  • 通常、WebExtensionsベースのアドオンでは明示的に宣言されインストール時に許可された範囲の情報しか取得できない。タブの現在のタイトル、URL、アイコンもそのような情報の一種。
  • TSTやMTHは動作上どうしてもタブのタイトルやアイコンを知らなくてはいけないので、それらの権限を要求する旨宣言している。インストール時にもそのあたりの事が警告される。
  • TSTやMTHは、他のアドオンの求めに応じてツリーの情報や選択されたタブの情報を返したりプッシュ式に通知したりするためのAPIを提供している。このAPIを介して返される情報は、TSTやMTHが自身の権限に基づいてWebExtensions APIから得たtabs.Tabのオブジェクトに基づいて、それとの互換性を保つようにしていたため、その中に無条件でタブのタイトル、URL、アイコン、およびプライベートウィンドウのタブの情報が含まれていた。これらの情報が、本来は取得するために特別な権限が必要な物であるという事を失念していた
  • このAPIはbrowser.runtime.sendMessage()というWebExtensions APIを使って呼び出す物だが、browser.runtime.sendMessage()の呼び出しには特別な権限は要らない
  • よって、TSTやMTHがインストールされている環境では、どんな権限でインストールされたアドオンからでも、全く無警告に、そういった情報を参照できてしまうという状態だった。

一応、この「脆弱性」を使って悪さをするアドオンとしてこういう物が実際にある、というような報告はまだ受けていません。あくまで、作者が自主的に行った仕様の再チェックで脆弱性が見つかったという事になります。

が、「誰も気付かないような所、仕様の穴を突いたイリーガルな使い方をすると情報を盗み出せる」というような物ではなく、技術情報としてドキュメントも公開しているAPIを堂々と使って、WebExtensionsのアドオンごとの制限を突破して情報を取得できる、しかもその情報自体は実は2017年のバージョン2.0リリースの時から1年半以上もの間ずっと公知だったという、我ながら書いていて情けなくなるお粗末な状況なので、この脆弱性を利用した悪意あるアドオンが既に存在していても、全くおかしくはないと思っています。

ということで、もしそのようなアドオンを見つけた方がいらっしゃいましたら、お手数ですがどうかタレコミをお願いします。

折良く(?)、Mozillaのアドオンコミュニティの担当者の方からおすすめアドオンの件について連絡を頂いたので、ついでにこの件についても相談してみました。何か進展があったらまた告知すると思います。


それにしても、何故こんなにお粗末な事をやらかしてしまったのか。理由はいくつか考えられます。

まず1つは、XULアドオン時代の設計思想を引きずってしまっていたこと。

元々、XULアドオン時代にはFirefoxのアドオンはすべての情報にフルアクセスというのが当たり前でした。なので、TSTやMTHのAPIには、呼び出し元ごとに返す情報に差を付けないといけないという考えが全くありませんでした。聞かれた事はなんでも返していい、何故ならどうせ呼び出し元のアドオンだって、それらにアクセスしようと思えばいくらでもできるんだから。これがXULアドオン時代の考え方でした。

他方、WebExtensionsでは、各アドオンは事前に許可された範囲の情報しかアクセスできないので、ユーザーは各アドオンの権限だけを気にすれば良いという事になっています。この事ばかりが強く印象に残っていたことと、とにかくXUL版から機能を移植するという事にばかり気を取られていたために、アドオン同士の間ですらもアクセスできる情報の範囲に差があるという事が、頭の中からすっかり抜け落ちていたのだと思います。

「アドオン同士の権限に差がある」というWebExtensionsの世界に合わせるには、TSTやMTHのAPIにもその考え方を反映しないといけなかったんですね。自分のアドオンが正当な手順でWebExtensions APIから入手した情報は、相手のアドオンにとっても同様に正当な手順で入手できる情報だ、とは限らないのです。それを忘れて呼び出し元の求めに応じてなんでも返してしまっていたというのは、「内緒にするから教えて」と言って教えてもらった情報を、他人にホイホイ言いふらすようなものです。実に嘆かわしいです。

2つ目の理由は、permissionsで宣言する権限の影響範囲を正しく把握しきっていなかったこと。

tabsという権限の名前からはいかにも、タブに関する事なら何をするのにも、例えばタブの一覧を取得するのにすらも必須であるような印象を受けます。TSTやMTHのAPIを呼ぶには呼び出し側のアドオンがタブのIDを知っている必要があり、タブのIDを知っているなら当然tabsの権限も持っているはず、ならタブのプロパティ全部渡しても問題無いはず(どうせ呼び出し側のアドオンが自分でも同じ情報を取得できるんだから)。というのが、これらのAPIを作った時点での自分の認識だったのだと思います。

しかし実際には、tabs.query()などのWebExtensions APIはtabsの権限無しでも呼ぶ事ができます。tabs/activeTabの権限の有無は、タブの一覧を取得できるかどうかではなく、返されるタブの情報にタイトルやURLなどのセンシティブな情報が含まれるかどうかという点にだけ影響します。TSTやMTHをWebExtensionsで実装するにあたって、まだWebExtensionsの仕様への理解が浅かったかなり初期の時点でtabsの権限を設定したきりだったために、この勘違いがずっと正されないまま今に至ってしまっていたのでしょう。他のアドオンを作る時にも、自分はひょっとしたらtabsの権限が不要なのに要求してしまっているという例があるかもしれません。

今回のことから得られた教訓は、以下のようにまとめることができると思います。

  • それぞれの権限が何を可能にするのか、その権限が無いと何ができなくなるのかを、ちゃんと理解しておく。不要な権限は要求しないようにして、常に必要最小限の権限だけをpermissionsには列挙する
  • WebExtensions APIから自分の権限で取得したオブジェクトをそのまま使い回して、他のアドオン向けのメッセージに含めてはいけない。必ず、センシティブでない事が分かっている情報だけをホワイトリスト形式で抽出して、スクラッチでメッセージを組み立てること。

僕のように「他のアドオン向けのAPI」を提供するアドオンを作ろうと思っている方は、どうか僕と同じ過ちを犯さないように、他山の石として下さい……

ツリー型タブ以外のタブ縦置き・ツリー表示アドオン - May 05, 2019

(2019年7月10日追記:fttを追加)

現時点で把握してる、Tree Style Tab(ツリー型タブ)以外の物をまとめた。ボタンでポップアップが表示されるタイプの物を除外した、サイドバー常時表示型の物だけです。

ツリー表示できる物

  • Tree Tabs
  • Sidebery(手動操作で設定を有効化する必要あり)
  • ftt:TSTの欠点である不安定さを原理的に排除する方針で設計されているとのこと。

グループ化できる物

縦置きだけ

じっくり使い込んだわけではないけど、これらの中ではSideberyがぱっと見の出来がいいように見えた。

(2019年7月10日追記)また、新顔のfttも興味深い。TSTと同様に他のアドオンとの互換性を維持することを目標に置きつつ、WebExtensionsのタブ関係のAPIをもう一層ラップするライブラリを用いて、安定性を損ねる原因となっている複雑な非同期処理を排除しているらしい。多機能ではないシンプル路線での有望株だと思う。

TSTをWebExtensionsに移行したときは、その時点でも既に結構な数の縦型タブバーアドオンがあったので、「縦型タブバーとしては後発だ」と認識してたんだけど、その後もまだまだ新しい物が作られてるというのは興味深いです。というのも、これらのアドオンが依存するサイドバーAPIはGoogle Chromeには存在せずFirefoxやOperaなどでしか使えないため、ブラウザのシェア的にはそこで頑張っても社会的にはあまり報われませんので。以前、アドオンの勉強会でFirefoxのWebExtensionsに固有のAPIを紹介した時に「Chromeで使えないなら使わない」という反応をもらった事を考えると、まだまだ自分の他にもへそ曲がりがいるものなんだなあ、とちょっと嬉しくなってしまいます。

 

それにしても、これらの中で最もユーザー数が多いTab Center Reduxでも1万2千人で、TSTの約13万人とは10倍の開きがあって、なんでこんなにTSTのユーザー数が多いのか?と我ながら不思議に思ってしまう。単純に歴史が古いから(XUL時代を含めると12年)だけでしょうか。先行者利益というか残存者利益というか。

古くからあるから出来が良いのかっていうと別にそんな事ないんですけどね。実際、1000とかメチャクチャ大量のタブがある状態で試してみると、ここに挙げたアドオンはどれもTSTに比べると圧倒的に起動が速かったし。というかTSTが桁外れにクソ遅かった(これは僕が動作速度や処理効率に比較的無頓着なせいだと思ってる)。TST 3.0でだいぶ高速化はしたつもりだけど。

(と、情報提供のフリして数字を出してせこい自慢をするだけのエントリなのでした)

ツリー型タブ バージョン3.0への道 - Mar 30, 2019

これはただの苦労話です。

2月末から3月末までをかけて、長らく懸案事項となっていたツリー型タブ(Tree Style Tab、TST)の大規模改修をやりました。具体的な変更の量としては、改修に取りかかる前の2.7.22からの差分 git diff 2.7.22 で約1MBありました。見た目は変えなかったのであまり代わり映えしませんし、挙動を決定づけるロジックもほぼそのままなのですが、それらが乗っかる基盤にあたる部分が入れ替わった感じです。

何を改修したのか、どういう成果があったのか、という事を説明するために、TSTのこれまでの歴史を振り返ってみます。WebExtensionsに移行した時の話ではあまり触れなかった、細かい実装の話が多めですが、誰の役に立つかは分かりません。

続きを表示する ...

Chrome Extensions Manifest V3とFirefoxアドオンの死(の可能性) - Feb 04, 2019

1523784 - Set up analytics for https://extensionworkshop.com というbug(※bugzilla.mozilla.orgではシステム上でトラックされているタスクを一般的に「bug」と呼ぶ)でどうやら何かアドオン(拡張機能)作ってみましょう的なイベント?の準備が進められているらしいという事を、人から教えて頂いた。キャンペーンサイトの準備中バージョンらしき物を見てみると、絵が豊富で見た目とっつきやすそうな感じに仕上がってる印象で「ほほう」って思ったんだけど、アドオンの構造を図解してる部分でbackground script等の話が出てきてるのを見て、ちょっとキナ臭いというか不安というかそういう思いが頭をよぎった。

というのも、FirefoxのWebExtensionsというのはGoogle Chromeの拡張機能の現行の仕様(Manifest V2)をベースに作られてるわけだけど、他ならぬGoogleがManifest V3でbackground scriptの廃止などのかなり大きな変更をしようとしている状況で、この内容で大々的にリリース打って大丈夫なのか? と。

だって、このキャンペーンサイト(多分)の内容を素直に受け取ると「WebExtensionsのAPIを使ってアドオンを作ろう。他のブラウザにも移植しやすいよ。」というような話になってると思うんだけど、いやちょっと待ってくださいよ。Firefox向けにWebExtensionsでアドオン作っても、Google ChromeがManifest V3に移行しちゃったら、それ動かないじゃんすか。っていうかChromeがManifest V3に移行するってことは当たり前だけどChromiumもManifest V3になるってことで、ということは、Chromiumベースのブラウザも(各ベンダがどう思ってるかに関係なく、強制的に)全部Manifest V3に移行するってことじゃんすか。次期EdgeもOperaもVivaldiもKinzaもみんなManifest V3に行っちゃって、そしたらChromiumベースでないFirefoxだけ置いてけぼりじゃんすか。

つまり、ChromeのManifest V3移行は、ちょっと前に騒がれてた「Chromeの拡張機能の大量死」という影響だけでなく、Firefoxにとっては「大多数の開発者にとっての、(Manifest V2互換の)WebExtensionsでFirefox用アドオンを作る意義の消失」という影響を及ぼすのではあるまいか? という事に、遅まきながら気付いたわけです。

昨年のTokyo WebExtensions Meetupに参加されていた方が実装上困っておられた事について「Firefoxでは(Chromiumに無い独自拡張の)APIがあるから、Firefox向けにだけちょっと便利にするみたいなことはできるよ」的な話をしたら、「いやあ……Chromeで使えないんじゃ、その機能は使えないですね……」と敬遠された時に、改めて思い知ったのですよね。ああ、WebアプリやWebページを作る人達だけじゃなくて、ブラウザを拡張するという拡張機能を作る人達にとっても、いまや「ふつうはChrome」であって「Firefox対応はオマケに過ぎない」んだな、って事を……

FirefoxのWebExtensionsはいまのところManifest V3に追従する予定はないみたいな話をどっかで又聞きした気がしてますが、いやいやそんな悠長なこと言うとれんやないですか。ツリー型タブを実装できなくなるからManifest V3に完全移行はしないで欲しいけど、Firefox一人だけ置いてけぼりを食らわないためには、オプションとしてでもManifest V3に対応は絶対しなきゃいけないじゃないですか。

ほんとどうなるんだこれ。

 

難しい事は脇に置いて、「アドオンを作る人」「ベンダが提供しているUIに気に入らないところがあってイラッてなったときに自分で手を動かして解決したい人」という立場から「こうだったらいいのに」という事を書き留めておきますと、

  • ブラウザ本体が提供するUIが気に食わない時に努力次第でゴッソリ置き換えられる余地は欲しい。(Tree Style TabやVimiumのような)
    • こういう物を実現するにあたっては、筋の良い設計にしようとするとどうしたって「永続的なバックグラウンドプロセスが全体を統括する」という設計になるので、それができる余地は欲しい。
    • 常時見える状態で画面上の領域を占有できる余地は欲しい。現状で言えばSidebar APIはその代表例。
    • この点で、「サイドバーみたいなものは不要」「拡張機能は原則として一時的に呼び出されすぐ終了する物であるべき」「UIの領域においそれと踏み込ませたくない」みたいな設計者達の思想が強く表れているGoogle Chrome、そしてそのChromeの設計に強く引きずられたあまり永続的なバックグラウンドプロセスまで廃止してしまう(と自分には読めた)Manifest V3は、自分が欲している物とはかけ離れている
  • サイトごとの権限と機能の権限は、確かに別々に記述できるべきだと思う。Manifest V2/現行WebExtensionsのpermissionsの書き方はあまりにダサイ
  • フィルタリングを宣言的に簡単に設定できることは、単純に良い事に思える。
  • 現行のwebRequestのような「クライアントサイドでリクエストやレスポンスの書き換えを任意に行える機能」は、新しい事をやる余地を残すために必要だと思う。
    • ただし、現状のようにシレッと他の権限と一緒に一言だけの注意書きで確認を求められる程度ではなく、「黄色と黒の縞々」みたいな禍々しい外観で激しく警告するような事は必要だと思うし、もっと細かく権限の段階を分けた方が良いと思う。
    • このページは拡張機能の何々によって内容が改変されています、みたいな警告をロケーションバーあたりに表示しておくというのもありかもしれない。
  • 総じて、自己責任で愚行権を行使できる余地は認められていて欲しい。警告も何も読まず、危険な物をホイホイインストールして、重くなったとか不安定になったとか言う、自分がやっている事の意味を自分で分からない・分かろうとしないような人のことまで手厚くケアする必要は無いんじゃないの? そういうのが欲しい人はApple製品を使うのが筋だと思う。技術屋が高級ブランド家電屋になろうというのが無理がある。

という風な事を自分は思っています。

ツリー型タブでタブの並び順が壊れる問題との格闘 - Jan 12, 2019

ただの苦労話です。

ツリー型タブWebExtensionsに移行してからこっち、ずーーーーーっと悩まされてる事がある。それは、TSTのサイドバー上のタブとFirefox本体のタブの並び順が一致しなくなる事があるという問題。

何故そういう事が起こるのかというと、色々な背景があるのですが……

  • 当初はタブの並び順の制御については、WebExtensionsのAPI経由でタブを移動した後、それがtabs.onMovedで通知されるのを待って、通知された情報に基づいてツリーを更新するということを考えていた。
  • しかし実際にそうしてみると、まあ遅いこと遅いこと。Firefoxとアドオンのプロセスが別だからなのかそれとも別の理由があるのか、イベントがTSTに通知されてくる(結果が返ってくる)までにかなりのタイムラグがある事が分かった。
  • そこで、WebExtensionsのAPI経由でFirefoxのタブを並べ替えるのと同時に、別途runtime.sendMessage()でタブの移動を行う指示をバックグラウンドページとサイドバーの間でやり取りして、イベントの通知を待たずに先にツリーだけ更新するようにした。

で、タブを一つ一つゆっくり操作している場面(僕の普段の使い方)ではこれで問題無かったんですが、このやり方には、リンクからタブをまとめて開くとかブックマークレットを使うとかしてTSTのあずかり知らぬ所で複数のタブが一気に開かれた場合に、TSTのツリーとFirefoxのタブの順番の同期が崩れやすくなってしまうという、大きな大きな副作用がありました。

  • tabs.onCreatedtabs.onMovedなどのイベントがそれぞれ非同期で送られてくる。
  • そのリスナの中で非同期に、タブを親タブにattachしたり、タブを並べ替えたりしている。
  • そうこうしている間に新しいタブが開かれたり移動されたりする事もある。
  • それどころか、処理中だったタブが閉じられてしまう事もある。(開かれたタブを自動的に別のコンテナで開き直す、みたいなアドオンがあると起こる)

こういった感じでもうシッチャカメッチャカで、こんな状況の中でタブの並び順やら存在確認やらを厳密に把握して破綻の無いように管理するなんて、どだい無理なわけです……全部のイベントをキューに積んでシーケンシャルに処理していけば安定はするだろうけど、そうしたら死ぬほど遅くなるだろうし。いまですら遅い遅いと言われているのに、これ以上遅くなったらどうなることか、考えるだけでもそら恐ろしい。

それでも何もしないわけにはいかないので、とりあえず、挿入中のタブがある時は最低限IDが確定するまでは次のタブを挿入する処理を止めるとかの、焼け石に水のような細かい対策をちまちま重ねていました。が、最近になって「ちまちまやっててももうどうしようもない」という境地にようやく至りまして、とりあえずタブの並び順の制御についてだけは、

  • まずruntime.sendMessage()でTSTのツリーだけ並べ替える。これはリアルタイムに行う。
  • tabs.move()の呼び出しは、ある程度キューを溜めておいて後でまとめて行う。その時は、TSTのツリー上のタブの並び順をマスターとして、それに合わせるようにFirefoxのタブを並べ替えるという事を徹底する。

という事をやるようにしました。

ただ、この方向で愚直にやると、全部のタブの並び順をチェックするような処理が頻繁に実行される事になって、タブが何千とか開かれてる環境ではますます遅くなってしまうと容易に予想できるわけで。もうやだ……どうやっても詰みじゃん……安定性を突き詰めればクッソ遅くなっていって、体感速度の向上を意識すれば安定性が犠牲になって……

こんなことならもっと早くに現行の設計を諦めてReactDOMとかの仮想DOMベースに作り直しとけば良かった。あれなら確か、JSON形式のマスターデータを雑に編集してそれをビューに随時渡すだけで、ビュー側は現時点のDOMツリーから期待されるDOMツリーの状態への最短の編集距離を求めて、最小限の変更で高速にDOMツリーを更新してくれるっていうじゃないですか……という所まで考えて、はたと気が付きました。そうだ、タブの並べ替えも最小限の変更だけで済むようにすればいいんじゃん、と。

実はこれにはアテがありました。遙か昔にアドオンの自動テストをやりたくてUnitTest.XUL略してUxUというアドオンを会社で作っていたのですが、テストケース中のアサーションの期待値と実測値の差分をdiff形式で出力するために、当時すとうさんPythonのdiffの実装をJavaScriptに移植した物を入れて下さっていて、これが使えそうだったのです。

これは元々はUnified Diffのテキスト形式を出力するだけの実装だったのですが、僕がTortoiseSVNやTortoiseGitで見慣れていたカラフルな差分表示をUxUでもやりたくなって、HTMLのタグを含めた出力を組み立てるような処理を自分で書いた事があり、その時に、diffの実装は内部的には行単位ではなく1文字単位で違いを検出できているという事を知ったのでした。ということは、それを応用すれば「元のDOMツリーからこの要素だけ移動すれば期待したDOMツリーになる」「元の並び順のタブからこのタブだけ移動すれば期待したタブの並び順になる」というような必要最小限の編集手順を特定するのにも使えるはずです。

ということで、実装をそのままTSTに持ってきて、いまどきのES2017っぽいスタイルに直した上で、tabs.move()でのFirefoxのタブの並べ替えDOMツリーの編集をそれぞれなるべく少ない手順でやるようにしてみました。

コードを見ると分かりますが、実際に使っているのはdiffの実装の中のSequenceMatcherというコア機能だけです。本来は文字列を1文字単位で比較するための物ですが、実装的にはArrayを渡せばArrayの要素単位で比較してくれそうな雰囲気だったので、文字列の代わりにタブのIDを入れた配列を渡してみた所、いい感じにequal(変更無し)、delete(削除)、insert(追加)、replace(置き換え)という形で最小の編集手順を導き出してくれました。タブの並べ替えでは「タブがなくなる」という事は前提としてあり得ないため、insertreplaceのうち挿入箇所にあたる部分だけを編集情報として使っています。これのお陰で、いままでは無駄に何度もタブを行ったり来たりさせていたのが、場合によってははるかに高速に並べ替えが完了するようになってくれました。

レガシーなやり方を捨ててモダンな仮想DOMへの移行のきっかけになるはずが、何だかんだで結局、部分的にとはいえ似たような事を自前でやるようにしてしまったということで、なんだかなあ……と頭を抱えているのが「今ココ」ということで、特にオチはありません。

(まあ、仮に仮想DOMに全面的に設計を刷新したとしても、tabs.move()でFirefoxのタブの並び順を制御する部分はきっとそのまま使い続ける事ができそうではあるので、今回やった事も完全に無駄というわけではないかな、と……)

WebExtensionsのコンテキストメニューの新常識 on Firefox 64 - Nov 06, 2018

Here is the English version of this article.

朗報があります。2016年6月にWebExtensionsへ一本化の方針が示された時に出した要望であるBug 1280347 - Add ability to provide custom HTML elements working as alias of existing Firefox UI items, especially tabsが、最近ようやく解決されました。

何故これが朗報なのでしょうか? アドオンのXULからWebExtensionsへの移行の話をおさらいしてみましょう。

続きを表示する ...

WebExtensionsベースのアドオンが他のアドオンと連携するにはどうするのが良いか? - Nov 06, 2018

Here is the English version of this article. 7月に英語で書いた物の日本語版です。Qiitaにもクロスポストしています

ツリー型タブXULからWebExtensionsに移植した時の話で、アドオン同士の連携が取りづらくなる事への懸念について書きました。この点について現時点での知見をまとめておきます。

続きを表示する ...

WebExtensionsでのタブの複数選択APIのつかいかた - Nov 01, 2018

Tokyo WebExtensions Meetup #3で、標題の通りの発表をしました。スライドはQiitaにありますが、こちらにもクロスポストしておきます。

タブの「選択」状態とは?

タブの複数選択機能が入った事で「選択」という言葉が多義的になってしまったので、まずその点を整理します。 WebExtensions APIにおいては、「選択」という言葉で表されうる状態に以下の2つがあります。

  • アクティブ なタブの事
    • active tab というのがWebExtensions APIの語彙上の言い方
    • current tab という言い方も古くからある
    • foreground tab という言い方もある(background tabとの対比)
  • 選択複数選択) 状態にあるタブの事
    • highlighted tab というのがWebExtensions APIの語彙上の言い方
    • multiselected tab という言い方もされる

「selected tab」という表現は紛らわしいので、このエントリでは使いません。

Firefox本体に入ったタブの複数選択機能

Firefox 63でabout:configbrowser.tabs.multiselecttrueにすると試せます。Chromeでも同じ操作ができます。

  • タブの上でShift/Ctrl(Command)-クリックでタブを選択できます。
    • 選択されたタブは、上に青い線(Linux版ではシステムカラーに依存する。UbuntuのAmbientテーマではオレンジ色)が出る。
      (スクリーンショット)
  • タブが選択されていると、コンテキストメニューほか一部の機能が「選択中のタブすべてについてそれをする」ようになります。 (スクリーンショット:メニューのラベルが「Tab*s*」になっている)
    • まだ一部の機能はそのようになっていない。Firefox 64で一通りの機能がすべてタブの複数選択に対応するという事だと予想される。

アドオンからの2つの利用局面

WebExtensionsベースのアドオンにとっては、このタブの複数選択機能に対して2つの関わり方があります。

  • 選択されたタブに何かする
    • タブを処理対象にした便利な機能を提供するアドオンの場合
  • タブを選択する
    • タブを管理する、タブバーの代替となるアドオンの場合

それぞれ順番に紹介します。 なお、基本的にはChromeの拡張機能でも同様のAPIが使えます。

続きを表示する ...

Page 1/248: 1 2 3 4 5 6 7 8 9 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のコメント

最近のつぶやき