Category Archives: HTML

HTML

Androidで画像がぼやけるのはなんで

画像の1ピクセルが実際のデバイス上で何ピクセルとしてレンダリングされるかを示す-webkit-device-pixel-ratioという値があります(この値はwebkit系であればwindow.devicePixelRatioに入っています)。
iPhone4ではこの値が2になっていて、今まで通りに画像を表示させると自動的に拡大されてなんかぼんやりしたかんじになってしまいます。

これを避けるには、実際に表示させたいサイズの2倍のピクセルサイズのものを用意して、でも縦横を1/2にして表示させると画像の1ピクセルとデバイスの1ピクセルが1:1対応になって拡大されることなく表示されるようになります。

で、日本のキャリアが販売している多くのandroid端末でこのdevicePixelRatio1.5になっています。意味わかんない。
Lynx
画面を写真に撮ったものなのでわかりにくいですが、上が45×100ピクセルの画像をそのまま45×100ピクセルで表示したもの。下は90×200ピクセルの画像を45×100ピクセルで表示したものです。上はなんか滲んでますが下はきれいに表示されています(ぼんやりしてるのは写真だから)。

身近にあった端末でdevicePixelRatioを調べたところ以下のとおり。

端末 devicePixelRatio
IS02 1.5
IS03 2
xperia 1.5(たぶん)
SH-03C(LYNX 3D) 1.5
005SH 1.5
001HT(Disire HD) 1.5
001DL 1
004HW 1

というわけで表示させたいピクセル数の1.5/2倍のサイズで画像を用意して表示すればぼんやりしないです。

追記

Philosophical Games: Customize Android Browser Scaling with target-densityDpi
WebView | Android Developers

<meta name="viewport" content="width=device-width; target-densitydpi=device-dpi" />でデバイスの1ピクセルに画像の1ピクセルが表示されるようになります(そのぶんページ全てがちっちゃく表示されます)。 via Twitter / shogo: @ku これかな http://darkforge. …

PubSubHubbubとWebSocketsとクライアントサイドのjavascript

ここのところwebのことからはさっぱりはなれてたのでTwitter / Ken Nishimura: PubSubHubbubがWebhookよりいい点って …をきっかけにクライアントサイドのjavascriptでなにかするという観点からPubSubHubbubとWebhookとWeb Socketについて調べた。

PubSubHubbub+ReverseHttp

Twitter / mala: @knsmr webhookは概念でpubsubhubbubは具体的なプロトコル仕様。

HTTP PubSub: Webhooks & PubSubHubbub – igvita.comの下の方にあるgoogle docsの資料と(関係ないけどこれflashだとおもったらiframe+HTMLでできてて驚いた)、仕様そのものDraft: PubSubHubbub Core 0.1 — Working Draftをざっと見るのがわかりやすかった。discovery, subscribe/unsubscribe, notification, distributionのプロトコルが規定されている。

subscriberになるにはpublisherからHTTPのリクエストを受け取ることがでる必要がある。通常firewallの中にいるブラウザは受け取ることができない。そこで出てくるのがReverseHttpでグローバルにあるサーバをsubscriberとして置き、通常firewallの裏にあるブラウザからそのサーバにcometなりweb socketなりで接続しておいてリアルタイムにnotificationを読み出せるようにしておく。

アホみたいな感じもするけどOpera Uniteも似たようなものでMSもTeredo serviceとしてやっている(ってSmart Clients: ReverseHTTP & WebSockets – igvita.comに書いてある)。

Opera Uniteの中身は全然知らなかったので調べた。通常firewallの裏側にあるPC上で動いているOperaをHTTPサーバとして機能させるために*.*.operaunite.comというサーバをproxyとしてリクエストを受け取る(ref. Opera、Webブラウザをサーバー化する「Opera Unite」を公開 -INTERNET Watch)。operaunite.comとoperaの間の通信方法は調べてないです。ここでTCPSocketなのかな。結果opera uniteのwidget(jsで記述)でHTTPリクエストをハンドルできる(ref.Unite API Overview)。
nokia端末向けのmobile web server*.mymobilesite.net経由で繋いでるのも似たようなもの。たしかにクレイジーな気がするだけで実績のあるやり方。

Web Socket

The Web Sockets APIがブラウザのjavascriptエンジンがサポートすべきWeb Socketの仕様で、Web Socketサーバとの通信プロトコル(少なくとも現状では素のApacheにはWeb Socketのリクエストをうまくハンドルできない)の詳細はIETF draftのThe Web Socket protocolに書かれている。

