2017/02/20 3:52 am
migrating Dropbox API v1 to v2
Uncategorized, , , ,

去年からさんざんアナウンスされつつも、夏休みの宿題のノリでまあ切羽詰まったらやればいいだろう…と思っていた、Dropbox の API が v1 から v2 に移行するというトピックにそろそろ対応したい。

移行のガイドとしてはこんな文書がある。そのとおりに進めればいいはずだ。たぶんきっと。

幸いなことに、wasaviがDropboxを始めとしたオンラインストレージとやり取りする際に使用するAPIはそれほど多くはない。

  • OAuth2で認証する
  • 任意のディレクトリの直下のファイル・ディレクトリを列挙する
  • 任意のパスからテキストを読み込む
  • 任意のパスへテキストを書き込む

てな感じなので順番に片付けていこう。まず認証に関しては単にエンドポイントが変わるだけなのでこれには問題はない。そして残りは全部面倒くさい。

ディレクトリの列挙。v1では、任意のディレクトリに対してmetadataAPIをlistオプション付きで呼ぶと、そのディレクトリ自身のメタデータと併せて、一緒に直接の子供のメタデータのリストも得ることができた。ガイドによればこのAPIのv2における代替はfiles/get_metadataなのだが。しかしlistオプション付きの場合はそれではまったく代替になり得なく、複数のAPIを組み合わせて使わなければならない。困ったことにこの辺はガイドにはさっぱり解説されていない。というわけでv1のlistオプション付きmetadata APIの代替になる処理を自前で書かざるを得ない。

まず任意のディレクトリ自身のメタデータを読み込むために、files/get_metadataを呼ぶ。それが完了したら(ここは別に並列でもいいが)files/list_folderを呼び子供のメタデータを得る。また、このAPIは一度に返される結果セットのサイズが決まっているようで、その呼び出しが結果セット全体の最後かどうかは不定だ。そのかわり呼び出しのたび最後の結果かを示すフラグ has_more を返すので、つまりこの後呼び出し側が責任を持ってfiles/list_folder/continueを複数回呼び出す必要がある。それから列挙対象のパスなどを与える方法が若干面倒でContent-Typeをapplication/x-www-form-urlencodedではなくapplication/jsonにした上でJSONをPOSTしなければいけない。

なんだこれは。バージョンが進んだのにクライアント側でやらないといけない仕事が格段に増えている。ここまでの遷移を見てみると、個々のAPIができることがより制限され、かつその代替がよく解説されないという状況にしか見えない。どうにもいまいちだと言わざるを得ない。

ファイルの読み込み。files/download を呼ぶのだが、読み込み対象のパスなどを与える方法が若干面倒。POST のボディではなく、Dropbox-API-Argなるヘッダに、JSONを文字列化したものを与える必要がある。それができない場合は、クエリ文字列として?arg=<stringified JSON parameter>てな感じで与える必要がある。XHRの場合何でも好き勝手なヘッダを追加することはできないのでクエリ文字列を使用することになる。で、読み込み結果はレスポンスボディとして帰ってくるのだが、メタデータはDropbox-API-ResultなるヘッダにこれもやはりJSON形式で帰ってくる。

ファイルの書き込みにおける変更点は読み込みと同じ。v1では書き込みの際はPUTメソッドを使用した。これはこれで筋が通っている仕様だと思うけど、v2ではPOSTに変更されている。

というわけでAPIの使い勝手としてはv1のほうが勝ってたんじゃないかなあ…と思わなくもない。

それから、v1では多くのパラメータに汎用的にlocaleを付加することができたがv2ではHTTPのヘッダ(Dropbox-Locale)に含めるように変更になったとか、全てのアクセスはPOSTメソッドでないと「いけない」(GETも受け付けるとかではない)などの細かな変更もある。メソッドはともかく、ロケールは難しい。前述の通りXHRでは任意のリクエストヘッダを送出できるわけではないからだ。X-Dropbox-Locale とかなら送れるんだけどなー。まあロケールは主にエラーメッセージとかに影響するはずなのでそれほど困るわけではないのだけど。

