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

提供: STUDIO DDT ONLINE
移動先: 案内検索
(Chrome28のRicher Notificationに対応してみる)
(fork)
 
(同じ利用者による、間の31版が非表示)
1行目: 1行目:
 
==前書き==
 
==前書き==
毎日会社にサラリーマンのコスプレをして出かけているものの、俺って全然仕事がなくて暇なので、何か暇つぶしをやろうという話。は?コンプラ?なにそれ美味しいの?
+
2013年頃のはなし。毎日会社にサラリーマンのコスプレをして出かけているものの、俺って全然仕事がなくて暇なので、何か暇つぶしをやろうという話(だった)。
  
Twitterのクライアントとして、Chrome拡張のSilverbirdを利用している。フォローするまででもないかな?的人たちをリストに分離するということは誰でもやっていると思うが、Unified Timelineとして合一したTLを表示してくれるため、とっても便利。
+
オフィスなどで隠れてTwitter廃人やろうとすると、こそこそ度合いの高めなクライアントとして、Chrome拡張のSilverbirdがあった。フォローするまででもないかな?的人たちをリストに分離するということは誰でもやっていると思うが、Unified Timelineとして合一したTLを表示してくれるため、とっても便利。
  
ただ、不満もある。
+
ただ、不満もあった。
 
* リストのユーザの公式RTがUnified Timelineに載らないので載せてほしい
 
* リストのユーザの公式RTがUnified Timelineに載らないので載せてほしい
 
* 公式対応も始まっている、改行付きツイートに対応してほしい
 
* 公式対応も始まっている、改行付きツイートに対応してほしい
* 最近Chromeをアップデートしたら、リストタブが表示崩れを起こしてる(WindowsのChromeだけのようだが)
+
* Twitterの仕様変更に追従してくれないので不具合が多い(リストとか)
* Twitter API 1.1対応ってしてないよね(本家でも1.1対応したけど、コード見たらすごいやっつけでワロタ)
+
* 俺が使わない機能が多い
  
 
よろしい。ならばforkだ。(SilverbirdのライセンスはMIT)
 
よろしい。ならばforkだ。(SilverbirdのライセンスはMIT)
16行目: 16行目:
 
===前提===
 
===前提===
 
* 元々のソースはGithub上にMasterリポジトリがある。
 
* 元々のソースはGithub上にMasterリポジトリがある。
* ビルド用にRakeを使っているみたい。Rubyインスコですか。
+
* ビルド用にRakeを使っている。モダンな感じじゃない。
* でもPCはWindowsで、しかもXPです。^q^
+
* そもそも俺、本業プログラマじゃないよ。
* そもそも俺、プログラムしばらく書いてないし、本業プログラマじゃなかったからウォォォォォ
 
* 当然Chome拡張なんて作ったこともないし、jQueryみたいなモダンJavascript触ってもいない。Javascript 1.6?もよく知らないので、Arrayにforeachなんてあるの?の世界観。
 
  
 
===Git for Windows===
 
===Git for Windows===
2013年3月現在では、便利なものでWindows環境向けにもGitの実装がある。Git for Windowsをダウンロードしてきてインスコ。このとき、パスの設定のところでRun Git from the Windows Command Prompt、つまりWindowsのコマンドプロンプト内でもGitが叩けるオプションを選んでおく必要がある。これは、後述するRakefileスクリプト内でGitのコマンドを実行しているため。
+
2013年3月現在では、便利なものでWindows環境向けにもGitの実装がある。Git for Windowsをダウンロードしてきてインスコ。(このとき、パスの設定のところでRun Git from the Windows Command Prompt、つまりWindowsのコマンドプロンプト内でもGitが叩けるオプションを選んでおくと他にも使えて便利)
  
 
そうするとGit BashというBashライクなシェルが提供されるので、そこからリポジトリを操作するみたい。ディレクトリ構造はルートの下にドライブレター、各ディレクトリという構造でアクセスできるみたい。コマンド系はLinuxっぽい。シェルのプロンプトにユーザ名とカレントディレクトリとカレントブランチが表示されている。
 
そうするとGit BashというBashライクなシェルが提供されるので、そこからリポジトリを操作するみたい。ディレクトリ構造はルートの下にドライブレター、各ディレクトリという構造でアクセスできるみたい。コマンド系はLinuxっぽい。シェルのプロンプトにユーザ名とカレントディレクトリとカレントブランチが表示されている。
33行目: 31行目:
 
cloneされたローカルのリポジトリを見てみると、Rakefileが置いてある。RakeはRubyで書かれたMakeの実装(だったと思う)。Chrome拡張のパッキングにRakeを使っているみたい。
 
cloneされたローカルのリポジトリを見てみると、Rakefileが置いてある。RakeはRubyで書かれたMakeの実装(だったと思う)。Chrome拡張のパッキングにRakeを使っているみたい。
  
====Ruby for Windows====
+
……ここでRubyインスコしたり以前はやってたのだが、Rubyでのビルドをばっさりやめたので、削除。
まずは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==
 
==fork==
 
ここからが本題。最初はgithub使おうと思ってなかったのでローカルにcloneしてそこだけで作業するつもりだったのだけど、週末挟んで自宅でも遊ぼうと思ったので、github使うことにした。今までやってた変更は大した変更でもないし、結果はわかっているので後でやり直しても大したことはない。まずはgithub上でリポジトリのforkを実行した。
 
ここからが本題。最初はgithub使おうと思ってなかったのでローカルにcloneしてそこだけで作業するつもりだったのだけど、週末挟んで自宅でも遊ぼうと思ったので、github使うことにした。今までやってた変更は大した変更でもないし、結果はわかっているので後でやり直しても大したことはない。まずはgithub上でリポジトリのforkを実行した。
  
forkするにあたって、manifestファイルのnameとかversionは変えてしまう。とりあえず名前はSilverbird Mにした。
+
forkするにあたって、manifestファイルのnameとかversionは変えてしまう。とりあえず名前はSilverbird Mにした。これは、Modとかそういう意味ではなく、「for Me」という気持ち。だから、汁Mでは要望を基本的に受け付けない。forkして自分でやってね。
  
 
機能追加などはこのfork用のブランチから再度ブランチして、あとでmergeするつもりだったんだけど、ぶっちゃけ面倒くさいのでほとんどmasterで作業してrebase -iしまくりで運用してる。計画持って機能をいじってるわけじゃないので、開発内容ごとにブランチ切ると、何をどこのブランチで作業していたのか忘れるし、ほとんどコンフリクトしてマージできやしないから。
 
機能追加などはこのfork用のブランチから再度ブランチして、あとでmergeするつもりだったんだけど、ぶっちゃけ面倒くさいのでほとんどmasterで作業してrebase -iしまくりで運用してる。計画持って機能をいじってるわけじゃないので、開発内容ごとにブランチ切ると、何をどこのブランチで作業していたのか忘れるし、ほとんどコンフリクトしてマージできやしないから。
78行目: 43行目:
 
