Blocking something

今日のwwwでは、広告は貼られることが当然のものになっている。しかし広告がページの閲覧をどの程度邪魔するかとか、どの程度リソースを消費できるかとか、そういったものを規制する統一された基準は特にないように思える。ないので、やりようによっては全画面広告だとかいきなり動画が再生される広告とかマシンパワーをやたら横取りする広告とかなんでもできうる。

もちろんそんなことを勝手にされたらたまらないわけで、PCのブラウザでは広告ブロッカー拡張を導入するユーザーが少なくない。いやwebサイトの維持に広告収入が重要なのはよく分かるのですが。しかし広告をブロックするとページの読み込みが何倍も速くなってついでにバッテリーも保つんだからしょうがないよね。全然しょうがない。

などと言いつつうちではその手のブロッカーは入れていない。というのは、ブロッカーってブロックのためのリストを内包してるわけだが、ページを読むたびに何万件ものリストと照合するのそれはそれで無駄にメモリやCPUパワーを使ってないですか? と思うからだ。その代わりにjavascriptの実行自体をブロックする拡張を入れている。

javascriptの実行自体をブロックする拡張と言えば有名なのはNoscriptというやつなのだが、これは基本的に常にjavascriptの実行を禁止するが許可リストに登録されたサイトだけは例外扱いとするスタイルである。これだと知らないサイトに行くとだいたい動かない→許可リストに登録→リロードという作業を強いられるのでけっこうというかかなりうざったい。怪しげなサイトだろうが全うなサイトだろうがどこでもjavascriptでドライブされるのが前提である今時のwwwに適合していない。

そこで、NoscriptではなくScriptBlockという拡張を入れている。これはもうちょっとマイルドなルールでブロックするか否かを決めているらしく、たいていの場合そういうめんどくさい作業をしなくても済むようになっている。ただそのルールの詳細がよく分からないのと、あとページのあらゆる要素にdata-ssなんちゃらという属性を嵌めまくるのが若干胃がムカつく。つまるところ、広告をブロックしたい!という要件に対して満足するソリューションを見付けられていないのである。

  • 同一ドメイン上のスクリプトは実行を許可する
  • 有名所のCDNなんかのスクリプトなんかも許可する
  • ブラウジングコンテキストのドメインと異なる場所から読み込まれるスクリプトは明示的に許可されていない限りブロックする

くらいのシンプルな拡張があればいいのだけど。そういうのないかな。

Can’t register a service worker on Vivaldi

あべ☆アニ Extension のシリーズ予約機能は、ブラウザ拡張内のバックグラウンドページでサービスワーカーを登録し、そこでここのサーバからのプッシュ通知を受け取るような構造になっている。

サービスワーカーのハンドリングや、プッシュ通知をために各デバイスに固有のトークンを得る処理などは、Firebase のライブラリに任せている。このため、今のところシリーズ予約機能は Chrome ファミリーであるブラウザ上でのみ動作する。

さて Vivaldi という謎のブラウザがある。これもまた基本的には Chrome の一族なのだが…以前も似たような Vivaldi 特有の現象に悩まされたことがあったが、どうもえ?そんなところまで手を入れる必要あります?ってくらいいろいろ魔改造されているようだ。

具体的には Vivaldi 上ではサービスワーカーの登録に失敗するのである。こんなエラーが出る:

FirebaseError: Messaging: We are unable to register the default service worker. Failed to register a ServiceWorker for scope ('chrome-extension://feohncbfalalaopkicfimhiphlnffile/firebase-cloud-messaging-push-scope') with script ('chrome-extension://feohncbfalalaopkicfimhiphlnffile/firebase-messaging-sw.js'): An unknown error occurred when fetching the script. (messaging/failed-service-worker-registration).
at ot.<anonymous> (https://www.gstatic.com/firebasejs/8.2.1/firebase-messaging.js:1:36826)
at https://www.gstatic.com/firebasejs/8.2.1/firebase-messaging.js:1:1982
at Object.throw (https://www.gstatic.com/firebasejs/8.2.1/firebase-messaging.js:1:2087)
at i (https://www.gstatic.com/firebasejs/8.2.1/firebase-messaging.js:1:884)

つまるところ、Firebase のライブラリ内でデフォルトのサービスワーカーとしてサイトのルートにある firebase-messaging-sw.js を登録しようとして失敗している。失敗した理由は、”An unknown error occurred when fetching the script.” だそうである。

この文言でググってみると特に Firebase に限ったものではなく、とにかく指定されたファイルを読み込めなかった際に Chrome 自身が出力するメッセージのようである。読み込めない理由は実際にファイルがないとか、スキームが https ではないとかそんな感じになる。しかしあべ☆アニ Extension の場合はそういう理由とは違うようだ。なにしろ Vivaldi でだけ失敗するので。

ということで、何か Vivaldi が抱えてる問題なのでは? という気がする。こういうバグもある。

chrome.input.ime

ブラウザの中にはたくさんの文字入力可能なオブジェクトがありさまざまな種別の文字が入力される。一方で IME には入力モードというものがある。そのため時々入力したいものと入力されるもののミスマッチが起こる。

そんなわけで、その辺をブラウザと IME が密接にやりとりして調整してくれると嬉しい。しかし実際には、ブラウザから IME を制御することはほとんどできないし、HTML の規格を見るにむしろできないようにしている感すらある。

ところで、タイトルのような chrome extension の API がある。これは本来は ChromeOS のためのもので、IME の機能のうちプリエディットや変換候補リストといった UI 部分を担当する。つまり変換エンジン自体は自分で書かないといけないのだが、これを活用すればブラウザから制御できる IME ができるかもしれない。

で、すでにこの API を用いて作成された SKK というものがあって:
http://blog.jmuk.org/2012/07/chromeosskk.html
インストールしてみたものの、ChromeOS には存在するのであろう入力メソッド選択インターフェースが ChromeOS 以外の OS で動くブラウザにはないので、つまりこの SKK をアクティブにする手段がない。おのれ…おのれ…!

tab emphasis

アクティブでないタブがトラッカーによって更新された場合、文書のタイトルを点滅させるようにした。しかし単に点滅だと意外に目立たない。タブの背景色をいじれるといいんだけどな。あるいは音を出せばいいのかな?

数年前の赤福プラスは (Presto)Opera、Chrome、Firefox(Add-on SDK) で動いていた。これらのブラウザがサポートするエクステンションの API は微妙に、あるいはかなり異なっていたので、それらの差異を吸収する層が必要で、赤福プラスにおいてはそれは Kosian というライブラリだった。

さてそこから時が過ぎてブラウザのエクステンションは Chromium のそれで統一されてしまう勢いになったので、Kosian の存在意義が危うくなってしまった。なのでラッパの層を段階的に剥がしていきたい。同時に、個々のモジュールは ECMAScript Module で組み込むようにしたい。

ということで、そうした。文章で書くと簡潔だけどこれはなかなか大変でした。

become a historian

ふたばの画像掲示板で履歴機能が拡充されたので見てみる。

もともと、カタログの1モードとして過去にレスしたスレッドを集計する機能があり、それが履歴という扱いだった。内部的に考えれば、post したリモートホストが自分であるレスを含むスレッドを select するというサーバ側の SQL のクエリの話になる。

最近拡充されたのはそれとは別立てで、開いたスレッドの履歴を持っていてその一覧を表示するというものである。開いたスレッドの一覧データはどこにあるかと言うと、サーバ側ではなく、クライアントの cookie である。つまりカタログをサーバへリクエストする際にその cookie を投げると、サーバはそれに対応したスレッドの一覧を組み立てて返してくる。

これは良くない設計に思える。これに限らず、クライアント側はセッション ID だけを持たせてセッションにぶら下がるデータはサーバ側が持つほうが正道に思える箇所が色々あるのだが、長いことふたばはその方法を取らない。

それはそれとしてもう1つ不思議なのは、post した履歴と開いた履歴は分ける必要なくない? ということだ。常に post した履歴は開いた履歴の部分集合であるはずだ。これは誰でもそう思う疑問のはずで、まあまだ色々と作りかけなのかもしれない。

そういうわけなのでとりあえず見た履歴の機能をベースにして、post したスレッドを先頭に寄せるという加工を施すようにした。見た履歴を追加したり削除したりする機能はまだ作ってない。

背景色が付いている方が見たスレッド、かつ post したスレッド。そうではない方は見ただけのスレッド

introduce the passive tracking

スレッドの自動追尾というものを先日作った。これはユーザの指示によってスレッドのオートリロードをオンオフするものであり、かつスレッドの勢いに応じたリロード頻度を用いるので、いわばアクティブトラッキングと言える。作った際の感想としては例えば実況スレとかよりもむしろ寝落ちしてしまった際に有効かもしれない…というのは以前書いた。ただし寝落ちするくらいフニャフニャな状態だと、自動追尾を事前にオンにしていない場合がほとんどである。これをどうするか。

というわけで今回はそれと対になるパッシブトラッキングというものを作った。これはスレッドの消える時刻を算出した際、その消える時刻と現在の時刻との中間地点で1回タイマーを仕掛け、そこで続きを読むものだ。例えばスレッドの寿命が最低1時間保証されている板でスレッドを立てた場合、30分後に自動的に続きを読む。その次は15分後、その次は7分30秒後…という調子だ。スレッドの寿命が尽きるまで追いかける機能はそのままで、アクティブトラッキングに対して続きを読む頻度は相当抑えられる。

パッシブトラッキングは常に有効で、どのスレッドでも開いた時点から動作する。ただしアクティブトラッキングとの併存はできず、アクティブ側が優先される。

Link to anything #2

めったにないことではあるが、塩辛瓶やあぷ小のファイル名をコメントに含める際、拡張子を省略する人がいる。その場合の自動リンクはどうあるべきか。

その場合、赤福プラスはいずれのアップローダに対してもファイル名の補完を試みるようになっている。塩辛瓶の場合は、補完のための JSONP インターフェースが用意されているのでそれを使う。誰が作ってくれたのか知らないけど、ありがたいことです。

さて、あぷとあぷ小のサムネイルサービスの場合はと言うとこちらも似たようなインターフェースを用意してある。https://appsweets.net/thumbnail/up2/fu99999s.js などと呼び出すと、拡張子は js だが、そのファイルの情報が json で返ってくる。なぜ js かと言えばそれによって CloudFlare のキャッシュに乗るからである。ちなみに Access-Control-Allow-Origin: * としているので cors の制限は受けない。普通に xhr や fetch で読み出せばいい。

{
  "content": "data:image/png;base64,...",
  "board":"up2",
  "name":"fu25465.png",
  "base":"fu25465",
  "mime":"image/png",
  "size":9152,
  "comment":"",
  "dimensions":{
    "thumbnail":{
      "width":188,
      "height":250
    },
    "original":{
      "width":300,
      "height":400
    }
  }
}

こんな感じで返ってくる。content はサムネイル画像を data URI にエンコードしたものだ。

Link to anything

コメント中の自動リンク処理を整理し、100件弱くらいのテストケースを書き、それにパスするようにした。また、いくつかの既知のサイトに関して override scheme/preferred schemeの機構を設けた。前者はつまり、youtube とかは http://〜 と書かれていても https でリンクされる。逆に塩辛瓶は https://〜 と書いても http でリンクされる。後者はスキームが省略された時に選択されるデフォルトのスキームであり、ふたば内のリンクに https が適用される。

building XPI

赤福プラスはFirefox版もビルドしている。個人的にはFirefoxを常用していないのでビルドする必要性は全然ないのだけど、Firefoxで動かしてみると思わぬバグを見つけたりするのでそういう意味での必要性はある。

そういうわけで動作することを保証するわけでも何でもない扱いなので、XPIをAMOで公開はしていない。あくまでgithubのリポジトリの隅っこに置いてるだけである。

さてそのXPIをビルドする際、rest APIが用意されていてweb-extからそれを呼び出すのだが、たまにビルドが失敗することがある。なんじゃこりゃ。

Before starting, please read and accept our Firefox Add-on Distribution Agreement as well as our Review Policies and Rules. The Firefox Add-on Distribution Agreement also links to our Privacy Notice which explains how we handle your information.

つまりAMOのアドオン配布ポリシーやルールに同意してね!という感じなのだが、同意するために具体的に何をすればいいのかは伝えてくれない。心の中ではい! 同意します! と念じても何も起こらない。

結論から言えば https://addons.mozilla.org/ja/developers/addon/api/key/ に行ってAPIキーを再承認すればいいらしいのだがいやそんなの分かんないでしょ。何をしたいんでしょうか…。あるいは実際に人によるポリシー同意を定期的に行わせるためにわざと不親切なエラーを出しているのかもしれない。考え過ぎか。

several thumbnails

-bash-4.2$ php70cli -r 'echo implode("\n",get_defined_functions()["internal"]);'| grep '^imagecreate'
imagecreatefromstring
imagecreate
imagecreatetruecolor
imagecreatefrompng
imagecreatefromwebp
imagecreatefromgif
imagecreatefromjpeg
imagecreatefromwbmp
imagecreatefromxbm
imagecreatefromxpm
imagecreatefromgd
imagecreatefromgd2
imagecreatefromgd2part
-bash-4.2$ php71cli -r 'echo implode("\n",get_defined_functions()["internal"]);'| grep '^imagecreate'
imagecreatefromstring
imagecreate
imagecreatetruecolor
imagecreatefrompng
imagecreatefromgif
imagecreatefromjpeg
imagecreatefromwbmp
imagecreatefromxbm
imagecreatefromgd
imagecreatefromgd2
imagecreatefromgd2part
-bash-4.2$ php72cli -r 'echo implode("\n",get_defined_functions()["internal"]);'| grep '^imagecreate'
imagecreatefromstring
imagecreate
imagecreatetruecolor
imagecreatefrompng
imagecreatefromgif
imagecreatefromjpeg
imagecreatefromwbmp
imagecreatefromxbm
imagecreatefromgd
imagecreatefromgd2
imagecreatefromgd2part
imagecreatefrombmp

ここのサーバであぷ/あぷ小のサムネイルを作っているが、jpg/gif/png に加えて webp にも対応した。これがなかなか面倒くさい。というのは現在このサーバの php のバージョンは 7.2 なのだがどういうわけかそれについてくる GD には webp を操作する機能が外されているからだ。

そういうわけで仕方がないのでごにょごにょっとして対応。