2017/02/13 6:13 pm
escape
Uncategorized, , ,

sコマンド中にバックスラッシュによるエスケープシーケンスを含んだコンポーネントを含めると正しく引数が解析されないのを修正。

つまり、:%s/$/ \\/g みたいなのが正しく動作しなかった。

そろそろ各ブラウザの公式エクステンションストアに置いてるwasaviをアップデートする頃合いだ。

 * * *

アップデートした。今年はこまめにストア版もアップデートするようにがんばるぞい。

2017/02/11 9:34 am
Extend the “writeas” effect
Uncategorized, , ,

wasaviはtextareaの他にcontenteditable属性の付いた要素も編集できるわけだが、そういう要素にどのように中身を埋めていくかはサイトによって異なり、統一されたものはない。そういうわけで、writeasというオプションを用意している。これには以下の値を割り当てられる:

  • html — バッファの内容を markdown とみなし、html を構築する(デフォルト)
  • div — 各行につき div 要素を生成する
  • p — 各行につき p 要素を生成する
  • textAndBreak — 各行につきテキストノードを生成し、改行として br 要素を追加する
  • plaintext — バッファ全体を単体のテキストノードにする

issue #156でこの割当をサイトごとにできないかという要望があった。なるほど確かにそのほうがいいだろう。同様にサイトごとに動作を割り当てられる設定としては、wasaviの起動をさせないためのブラックリストがすでにある。こちらはさらにサイトの下にCSSセレクタを指定できるようにしてあり、個別の要素のレベルで制御が可能だ。writeasもそのようにすべきだろう。

具体的にはwriteasに対して上記の値の他に、JSONとしてパース可能な文字列を与えることを許す。このJSONは例えば:

{
  "http://example.com/*": "div",
  "http://example.net/*": [
    {
      "selector": "#element-id",
      "writeas": "textAndBreak"
    }
  ]
}

と言った感じの連想配列になり、URLに対するwriteasの値、あるいはURLおよびセレクタに合致する箇所のwriteasの値が取られる。

ところで、ブラックリストはwasaviのオプションではなくwasaviの起動前の設定である。この区別はとても重要だ。本質的にwasaviの設定はwasaviのsetコマンドから操作できるのが望ましい。そういうわけに行かないものだけが起動前設定として独立されるべきである。起動前の設定というのは他にexrcや、起動ショートカットや、起動時の効果音などだがこれはまさにwasaviが起動する前に処理される事柄なのでwasaviの設定からは「止むを得ず」独立させてあるということだ。それ以外の設定、つまりwasaviが起動した後に参照される設定はすべてwasaviのオプションにしてある。とこのように厳密に区別しているのである。さて今回の変更を施す場合、URLごとのリストという点ではブラックリストと似たものになるのだが、しかしいつ参照されるのか? という点で考えるとwriteasは依然としてwasaviのオプションだ。そういうわけで独立した設定にはしない。

ということは、上記のようなJSONをwriteasに代入する際は

set writeas='{ \
  "http://example.com/*": "div", \
  "http://example.net/*": [ \
    { \
      "selector": "#element-id", \
      "writeas": "textAndBreak" \
    } \
  ] \
}'

という感じのexコマンドになるのだが、これはこれで改行のエスケープがすごく冗長だ。これはちょっと何とかしたい…。

2017/02/04 5:00 pm
Showing a newline mark
Uncategorized, , ,

viにはlistオプションというものがあり、それをオンにすると

  • 行の末尾に $ が表示されるようになる
  • タブ文字が ^I で代替表示されるようになる

となる。この内、行末尾の表示について実装したい。残念ながら、タブ文字の代替表示は難しい。

ここで行末尾を示すために文字 $ が用いられるというのは、vi が端末上のテキストエディタだからで、wasavi の場合はもうちょっと表現力があるので別の記号にしたい。