まず手を付けるのはコレ。
 
まず手を付けるのはコレ。
  
なぜかというと、試しにChromeにインストールしてみると、ごく普通にSilverbirdとしてTwitterへのアクセス許可を申請しちゃうため。コンシューマキーとコンシューマシークレットが公開されているソースコードにそのまま記載されているみたい。俺がこのまま変な挙動をするクライアントアプリを書いてしまった場合に、SilverbirdそのものがTwitterからBanされるのは困る。
+
なぜかというと、試しにChromeにインストールしてみると、ごく普通にSilverbirdとしてTwitterへのアクセス許可を申請しちゃうため。コンシューマキーとコンシューマシークレットが公開されているソースコードにそのまま記載されている。俺がこのまま変な挙動をするクライアントアプリを書いてしまった場合に、SilverbirdそのものがTwitterからBanされるのは困る。
  
 
いちおう.gitignoreファイルを確認してみるとsecret_keys.jsが入ってるのだけど、manifest version2対応の時にlibの下に移動したようなので、そのときからアップロードされてしまっていたのかも。
 
いちおう.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の全履歴から当該ファイルの記録を削除する。これはググって見つけたので、必要なひとは適宜やってください。
+
fork元のほうはどうしようもないんだけど、forkした俺のリポジトリ上からは、完全に(履歴からも)削除した。
  
 
====Twitter APIにアクセスできるようにする====
 
====Twitter APIにアクセスできるようにする====
98行目: 63行目:
 
ちなみに、最初コンシューマキーとシークレットを編集しようとsecret_key.jsを開いたら、identicaとyfrogとtwitpicのAPIアクセスキーも記載してあってウォォォォォ!identicaはともかく、yfrogとtwitpicのAPIキーは取得して記載してみた。
 
ちなみに、最初コンシューマキーとシークレットを編集しようとsecret_key.jsを開いたら、identicaとyfrogとtwitpicのAPIアクセスキーも記載してあってウォォォォォ!identicaはともかく、yfrogとtwitpicのAPIキーは取得して記載してみた。
  
====Google Analytics アプリケーションキーを変更する====
+
====Google Analytics関係====
manifestファイルにGoogle Analytics使ってる感のある設定があるので、どこかにアプリケーションキーが埋まってるのかなーと思ったらlib/analytics.jsにキーが埋まってた。デバッグの時に使ったりしていたのだろうが、せっかくなので一時的に有効にしてみる。リポジトリや履歴からは削除しちゃっているのでアレだが、内容はGoogle Analyticsのチュートリアルで書いてあるものがそのまま書いてあるだけだった。
+
オリジナルのsilverbirdではmanifestファイルにGoogle Analytics使ってる感のある設定があるので、どこかにアプリケーションキーが埋まってるのかなーと思ったらlib/analytics.jsにキーが埋まってた。
  
Google Analyticsは利用規約の都合で利用者になにを目的にトラックしているのか明示しなければいけないので、普段使いには面倒臭いサービスだと思うな。当サイトでも使ってますけど!
+
汁Mでは、誰かの動作をトラックしてデバッグに利用したりする気はない(つまり俺以外の利用者からのフィードバックを受け付ける気がない)ので、スパっとGAは削除。
 
 
さて、変更したはいいのだけど、一向にトラッキングがされない感ある。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 Shortener と Expander====
短縮URL作成と展開にウェブサービスを活用しているようなのだが、APIキーとかそのまんま埋まってる。とりあえず、サムネイル表示に対応するものを増やすついでに、APIキーのたぐいは分離しておいた。(とはいえ、暗号化なんかがされているわけではないので、Chromeウェブストアなんかで配布を始めるとバレバレになるだろう)
+
短縮URL作成と展開にウェブサービスを活用しているようなのだが、APIキーとかそのまんま埋まってるし、現在ではサポートされていない形式のままだったりした。
 
 
当初はリンクにホバーすると、URL展開のプロセスが始まり、URL展開→画像展開の順番で一方通行になっていた。サムネイル画像のURLを取得するのに非同期APIを叩かないと行けないものは対応できていなかったので、URL展開を再起で回せるようにして、最終的にJPEGファイルなどになったら画像展開するように変更したい。
 
  
Shortenerとしてbit.lyとGoogleを使っているのだが、GoolgeはOAUTH2によるトークン取得にコンシューマキー&コンシューマシークレットを投げてクレデンシャル・バリデーションとやるのだが、bit.lyのほうはログインID&パスワードを投げるだけで、いろいろ危ない。なので、エンドユーザのIDとパスワードを使ってトークンをもらうように変更しようと思っている。
+
汁Mでは、このへんの実装を大きく変えた。詳しくは後述。
  
 
===日本語ロケールに対応してみる===
 
===日本語ロケールに対応してみる===
アプリケーション内でSilver Birdとなっている部分をSilverbird Mと置換しようと思うと、_localesディレクトリ下部にある各言語ファイルも置換する必要がある。せっかくなので、jaロケールを追加して日本語化してみよう。
+
アプリケーション内でSilver Birdとなっている部分をSilverbird Mと置換しようと思うと、_localesディレクトリ下部にある各言語ファイルも置換する必要がある。せっかくなので、jaロケールを追加して日本語化した。ベースになっている英語、つまるところenディレクトリをコピーしてjpにリネーム。中身は単純なJSONファイルになっていて、ちくちくとそれらしい単語やら訳文やらに置き換えるだけ。
 
 
====_locales/jaディレクトリを掘る====
 
ベースになっている英語、つまるところenディレクトリをコピーしてリネームしてみる。中身は単純なJSONファイルになっていて、ちくちくとそれらしい単語やら訳文やらに置き換えるだけ。
 
  
オプション画面の最下部にLocales Reportというリンクがあるのでクリックしてみると、ローカライズの抜け部分の指摘がされていて、赤く変わっている部分がなくなればローカライズ完了となるみたい。ただ、各ロケールのmessages.jsonの値を横断的に表示しているだけのよう。enロケールでもエラーが2つあるなー、と調べたら、ukロケールに要らん値が追加されていることがわかった。特に何も考えずに削除しておく。
+
オプション画面の最下部にLocales Reportというリンクがあるのでクリックしてみると、ローカライズの抜け部分の指摘がされていて、赤く変わっている部分がなくなればローカライズ完了となる。ただ、各ロケールのmessages.jsonの値を横断的に表示しているだけのよう。俺は日本語しかまともに解さないので、スペイン語とかよくわかんないロケールは削除した。今後も一切サポートする気はない。
  
訳文がそろっても、CSSの都合で見た目が崩れることがあるようなので、長い文章には改行を入れたり、訳文を調整したりする必要がある。また、「retweet by ○○」みたいなものは「○○さんのリツイート」のようにしないと日本語としては不自然なのだが、どうも文字列の順番はハードコーディングのようなので、ここでは調節できない。ひとまず「リツイートしたひと」ぐらいにしておく。
+
また、「retweet by ○○」みたいなものは「○○さんのリツイート」のようにしないと日本語としては不自然なので、泥臭く翻訳を適用できるように組み替えている。
  
