Home > Latest topics

Latest topics 近況報告

たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。

萌えるふぉくす子さんだば子本制作プロジェクトの動向はもえじら組ブログで。

宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

Page 6/245: « 2 3 4 5 6 7 8 9 10 »

テキストリンクとpopInとjQuery - Mar 31, 2009

テキストリンクpopInの競合、について調べてる。

popInが入っているとテキストリンクが動かない、ことの理由はどうも以下の2点によるみたい。

  • popInがdblclickイベントをstopPropagation()しているため、テキストリンクにイベントが渡されなくなっている。
  • popInがポップアップアイコンの挿入位置を決定するために(?)、選択位置のテキストノードを動的に分割しており、仮にstopPropagation()されない状態にしてテキストリンク側でイベントを拾って処理を行おうとしても、イベント発生時とテキストリンクが処理を行う時とでDOMツリーの構造が微妙に変わっていて、正常に動かない。

何か有効な対策が無いか考えてる。

ところでpopInは内部的にjQueryを使ってるようなんだけど、その旨の表記を僕には見つけられなかった。jQueryはMITライセンスとGPLのデュアルライセンスで、MITを選択した場合でもThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.(著作権表示とMITライセンスの許諾表示をソフトウェアの全コピーかもしくは重要な箇所で示す必要がある)ということなので、下手したらMITライセンスの違反ということになるような気が…… (一応フィードバックフォームから送ってはみた)

JavaScriptでsleepしたい、を実現する方法(require JavaScript 1.7) - Feb 20, 2009

中野さんが、JavaScriptにはsleep(一定時間待ってから次の処理に進むという命令文)が無いせいでテストを書くのに難儀したという話を書かれているけれども。まさにこれをどうにかしたくて、UxUは進化してきたようなものと言える。

知ってる人は知ってるだろうけど、Firefox/Thunderbirdアドオン向けの自動テスト実行ツール・UxUでは、テストを書く上でsleepに相当する機能を利用できる。これは、JavaScript 1.7でジェネレータ・イテレータ機能を実現するために追加されたyield式のトリッキーな使い方だ。

これを実現しているのは、lib/utils.jsdoIteration()と、test/test_case.jsrun()ということになる。ここから要点だけを取り出すと、以下のような事をしている。

まず、スクリプトの書き手は、「sleepを使いたい処理」を関数として定義する。この時、sleepの代わりにyieldを使う。

var task = function() {
  doSomething1();
  yield 1000;
  doSomething2();
  yield 1000;
  doSomething3();
}

次に、スクリプトの書き手は、この関数を後述するdoIteration()に渡す。すると、doIteration()がよしなに計らって、「doSomething1()を実行した後、1000ミリ秒待って、doSomething2()を実行し、また1000ミリ秒待って、doSomething3()を実行する」という風な形で先程の関数の内容を実行する。

この時のdoIteration()の内容は、以下のような感じ。

function doIteration(aTask) {
  if (typeof aTask == 'function') {
    // 渡されたのが関数だったら、まず、評価した返り値を得る。
    aTask = aTask();
  }
  if (!aTask ||
      !('next' in aObject) ||
      !('send' in aObject) ||
      !('throw' in aObject) ||
      !('close' in aObject) ||
      aObject != '[object Generator]') {
    // 渡されたオブジェクトまたは関数の返り値が
    // ジェネレータ・イテレータではない場合、何もしない。
    return;
  }

  // ここからがミソ!

  // 全部の処理が終わったかどうか、を示すオブジェクトを定義
  var finishFlag = { value : false, error : null };
  var last = 0; // スリープ開始時点の時刻を保持する変数
  var sleep = 0; // スリープの長さ(秒数)を保持する変数
  var timer = window.setInterval(function() {
    if (
        // スリープの長さがちゃんと指定されていて
        sleep > 0 &&
        // スリープ開始時点からの経過時間がスリープとして
        // 指定された時間未満であれば
        (Date.now() - last) < sleep
       ) {
      // ここで処理を終えて、100ミリ秒後まで待つ。
      return;
    }
    // スリープとして指定された時間が経過したので、処理を進める。
    try {
      // 次にyieldが登場するまでの間の処理を実行。
      sleep = aTask.next();
      // next()の返り値はyield式に渡された値。
      last = Date.now(); // スリープ開始時刻を保持して
      return; // 処理を一旦終えて100ミリ秒後を待つ。
    }
    catch(e if e instanceof StopIteration) {
      // 最後のyield式の後の内容が実行されて、定義された関数の内容が
      // 最後まですべて実行されると、StopIteration例外が発生する。
      // よって、処理完了とみなす。
      finishFlag.value = true;
    }
    catch(e) {
      // それ以外の未知の例外が発生した時は、処理中断とする。
      finishFlag.error = e;
    }
    // 100ミリ秒ごとの繰り返し処理を停止。
    window.clearInterval(timer);
  }, 100);
  return finishFlag;
}

UxUの内部でやってる事は基本的にはこういう事。ただ、実際にはもうちょっと使い勝手をよくするために細かい処理が加わってる。

