2018/11/25 12:42 am
Kenya Television Network #4
Uncategorized, , , ,

Firefox で window.getSelection().modify() が動かない件。textarea 上で動かないのはそうなのだが、textarea の外では動く。textarea の外というのは例えば contentEditable=true にした要素のことだ。ということは textarea の代わりにそれをコメント欄に置けばいいことになる。

などと簡単に言ってしまったが、これはかなり面倒くさい。Chrome では textarea 上でも modify() は普通に動くし、個人的に Firefox は常用しておらずちょっとしたショートカットが動かなかろうが特に困らないので、「Firefox などというゴミみたいなダサいブラウザでは動きません」という対応でもいいのだが、それもなんなので、まあやってみよう。

ということでやってみた。とても疲れた。コメント欄が div 要素になったことで:

  • placeholder も自前で表示せざるを得なくなったのだが、その代わり多少表現の自由度が高まったのでとりあえず斜めにしてみた
  • 将来的には絵文字を画像で挿入したりできるかもしれない。その辺や絵文字パレットはやる気が満ち溢れてきたらやる

ところでこの赤福プラスが内包しているちょっとしたショートカットのうち Ctrl+A は 1 回押すと全選択し、全選択の状態でさらに押すと最初に押す前のキャレット位置から前方に向かって折り返し位置境界へ飛ぶ。これはそういうふうに動くように組んであり、仕様である。

2018/11/21 10:18 pm
Reloading Optimization #2
Uncategorized, , , ,

別のアプローチからも高速化を施してみよう。お知らせによると、2018/11/09 付でリロードの高速化が施されたとある。つまり、ふたばが標準で返してくる html における [リロード] ボタンの動作が変更されたということだ。

リロードボタンを押した時、従来はまず HEAD リクエストを飛ばして、更新されていたらページ全体をリロードという形になっていた(たぶん)が、11/9 の変更ではこのページ全体のリロードに代えて、XMLHttpRequest で futaba.php?mode=json 云々をリクエストして、新しく増えたレスの情報だけを json 形式で受け取るようになっている。これによりまず転送量が劇的に減る。転送量が減ることでレスポンスタイムも短くなる。これは立派な差分取得 API と捉えられるので使わない手はない。

ということで組み込んでみた。ここで重要なのは、リロードの手段が 2 つになるということだ。差分形式で得られるのは新着レスとそうだねの全データのみであり、ID 付与やレス削除を同期するにはフルリロードしなければならない。なので、対応する UI としてはまず従来の [続きを読む] リンクの隣に、[差分で続きを読む] リンクを置くことが考えられる。あるいは逆にデフォルトを差分読み込みにして、[フルで続きを読む] でもいい。しかしこれはユーザに対していちいち気にしながらリロード手段を振り分けることを強いているわけで、あまりいい UI ではないかもしれない。自分を含めて、「」もとしあきもそんなに賢くない。

従ってあくまでリロードボタンは 1 個だけで、赤福プラス側がよきに計らって適宜フルと差分を切り替えるのが良いように思える。例えば最後にフルリロードした時刻を覚えておいて、そこから n 分経過するまでは差分リロードとかでどうか。n は定数でもいいし、スレッドの勢いか何かから動的に算出する形式でもいいだろう。

とそういうわけでそうしてみた。n は設定可能な定数にした。デフォルトは 2 分間隔でフルリロードする。この状態でいつもどおりスレッドを見ると、転送量はだいたい 90% 削減できるようだ。もちろん読み込みも速い。すごい。

このあたりの話の流れは、むしろ懐かしい。というのは12年くらい前に Opera 版の赤福プラスに続きを読む処理を加えた際、もともとリクエストヘッダに Range を追記しておんなじような転送量削減の措置を施していたからだ。ところがほどなくして Apache の Range の取り扱いに関して脆弱性が発覚してしまった。そんなわけで双葉の鯖もパッチが当てられ、Range は無事無視されるようになったのであった。がっくり。

2018/11/05 7:28 pm
Kenya Television Network #3
Uncategorized, , ,

というわけで私的な環境におけるキーバインディングを整理したのだが、実は赤福プラス自身も textarea においていくつか Emacs ライクなショートカットを定義している。

でもそもそもこれ、いるんだろうか。textarea にショートカットがたくさん用意されていないと死んでしまうような輩は、そもそも自分でシステムレベルで有効な環境を構築している気がする。

まああっても害になるものではないし、残しておくかな。とりあえず設定にそういったショートカットを有効にするかどうかの項目だけ追加しておくことにしよう。

ところで javascript から textarea 内のキャレット位置を変更するには、selectionStart/selectionEnd をいじる他に、Selection#modify() がとても有用なのだが…どうもこれ、Firefox だと全然動かないみたい。63.0 on Linux、63.0.1 on Windows10 の両方で動かない。リファレンスを自分とこで用意しておきながら動かないってどういうことなんだろう。