====アプリケーション内の名称を統一する====
+
===jQuery関係===
jaロケールは作業中から名称をSilverbird Mにしておいたのだが、ここでついでに他のロケールのローカライズ文章も、Silver Birdとなっている部分をSilverbird Mに変更する。
+
Silver Birdではサードパーティライブラリという位置づけでjQueryとjQuery UIを主に使っている。ただし、コアは1.4.2、UIは1.8.1と、現行のjQueryよりも超絶古い。CSP準拠のためにjQueryは1.5.2にアップデートしてみたが、それでも古い。最新化せねばならない(強迫観念)。
  
さらにそのあと、Silver Birdの名称が残っている部分をgrepすると、libディレクトリ下に3つほどあった
+
====jQueryコアをアップデート====
+
さて、いろいろ試すと、1.5.2から1.6.4にアップデートすると、タイムラインのページングがうまくいかない。1.5系と1.6系で大きく変わったのは、HTMLの属性およびjQueryオブジェクトのプロパティへのアクセスとしてattr()が利用されていたようなのだが、どうもHTML属性にアクセスしているのか、jQueryオブジェクトのプロパティにアクセスしているのかを明確に使い分けようぜ、という風潮がでてきて、なにやらprop()という奴が増えたそうな。attrを使っている箇所をgrepして適宜propに入れ替えるとjQuery 1.6.4にアップデートできるようになった。さらに1.7.2に更新してみたが問題なし。
* 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仕様で規定されているだろうから、値を変えると動かなくなるとかあるかもしれないのだが、動かなくなってから考えればいいというスタンスで変更しておいた。
 
  
====よりまともな日本語化====
+
調子に乗って、1.9系へのマイグレーションガイドを読み、on/offへと、ajaxのイベントハンドラ周りを大きく変える。そこまでくれば2.0系への移行は問題なかった。現在では、常に最新のjQueryを同梱するようにしている。
結局のところ、ハードコーディングされている部分を小分けにすることしかできそうになかったので、地道に小分けにした。ほぼ、日本語化はできたんじゃないかと思う。あまり発生しそうにないエラーなどはエラーメッセージが英語で埋め込まれていたりするが、まぁ、気にしない方向で。
 
  
本当は、レイアウトも含めたテンプレート化ができれば良いんだろうけど……。ゴリ押しっぽい実装をどうにかかっこ良くする知識は俺にはないのです。プログラマじゃないので。
+
====jQuery UIをアップデート====
 +
次はUI側。単純に1.8系の最終版1.8.24にアップデートする分には問題なかったが、1.9〜1.10とアプデすると仕様変更があるようで、アップグレードガイドを見ながら修正する必要があった。
  
===jQuery関係をアップデートしてみる===
+
1.9系にすると、オートコンプリートとツールチップ系はプラグインに頼らなくてもjQuery UIだけで完結できるようになった。ほんとはActionMenuとかコンテキストメニューはどうにかしたいのだが、いろいろと根深いことがわかっていて、面倒くさいので放置している。キャプチャリングフェーズでイベントハンドラを登録していたりするせいで、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の時と異なるのだが、まぁあまり気になるような差分はないんじゃないかと思う。
 
 
 
コンテキストメニューはどうにかしたいのだが、いろいろと根深いことがわかっていて、面倒くさいので放置している。
 
  
 
===Twitter API 1.1に対応してみる===
 
===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に手を加えたことは、まだないので、すげー面倒な予感がするし。
+
当初の予定ではもっとあとにやろうと思っていたのだが、たまにTwitterのAPI 1.0のエンドポイントを停止するテストなんかをやるせいで、先に片付けることになった。
 
 
* APIエンドポイントのURLを変える
 
* APIの呼び出しオプションをなおす
 
* レスポンス内容のパース部分などをなおす
 
 
 
とりあえず、ひととおり動いてると思う。スパム報告なんかは試してないけど。
 
  
====リストユーザの公式RTをUnifided Timelineに載せてみる====
 
で、あっさりこれに対応出来てしまった。リスト一覧の取得やタブ入れ替え順番の考慮などのほうがよっぼどむずかしかった。なにしろ、APIに渡すオプションを増やすだけでいいのだ。当初の目的の5割はコレだったので、これだけで開発完了でもいいくらい。
 
 
====API使用回数の表示を変える====
 
Twitter API 1.1では、各APIごとに使用回数が異なるので、単純に合算で管理しても意味がない。Silver Birdではこのへんを根本的に変えないとダメだった。
 
 
* 個別にAPI使用回数を保存するようにした
 
* API毎に使用回数を表示するようにした
 
* オプション画面でやれることを削りまくった
 
 
====APIの呼び出し方を変える====
 
 
もともと、Twitter API 1.0ではどのAPIを使っても一時間あたりの回数以内なら問題がないという作りだった。これがAPI 1.1では各API毎に15分単位で呼び出し可能な回数が決まってしまっているので、本家Silver birdではポップアップを開いて閉じて開いて閉じて……というのを15分のなかで何回もやると、それだけで一部のAPI呼び出し回数が超過してしまっていた。
 
もともと、Twitter API 1.0ではどのAPIを使っても一時間あたりの回数以内なら問題がないという作りだった。これがAPI 1.1では各API毎に15分単位で呼び出し可能な回数が決まってしまっているので、本家Silver birdではポップアップを開いて閉じて開いて閉じて……というのを15分のなかで何回もやると、それだけで一部のAPI呼び出し回数が超過してしまっていた。
  
 
これを根本的に変えるためには、APIを呼び出すポイントをBackgroundページに集約しなければならない。BrowserActionでポップアップするページ内では、Backgroundページで保存した値にアクセスするだけにして、Backgroundページで定期的にその内容をリフレッシュしてやるだけだ。とはいえ、そこそこ仕組みを変える必要があったし、ちょっとタイミング依存なものを小汚く実装してしまっている。
 
これを根本的に変えるためには、APIを呼び出すポイントをBackgroundページに集約しなければならない。BrowserActionでポップアップするページ内では、Backgroundページで保存した値にアクセスするだけにして、Backgroundページで定期的にその内容をリフレッシュしてやるだけだ。とはいえ、そこそこ仕組みを変える必要があったし、ちょっとタイミング依存なものを小汚く実装してしまっている。
  
またとうぜん弊害もあって、今まではポップアップのたびに取り込んでいたローカルのトレンドなんかは、即時性を失った。Silverbird Mで新規に対応したSaved Searchesも同じで、同期は5分に一度だ。リスト一覧はUI上優遇されていて、自発的に更新するためのメニューがある。それに今後Streaming APIでの即時同期に対応する予定がある。Saved SearchesはStreaming APIで降ってこないのかしら(よく調べてない)
+
なお、これに関連するのだが、オリジナルのSilverbirdではAPI使用回数のグラフが見られた。しかしAPI 1.1では全体の使用回数のグラフなど見ても意味がない(単位時間あたりに各APIエンドポイントにアクセスできる回数が、APIエンドポイント毎にそれぞれ異なる)ので、機能ごと削除した。
  