doIteration()が中で何をやってるのかを知らなければ、パッと見は、sleepという命令文の名前がyieldに変わっただけのようにすら見えるんじゃないだろうか。そこが、このやり方の狙いだ。スクリプトの書き手はタイムアウトだのコールバックだのといった難しい事を何も考えなくても良くて、単に「sleep文に相当する機能が加わったJavaScript」として好きなように処理を書く事ができる。テストを書くための工数が大幅に削減される(かもしれない)ので、テストを書くのが苦にならず、ばりばりテストを書けるようになる(はず)。その結果、充実したテストのおかげでより安心して開発に専念できるようになる(はず)。という理屈です。

ちなみに、勘のいい人は気付くだろうけど、setInterval()に渡している関数の冒頭に以下の内容を挿入すれば、doIteration()をいくらでも入れ子にできるようになる。

    if (
        typeof sleep == 'object' &&
        (!sleep.value || !sleep.error)
       ) {
      return;
    }
var task = function() {
  doSomething1();
  yield 1000;
  yield doIteration(function() {
          doSomething2();
          yield 500;
          doSomething3();
          yield doIteration(function() {
            doSomething4();
            yield 100;
            doSomething5();
          });
        });
  doSomething6();
};
doIteration(task);

さらに、こんなこともできる。

var task = function() {
  doSomething1();
  yield doIteration(function() {
          var flag = { value : false; }
          frame.addEventListener('load', function() {
            frame.removeEventListener('load', arguments.callee, false);
            flag.value = true;
          }, true);
          frame.contentDocument
               .defaultView
               .location.href = 'http://www.example.com/';
          yield flag;
          // フレームの読み込みが終わったらここに進む
          doSomething2();
        });
  doSomething3();
};
doIteration(task);

この辺をもっと簡単に書けるようにヘルパーメソッドを色々と整備したのがUxUのテスト実行環境、と思ってもらえれば大体それで正解です。

25日追記。他にも色々やり方があるようです。(どっちもMozilla限定だけど)

Adobe AIRでMigemo - Nov 25, 2008

AIRMigemoというライブラリがあることをリファラで知った。AIRアプリにMigemo検索機能を組み込むのに使えるということだろうか。でもライセンスが分からない……

肝心の正規表現の生成処理は、かなり真面目にやってるような印象。XUL/Migemoの現在の実装は長い文字列の一括置換と分割とソートによる擬似的な物なので、「短い入力で長い単語にマッチさせる」という元のMigemoの特徴の一つを損なうことなく持っていると考えられる。

UxUを使った、自動テストを伴うデバッグ手法の実践 - Nov 18, 2008

UxU(UnitTest.XUL)を利用したFirefoxアドオンのデバッグの例 - ククログ(2008-11-17)

XUL/Migemo 0.11.7での修正内容が典型的な「自動テストを使ったデバッグ」だったので、UxUのチュートリアルを兼ねて、会社のサイトの方に書いてみました。UxUの解説って言うよりは、テスト駆動開発自体の解説という気もしますが。

リンク先に解説してるのはpXMigemoFindのfindFirstVisibleNodeメソッドだけのデバッグ話ですが、実際にはこのメソッドはだいぶ根幹に関わる物で、このメソッドの挙動の変更によって他の機能に色々と影響が出る可能性がありました。が、他の挙動に関しては一通り自動テストを作成済みだったために、後退バグの発生で収拾不能な事態に陥るということを恐れずに安心して修正に取り組むことができた、というまさに自動テスト様々な事例だったということも忘れずに付け加えておきたい所です。

よくある風景 - Nov 15, 2008

XUL/Migemoの動作で怪しい所を見つける→再現条件確定→その条件下でのテストを行うためのUxUのテストケースを作成→何かちゃんと動かない→UxUのバグ発見→抜本的修正開始→途中で疲れて寝る→抜本的修正続き→やっとチェックイン→XUL/Migemoのテストを書く気力がなくなってる→それでもめげずにテスト書き再開→UxUの別の問題発覚→心が折れかける(今ここ)

XUL/Migemoとvimperatorと単体テスト - Nov 10, 2008

今まで全然知らなかったんだけど、vimperatorでXUL/MigemoのAPIを使ってタブの切り替えヒントモードを強化するなんてことをやってる人がいたんだ。(←って、分かったような書き方をしてるけどvimperatorの事は全然分かってません……)

その関係でいくつかページを渡り歩いてたら、XUL/Migemoのバグって話題が出ていて、なぬ!と思ってさらに辿ってみた所、半角括弧がらみの問題のことらしい。あーこの辺ちゃんと見直さないままずっとここまで来てたんですよね……UxU用のテストも基礎部分の単体テストはさっぱり手つかずのままだったし(ぉぃ)。ということで本腰入れてテスト書いて潜在してたバグを潰し始めました。でもまだまだ見落としがありそう。

FXツールバー - Nov 10, 2008