行末尾、つまり改行を示すための記号というとまあだいたいこんな感じだと思う。特に日本産のエディタでこれじゃない奴はないのではなかろうか。あと、MS Word。海外産だと行をパラグラフとみなして、Pilcrowが多いかもしれない。まあどちらでもいいけれど、とりあえず前者を使うことにしよう。

ところで、改行マークを表示することに意味があるとは特に思えない。それが役に立つのは、行末に余計な空白文字を残している、いわばダーティというか、非正規な状態の行を目視で確認したい場合くらいだろう。そういう行を正規化したいのなら目視確認なんてせずに正規表現による全置換すればいいだけの話で、改行マークを表示することに意味はない。また、正規化済みの行は空白ではないグリフの直後に改行があることが自明なわけで改行マークを表示する必要はなく、非正規な状態の行だけその旨を表示するようにすればいいのだが、非正規な行の場合でも本来強調表示すべきは行末の余計な空白文字であって、改行マークではない(vimの場合で言えばlistcharsに対してeolではなくtailだけ設定した状態)。従っていずれの場合でも改行マークを表示しなければならない積極的な理由はないように思える。

と、そう考えればそうなのだが、実は去年はあんまりwasaviの開発を進められなかったのでいまいち内部の構造を忘れかけているのであった。そのリハビリのためにあえて実装してみる。

ということでこうなった。

2016/04/28 4:56 am
Assign to a register
Uncategorized, , ,

エディタを開いた時に特定のレジスタに常に任意の内容が入っているようにするには、vim では vimrc に

:let &z="foobar"

みたいにすると思う。この手のことは、wasavi ではまだできない(レジスタの内容自体は wasavi を閉じるときにエクステンションのバックエンドへ永続化されるので結果的に件のツイートの要件はすでに満たしている)。

できないというのは、vim の let 文は、ex コマンドというよりは vimscript のいわゆる assignment statement で(ただしヘルプ上では expression command ということになっている)、wasavi にはまだスクリプティングの機能がないということだ。

もしその手の機能を設けるとしたら、ex コマンドに script を新設して

:script wasavi.registers["z"] = "foobar";
:script << END
wasavi.registers["z"] = "foobar";
END

な感じになると思う(スクリプトの文法がこうなるかどうかは未定だ)。vim のようにスクリプトの構造が ex コマンドのスーパーセットになっているような設計には、多分しない。

実は以前に script コマンドを実装しかけたことはあったのだが

  • wasavi の動作のうち、バックエンドにメッセージを送信して返事を待つ箇所は非同期だ。したがって、スクリプトも単にシーケンシャルに実行するわけにはいかない。つまり
    a = wasavi.registers["*"];
    print a;

    というようにはいかない。

    wasav.registers.get("*").then(function (content) {
      print a;
    });

    のような形式にせざるを得ない。すべてこの形式だとお気楽なスクリプトの範疇を超えている。同期か非同期かで文法を使い分けるとしてもそれも煩雑すぎる

  • スクリプトが例えば javascript であると定義するとそれを実行するのは eval 文になるが、それだとできることが多すぎる。何でもできすぎると動作の保証もセキュリティ面での安全性の保証もできない
  • 一方で完全に動作を制御可能なスクリプトインタープリタを実装し、その管理下でスクリプトを動かすとなるとコードサイズと実行速度の危険が危ない

などなど問題がありすぎてこりゃ時期尚早だということで棚上げになっている。どうしたものか。

2016/04/23 8:24 am
Options page
Uncategorized, , ,

前の記事で触れたように、かつては Chrome のエクステンションに関するオプションページは、1つのタブを消費するタイプだった。そのためのリファレンス

それが、最新のエクステンションの仕様では chrome://extensions のページにオーバーレイされる形で表示するようになっている。そのためのリファレンス。オプションページの構造自体は変わっていないのだが、表示される場所が違う。この場所の違いを吸収するため、chrome.runtime.openOptionsPage() という API が新設されていて、manifest.json に記述されているオプションページの設定に従ってふさわしいそれを開くようになっている。