====Display Requirementsに対応してみる====
+
また、大雑把には文句を言われない程度にTwitter Display Requirementsの記載を守っているつもり。(なんだけど、ちょくちょく書き込み権限をつぶされる)
いちおう、大雑把には文句を言われない程度にTwitter Display Requirementsの記載を守っているつもり。
 
  
で、Silverbird MをChrome WebStoreで一時的に公開しているときにエゴサーチしてみると、やっぱりTwitter Display Requirementsに沿ったタイムライン表示は評判が悪いみたい。個人的には、あんまり気にしてもしかたないし、アイコンでキュアソードちゃんが並ぶと嬉しいのでアイコンがでかくても問題ないと思うんだけど、アイコン小さいほうが良いとか、そもそも要らないとか、まぁ人の好みは好き好きだと思った。
+
===機能追加関係===
 +
====短縮URL展開とサムネイル対応を根本的にどうにかする====
 +
これは簡単。一つ一つ短縮URLサービスとかURL展開サービスとかオンライン写真ストレージサービスを見に行くと、だいたいURL展開やサムネイル閲覧用のAPIが整備してある。???「司令官!そいつを増やしていけばいいじゃない!」「よし増やすか」
  
で、Silverbird MのクソコードをForkしている人がいたので、その実装を丸パk……もとい参考に、Twitter Display Requirementsを無視するオプションを付け加えてみた。もともと本家Silverbirdでは、Tweet単体のレンダリングがゴリ押しっぽい感じだったのだが、Silverbird Mではさらにゴリ押し臭い感じになっている。キモいなー、とは思うんだけど、これをスタイリーーシュッにする方法は思いつかない。俺がプログラマでない、というのはここがキモであって、コードの読み書きはまぁできるんだけど、(必要十分な)設計ができないのだ。俺みたいな人は、ソフトウェア開発の上流にいてはならないと思う。
+
まぁ、最初はそういう方針で俺得な感じのやつを増やしていったのだが、廃止になるサービスもあったり、そもそもAPIがないものもあったりしたので泣いてた。そこで考え方を変え、XMLHttpRequest Level2のresponse URLと、OpenGragh Protocolで公開されている画像URLを利用することにした。
  
===機能追加してみる===
+
XHRはもともとリダイレクトをフックすることができない(ChromeのWebRequest APIやHTML5世代のFetch APIなんかではリダイレクトをフックする仕組みがある)。つまり何度かリダイレクトされる程度なら、XHRでアクセスすると最終的なコンテンツのURLが得られるのだ。???「もうこれでいいじゃない司令官!」
====改行付きTweetに対応してみる====
 
もともとは、createTextNodeでDOMStringとしてツイートの内容をレンダリングしていたため、改行をはさもうとすると、DOMでBR要素を挟まなければならなかった。これがヒッジョーに面倒くさかった。とはいえ、なんとか対応できたっぽい。
 
  
で、高速化対応の際に全文テキスト化を行ったので、そこでは単純に文字列を挟んでいるだけなので楽ちん。
+
これでURL展開サービスは完全に要らなくなった。ただし、ただのURL展開にGETは大げさなので、この部分にはHEADを使っている。
====サムネイル対応を増やしてみる====
 
これは簡単。一つ一つサービスを見に行くと、だいたい閲覧用のAPIが整備してある。
 
  
ただし、Silver Birdのオリジナルの実装では、URL展開とサムネイル表示が雑にまとめられていて、
+
サムネイルはナウなヤングにバカウケのチョベリグなOGPをフル活用する。OpenGraphはHTMLのヘッダにMicroformat的に埋め込まれているメタデータ。外部から認証なしにアクセスできるようなサムネイルのURLなんかが載せてあったりするし、サムネとか関係ないサイトでもサイトのロゴとかサービスロゴとか載せてる場合が多い。ほほぅそれは好都合、とそいつを使おうとするなら、HTMLをGETしてスクレイピングをやる必要がある。「一度HEADしてURL展開してから、やっぱりスクレイピングのためにGETしてんのかよ!」……というのはおっしゃるとおりなんだが、モダンなウェブサイトならOGPに対応していないことはあんまりないし、これをやるだけでサムネイル取得し放題なわけよ。やるっきゃ騎士なわけよ。
* 単純置換でサムネイルかどうかチェック
 
* APIを駆使してURL展開(コールバックあり)
 
* 単純置換でサムネイルかどうかを再チェック
 
という実装。単純置換してるフェーズでしかサムネイル表示へのパスがない。via.meみたいに、サムネイルURLの取得にAPIコール(コールバック)が必要なタイプのやつは、URL展開の一部に混ぜてやらないとうまく表示できないことがわかった。あとはてなフォトライフ、オメーはダメだ。
 
  
ここはけっこう実装を変えた。高速化対応をするときに、出力用HTML作成をすべてテキストとクラス名、HTML5のdata属性でこなすようにしたのだが、mouseoverイベント発生時にURL展開はまとめてやらせるようにして、ツールチップとしてのポップアップも、特定のクラス名がついていれば勝手にポップアップするようにさせた。
+
なお、TwitterとニコニコとPixivに対しては特別な対応(これは汁Mが俺得でなければ意味がないため)をしている。Pixivはログイン済みのセッションクッキーがあるだけではダメで、ChromeのWebRequest APIを使ってRefererを付けてやっている。ゴリ押しすごい。
  
で、via.meが画像のホスティングをやめちゃったので、via.meのコードは抜いた。
 
 
====高速化できないかどうか====
 
====高速化できないかどうか====
 
当初のボトルネックは2点あった。
 
当初のボトルネックは2点あった。
223行目: 126行目:
 
* 全部終わってからレンダリングに回さずに、Tweet一つだけ表示してsetIntervalとかで非同期処理にする
 
* 全部終わってからレンダリングに回さずに、Tweet一つだけ表示してsetIntervalとかで非同期処理にする
 
* DOM操作をしないで、全て文字列にしたのを最後の一回だけinnerHTMLに突っ込んで高速化する
 