プロトコルの概要をつかむにはComet Daily » Blog Archive » Independence Day: HTML5 WebSocket Liberates Comet From Hacksが一番わかりやすかった。

できることを挙げると以下のようなことができる。

  • コネクションを閉じないことを除いて基本的にHTTPと同じ
  • 平文で通信するデフォルトポート81のws://とSSLで通信するデフォルトポート816のwss://とがある
  • firewall/proxyを通過できる
  • サーバで許可されていればクロスオリジン通信が可能(HTTP access controlではなくWeb Scoketの仕様に同等のものが含まれている)
  • HTTPのcookieを利用可能
  • バイナリデータも扱える

逆にできないことはThe Web Sockets API

This interface does not allow for raw access to the underlying network. For example, this interface could not be used to implement an IRC client without proxying messages through a custom server.

と書かれているようにIRCのクライアントのようなもの(というよりかはWeb Scoketで利用することを前提としていない全てのプロトコル)はWeb Socketでは実現できない。

それはなぜか。Web Socketは基本的にはHTTPと同じようなやり取りをする。つまりはじめにクライアントからリクエストを送りサーバがそれに対してレスポンスを返す。HTTPとの違いはレスポンスを返した後コネクションが切断されるのに対してWebSocketでは切断されずに全二重双方向通信に使うことができるところ。このはじめのリクエスト/レスポンスのやり取りはhandshakeと呼ばれていてる。

draftによれば、このhandshakeはクライアントが


GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com:81
Origin: http://example.com
WebSocket-Protocol: sample

というリクエストを送り、それを受け取ったWebScoketサーバが


HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://example.com
WebSocket-Location: ws://example.com/demo
WebSocket-Protocol: sample

というレスポンスを返すことで行われる。

このhandshakeは必ず必要なので、このWeb Socketのhandshakeを理解できないサーバとはWeb Socketで通信することはできない。つまり既存のプロトコルと自由にソケット通信することはできない(ただ既存のサーバの側でWeb Socketに対応することは可能だと思う)。

TCPSocket(Network Communication API)であれば可能だがOperaでしか実装されていない。

サーバ側の対応状況

現状ではApacheでWeb Socketのリクエストをハンドルすることができない(理由は追ってません)。
mozillaのbugzillaではBug 472529 ? Support for Web sockets’ HTML5 Draft Recommendation (websocket)にだめみたいと書かれており、WebKitのbugzillaでもBug 27490 – Web Sockets Test Infrastructure Part 1/3: Serverにmod_pythonを使ったweb socketサーバを作ってテストを動かすと書いてある。

apacheのbugzillaでもBug 47485 ? HTML5 Websocket implementationにモジュールとして作ればいいんじゃないの、と書かれているので(ApacheでWeb Socketをサポートすることについて微妙にもめてて野次馬的に面白かったんですけどそこは割愛)Web Socketをサポートしたければそういうサーバを用意する必要があります。

現状ではWebSocket実装Kaazing – html5-developers-jp | Google グループで紹介されているKaazingというものがあるようです。

使用シナリオ

  • cometの代替手段として(ブラウザ上で動作するゲームのようなものやチャットなどのレスポンス改善)
  • ウェブアプリケーション全般のインタラクティビティ改善

など。

HTML5のbbとして入っているBackground Browser Taskとの組み合わせがよさそうと思ったらbbはHTML5の仕様からなくなってた…. (X)HTML5 Tracking
chromiumのほうでも動きがないのでなくなったのかも。

まとめ

