「Silverbirdをforkする」の版間の差分

提供: STUDIO DDT ONLINE
移動先: 案内検索
(RAM消費を抑えられないか)
(ES6機能を導入してみる)
254行目: 254行目:
  
 
====ES6機能を導入してみる====
 
====ES6機能を導入してみる====
ChromeでもFirefoxでも、ECMAscript6で導入予定の機能がいろいろ実装されている。たいていの場合で、新しい機能のほうが既存のJavascriptのものよりも便利だったりパフォーマンスがよくなるようになっているので、いくつか汁Mにも実装を試している。主に、Object.observe、Set、Mapなんだが。
+
ChromeでもFirefoxでも、ECMAscript6で導入予定の機能がいろいろ実装されている。たいていの場合で、新しい機能のほうが既存のJavascriptのものよりも便利だったりパフォーマンスがよくなるようになっているので、いくつか汁Mにも実装を試している。主に、Object.observe、Set、Mapなんだが。Chrome41からはTemplate literalsが使える見込みなので、一部のテキストまわりはこれに変えるつもり。
  
 
==ソースとバイナリ==
 
==ソースとバイナリ==

2015年2月9日 (月) 09:39時点における版

目次

前書き

毎日会社にサラリーマンのコスプレをして出かけているものの、俺って全然仕事がなくて暇なので、何か暇つぶしをやろうという話。は?コンプラ?なにそれ美味しいの?

Twitterのクライアントとして、Chrome拡張のSilverbirdを利用している。フォローするまででもないかな?的人たちをリストに分離するということは誰でもやっていると思うが、Unified Timelineとして合一したTLを表示してくれるため、とっても便利。

ただ、不満もある。

  • リストのユーザの公式RTがUnified Timelineに載らないので載せてほしい
  • 公式対応も始まっている、改行付きツイートに対応してほしい
  • 最近Chromeをアップデートしたら、リストタブが表示崩れを起こしてる(WindowsのChromeだけのようだが)
  • Twitter API 1.1対応ってしてないよね(本家でも1.1対応したけど、コード見たらすごいやっつけでワロタ)

よろしい。ならばforkだ。(SilverbirdのライセンスはMIT)

環境を作る

前提

  • 元々のソースはGithub上にMasterリポジトリがある。
  • ビルド用にRakeを使っているみたい。Rubyインスコですか。
  • でもPCはWindowsで、しかもXPです。^q^
  • そもそも俺、プログラムしばらく書いてないし、本業プログラマじゃなかったからウォォォォォ
  • 当然Chome拡張なんて作ったこともないし、jQueryみたいなモダンJavascript触ってもいない。Javascript 1.6?もよく知らないので、Arrayにforeachなんてあるの?の世界観。

Git for Windows

2013年3月現在では、便利なものでWindows環境向けにもGitの実装がある。Git for Windowsをダウンロードしてきてインスコ。このとき、パスの設定のところでRun Git from the Windows Command Prompt、つまりWindowsのコマンドプロンプト内でもGitが叩けるオプションを選んでおく必要がある。これは、後述するRakefileスクリプト内でGitのコマンドを実行しているため。

そうするとGit BashというBashライクなシェルが提供されるので、そこからリポジトリを操作するみたい。ディレクトリ構造はルートの下にドライブレター、各ディレクトリという構造でアクセスできるみたい。コマンド系はLinuxっぽい。シェルのプロンプトにユーザ名とカレントディレクトリとカレントブランチが表示されている。

Githubからcloneしておく。

あと、git config --global user.nameとgit config --global user.emailも忘れないように。(いつも忘れる)

Rake

cloneされたローカルのリポジトリを見てみると、Rakefileが置いてある。RakeはRubyで書かれたMakeの実装(だったと思う)。Chrome拡張のパッキングにRakeを使っているみたい。

Ruby for Windows

まずはRubyをインストールしなきゃならない。

といっても、今はRubyInstallerをダウンロードしてきてインスコするだけ。2.0系じゃなくて1.9.3系をダウンロードしてきたのだけど、特に理由があったわけではない。インスコ時にパスを通してもらう。

Windowsのコマンドプロンプトからruby -vとgem -vが動くことを確認。

Rake

コマンドプロンプトでgem install --remote rakeする。

Silverbirdのローカルリポジトリに移動して、試しにrakeしてみたがcrxmakeがねーぞと怒られたので、gem install --remote crxmakeすると、zipとcrxmakeがインスコされた。

ビルド