* DOM操作をしないで、全て文字列にしたのを最後の一回だけinnerHTMLに突っ込んで高速化する
の2点しかないと思っているが、前者はスクロール位置の保存と再スクロールの調整でうまいこと実装を思いつかずに頓挫しており、後者は確かにすべて文字列化するとparseEntitesあたり3msくらい速くなるのを確認したのだけど、ハッシュとメンションのリンクがぶっ壊れてしまうのを解決する実装を思いつけずにいた。これは後者の方法をどうにかすることができたため、全要素の生成は文字列を連結するだけにして、あとは閲覧時にイベント動作でクリックだのポップアップだのを処理させるように分離できた。このおかげで、オリジナルとほぼ同等のレベルの速度を達成できたと思う。
+
の2点しかないと思っているが、前者はスクロール位置の保存と再スクロールの調整でうまいこと実装を思いつかずに頓挫しており、後者は確かにすべて文字列化するとparseEntitesあたり3msくらい速くなるのを確認したのだけど、ハッシュとメンションのリンクがぶっ壊れてしまうのを解決する実装を思いつけずにいた。これは後者の方法をどうにかすることができたため、全要素の生成は文字列を連結するだけにして、あとは閲覧時にイベント動作でクリックだのポップアップだのを処理させるように分離できた。
あとは必要に応じて非同期化できれば体感上はより高速になるものの、現状のスピードで概ね満足しているので、検討していない。
 
  
また、タブ切り替えが遅い。これはそもそもjQuery UIの仕様変更が発端で、コードの構成を変えなければならなくなったあとに、ものすごい遅くなった。第一の原因はタブ切り替えの前後で同期の処理をやっていることで、ネットワークからのアウトプットも待っていたせいで糞重くなっていたのを分離してマシになったのだが、それでもあと200msくらい反応がよくならないかと思っている。でもこれ、jQuery UIが遅い感じ。Twitter Bootstrapとかに変えれば速くなったりするのかしら。
+
ちなみに、最近の汁Mではホームタイムラインでフェッチしてくるツイート数をレンダリング速度から逆算するように仕様変更した。要は、利用者が「200件フェッチしろよボケ」と思っても、クソみたいなスペックのPCで使っているひとは、フェッチしてくる数がだんだん減っていくようになっている。レンダリングが250ms程度であれば「ぱっ」と表示されるように感じると思う。これはなぜかというと、RTや特定の情報でタイムラインをフィルタさせることを考えると、画面上に表示されるエントリの数と設定上の件数を一致させるのはアホらしいくらい面倒だからだ。自動で件数が上下するなら、どれだけフィルタされてもユーザが件数を意識することはないので、そんなふうに落とし込んだ。
 +
 
 +
とはいえ、汁Mあんまり早くない。Core i7の端末だとさくさくだけど、Core2 Duoとかだとちょっとかなりアレ。
  
 
====RAM消費を抑えられないか====
 
====RAM消費を抑えられないか====
既読管理をスパっと削除したら、稼働中の使用メモリ量がザクっと減るようになったので、味を占めた。でもあんまり減らない。そのうえしばらく使ってると、徐々に使用メモリ量が増えていく。これはリークしてるんだと思うんだが、参照を切ってないオブジェクトがそんなにあるものかと思っているのも事実。昔のIEはむりやりGCさせるメソッドがあったりしたのだが、Chromeは起動オプションでV8にオプションを渡さないと明示的にGCさせることはできないみたい。
+
俺がまったく使わない機能であるところの既読管理をスパっと削除したら稼働中の使用メモリ量がザクっと減るようになったので、味を占めた。
  
GoogleのLeak Finderが使えないかと思ったこともあったが、あれってGoogleのライブラリ専用っぽく、jQueryではダメっぽい。ノウハウ持ってる人が教えてくれれば良いのになー。
+
でもその後いろいろやってもあんまり減らない。そのうえしばらく使ってると、徐々に使用メモリ量が増えていく。これはリークしてるんだと思うんだが、ぶっちゃけどこで漏れてるのかわかんない。(なんかChromeのプロセス間通信でオブジェクト参照が切れずに残ってるような感じはある。その全てでDeep Copyして渡すのは面倒くさいので、Object.assginでシャローコピーしたり、古典的なJSON.pase(JSON.stringify())でぶっ壊れコピーするとマシになる)
  
けっきょく、参照作ったら最後に参照切る、ってのを泥臭くやるようにさせてる。クロージャの取り扱いについては、オリジナルでも努力してるっぽい部分があるんだが、thisの複製を適当に使いまわそうとしている部分は参照切りを入れたり、そーゆーのに頼らないようにちょこちょこ変えている。オリジナルよりは、GC発生後のメモリ使用量が少なくなってると思う。
+
まぁ、このへんは諦めが肝心。Chrome拡張をリロードさせればRAM上は初期化されるので、ザクっとRAM使用量が減る。
 +
 
 +
キーボードショートカットを有効にしているひとはポップアップページ上でAlt+Shift+Rキーで拡張をリロードできるので、しょぼいRAM環境のひとは定期的にリロードしよう。そうでない人は汁Mのポップアップページがバックグラウンドページ上で開発者コンソールを開き、chrome.runtime.reload()すりゃいいよ。
  
 
====キーボードショートカットを実装する====
 
====キーボードショートカットを実装する====
RSSリーダーとしてLivedoor Readerを愛用しているのだが、こいつの特徴のひとつにVimライクなショートカットキーの存在がある。ふだんLinux上のエディタとしてVi/Vim系を使うことはあまり多くないし、別に好きでもなんでもないのだが、j/kキーで記事移動ができるのはGUIとして便利だと思っていた。
+
RSSリーダーとしてLivedoor Readerを愛用しているのだが、こいつの特徴のひとつにVimライクなショートカットキーの存在があるじゃん?ふだんLinux上のエディタとしてVi/Vim系を使うことはあまり多くないし、別に好きでもなんでもないのだが、j/kキーで記事移動ができるのはGUIとして便利だと思っていた。TwitterもRSSと同じで、ある時間軸上に複数のエントリがある構造ではあるので、j/kキーで上下のつぶやきに移動できれば良いし、タブ切り替えもキーでできるとより良いだろうと思ってた。
 
 
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監視はキーボードだけでやることも可能になった。
 
ポップアップが表示されているときは、jで下の記事、kで上の記事、h/aで左タブ、l/sで右タブに移動する。tでタイムラインの最上部、rでカレントタイムラインのリロードをする。拡張の設定で、キーボードショートカットでSilverbird Mがポップアップするように設定すれば(別にmanifest.jsonで_execute_browser_actionにしても良いんだけど)、普段のTL監視はキーボードだけでやることも可能になった。
  
これ、エントリ位置を描画後に保存してスクロール量を決めているんだが、Compose表示中やアップデート通知中は座標がズレてしまって期待したようにスクロールできないバグがある。これをどうにかできればExperimental Featureを外してもいいんじゃないかと思うんだが、良い方法を思いついてない。
+
これ、エントリ位置を描画後に保存してスクロール量を決めているんだが、Compose表示中やアップデート通知中は座標がズレてしまって期待したようにスクロールできない。いちいち再計算させればいいんだけど、Compose表示を分離して新しくしたいと思っているので、この辺はアップデートなし。
  
 
====Chrome28のRicher Notificationに対応してみる====
 