クライアントサイドで考えるとPubSubHubbub(+ReverseHttp)はWeb Socketまでのつなぎ。ただデスクトップの世界ではIEがWeb Socketをサポートするのは期待できないのでWeb Socketを利用できる場面はfirefoxやchromeの拡張機能などに限られる。モバイルであればWebKit rules!なので数ヶ月後にはchromiumにWeb Socketが載ってそれはWebKitにフィードバックされるので1年くらい経てばWeb Socketを使ったアプリケーションを普通に使えるようになるかもしれない(ただ#ifdef ENABLE_WEB_SOCKETSつきなのでイコールsafariがサポートという訳ではない)。

あとデスクトップのクライアントサイドであってもOpera uniteのように自前でoperaunite.comのようなReverseHttpを持つことでPubSubHubbubを直接受け取ることができるようになる。

サーバサイドでは全く拡張性がなく使用目的が限られるXMLRPC pingの代替としてPubSubHubbubは汎用的に使える。

Web Socketは何でも通信できる神機能ソケットではないが、いままでcometのようなハックを駆使する必要があったものをシンプルに作ることができるようになる(これもIEのことがあるのでモバイルの世界限定の話)。

AutoPagerizeのスクリプト実行順序制約をなくせるようになりました

Tumblrが新しくなって、よく見ていた/show/quotes/by/everyoneがちゃんとページングされなくなって悲しいと思っていたらcxxさんがFix Tumblr Dashboard Pagination for Greasemonkeyというスクリプトを書いてくれていました。

しかし21世紀はじめの10年最後の2009年ももう終わろうとしているにも関わらず、未だにTumblr dashboard reblog 4点セットのAutoPagerizeLDRizeMiniBufferreblogCommandの実行される順序をちゃんと覚えておかないといけないなんてローテクすぎる!という怒りにまかせて、順番に関係なく入れておけば動くように細工をしました。

それぞれ上記以降のバージョンであれば、Greasemonkeyで実行される時の順番を気にせず、とにかくインストールしておけばよくなりました。

しくみ

AutoPagerizeが実行されたときに

	var ev = document.createEvent('Events')
	ev.initEvent('GM_AutoPagerizeLoaded', false, true)
	window.dispatchEvent(ev)

というコードが実行されてGM_AutoPagerizeLoadedという名前のイベントが送られてくるようにしました。LDRizeのようにAutoPagerizeに依存してなにかを実行したときに、もしwindow.AutoPagerizeが存在しなかったらGM_AutoPagerizeLoadedイベントが送られてくるまで待ってから実行するようなコードを書けば、スクリプトが実行される順序に関係なく動作させることができるようになります。

以下はAutoPagerizeでページが継ぎ足されたときに、継ぎ足された部分をLDRizeが正しく認識するためのコードです。実行したいコードをaddFilterHandlerという名前の関数にしておいて、window.AutoPagerizeが存在していればそのまま実行、存在していない時はAutoPagerizeからGM_AutoPagerizeLoadedイベントが送られてくるのを待ってから実行することで、AutoPagerizeに依存するスクリプトを順序に関係なく正しく動作させることができます。

      var addFilterHandler = function(){
          window.AutoPagerize.addFilter(
              function(pages){
                  self.removeSpace();
                  setTimeout(function(){
                      self.initParagraph(pages);
                  }, 0);
              });
      }
      if(window.AutoPagerize){
        addFilterHandler();
      }else{
        window.addEventListener('GM_AutoPagerizeLoaded', addFilterHandler, false);
      }

AutoPagerizeと連動するスクリプトを書く人へのおねがい

AutoPagerizeに依存する部分を

      var f = function () {
        // やりたいこと
      }
      if(window.AutoPagerize){
        f();
      }else{
        window.addEventListener('GM_AutoPagerizeLoaded', f, false);
      }

と書くとスクリプトが実行される順序を気にしなくてよくなって、使ってくれる人の手間も省けるのでぜひご採用ください。

Webサイト側でのAutoPagerize検出

うれしい副作用として、ユーザスクリプトと同じようにHTMLドキュメント側でもGM_AutoPagerizeLoadedをlistenしておくことでAutoPagerizeが存在するかどうかを検出することができます。

  var isAutoPagerizePresent = false;
  window.addEventListener( 'load', function () {
    alert(isAutoPagerizePresent);
  }, false );
  window.addEventListener( 'GM_AutoPagerizeLoaded', function () {
    isAutoPagerizePresent = true;
  }, false );

Firefox3.1beta2+OSXの場合、GM_AutoPagerizeLoadedイベントが発生するタイミングはjQueryのreadyメソッドよりも後、loadイベントよりも前でした。

オチ

もとのcxxさんがFix Tumblr Dashboard Pagination for Greasemonkeyの説明をよく読むと

AutoPagerizeと併用する場合は、「ユーザスクリプトの管理」でAutoPagerizeよりもに置いておく必要があります。

と書かれていたのでした… にしないといけない制約しか考慮していなかったのでにしないといけない場合は、今後も順番を気にし続けるか、AutoPagerizeにそういう細工をして取り込んでくれるようにとswdyhにpull requestを送ってください。