2010年3月31日水曜日

Flash の埋め込みパラメータ wmode で発生したトラブルいろいろ

Flashまわりの仕事をしていて wmode パラメータで躓いたのでメモ。


■事の発端
開発機のMac上では正常に動作していたコンテンツが、Windows機のFirefoxでテストを開始したとたん以下のような症状に見舞われてしまいました。
  • TextFieldにフォーカスを合わせて日本語をタイプするとTextFieldとは全然違うとんちんかんな位置に入力中の文字列が表示される
  • しかも入力した日本語が文字化けする
ちょっと調べてみて、どうやら wmode パラメータが悪さをしているのではないかということが分かりました。
http://www.326studio.net/blog/2009/10/firefox.html
http://fukata.org/2009/11/29/1905/

ということで早速 wmode パラメータを消してみたところ上手く動作しました。


■そのときのSkype上での会話(一部改変)
登場する皆様ご紹介
 akisute: そもそもテキストボックス内に入力が表示されないのが普通な気がしてきた
easy: あいうえお=0B0D0F0H
akisute: http://www.326studio.net/blog/2009/10/firefox.html
akisute: こいつだな
wozozo: なんでその指定してたんだっけ
AE35: > htmlにswfファイルを呼び出すときにwmodeの指定でtransparent,opaqueの指定をするとこの現象が起きます。
akisute: http://fukata.org/2009/11/29/1905/
akisute: ということでをぞぞんよろしくお願いします
wozozo:
{
id : 'externalswf',
quality : 'high',
wmode : 'transparent',
allowScriptAccess : 'always',
width: 100,
height: 100,
flashvars: 'abesi hidebu tawaba'
});
wozozo: いまこうなってます。
wozozo: wmodeごと削除?
feiz: お、そこは以前のプロジェクトでぶつかった問題ですね
feiz: wmode=transparentを付けると
feiz: flash上にhtmlのダイアログを表示したりしたときにflashが上に出てしまう問題が回避できます
feiz: ただし日本語が化けます。
wozozo: e
wozozo: じゃあこれ消していいの
feiz: 両方いっぺんに回避する方法はあったっけ・・・
wozozo: でもいま
feiz: ああ、無いんだった
wozozo: flashの上にhtmlを表示するときには、
wozozo: embedされてるdivごと hide してる
feiz: あ、それが正しいはず
feiz: 以前のプロジェクトでもそうした。
wozozo: naru
wozozo: じゃあ消す。
feiz: ちなみに二つの問題は両方とも特定ブラウザ限がついてたはず。
feiz: どうでもいいけど。
akisute: それはすごい面白いネタなので
akisute: メモってBlogる


■さらに調査

調べれば調べるほどこの wmode には問題だのバグだのが山盛りだと言うことが分かって驚愕。
http://blog.graffiti-web.org/archives/2006/04/wmode.html
http://chabudai.org/blog/?p=34
http://d.hatena.ne.jp/jedisystemer/20090227/1235754536
http://3ping.org/2004/05/15/1441
Flash Player 10では修正されているらしいのですが、上記のバグはFlash Player 10で発生してますし、うーん。これは触らぬ神にたたり無しですね。


■結論
wmode は指定しないようにしましょう、と。Flash Player 9以下ではIEでフレームレートが落ちる問題がありますが、そもそも9を使っている人も少なくなりましたし問題ないと思います。

2010年3月20日土曜日

IE6, IE7 上で flash.net.URLLoader を使って Comet (Long Poling) によるプッシュ通知を行う際の注意点

Flashは一度書いたらどのブラウザでも同じように動く、そう信じていた時が私にもありました。その私の考えを裏切るかのように、 またIEか と言いたくなる現象が発見されました。

調査の際に参考にしたページはこちら。
http://saruzaurus.blogspot.com/2008/07/comet_10.html