2018/10/22 8:41 pm
Let the box be light
Uncategorized, , , ,

画像をクリックするとデフォルトで lightbox 的な振る舞いをするのだが、これにはいくつか積み残しになっている点があった。

まず、画像を原寸表示させた際。このモードではマウスのドラッグにより画像のスクロールが可能なのだが、ドラッグしたままポインタをブラウザ外に持っていき、そこでボタンを離したりすると状態管理の不整合が起きてドラッグしてないのに画像がポインタについて来るみたいなことになってしまう。

これはつまりポインタのキャプチャをしていないからなのだが、何故していないのかと言うと、正確には覚えていないけど多分 Presto Opera にその機能がなかったんだと思う。これを直したい。

次に、全体表示から原寸表示モードに移行した際に拡大の原点がつねに画像の中心なのが不便。たとえば漫画なんかを1枚絵にした画像の場合に、原寸表示にしたあと上端へスクロールさせなければならないのが煩わしい。これを解決したい。

最後に、スマホで撮った写真など、本来90度回転して表示されるべきものが、そうなっていない場合がある。これに対応するため、画像の回転機能が欲しい。

というわけで、上記を解決するために書き直した。

回転にも transition をかけたのでやたらスムーズにくるくる回るのが面白い。

2番めの問題を解決するために、従来は画像をクリックすると lightbox を抜ける動作をしていたのだが、新しいバージョンではクリックしたポイントを原点に拡大縮小するようになった。lightbox を抜けるには画像以外の部分をクリックする必要がある。これはちょっと優しくない仕様変更かもしれない。

それと、画像のドラッグ時にいわゆる bounce-back 機能をつけたのだけど。これをつけると Apple に訴えられるんでしたっけ? ヤバイのかな。

* * *

とかなんとかやってたらまたふたばが全滅してるし…。

2018/10/13 4:22 pm
Improve 💃🏃 handling
Uncategorized, , , ,

赤福プラス上では絵文字を Twitter のそれに準拠して表示するようにしているが、それを v2 に引き上げた。twemoji が謳っているとおり、2,841パターンの絵文字に対応している、はず。

Firefox だとタブのタイトル部分もカラーの絵文字で表示されてえらい。

2018/10/04 9:15 pm
sync, sync, sync
Uncategorized, , , , ,

従来は設定を localStorage に持っていたが、chrome.storage.sync に持つようにした。storage の読み出しは非同期的に行われる。バックエンドの接続と、DOMContentLoadedの待機と併せて、起動時に非同期に行われる処理がこれで3つになった。これらを Promise.all を用いて並列的に処理するようにした。

そういえば sync って Firefox や Opera だとどういう扱いなんでしたっけ? Firefox は対応しているようだ。Opera は対応していないらしい。対応はしていないが、chrome.storage.local にフォールバックされるようで呼び出してもエラーにはならない。なにそれ。Opera Sync があるんだからそれとくっつけてよ。

カタログのリロード処理を見直した。また、デフォルトのソート順の場合はページ数を表示するようにした。選択したカタログのソート順を覚えておき、ページ全体をリロードした際も再現するようにもした。

ところで、xsl において、あるコンディションによって出力する内容を変えたいときがある。例えば出力する要素のクラス名に特定の文字列を状況によって含めたり、外したりしたい場合。

<span class="{if $need_hide='yes' then 'hide' else ''}">...</span>

のようなことをしたい。波括弧に囲まれた属性値テンプレートは xpath の式だ。当然上記はエラーになる。

そんなわけで

<span class="{substring('hide',1,4*number($need_hide='yes'))}">...</span>

のようなとてもトリッキーな書き方をせざるを得ない。= 演算子が true か false かを返し、number() がそれを 1 か 0 に変換し、式が真の場合は hide の先頭から4文字(つまり “hide” そのもの)、偽の場合は先頭から0文字(つまり空文字列)を出力する。

もうちょっとスマートな方法がないものかな。

2018/09/26 12:53 am
WebExtensions and clipboard operation, revised
Uncategorized, , , ,

現状では赤福プラスのファーストクラスブラウザは Chrome なのだが、一応 Firefox の WebExtensions でも動作確認はしている。でもどうなんでしょう。そもそも Firefox たまにしか立ち上げないし、また Firefox 上の他のふたば用エクステンションはここの所かなりたくさんリリースされているので、そこに加わるのもなあという感じ。

まあそれはそれとして。