オーバーレイ表示といってもどうということはなく、要するに iframe みたいなものだ(実際には Shadow DOM や object 要素などを駆使したナウいものになっている)。ということは、やろうと思えば chrome.runtime.openOptionsPage() を読んだ時、その時点のカレントタブにオーバーレイ表示することもできるはずだ。その方が無駄なタブ移動等々が少なくて混乱することはないのだが、実際はこの API を呼ぶと 必ず chrome://extensions を開いた上で(すでにそれを開いている場合はそれをアクティブにした上で)オーバーレイ表示する。これはまあきっと、偽装をさせないための制限なのだろう。

そういうわけで wasavi についても新しいオプションページで開くようにしてみたのだが、別にこれと言って新しくなったメリットはないわけで、リバートしてしまった。

2016/04/21 8:41 am
wasavi and SSL
Uncategorized, , ,

ペンディングになっていた件。つまり wasavi が参照する、appsweets.net 上のアドレスを http 化したいな、という件。そういったアドレスは 2 種類あって:

  • http://wasavi.appsweets.net/ – app モードのプレースホルダとなる URL
  • http://appsweets.net/authorized.html – オンラインストレージに接続した際のコールバック URL

これらを https 化するために、cloudflare の助けを借りることにした。cloudflare というのは CDN サービスだ。cloudflare 側にアカウントを作り諸々設定した上で appsweets.net ドメインに割り当てていたネームサーバを cloudflare が提供するそれに書き換える。そうすると appsweets.net を正引きすると今までは直接 xrea のサーバ s278 を指していたわけだが、cloudflare 内のサーバを指すようになる。このサーバがプロクシとして動作し、ブラウザ – cloudflare プロクシ – s278 という形でデータが流れる。このときメディアファイルについては cloudflare 側で適当にキャッシュされたりするのでその分 s278 側の転送量が抑えられる……等々のメリットがある。

そして SSL まわりだが、不思議なことに cloudflare に登録したドメインに SSL の証明書が即与えられる。従ってブラウザと cloudflare プロクシ間の接続については https でアクセスできるようになる。この時に使用するドメインはもちろん appsweets.net なので、結果的に appsweets.net が https でアクセスできるようになる。ちなみにここまで完全に無料である。なんか騙されたみたいな話だが、実際にこうなる。

そういうわけで、上記の 2 アドレスについても https を使えるようになる。

デメリットとしては appsweets.net の内容を cloudflare に 100% 預ける形で仲介させているので、悪意の有無を問わず、そちらで変な操作をされることが絶対にないとはいえない。SSL 化されるのはあくまでブラウザと cloudflare 間であって、cloudflare と appsweets.net 間は http でやりとりされる。従ってすごくセキュアというわけではない。ブラウザと cloudflare 間の SSL 接続はバーチャルホスト名ベースの割と新しい仕組み(SNI: Server Name Indication)を利用していて、古めの OS/ブラウザからはアクセスできない(たとえば、Presto Opera 12.17 ではアクセスできない)

2016/04/14 7:45 pm
Mysterious bound keyword
Uncategorized, , , , ,

Firefox 46a だったか 47a だったか忘れたが、wasavi と赤福プラスが両方動かなくなったことがあって調べてみた所、

  1. 両方が使っている extension_wrapper.js 内で、それが Firefox + Add on SDK で動いているかどうかを判断している箇所があり
  2. Firefox のその該当バージョンで判断のもとになっている箇所が変更されたため

動かなくなっているということだった。

extension_wrapper.js は Chrome でも Opera でも Firefox でも共通して動くので、それぞれのブラウザのうちどれで動いているのかを判断しないといけない。Chrome なら window.chrome が存在しており、Opera なら window.opera が存在しているのでそれが判断の助けになる。。