====Chrome28のRicher Notificationに対応してみる====
Chrome28に導入された機能の一つに、Richer Notificationというものがある。これは、HTML5としてのNotificationではなく、よりリッチな通知機能を実現するもの(らしい)だ。ぶっちゃけ、現状はテンプレートとして見たときは、まぁリッチかなという程度で、createHTMLNotificationで作成できる、完全にHTMLなデスクトップ通知よりもできることは限られている。
+
Chrome28に導入された機能の一つに、Richer Notificationというものがある。これは、HTML5としてのNotificationではなく、よりリッチな通知機能を実現するもの(らしい)だ。ぶっちゃけ、現状はテンプレートとして見たときは、まぁリッチかなという程度で、window.webkitNotification.createHTMLNotificationで作成できた、完全にHTMLベースのデスクトップ通知よりもできることは限られていたのだが、いきなり廃止されちゃった。
 +
 
 +
仕方がないのでRicher Notificationに切り替えた。
 +
 
 +
====ES201x機能を導入してみる====
 +
ChromeでもFirefoxでも、ECMAscript201xで導入予定の機能がいろいろ実装されている。たいていの場合で、新しい機能のほうが既存のJavascriptのものよりも便利だったりパフォーマンスがよくなるようになっているので、いくつか汁Mにも実装を試している。主に、Set、Map、Template literals、Classとか試しに使ってるけど、ほんと試しに使ってみたままろくにアップデートしてないところあるよ。
  
この機能自体は別に使うつもりもなく、ふーん、と思っていたのだが、Windows版でデスクトップ通知ができないという状態になって困った。デバッガで追うと、createHTMLNotificationで例外が出ている。MacやUbuntuのChromeではいつもどおりなので、Windows版に導入されたRicher Notificationが原因なのは明らかだろう。createNotificationでシンプルな通知を出してみると、これもちゃんと表示される。つまるところ、Windows版Chrome28で、createHTMLNotificationを呼ぶときだけ、例外が投げられる。こうなると、Silver birdの仕様ではデスクトップ通知の設定が、オンページ通知にフォールバックしてしまうのだ。エゴサーチしてみると、デフォルト設定で使ってる人たちは、設定が勝手に戻る現象を見て混乱してる人もいる。
+
そんなことよりStringに追加されたイテレータ機能のほうが重要。最近Twitterに対してEmojiを入力できるようになっていることもあってEmojiの利用者が多いのだが、Emojiの多くがUnicodeでサロゲートペアというコードポイントを割り当てられていて、旧来のJavascriptのString系メソッドでは適切に扱えていなかった。具体的には、Emojiの含まれるツイート内にハッシュタグやリンクが含まれている場合に、適切なリンク文字列を生成できていなかった。これは文字数のカウントにString.substringを利用していたためで、サロゲートペアを含む場合にカウントが1文字分ずれてしまっていたから。これをちゃんと直そうとすると、(俺はまったくわかっていない)Unicodeのコードポイント構成を正規表現で検索して〜、とかいろいろやらないとダメ。でも比較的最近Javascriptに導入されたString向けのイテレータがサロゲートペアを認識してくれるので、これを使うようにゼロから書き直した。
 +
動くことは動いているっぽいのだが、すべての文字を配列化してループ回して処理しているせいか、旧バージョンよりちょっとパフォーマンス落ちた感じある。
  
まぁ、仕方がないのでRicher Notificationに対応させた。Windows版Chrome28以降は、createHTMLNotificationで例外が投げられたら、代替としてRicher Notificationを出す。それでも何かしら例外が投げられたら、オンページ通知にフォールバックする。そのうちMac版でもこの動作になるはず。Linux向けChromeについては、まず通知センターの実装はされないのではないかな。
+
====Web Fontsを使ってみる====
 +
ぶっちゃけ俺はWindows環境でMS Pゴシックが表示されるのが嫌で、汁Mでもいろいろやっていたのだが、Google先生がGoogle FontsにNoto Sans Japanese(のサブセット)を提供してくれたのですぐさま導入した。ついでにアルファベット等用にRobotoをデフォルトにした。このため、WindowsでもMacでもLinuxでも、基本的にほぼ同じ文字が表示されているはず。でもこのフォントは容量を抑えるためにサブセットになっていて半角カナなんかは含まれていない。Windows 10以降だと、そんな文字でも漣フォントがかなりマシなので気にしないでよい。Windows 7以前は死ね。
  
で、現在は通知を呼ぶ前にツイートの内容をパースする構造になっていないし、Richer Notification
+
====Twitterの新機能に対応してみる====
自体もHTMLDOMElementを内包するような作りになっていないっぽいため、通知の中にリンク張ったりすることはできなそう。Silverbird Mの現仕様では、通知がされて、通知に触ると通知センターに残り、放っておけばタイムアウト設定時間後に勝手に消えて通知センターにも残らないようになっているはず。リプライとふぁぼくらいはボタンを作れそうだけど、あまり興味ない。
+
汁Mでは複数の画像ファイルを添付できるように対応している。また、引用ツイート(コメント付きRT)に対応している。このへんは仕様がちょいちょい変わるので、追いかけるの面倒くさい。
  
なお、オンページ通知はものすごいゴリ押しで実装されていてウンコだったし、オンページ通知使うことって俺の中ではなかったので、機能ごとごっそり抜いた。なので、デスクトップ通知に失敗したら、通知しないという設定にフォールバックするようになってます。
+
なお、動画とアニメーションGIFのアップロードも、裏ではそこそこ動かせる状態なのだが、ちょっと放置してる。
  