WebExtensions では、パッケージ内のリソースを参照する際は moz-extension: スキームを使う。Chrome では chrome-extension: だ。これは内部で使用している xsl ファイルに埋め込まれたスタイルシートの中の background-image なんかで問題になる。background-image を使用する箇所だけを別の css ファイルにして解決(あんまりイケている解法ではない)。

以前クリップボードの取り扱いについて四苦八苦したことがあったが、基本的に変わってない。なぜか Chrome と全く違うクリップボード操作を強いられる。ただしそれとは別に、javascript からクリップボードを扱うための API ができて、割と最新のブラウザなら使えるようだ。とりあえず Chrome 69 なら使える。Firefox 63 では about:config から dom.events.asyncClipboard.dataTransfer を true にすることで使える。デフォルトの状態では使えない。

そういうわけなので

if ('clipboard' in navigator)
  navigator.clipboard.writeText(text);
else if (IS_GECKO)
  setClipboardGecko(text);
else
  setClipboard(text);

…みたいな感じにした。

2017/01/18 6:12 pm
Testing with selenium javascript binding #4
Uncategorized, , , ,

そういうわけで Linux 上では WebExtensions 版の wasavi の、Selenium による、Firefox 上の機能テストを一通り通せるようになったのだが、結局これを Windows 上ではできていない。過去の記事で何度か書いているが Windows では既存のプロファイルでもって Firefox を起動させることができず、かつ新規プロファイルに WebExtensions ベースの拡張を登録させつつ Firefox を起動させることもできない。両方できないと身動きが取れない。

実を言うと Linux 上でもテストが問題なく滞りなくできてるわけではない。例えば:

  • なにかが leak してますよ〜的なログがいっぱい出る。
    (node) warning: possible EventEmitter memory leak detected. 11 listeners added.

    云々。このエラー自体は書いてあるとおりのそのままの意味なんだろうけど、Chrome でテストする際は出ないので Selenium 内の Firefox とのやり取りをするコードにおける問題だと思う

  • wasavi でテストする際は当然ながら様々なキーストロークを発生させる必要がある。で、selenium.Key.chord() というものがあり、chord(Key.CONTROL, 'c') などと呼ぶと ctrl+C を押されたとブラウザに振る舞わせるような擬似的なキーストロークを生成する。Key[なんとか] は、型自体は普通の string で、Unicode の Private Use Area 内の文字である。chord 自体は単に引数を全部 join(”) したものに Key.NULL をくっつけたものを返す。したがって擬似的なキーストロークもまた単純な文字列である。さてそういうルールのもとに構成された chord を送出したあと、geckodriver かあるいは Firefox 本体が例えば前述の例だとそれを ctrl+C としてデコードしてくれないと困るわけなのだが。困ったことに、誰もデコードしてくれない。そのまんま PUA が入力され、テストは失敗する。ひどい
  • テスト中 Web Content なるプロセスが生成されて、これが60〜80%の CPU パワーを消費する。同時に Firefox 自体のプロセスが残りの CPU パワーを消費する。そんなわけでテスト中は待ってる以外にほとんど何もできないし、そもそも端末以外のアプリケーションを全て落とした状態じゃないとテスト自体がうまく始まらない。Chrome を起動しながらとか成功した試しがないのである。そして CPU パワーはめちゃくちゃ食いまくる割にテストの進行は Chrome でのそれより 2 割増しくらいで遅い

前述のプロファイル関連の不具合と合わせて、どーにもこーにも…… Firefox が絡む Selenium のテスト環境はいつも質が悪い、質が低いと言わざるを得ない。どうなってんのかなあこれ……。この辺が快適になってくれないと、Firefox 向けの拡張を作るという事自体にくじけてしまいそうなのだけど。

2017/01/14 10:06 pm
Testing with selenium javascript binding #3
Uncategorized, , , , , ,

テストを開始しても、Chrome は起動するもののテストページへのナビゲーションが発生せず、そのままタイムアウトで失敗してしまうことがたまにある。これの原因がよくわからない。とりあえずエラーメッセージとしては “unable to discover open pages” というものが返される。

原因はよくわからない。テスト用とは別の常用の Chrome を起動している状態でテストを開始すると、そういう状況が発生することがあるが、しないこともある。常用の Chrome を落とした状態でも発生することがある。また youtube とかの動画を再生中だと発生する確率が上がるような気がしないでもない。要するに chromedriver が Chrome を起動するものの、複数ある Chrome のうち自分が起動させたものを見失うことがある、というような感じなのだが……そんなわけあるかいな。

上記のエラーメッセージでググっても特にこれだというものもない。謎。

ちなみに Linux 版の Chrome で UI のロケールを任意のものにするには、LANGUAGE=en google-chrome などとする。LANG でも LC_ALL でもなく、LANGUAGE。また、--lang スイッチは効かない。

 * * *