ツリー型タブクリック証券のFXツールバーが衝突しているという話を見かけたので調べてみたんだけど、だいぶお手上げです。

  • 原因は、クリック証券のFXツールバーが、初期化処理の中でXMLHttpRequestの同期読み込みを使っているせいである。
    • 同期読み込みを行っている間、ツリー型タブを含む他のアドオンの処理がすべてストップしてしまう。
    • ツリー型タブはセッション復元のイベントを捕捉してツリー構造を復元している。正常な状態では、こうなっている。
      1. ツリー型タブの初期化処理が始まる。セッション復元関係のイベントの監視が始まる。
      2. セッション復元関係の処理が行われる。
      3. ツリー型タブがセッション復元関係のイベントを捕捉し、ツリー構造を復元する。
    • しかしFXツールバーが入っている場合はこうなる。
      1. FXツールバーの初期化処理が始まる。同期読み込みのために処理が止まる。
      2. セッション復元関係の処理が行われ、完了する。
      3. 同期読み込みが完了し、FXツールバーの初期化処理が終わる。
      4. ツリー型タブの初期化処理が始まる。セッション復元関係のイベントの監視が始まる(が、もはや手遅れ)。
  • FXツールバーの初期化処理を書き換えてみようと思ったけど、変数スコープの関係で、ツリー型タブの中からFXツールバーの初期化処理を書き換えたら全く動かなくなってしまう。
  • FXツールバーの初期化処理が走る前にセッション復元イベントの捕捉を開始して、ツリー型タブの初期化が終わる前に起こったイベントはすべてキューに溜め込み、ツリー型タブの初期化が終わった後でそのキューを処理する、という風にしてみたけど、タイミング次第ではまだ復元に失敗する。

リバースエンジニアリング禁止とあったけどべつに僕はこれを利用するつもりも使用するつもりもないのであくまで自作アドオンとの競合の原因を調査するために難読化されたコードを頑張って解読してみたところ、FXツールバーは非同期処理のための便利メソッドを持ってるくせに何故か初期化の時は同期処理にしているという事が分かった。何故そんなところで手を抜くんだ……

サーバからの設定の読み込みを非同期で行って、設定の読み込み完了後に初期化処理の続きを行うようにすれば、この問題は解消されると思うんだけど。ツリー型タブ以外にも物によっては衝突する可能性があるし、向こうの方で対処してくんないかなあ。望み薄かなあ。一応問い合わせ先のアドレス宛にメールしてみたけど……

続報。返信があり、初期化処理を非同期で行うように改良された新バージョンがもうすぐ公開されるとのことです。反応はやっ!(僕がメールするより前から準備してたっぽい?)

UnitTest.XUL 0.5.0 - Oct 30, 2008

会社サイトのブログの方で解説を書くつもりですが、UxU新バージョン(0.5.0)を公開しました。日本時間10月30日の昼下がり、太平洋標準時で29日の肉の日リリースです。変更点はMozilla Add-onsのバージョン詳細の方を見てください。

自分自身がサーバソケットの方を使ってなかったのでMozRepl互換のサーバ機能の方がだいぶ長らく死亡してたみたいなんですが、今回プロファイルを指定してテストを実行する機能を作るにあたり、新たに起動された子プロセスと親プロセス側でテストの実行結果や中止の命令とかを通信しあうために、UxUサーバで使われていた機能をブラッシュアップして流用するようにしたため、ぶっ壊れていた所がだいぶ直りました。今後はサーバ周りの実装も気をつけて見るようになると思いますので、ぶっ壊れっぱなしのまま放置ということは減るんじゃないかと思います。多分。

ところで、子プロセス起動してうんたらかんたらということができるようになったということは、FirefoxやThunderbirdの上で動かすことにこだわらなくてもいいようになった(必要に応じてそれらを呼び出せば済む)という事で、やる気次第ではXULRunnerアプリケーション化という道もあり得るかもしれませんね。

Firefox 2.0.0.17とThnderbird 2.0.0.17のJavaScriptエンジンに極めて深刻な問題があるようです - Sep 30, 2008

仕事で書いてるコードがどーにもうまく動かなくて、ついこないだまで正常に動いてたのにどうして?!と思って色々試していたら、特定の関数をtoString()等で文字列化した時におかしな現象が起こっていた。具体的には、Thunderbird 2.0.0.17のAccountManager.jsで定義されているsaveAccount()を書き換えようとしたら例外が発生して、onSave()を文字列にしてreplace()で改変した物はfor〜inループだった箇所がwhileループになってしまっていた。コードを削っていったらめちゃめちゃ短いコードでも再現したので、Bugzillaに報告しておいた。

regression: Function.toString(), Function.toSource() and uneval() return wrong JavaScript code if the function includes "for ... in" roop

上記のような方法で動的にパッチを当てる手法を僕は多用してるので、仕事で書いたコードもここで公開してる拡張機能のコードも、正直、どのくらい影響を受けるのか見当も付かない。早急に解決されることを祈るのみだ。

ちなみにFirefox 3.0.3ではこの問題は起こらなかった。2.0.0.17で入ったセキュリティ上の修正による後退バグっぽい?

追記。29日時点ですでに修正済みだったようだ。これ、あと1ヶ月間放置ですか……? ものっそクリティカルなバグだと思うんだけど。

Page 6/245: « 2 3 4 5 6 7 8 9 10 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のつぶやき