Silverbirdのローカルリポジトリでrakeしてみると、private key not existと怒られる。アイエエエエ!?

Rakefileを読んでみる。Chrome拡張としてパッキングする部分はcrxmakeが担っていて、なんかリポジトリ外にある秘密鍵ファイルを参照している。当然だけど、開発者しか秘密鍵ファイルなんて持ってないから、適当な秘密鍵ファイルを自前で用意しないといけない。このRakefileではベータ版リリース用の署名も考慮されているようで、賞味2つの秘密鍵が必要なかんじ。

Chrome Webstoreで公開するためのことをググると、そもそもChrome拡張の動作検証にはデベロッパーモードで拡張のあるディレクトリを開けば良いっぽいのだが、とりあえず公開しないなら秘密鍵は要らないのかな?

ともあれ、crxmakeの機能として、秘密鍵ファイルの生成まで勝手にやってくれるっぽい。crxmake --pack-extension=ローカルリポジトリのディレクトリ --key-output=秘密鍵の出力先でcrxファイルと署名に使った秘密鍵ファイルが生成されるので、それに合わせてRakefileのKEY_FILEの内容を修正しておく。環境によってはTEMP_PACKAGE_FILEも修正しておこう。俺はテンポラリファイル作れねーぞ的な怒られ方をした。

これでrakeを実行すると、crxファイルとzipファイルが生成されるようになる。ベータ版をリリースしたい場合は、rake pack:betaするとベータ版用の秘密鍵で署名される(みたい)。

動作確認