というわけで、とりあえずすべての機能テストを java から javascript へ移植した。疲れた。次にこれを Windows 上の Firefox にて通してテストする。とその前に、Linux 上でも動かしてみよう。

ナウい Firefox で Selenium のテストをするには、geckodriver が必要なので、これをパスの通ったところに置いておく。

var options = new firefox.Options();
options.setProfile(profilePath);
result = new webdriver.Builder()
      .withCapabilities(webdriver.Capabilities.firefox())
      .setFirefoxOptions(options)
      .build()

こんな感じで起動。これは既存のプロファイルを利用するような動作を意図しているが、どうも既存のプロファイルを /tmp あたりにまるごとコピーしてから起動するような感じがする。そのため実際に Firefox のウィンドウが表示されるまでは結構待たされる。

それから、webdriver.actions().sendKeys().perform() が未実装なのだそうでエラーになる。その代わり WebElement#sendKeys() を使う。geckodriver 自体が新しいプロジェクトなので、すべての想定された機能が実装されるまでにはもうちょっとかかる雰囲気。

 * * *

結局のところ wasavi でテストするには

  1. npm install -g selenium-webdriver
  2. npm install -g chromedriver geckodriver operadriver
  3. npm install -g mocha
  4. npm install

と入れて、make run-chrome としてとりあえずテスト用プロファイルでもって起動し、開発者モードで wasavi を組み込み、ついでに dropbox などに wasavi からアクセスして認証を得ておき、ブラウザを閉じてから make test-chrome とする……という感じ。ただし Firefox に関しては、過去の記事の通りオンザフライで WebExtensions ベースの拡張を登録するのが現在のところできないので、wasavi をビルドした上で xpi を登録したプロファイルを用意する必要がある。

2016/12/25 10:20 am
Add-on SDK to WebExtensions #5
Uncategorized, , , ,

ところが結局、Windows 上の Firefox で Selenium によるテストを行うところまで至っていない。現状では

  • Windows 上で
  • 任意の拡張を一時的に読み込ませつつ
  • そのプロファイルを使うよう Selenium に指示する

という術がないように思える。となるとテストのために一旦 xpi をビルドして、署名して、それを使うしかない。

ちなみに Selenium は現在 version 3 になっていて、Firefox に関しては Chrome の chromedriver.exe と同様 geckodriver.exe を介在させるようになっている。そのためにシステムプロパティ webdriver.gecko.driver に geckodriver.exe へのフルパスを書いておく必要がある。

ということで試してみたところ

File p = new File("C:\\path\\to\\profile");
WebDriver driver = new FirefoxDriver(p);

の呼び出し中に

Java heap space
java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:3332)
        at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
        at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
        at java.lang.StringBuilder.append(StringBuilder.java:136)
        at org.openqa.selenium.remote.ProtocolHandshake.amendOssParamters(ProtocolHandshake.java:183)
        at org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:61)
        at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:141)
        at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:82)
        at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:601)
        at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:241)
        at org.openqa.selenium.remote.RemoteWebDriver.<init>(RemoteWebDriver.java:128)
        at org.openqa.selenium.firefox.FirefoxDriver.<init>(FirefoxDriver.java:259)
        at org.openqa.selenium.firefox.FirefoxDriver.<init>(FirefoxDriver.java:247)
        at org.openqa.selenium.firefox.FirefoxDriver.<init>(FirefoxDriver.java:242)
        at org.openqa.selenium.firefox.FirefoxDriver.<init>(FirefoxDriver.java:238)
        at org.openqa.selenium.firefox.FirefoxDriver.<init>(FirefoxDriver.java:131)
        at WasaviTest.WasaviTest.createDriver(Unknown Source)
        at WasaviTest.WasaviTest.beforeClass(Unknown Source)

……どうしろというのか。

 * * *

FirefoxDriver のコンストラクタに独自のプロファイルを与えるのではなくデフォルトのそれを使用するようにすると(要するに何も引数を与えないと)起動した。なにそれ。テストのためのツールである Selenium ががろくにテストされてない…。真面目にやってくださいよ本当に。

 * * *

FirefoxProfile p = new FirefoxProfile();
p.addExtension("/path/to/wasavi.xpi");
driver = new FirefoxDriver(p);

とやるととりあえず OutOfMemoryError で落ちることはないのだが、拡張を登録する処理で「アーカイブの中に install.rdf がないよ〜」的なエラーになる。この場合の wasavi.xpi は WebExtensions ベースの拡張なので install.rdf ではなく manifest.json を見に行ってくれないといけないのだが、Selenium 側がまだ WebExtensions ベースの拡張を知らない状態なのだろう。

うーん! だめじゃん。WebExtensions の拡張を Selenium でテストするのは時期尚早なのか。

Archives