■問題
IE6, IE7 で、 flash.net.URLLoader / flash.net.Loader を使用すると、同時に2本しかコネクションが張れません。通常の使い道でしたらこの制限で何ら問題はないのですが、問題になるのは CometTornado のように所謂ロングポーリングと呼ばれるHTTP通信の仕方をするアプリケーションを書いたときです。ロングポーリングではいったんHTTPリクエストをサーバーに投げた後、サーバーで何かイベントが発生するまでそのレスポンスをひたすら待ちます。従って常時HTTPコネクションが1本消費されている状態になるため、事実上1本しかHTTPコネクションを使うことができません。

またIE6, IE7 では、 flash.net.URLLoader.close() の実装にも問題があるようです。
http://www.kirupa.com/forum/showthread.php?t=335691
http://stackoverflow.com/questions/455656/urlloader-gets-stuck-when-polling
きちんと使用後に URLLoaderclose しているのですが、それでも何故かメモリ使用量が減らなかったり、コネクションが閉じられなかったりして貴重なコネクション数が消費されっぱなしになったりするなど、どうにもこうにも不安定です。



これらが原因となって、常時ロングポーリングでイベントを待ち、イベントを受け取ったら処理を行ってまたすぐロングポーリングを張りに行くようなアプリケーションを書くと、その他の通信を行った際にロングポーリングが止まってしまったりその他の通信がコネクション数限界で実行されなかったりします。


■原因
Flashは内部的にはブラウザの機能を用いて実装されています。したがってブラウザ側のHTTP接続数が2本であれば自動的にFlash側のHTTP接続限界も2本になります。当たり前と言えば当たり前なのですが、盲点でした。

Flash側の接続数とブラウザ側の接続数が共有されるかどうかは未知数・未検証ですが、おそらく共有されるのではないかと思っています。従って(まずこんな構成にすることはないと思いますが)FlashでCometによる通知待ちを行い、Ajaxでも同様の通知待ちを行うと、それだけで接続限界になってしまいます。


■対策
今のところ最良の方法がまだ分かっていません・・・ごめんなさいorz 一応いくつか思いついている作戦は、
  1. Windowsのレジストリを操作してIE6/IE7の同時接続数を増やす
  2. 同時接続数制限は同一ドメインに対してのみ発生するらしいので、サブドメインを切って別々のドメインに分ける
  3. 他の通信が発生しているときはロングポーリングを切断する
などなど。上手い対策が発見されましたらまた書きます。

2010/03/31 23:59 追記:サブドメインを切って別々のドメインに分けたら上手く動作しましたので、この方法を一応お勧めしておきます。

2010年3月18日木曜日

Flash の fl.controls / fl.containers の visible プロパティはADDED_TO_STAGEで変更する

最近イマイチなFlashネタしかお届けできておらず残念です>< が、ちょっとしたネタを発見しましたので書いてみます。


■問題
fl.controls.Buttonとか, fl.containers.ScrollPaneのvisibleプロパティをコンストラクタの中でfalseにしても、直後にtrueに戻されてしまうようです。試しに、以下のような構成のMovieClipをFlashで作成して、
  • TestMovieClip
    • aButton
    • aScrollPane
以下のようなコードを書いてみます。
package {
import flash.display.*;
import fl.controls.*;
import fl.containers.*;
public class TestMovieClip extends MovieClip () {
public function TestMovieClip() {
aButton.visible = false;
aScrollPane.visible = false;
}
}
}
このコードを実行すると、なぜかボタンとスクロールペーンが 消えません 。なんでやねん。デバッガを使って調べてみると、どうやらコンストラクタが終わった後、どこかのタイミング?でvisible=trueに戻っているようです。


■対策
以下のようにADDED_TO_STAGEイベントを使って回避します。
package {
import flash.display.*;
import flash.events.*;
import fl.controls.*;
import fl.containers.*;
public class TestMovieClip extends MovieClip () {
public function TestMovieClip() {
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(event:Event):void {
aButton.visible = false;
aScrollPane.visible = false;
}
}
}
公式のドキュメントを見てもこのあたりの解説が無くて困りました。