一方 Add on SDK の PageMod における content script かどうか、というのを判断するためのあんしんあんぜんな方法はないように思える。とりあえずのところ、self.on が存在し、それが function であり、かつその toString() の値が “function on (.*?) { [native code] }” とかそんな感じになっていればまあたぶんきっと SDK ベースの content script だろう…というような判断をしている。”[native code]” がミソなのだが、これが最適解というわけでは全然ない。

これが、今回動かなくなってしまったのである。それは、self.on の toString() の値が “function bound on (” というように、謎のキーワード bound が入るようになっていたからだ。bound……って何?

2016/04/12 12:34 pm
wasavi and content type of a page
Uncategorized, , ,

issue #128 と関連して。

discuz という PHP アプリケーションがある。これは要するに中国版 phpBB であって、phpBB 同様にいわゆるフォーラム機能を提供する。中国語圏内ではとても有名なのだそうだ。ざっと見た感じそれほど phpBB との機能差は見受けられなく、なんで似たようなものを一から作りなおす必要があるのかよく分からないが、まあそれは特に問題ではない。ちなみに disqus とは何の関連もない。

で、wasavi を有効にしていると discuz 上での post が上手くいかないというクレームが来たのである。それに対応してみたい。

まずダウンロードし、ローカルにインストールした。エンコーディングによって何種類かあるが、とりあえず繁体字・UTF-8版を持ってきて、適当なディレクトリに展開する。discuz はバックエンドとして MySQL を使用するので、あらかじめ適当なデータベース、それにアクセスするためのユーザアカウントとパスワードを作っておく。ブラウザから 展開ディレクトリ/upload/install/ にアクセスし、適当に必要な項目(含データベース情報)を埋めつつウィザードを進めるとインストールが完了する。それにしても繁体字ならなんとなく雰囲気で読めるんじゃないかと期待したが、さっぱりわからないものです。登録、がログインのことなのだそうだ。そうなんだ…。

そんなわけでその環境で試してみると、たしかに上手くいかない。discuz はサーバへの post は ajax ベースであり、隠れた iframe を経由している。post の応答である XML ドキュメントを iframe に受けて、その結果が動作を左右する。ここで wasavi のエージェントが XML ドキュメントに対して余計な script 要素を追加してしまうと、Chrome がそれを XML とみなさなくなってしまい、XMLDocument プロパティが無効になってしまう(このへんは深くは追ってないけど)。その後 discuz 側が無効な XMLDocument プロパティを参照して例外で中断してしまう…という筋書きだ。つまりつきつめると XML 文書に対して wasavi のエージェントを動かしてしまうことがバグの原因だ。

これは正に issue #128 と関連している。前の記事では text/html だけではなく application/xml(あるいは、+xml を含むもの)に対しても実行したほうがいいのかなと思ったわけだが、今回のバグは実行してはダメなパターンなのである。

エージェントは wasavi を起動するために textarea などの HTML 要素を含む文書を対象としなければいけない。なので、単純に考えれば対象となる文書は text/html か、application/xhtml のいずれかだ。ただしもう一つ可能性があって、サーバからは xml が送られてくるが、ブラウザ上でそれを XSLT で html に変換するという構造のアプリケーションだ。この手のアプリケーションを、そういう構造だと判断する処理はかなりめんどくさい。

しかしそんなへんてこな構造のアプリケーション存在するんだろうかと考えると、うーんまあ、ないか。ないな。うんうん。ないない。

というわけでエージェントを実行すべき contentType は上記の通り html と xhtml だけにしよう。xhtml にしても生きてるのか死んでるのかよく分からない状態ではあるが…。

2016/04/08 9:56 pm
BotanJS-vim: Yet another vi editor in your browser
Uncategorized, ,

Demo: http://tgckpg.github.io/BotanJS-vim/
Source: https://github.com/tgckpg/BotanJS-vim
Reddit comments: https://www.reddit.com/r/vim/comments/4d42r3/hi_ive_implemented_vim_in_a_textarea/

Looks good.

アーカイブ