とりあえず、Chromeの拡張ページ(chrome://extensions/)を開いて、デベロッパーモードのチェックボックスを有効にしてしまい、crxファイルをドロップすればインストール可能。未パッケージ状態でも拡張は利用できる。

Chrome Web Store向け

Chrome Web Storeに公開してみるにあたって、いつまでたってもアップロードが失敗してワケワカだった(エラーメッセージ見てもどう対処すりゃいいのか書いてない)のだが、どうもChrome Web Storeではサーバ側で署名を勝手にするようで、key.pemがZipファイル内に含まれているとアップロードを拒否されることがわかった(エラーメッセージ見てエスパーする必要があった)。

それなら簡単。いったん普通にrakeして、Zipファイルからkey.pemを削除してからアップロードするだけ。

……ここまで来て気づいたわけだが、Chrome Web Store公開が前提の場合は、CRXファイルの配布なんてやらないので、Rakeでビルドとか要らないんじゃね……?

(まぁ、Chrome Web Storeでの公開は短期間にする or 完全放置になるだろうから、CRXファイル配布のための道筋を作っておくのも悪くないかな)

fork

ここからが本題。最初はgithub使おうと思ってなかったのでローカルにcloneしてそこだけで作業するつもりだったのだけど、週末挟んで自宅でも遊ぼうと思ったので、github使うことにした。今までやってた変更は大した変更でもないし、結果はわかっているので後でやり直しても大したことはない。まずはgithub上でリポジトリのforkを実行した。

forkするにあたって、manifestファイルのnameとかversionは変えてしまう。とりあえず名前はSilverbird Mにした。

機能追加などはこのfork用のブランチから再度ブランチして、あとでmergeするつもりだったんだけど、ぶっちゃけ面倒くさいのでほとんどmasterで作業してrebase -iしまくりで運用してる。計画持って機能をいじってるわけじゃないので、開発内容ごとにブランチ切ると、何をどこのブランチで作業していたのか忘れるし、ほとんどコンフリクトしてマージできやしないから。

コンシューマキーとコンシューマシークレットを変更する

まず手を付けるのはコレ。

なぜかというと、試しにChromeにインストールしてみると、ごく普通にSilverbirdとしてTwitterへのアクセス許可を申請しちゃうため。コンシューマキーとコンシューマシークレットが公開されているソースコードにそのまま記載されているみたい。俺がこのまま変な挙動をするクライアントアプリを書いてしまった場合に、SilverbirdそのものがTwitterからBanされるのは困る。

いちおう.gitignoreファイルを確認してみるとsecret_keys.jsが入ってるのだけど、manifest version2対応の時にlibの下に移動したようなので、そのときからアップロードされてしまっていたのかも。 fork元のほうはどうしようもないんだけど、forkした俺のリポジトリ上からは完全に履歴からも削除することにした。まずgit rmでlib/secret_keys.jsとGoogle Analytics用のlib/analytics.jsを削除してgit pushする。リモートでもファイルが一覧に出なくなったら、.gitignoreを変更してバックアップしてあったsecret_keys.jsとanalytics.jsを付け加えてもgitが認識しないことを確かめてから、gitの全履歴から当該ファイルの記録を削除する。これはググって見つけたので、必要なひとは適宜やってください。

Twitter APIにアクセスできるようにする

さて、あらためてコンシューマキーやコンシューマシークレットを取得しなおす。具体的には、Twitterに対してクライアントアプリケーション作りますよ、という登録をする。ふだん使ってるTwitterアカウントを開発で使うのも色々とアレなので、専用のアカウントを取得しておくことにした。@Silverbird_Mです。

アプリケーションを登録する

Twitterの開発者向けウェブサイトに、作成した専用のアカウントでログインし、アプリケーション登録をする。これでConsumer keyとConsumer sercretが利用可能になっているはず。とってもかんたん。

最初はApplication TypeのAccessパーミッションはRead onlyになっているので、SettingsタブからAccess levelを変更する。いちおうフル機能のTwtterクライアントなので、Read, Write and Access direct messagesに変更しておく。Twitterのウェブサイト経由でログインする(というかOAuth用か)ので、Allow this application to be used to Sign in with Twitterのチェックも必要。

サーバサイドアプリやBotの場合は必要なのだと思うが、ここではCallback URLの入力は必要がない。manifest.jsonを見るとcontent_scriptsが指定されており、Oauthの結果画面からPINコードを自動取得するスクリプトが動作する。ここでは単純な文字列(silver birdという文字列)のマッチを確認していたので、この評価部分を合わせたら自動的にPINコードを認証してアクセストークンを取得するつくりのようだ。

コンシューマキーとコンシューマシークレットを変更する

変更対象ファイルは/lib/secret_keys.js。もうすでに.gitignoreの編集や履歴からの削除をやっているのでアレだが、該当部分はすぐにわかるので、文字列を変更してアプリケーション認証させてみると、Silver BirdからSilverbird Mの認証画面に変わった。

ちなみに、最初コンシューマキーとシークレットを編集しようとsecret_key.jsを開いたら、identicaとyfrogとtwitpicのAPIアクセスキーも記載してあってウォォォォォ!identicaはともかく、yfrogとtwitpicのAPIキーは取得して記載してみた。

Google Analytics アプリケーションキーを変更する

manifestファイルにGoogle Analytics使ってる感のある設定があるので、どこかにアプリケーションキーが埋まってるのかなーと思ったらlib/analytics.jsにキーが埋まってた。デバッグの時に使ったりしていたのだろうが、せっかくなので一時的に有効にしてみる。リポジトリや履歴からは削除しちゃっているのでアレだが、内容はGoogle Analyticsのチュートリアルで書いてあるものがそのまま書いてあるだけだった。

Google Analyticsは利用規約の都合で利用者になにを目的にトラックしているのか明示しなければいけないので、普段使いには面倒臭いサービスだと思うな。当サイトでも使ってますけど!

さて、変更したはいいのだけど、一向にトラッキングがされない感ある。Chromeのデベロッパーツールでコンソールログを見てみると、どうもCSPにインラインスクリプトがあるからバーカバーカと言われている。そうはいっても、インラインスクリプト使ってるところなんてないよなぁ、と思ったら、よく見るとエラー吐いてたのはjQueryコアだった。この時点ではjQueryのアップデートはすげーめんどい感じだと思っていたのでソロソロと1.4.2から1.5.2に上げてみたら、CSPの警告が消えた。インラインスタイルシートが各所に含まれていることもあるので、manifestファイルには

"script-src 'self' https://ssl.google-analytics.com; object-src 'self'; style-src 'self' 'unsafe-inline'"

こんな感じで書いてあった。

で、どうやらGoogle Analyticsが変わるんだそうだ。Univarsal Analyticsというふうに変わるそうで、APIの作りも変わるし、呼び出し方法やURLも変わる。一応Univarsal Analyticsに対応させてみたつもりなのだが、やっぱりトラッキングされている感じはないな。よくわかんない。よくわかんない……。よくわかんないので……、Google Analytics抜いちゃった!もう汁Mでは使わないよ!

URL Shortener と Expander

短縮URL作成と展開にウェブサービスを活用しているようなのだが、APIキーとかそのまんま埋まってる。とりあえず、サムネイル表示に対応するものを増やすついでに、APIキーのたぐいは分離しておいた。(とはいえ、暗号化なんかがされているわけではないので、Chromeウェブストアなんかで配布を始めるとバレバレになるだろう)

当初はリンクにホバーすると、URL展開のプロセスが始まり、URL展開→画像展開の順番で一方通行になっていた。サムネイル画像のURLを取得するのに非同期APIを叩かないと行けないものは対応できていなかったので、URL展開を再起で回せるようにして、最終的にJPEGファイルなどになったら画像展開するように変更したい。

Shortenerとしてbit.lyとGoogleを使っているのだが、GoolgeはOAUTH2によるトークン取得にコンシューマキー&コンシューマシークレットを投げてクレデンシャル・バリデーションとやるのだが、bit.lyのほうはログインID&パスワードを投げるだけで、いろいろ危ない。なので、エンドユーザのIDとパスワードを使ってトークンをもらうように変更した。つまり、汁MではURL短縮のためにはGoogleアカウントかBitlyアカウントが必要だよ。

日本語ロケールに対応してみる

アプリケーション内でSilver Birdとなっている部分をSilverbird Mと置換しようと思うと、_localesディレクトリ下部にある各言語ファイルも置換する必要がある。せっかくなので、jaロケールを追加して日本語化してみよう。

_locales/jaディレクトリを掘る

ベースになっている英語、つまるところenディレクトリをコピーしてリネームしてみる。中身は単純なJSONファイルになっていて、ちくちくとそれらしい単語やら訳文やらに置き換えるだけ。

オプション画面の最下部にLocales Reportというリンクがあるのでクリックしてみると、ローカライズの抜け部分の指摘がされていて、赤く変わっている部分がなくなればローカライズ完了となるみたい。ただ、各ロケールのmessages.jsonの値を横断的に表示しているだけのよう。enロケールでもエラーが2つあるなー、と調べたら、ukロケールに要らん値が追加されていることがわかった。特に何も考えずに削除しておく。

訳文がそろっても、CSSの都合で見た目が崩れることがあるようなので、長い文章には改行を入れたり、訳文を調整したりする必要がある。また、「retweet by ○○」みたいなものは「○○さんのリツイート」のようにしないと日本語としては不自然なのだが、どうも文字列の順番はハードコーディングのようなので、ここでは調節できない。ひとまず「リツイートしたひと」ぐらいにしておく。

アプリケーション内の名称を統一する

jaロケールは作業中から名称をSilverbird Mにしておいたのだが、ここでついでに他のロケールのローカライズ文章も、Silver Birdとなっている部分をSilverbird Mに変更する。

さらにそのあと、Silver Birdの名称が残っている部分をgrepすると、libディレクトリ下に3つほどあった

  • lib/shortener_lib.js
  • lib/stream_listener.js
  • lib/tweet_manager.js

tweet_manager.js内で使用されている箇所はchrome.browserAction.setTitle、おそらくマウスカーソルをホバーした時にチップメニュー上の文字列の指定部分なので変えても問題ないだろうが、残りの2つはGoogleのURL短縮サービスのところでOAuthシーケンスのどこかで使うパラメータ、Userstream APIの要求時拡張ヘッダX-User-Agentの値だった。どちらも本来なら各サービスのAPI仕様で規定されているだろうから、値を変えると動かなくなるとかあるかもしれないのだが、動かなくなってから考えればいいというスタンスで変更しておいた。

よりまともな日本語化

結局のところ、ハードコーディングされている部分を小分けにすることしかできそうになかったので、地道に小分けにした。ほぼ、日本語化はできたんじゃないかと思う。あまり発生しそうにないエラーなどはエラーメッセージが英語で埋め込まれていたりするが、まぁ、気にしない方向で。

本当は、レイアウトも含めたテンプレート化ができれば良いんだろうけど……。ゴリ押しっぽい実装をどうにかかっこ良くする知識は俺にはないのです。プログラマじゃないので。

Chrome41からはES6のTemplate literalsが利用可能になる見込みなので、この辺のコードはTemplate literalsを使って書き直すかもしれない。

jQuery関係をアップデートしてみる

Silver Birdではサードパーティライブラリという位置づけでjQueryとjQuery UIを主に使っている。ただし、コアは1.4.2、UIは1.8.1と、現行のjQueryよりも古い。Google Analyticsを動かすためにmanifest version2で厳しくなったCSPのコンプライアンスのためにjQueryは1.5.2にアップデートしてみたが、それでも古い。

あまり深く考えてやろうと思ったわけではないのだが、新しいライブラリであれば高速になっていることが期待できるし、もうちょっとサクサク動くようになるのではないか?という期待があった。(実際には、あまり変わりないどころか、いまいち使い方が悪いのか遅くなった気がする)

jQueryコアをアップデートしてみる

さて、いろいろ試すと、1.5.2から1.6.4にアップデートすると、タイムラインのページングがうまくいかない。となるとそこに差分があるはずだ。

ググるとjQueryのバージョン差を解説しているサイトはいっぱいでてくる。1.5系と1.6系で大きく変わったのは、属性・プロパティへのアクセスとしてattr()がよく利用されていたようなのだが、どうもHTML属性にアクセスしているのか、オブジェクトのプロパティにアクセスしているのかを明確に使い分けようぜ、という風潮がでてきて、なにやらprop()という奴が増えたそうな。で、試しに各スクリプト内でattrでアクセスしているようなものをpropに書き換えるということを(何も考えずに)やってみると、ページャが機能するようになった。(これはあまりにも何も考えずに置き換えたので、あとでいくつか戻したものもある。jQuery特有のプロパティアクセスにはpropを使い、HTMLエレメントへのアクセスも兼ねるようなときはattrを使えばいいというのをちゃんと理解したのは、けっこう後になってからだった)

そこでいったんgit reset --hard HEADとかやってから、一つ一つ調整してみると、犯人はlib/popup/timeline_tab.jsにいた。どうもscrollTopの値を数値で取りたいところで、propでのアクセスに切り換えなければ期待したように動かない状況になっていたみたい。これで、jQuery 1.6.4にアップデートできるようにはなり、そのまま1.7.2に更新してみたが特に問題がないようだった。ついでに、1.9系へのアップデートをにらんで、on/offへと、ajaxのイベントハンドラ周りを1.9系の推奨仕様に移行してみた。ブランチを切って1.8.3と1.9.1にアプデしてみたが、1.9.1の時に古いjQuery UI関係でブラウザ判定のコードでエラーが出ていたが、Chrome拡張なのにIEもへったくれもないので削除したら動いた。

jQueryは2.0系がリリースされているが、1.9系へのアップデートのために行った修正以外に必要な点はないようで、2.0系への移行も問題なく動作している。

jQuery UIをアップデートしてみる

次はこれだろう。単純に1.8系の最終版1.8.24にアップデートする分には問題なかったが、1.9にアプデするとちょいちょい仕様変更があるようで、アップグレードガイドを見ながら修正する必要がありそう。一足飛びに1.10系にしてみたらまったく動かなくなるので、段階踏んでアップデートさせる必要がありそう。

1.9系にすると、コンテキストメニューやツールチップ系はプラグインに頼らなくても書き換えられるかもしれない。オートコンプリート用のプラグインは、インターフェース構成がjQuery UIのオートコンプリートとインターフェースが同じっぽいので、何も考えずに削除してみた。そのあとは、まずjQuery UI 1.9へのアップグレードガイドを見ながら、TabsウィジェットのAPIをしばらく弄っていた。とりあえずdeprecatedになっていたAPIは置き換えて、おおむね以前通りの動作をするようになったので、1.10を突っ込んでみたら問題なく動作しているみたい。でも、なんか遅くなった気がする……。

ツールチップについてはjQuery UI Tipsyというプラグインが当初使われていたのだが、jQuery UI 1.9への移行を機にjQuery UI Tooltipウィジェットに変えた。現在ではツールチップ表示の契機まで変えたので、ツールチップが表示される位置と内容は完全にTipsyの時と異なるのだが、まぁあまり気になるような差分はないんじゃないかと思う。

コンテキストメニューはどうにかしたいのだが、いろいろと根深いことがわかっていて、面倒くさいので放置している。キャプチャリングフェーズでイベントハンドラを登録していたりするせいで、jQueryなどのイベント管理外にあるためにメモリリークの温床っぽい。

Twitter API 1.1に対応してみる

当初の予定ではもっとあとにやろうと思っていたのだが、たまにTwitterのAPI 1.0のエンドポイントを停止するテストなんかをやりやがるので、先に片付けることにした。そもそもSilver Birdはあまり詳細なオプションを付けてAPIを呼ぶようなことはしていないようで、利用しているAPIエンドポイント自体は多くないんじゃないか?という印象。ただし、API 1.1を使うにあたって、鬼門はDisplay Requirementsという見栄えに関する規定だ。まずはAPI 1.1そのものに対応することを目指しつつ、Display Requirementsへの対応は後回しにする。Silverbird MではSilver Bird時代の画像アセットやCSSに手を加えたことは、まだないので、すげー面倒な予感がするし。

  • APIエンドポイントのURLを変える
  • APIの呼び出しオプションをなおす
  • レスポンス内容のパース部分などをなおす

とりあえず、ひととおり動いてると思う。スパム報告なんかは試してないけど。

リストユーザの公式RTをUnifided Timelineに載せてみる

で、あっさりこれに対応出来てしまった。リスト一覧の取得やタブ入れ替え順番の考慮などのほうがよっぼどむずかしかった。なにしろ、APIに渡すオプションを増やすだけでいいのだ。当初の目的の5割はコレだったので、これだけで開発完了でもいいくらい。

API使用回数の表示を変える

Twitter API 1.1では、各APIごとに使用回数が異なるので、単純に合算で管理しても意味がない。Silver Birdではこのへんを根本的に変えないとダメだった。

いろいろ変えたり、表示の仕方を考えたりやっていたのだが、ぶっちゃけ使わないなーと思ったのでバッサリ切ることにした。

APIの呼び出し方を変える

もともと、Twitter API 1.0ではどのAPIを使っても一時間あたりの回数以内なら問題がないという作りだった。これがAPI 1.1では各API毎に15分単位で呼び出し可能な回数が決まってしまっているので、本家Silver birdではポップアップを開いて閉じて開いて閉じて……というのを15分のなかで何回もやると、それだけで一部のAPI呼び出し回数が超過してしまっていた。

これを根本的に変えるためには、APIを呼び出すポイントをBackgroundページに集約しなければならない。BrowserActionでポップアップするページ内では、Backgroundページで保存した値にアクセスするだけにして、Backgroundページで定期的にその内容をリフレッシュしてやるだけだ。とはいえ、そこそこ仕組みを変える必要があったし、ちょっとタイミング依存なものを小汚く実装してしまっている。

またとうぜん弊害もあって、今まではポップアップのたびに取り込んでいたローカルのトレンドなんかは、即時性を失った。Silverbird Mで新規に対応したSaved Searchesも同じで、同期は5分に一度だ。リスト一覧はUI上優遇されていて、自発的に更新するためのメニューがある。それに今後Streaming APIでの即時同期に対応する予定がある。Saved SearchesはStreaming APIで降ってこないのかしら(よく調べてない)

Display Requirementsに対応してみる

いちおう、大雑把には文句を言われない程度にTwitter Display Requirementsの記載を守っているつもり。

で、Silverbird MをChrome WebStoreで一時的に公開しているときにエゴサーチしてみると、やっぱりTwitter Display Requirementsに沿ったタイムライン表示は評判が悪いみたい。個人的には、あんまり気にしてもしかたないし、アイコンでキュアソードちゃんが並ぶと嬉しいのでアイコンがでかくても問題ないと思うんだけど、アイコン小さいほうが良いとか、そもそも要らないとか、まぁ人の好みは好き好きだと思った。

で、Silverbird MのクソコードをForkしている人がいたので、その実装を丸パk……もとい参考に、Twitter Display Requirementsを無視するオプションを付け加えてみた。もともと本家Silverbirdでは、Tweet単体のレンダリングがゴリ押しっぽい感じだったのだが、Silverbird Mではさらにゴリ押し臭い感じになっている。キモいなー、とは思うんだけど、これをスタイリーーシュッにする方法は思いつかない。俺がプログラマでない、というのはここがキモであって、コードの読み書きはまぁできるんだけど、(必要十分な)設計ができないのだ。俺みたいな人は、ソフトウェア開発の上流にいてはならないと思う。

機能追加してみる

改行付きTweetに対応してみる

もともとは、createTextNodeでDOMStringとしてツイートの内容をレンダリングしていたため、改行をはさもうとすると、DOMでBR要素を挟まなければならなかった。これがヒッジョーに面倒くさかった。とはいえ、なんとか対応できたっぽい。

で、高速化対応の際に全文テキスト化を行ったので、そこでは単純に文字列を挟んでいるだけなので楽ちん。

サムネイル対応を増やしてみる

これは簡単。一つ一つサービスを見に行くと、だいたい閲覧用のAPIが整備してある。???「司令官!そいつを増やしていけばいいじゃない!」

まぁ最初はそういう方針で増やしていったのだが、廃止になるサービスもあるしそもそもAPIがないものもあったりしたので考え方を変え、OpenGragh Protocolで公開されているURLを利用することにした。こいつはHTMLのヘッダにMicroformat的に埋め込まれているメタデータなので、HTMLのスクレイピングをやる必要がある。まぁ、このへんは決め打ちでog:imageなやつを引っ張るだけで良いわけでもなくて、ウェブサイト毎にイマイチ違う部分をどうにか吸収してやっている。

一部、Twitter/VineとPixivに対しては特別な対応をしている。(これは汁Mが俺得でなければ意味がないため)Pixivはログイン済みのセッションクッキーがあるだけではダメで、ChromeのWebRequest APIを使ってRefererを付けてやっている。

高速化できないかどうか

当初のボトルネックは2点あった。

  • tweet_assemblerでrenderTweetを各タイムラインの取得済みTweet全てに適用してタイムラインを作り終えてから表示しているので遅い
  • 一番叩かれるrenderTweetとparseEntitiesでjQueryのメソッドコールとDOM操作が多いので遅い

考え方としては、

  • 全部終わってからレンダリングに回さずに、Tweet一つだけ表示してsetIntervalとかで非同期処理にする
  • DOM操作をしないで、全て文字列にしたのを最後の一回だけinnerHTMLに突っ込んで高速化する

の2点しかないと思っているが、前者はスクロール位置の保存と再スクロールの調整でうまいこと実装を思いつかずに頓挫しており、後者は確かにすべて文字列化するとparseEntitesあたり3msくらい速くなるのを確認したのだけど、ハッシュとメンションのリンクがぶっ壊れてしまうのを解決する実装を思いつけずにいた。これは後者の方法をどうにかすることができたため、全要素の生成は文字列を連結するだけにして、あとは閲覧時にイベント動作でクリックだのポップアップだのを処理させるように分離できた。このおかげで、オリジナルとほぼ同等のレベルの速度を達成できたと思う。 あとは必要に応じて非同期化できれば体感上はより高速になるものの、現状のスピードで概ね満足しているので、検討していない。

また、タブ切り替えが遅い。これはそもそもjQuery UIの仕様変更が発端で、コードの構成を変えなければならなくなったあとに、ものすごい遅くなった。第一の原因はタブ切り替えの前後で同期の処理をやっていることで、ネットワークからのアウトプットも待っていたせいで糞重くなっていたのを分離してマシになったのだが、それでもあと200msくらい反応がよくならないかと思っている。でもこれ、jQuery UIが遅い感じ。Twitter Bootstrapとかに変えれば速くなったりするのかしら。

RAM消費を抑えられないか

既読管理をスパっと削除したら、稼働中の使用メモリ量がザクっと減るようになったので、味を占めた。でもあんまり減らない。そのうえしばらく使ってると、徐々に使用メモリ量が増えていく。これはリークしてるんだと思うんだが、参照を切ってないオブジェクトがそんなにあるものかと思っているのも事実。昔のIEはむりやりGCさせるメソッドがあったりしたのだが、Chromeは起動オプションでV8にオプションを渡さないと明示的にGCさせることはできないみたい。

GoogleのLeak Finderが使えないかと思ったこともあったが、あれってGoogleのライブラリ専用っぽく、jQueryではダメっぽい。ノウハウ持ってる人が教えてくれれば良いのになー。

けっきょく、参照作ったら最後に参照切る、ってのを泥臭くやるようにさせてるんだが、あんまり改善してない。キーボードショートカットを有効にしているひとはポップアップページ上でAlt+Shift+Rキーで拡張をリロードできるので、しょぼいRAM環境のひとは定期的にリロードするか、ブラウザを毎度閉じるようにしてね。(バックグラウンド動作は止めること)

キーボードショートカットを実装する

RSSリーダーとしてLivedoor Readerを愛用しているのだが、こいつの特徴のひとつにVimライクなショートカットキーの存在がある。ふだんLinux上のエディタとしてVi/Vim系を使うことはあまり多くないし、別に好きでもなんでもないのだが、j/kキーで記事移動ができるのはGUIとして便利だと思っていた。

TwitterもRSSと同じで、ある時間軸上に複数のエントリがある構造ではあるので、j/kキーで上下のつぶやきに移動できれば良いし、タブ切り替えもキーでできるとより良いだろうと思ったので実装した。

Ctrlキーを使うものは、当初Chrome拡張向けに用意されているchrome.commandsというAPIを利用しようとした。chrome.commandsではCtrlキーの設定をMac向けではCommandキーに読み替えてくれるので便利だと思っていたのだが、違うOS上では想像していなかった機能の衝突が頻発していたようなので、すべてJavascript上で律儀にハンドリングする最初期の実装に戻した。どうせ、CtrlキーやAltキーの不要なショートカットキーはchrome.commandsでは実現できなかったし。

ポップアップが表示されているときは、jで下の記事、kで上の記事、h/aで左タブ、l/sで右タブに移動する。tでタイムラインの最上部、rでカレントタイムラインのリロードをする。拡張の設定で、キーボードショートカットでSilverbird Mがポップアップするように設定すれば(別にmanifest.jsonで_execute_browser_actionにしても良いんだけど)、普段のTL監視はキーボードだけでやることも可能になった。

これ、エントリ位置を描画後に保存してスクロール量を決めているんだが、Compose表示中やアップデート通知中は座標がズレてしまって期待したようにスクロールできないバグがある。これをどうにかできればExperimental Featureを外してもいいんじゃないかと思うんだが、良い方法を思いついてない。

Chrome28のRicher Notificationに対応してみる

Chrome28に導入された機能の一つに、Richer Notificationというものがある。これは、HTML5としてのNotificationではなく、よりリッチな通知機能を実現するもの(らしい)だ。ぶっちゃけ、現状はテンプレートとして見たときは、まぁリッチかなという程度で、window.webkitNotification.createHTMLNotificationで作成できた、完全にHTMLベースのデスクトップ通知よりもできることは限られている。

この機能自体は別に使うつもりもなく、ふーん、と思っていたのだが、Windows版Chrome28でデスクトップ通知ができないという状態になって困った。デバッガで追うと、そのcreateHTMLNotificationで例外が出ている。Richer Notificationが導入されていないMacやUbuntuのChrome28ではいつもどおりなので、Windows版に先行導入されたRicher Notificationが原因なのは明らかだ。window.webkitNotification.createNotificationでHTMLじゃないシンプルな通知を出してみると、これはちゃんと表示される。つまるところ、Windows版Chrome28以降で、createHTMLNotificationを呼ぶときだけ、例外が投げられる。こうなると、Silver birdの仕様ではデスクトップ通知の設定が、オンページ通知にフォールバックしてしまっていた。いちおう設定は反映できる(ように見える)んだが、一度でもデスクトップ通知をしようとすると、その時に例外をフックしてオンページ通知に設定上も切り替えてしまう。エゴサーチしてみると、デフォルト設定で使ってる人たちは、設定が勝手に戻る現象を見て混乱してる人もいる。

まぁ、仕方がないのでRicher Notificationに対応させた。Windows版Chrome28以降(Mac版はChrome29以降)は、createHTMLNotificationで例外が投げられたら、代替としてRicher Notificationを出す。それでも何かしら例外が投げられたら、オンページ通知にフォールバックする。そのうちMac版でもこの動作になるはず。Linux向けChromeについては、まず通知センターの実装はされないのではないかな。

で、現在は通知を呼ぶ前にツイートの内容をパースする構造になっていないし、Richer Notification 自体もHTMLDOMElementを内包するような作りになっていないっぽいため、通知の中にリンク張ったりすることはできなそう。Silverbird Mの現仕様では、通知がされたあと通知に触る、あるいは通知を放っておけばタイムアウト設定時間後に勝手に消えて通知センターにも残らないようにした。当初は通知上のユーザ名などもカスタマイズが効くようにしていたのだが、バカバカしいので削除した。通知コンテンツが気に入らないなら、通知をつかわなければいい。また、リプライとふぁぼくらいはボタンを作れそうだけど、あまり興味ない。

なお、オンページ通知はものすごいゴリ押しで実装されていてウンコだったし、オンページ通知使うことって俺の中ではなかったので、機能ごとごっそり抜いた。さらにChrome29になってMac版にもRicher Notificationが導入されたので、createHTMLNotificationそのものを削除した。Linux環境でもChrome 32くらいからデスクトップ通知の見た目が他のプラットフォームと同じになったっぽい。

ES6機能を導入してみる

ChromeでもFirefoxでも、ECMAscript6で導入予定の機能がいろいろ実装されている。たいていの場合で、新しい機能のほうが既存のJavascriptのものよりも便利だったりパフォーマンスがよくなるようになっているので、いくつか汁Mにも実装を試している。主に、Object.observe、Set、Mapなんだが。Chrome41からはTemplate literalsが使える見込みなので、一部のテキストまわりはこれに変えるつもり。

ソースとバイナリ

githubに置いてます。

最近になって、Chrome Web Storeにも公開しました。

ダメなところ

  • 低機能(リスト操作できないとか)
  • デザインがアレ(気にすんな)