==ソースとバイナリ==
+
==ソース==
[https://github.com/studioddtonline/Silverbird-M github]に置いてます。
+
[https://github.com/studioddtonline/Silverbird-M github]に置いてる。
  
最近になって、[https://chrome.google.com/webstore/detail/silverbird-m/gopipemgedcleeegndifdeddpemopeda Chrome Web Store]にも公開しました。
+
いちおう[https://chrome.google.com/webstore/detail/silverbird-m/gopipemgedcleeegndifdeddpemopeda Chrome Web Store]でも公開してます。DMとかはまったく俺が使っていないので、ぜんぜんテストしてないけど、誰かが困っても俺は知らないよ。あと、定期的にTwitterのクソ野郎どもにAPIキーを殺されるので使わないほうが良いよ。
  
 
==ダメなところ==
 
==ダメなところ==
 
* 低機能(リスト操作できないとか)
 
* 低機能(リスト操作できないとか)
 
* デザインがアレ(気にすんな)
 
* デザインがアレ(気にすんな)

2017年9月8日 (金) 16:20時点における最新版

前書き

2013年頃のはなし。毎日会社にサラリーマンのコスプレをして出かけているものの、俺って全然仕事がなくて暇なので、何か暇つぶしをやろうという話(だった)。

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

ただ、不満もあった。

  • リストのユーザの公式RTがUnified Timelineに載らないので載せてほしい
  • 公式対応も始まっている、改行付きツイートに対応してほしい
  • Twitterの仕様変更に追従してくれないので不具合が多い(リストとか)
  • 俺が使わない機能が多い

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

環境を作る

前提

  • 元々のソースはGithub上にMasterリポジトリがある。
  • ビルド用にRakeを使っている。モダンな感じじゃない。
  • そもそも俺、本業プログラマじゃないよ。

Git for Windows

2013年3月現在では、便利なものでWindows環境向けにもGitの実装がある。Git for Windowsをダウンロードしてきてインスコ。(このとき、パスの設定のところでRun Git from the Windows Command Prompt、つまりWindowsのコマンドプロンプト内でも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インスコしたり以前はやってたのだが、Rubyでのビルドをばっさりやめたので、削除。

fork

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

forkするにあたって、manifestファイルのnameとかversionは変えてしまう。とりあえず名前はSilverbird Mにした。これは、Modとかそういう意味ではなく、「for Me」という気持ち。だから、汁Mでは要望を基本的に受け付けない。forkして自分でやってね。

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

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

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

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

いちおう.gitignoreファイルを確認してみるとsecret_keys.jsが入ってるのだけど、manifest version2対応の時にlibの下に移動したようなので、そのときからアップロードされてしまっていたのかも。 fork元のほうはどうしようもないんだけど、forkした俺のリポジトリ上からは、完全に(履歴からも)削除した。

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関係

オリジナルのsilverbirdではmanifestファイルにGoogle Analytics使ってる感のある設定があるので、どこかにアプリケーションキーが埋まってるのかなーと思ったらlib/analytics.jsにキーが埋まってた。

汁Mでは、誰かの動作をトラックしてデバッグに利用したりする気はない(つまり俺以外の利用者からのフィードバックを受け付ける気がない)ので、スパっとGAは削除。

URL Shortener と Expander

短縮URL作成と展開にウェブサービスを活用しているようなのだが、APIキーとかそのまんま埋まってるし、現在ではサポートされていない形式のままだったりした。

汁Mでは、このへんの実装を大きく変えた。詳しくは後述。

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

アプリケーション内でSilver Birdとなっている部分をSilverbird Mと置換しようと思うと、_localesディレクトリ下部にある各言語ファイルも置換する必要がある。せっかくなので、jaロケールを追加して日本語化した。ベースになっている英語、つまるところenディレクトリをコピーしてjpにリネーム。中身は単純なJSONファイルになっていて、ちくちくとそれらしい単語やら訳文やらに置き換えるだけ。

オプション画面の最下部にLocales Reportというリンクがあるのでクリックしてみると、ローカライズの抜け部分の指摘がされていて、赤く変わっている部分がなくなればローカライズ完了となる。ただ、各ロケールのmessages.jsonの値を横断的に表示しているだけのよう。俺は日本語しかまともに解さないので、スペイン語とかよくわかんないロケールは削除した。今後も一切サポートする気はない。

また、「retweet by ○○」みたいなものは「○○さんのリツイート」のようにしないと日本語としては不自然なので、泥臭く翻訳を適用できるように組み替えている。

jQuery関係

Silver Birdではサードパーティライブラリという位置づけでjQueryとjQuery UIを主に使っている。ただし、コアは1.4.2、UIは1.8.1と、現行のjQueryよりも超絶古い。CSP準拠のためにjQueryは1.5.2にアップデートしてみたが、それでも古い。最新化せねばならない(強迫観念)。

jQueryコアをアップデート

さて、いろいろ試すと、1.5.2から1.6.4にアップデートすると、タイムラインのページングがうまくいかない。1.5系と1.6系で大きく変わったのは、HTMLの属性およびjQueryオブジェクトのプロパティへのアクセスとしてattr()が利用されていたようなのだが、どうもHTML属性にアクセスしているのか、jQueryオブジェクトのプロパティにアクセスしているのかを明確に使い分けようぜ、という風潮がでてきて、なにやらprop()という奴が増えたそうな。attrを使っている箇所をgrepして適宜propに入れ替えるとjQuery 1.6.4にアップデートできるようになった。さらに1.7.2に更新してみたが問題なし。

調子に乗って、1.9系へのマイグレーションガイドを読み、on/offへと、ajaxのイベントハンドラ周りを大きく変える。そこまでくれば2.0系への移行は問題なかった。現在では、常に最新のjQueryを同梱するようにしている。

jQuery UIをアップデート

次はUI側。単純に1.8系の最終版1.8.24にアップデートする分には問題なかったが、1.9〜1.10とアプデすると仕様変更があるようで、アップグレードガイドを見ながら修正する必要があった。

1.9系にすると、オートコンプリートとツールチップ系はプラグインに頼らなくてもjQuery UIだけで完結できるようになった。ほんとはActionMenuとかコンテキストメニューはどうにかしたいのだが、いろいろと根深いことがわかっていて、面倒くさいので放置している。キャプチャリングフェーズでイベントハンドラを登録していたりするせいで、jQueryなどのイベント管理外にあるためにメモリリークの温床っぽい。

Twitter API 1.1に対応してみる

当初の予定ではもっとあとにやろうと思っていたのだが、たまにTwitterのAPI 1.0のエンドポイントを停止するテストなんかをやるせいで、先に片付けることになった。

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

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

なお、これに関連するのだが、オリジナルのSilverbirdではAPI使用回数のグラフが見られた。しかしAPI 1.1では全体の使用回数のグラフなど見ても意味がない(単位時間あたりに各APIエンドポイントにアクセスできる回数が、APIエンドポイント毎にそれぞれ異なる)ので、機能ごと削除した。

また、大雑把には文句を言われない程度にTwitter Display Requirementsの記載を守っているつもり。(なんだけど、ちょくちょく書き込み権限をつぶされる)

機能追加関係

短縮URL展開とサムネイル対応を根本的にどうにかする

これは簡単。一つ一つ短縮URLサービスとかURL展開サービスとかオンライン写真ストレージサービスを見に行くと、だいたいURL展開やサムネイル閲覧用のAPIが整備してある。???「司令官!そいつを増やしていけばいいじゃない!」「よし増やすか」

まぁ、最初はそういう方針で俺得な感じのやつを増やしていったのだが、廃止になるサービスもあったり、そもそもAPIがないものもあったりしたので泣いてた。そこで考え方を変え、XMLHttpRequest Level2のresponse URLと、OpenGragh Protocolで公開されている画像URLを利用することにした。

XHRはもともとリダイレクトをフックすることができない(ChromeのWebRequest APIやHTML5世代のFetch APIなんかではリダイレクトをフックする仕組みがある)。つまり何度かリダイレクトされる程度なら、XHRでアクセスすると最終的なコンテンツのURLが得られるのだ。???「もうこれでいいじゃない司令官!」

これでURL展開サービスは完全に要らなくなった。ただし、ただのURL展開にGETは大げさなので、この部分にはHEADを使っている。

サムネイルはナウなヤングにバカウケのチョベリグなOGPをフル活用する。OpenGraphはHTMLのヘッダにMicroformat的に埋め込まれているメタデータ。外部から認証なしにアクセスできるようなサムネイルのURLなんかが載せてあったりするし、サムネとか関係ないサイトでもサイトのロゴとかサービスロゴとか載せてる場合が多い。ほほぅそれは好都合、とそいつを使おうとするなら、HTMLをGETしてスクレイピングをやる必要がある。「一度HEADしてURL展開してから、やっぱりスクレイピングのためにGETしてんのかよ!」……というのはおっしゃるとおりなんだが、モダンなウェブサイトならOGPに対応していないことはあんまりないし、これをやるだけでサムネイル取得し放題なわけよ。やるっきゃ騎士なわけよ。

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

高速化できないかどうか

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

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

考え方としては、

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

の2点しかないと思っているが、前者はスクロール位置の保存と再スクロールの調整でうまいこと実装を思いつかずに頓挫しており、後者は確かにすべて文字列化するとparseEntitesあたり3msくらい速くなるのを確認したのだけど、ハッシュとメンションのリンクがぶっ壊れてしまうのを解決する実装を思いつけずにいた。これは後者の方法をどうにかすることができたため、全要素の生成は文字列を連結するだけにして、あとは閲覧時にイベント動作でクリックだのポップアップだのを処理させるように分離できた。

ちなみに、最近の汁Mではホームタイムラインでフェッチしてくるツイート数をレンダリング速度から逆算するように仕様変更した。要は、利用者が「200件フェッチしろよボケ」と思っても、クソみたいなスペックのPCで使っているひとは、フェッチしてくる数がだんだん減っていくようになっている。レンダリングが250ms程度であれば「ぱっ」と表示されるように感じると思う。これはなぜかというと、RTや特定の情報でタイムラインをフィルタさせることを考えると、画面上に表示されるエントリの数と設定上の件数を一致させるのはアホらしいくらい面倒だからだ。自動で件数が上下するなら、どれだけフィルタされてもユーザが件数を意識することはないので、そんなふうに落とし込んだ。

とはいえ、汁Mあんまり早くない。Core i7の端末だとさくさくだけど、Core2 Duoとかだとちょっとかなりアレ。

RAM消費を抑えられないか

俺がまったく使わない機能であるところの既読管理をスパっと削除したら稼働中の使用メモリ量がザクっと減るようになったので、味を占めた。

でもその後いろいろやってもあんまり減らない。そのうえしばらく使ってると、徐々に使用メモリ量が増えていく。これはリークしてるんだと思うんだが、ぶっちゃけどこで漏れてるのかわかんない。(なんかChromeのプロセス間通信でオブジェクト参照が切れずに残ってるような感じはある。その全てでDeep Copyして渡すのは面倒くさいので、Object.assginでシャローコピーしたり、古典的なJSON.pase(JSON.stringify())でぶっ壊れコピーするとマシになる)

まぁ、このへんは諦めが肝心。Chrome拡張をリロードさせればRAM上は初期化されるので、ザクっとRAM使用量が減る。

キーボードショートカットを有効にしているひとはポップアップページ上でAlt+Shift+Rキーで拡張をリロードできるので、しょぼいRAM環境のひとは定期的にリロードしよう。そうでない人は汁Mのポップアップページがバックグラウンドページ上で開発者コンソールを開き、chrome.runtime.reload()すりゃいいよ。

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

RSSリーダーとしてLivedoor Readerを愛用しているのだが、こいつの特徴のひとつにVimライクなショートカットキーの存在があるじゃん?ふだんLinux上のエディタとしてVi/Vim系を使うことはあまり多くないし、別に好きでもなんでもないのだが、j/kキーで記事移動ができるのはGUIとして便利だと思っていた。TwitterもRSSと同じで、ある時間軸上に複数のエントリがある構造ではあるので、j/kキーで上下のつぶやきに移動できれば良いし、タブ切り替えもキーでできるとより良いだろうと思ってた。

やっつけで実装。

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

これ、エントリ位置を描画後に保存してスクロール量を決めているんだが、Compose表示中やアップデート通知中は座標がズレてしまって期待したようにスクロールできない。いちいち再計算させればいいんだけど、Compose表示を分離して新しくしたいと思っているので、この辺はアップデートなし。

Chrome28のRicher Notificationに対応してみる

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

仕方がないのでRicher Notificationに切り替えた。

ES201x機能を導入してみる

ChromeでもFirefoxでも、ECMAscript201xで導入予定の機能がいろいろ実装されている。たいていの場合で、新しい機能のほうが既存のJavascriptのものよりも便利だったりパフォーマンスがよくなるようになっているので、いくつか汁Mにも実装を試している。主に、Set、Map、Template literals、Classとか試しに使ってるけど、ほんと試しに使ってみたままろくにアップデートしてないところあるよ。

そんなことよりStringに追加されたイテレータ機能のほうが重要。最近Twitterに対してEmojiを入力できるようになっていることもあってEmojiの利用者が多いのだが、Emojiの多くがUnicodeでサロゲートペアというコードポイントを割り当てられていて、旧来のJavascriptのString系メソッドでは適切に扱えていなかった。具体的には、Emojiの含まれるツイート内にハッシュタグやリンクが含まれている場合に、適切なリンク文字列を生成できていなかった。これは文字数のカウントにString.substringを利用していたためで、サロゲートペアを含む場合にカウントが1文字分ずれてしまっていたから。これをちゃんと直そうとすると、(俺はまったくわかっていない)Unicodeのコードポイント構成を正規表現で検索して〜、とかいろいろやらないとダメ。でも比較的最近Javascriptに導入されたString向けのイテレータがサロゲートペアを認識してくれるので、これを使うようにゼロから書き直した。 動くことは動いているっぽいのだが、すべての文字を配列化してループ回して処理しているせいか、旧バージョンよりちょっとパフォーマンス落ちた感じある。

Web Fontsを使ってみる

ぶっちゃけ俺はWindows環境でMS Pゴシックが表示されるのが嫌で、汁Mでもいろいろやっていたのだが、Google先生がGoogle FontsにNoto Sans Japanese(のサブセット)を提供してくれたのですぐさま導入した。ついでにアルファベット等用にRobotoをデフォルトにした。このため、WindowsでもMacでもLinuxでも、基本的にほぼ同じ文字が表示されているはず。でもこのフォントは容量を抑えるためにサブセットになっていて半角カナなんかは含まれていない。Windows 10以降だと、そんな文字でも漣フォントがかなりマシなので気にしないでよい。Windows 7以前は死ね。

Twitterの新機能に対応してみる

汁Mでは複数の画像ファイルを添付できるように対応している。また、引用ツイート(コメント付きRT)に対応している。このへんは仕様がちょいちょい変わるので、追いかけるの面倒くさい。

なお、動画とアニメーションGIFのアップロードも、裏ではそこそこ動かせる状態なのだが、ちょっと放置してる。

ソース

githubに置いてる。

いちおうChrome Web Storeでも公開してます。DMとかはまったく俺が使っていないので、ぜんぜんテストしてないけど、誰かが困っても俺は知らないよ。あと、定期的にTwitterのクソ野郎どもにAPIキーを殺されるので使わないほうが良いよ。

ダメなところ

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