Home > Latest topics

Latest topics 近況報告

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

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

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

Page 89/248: « 85 86 87 88 89 90 91 92 93 »

ハイライト表示のためにテキストフィールド内に挿入されたspan要素が邪魔になる件 - May 04, 2008

前のエントリの続き。

Safari風ハイライトに限らず元々、Firefoxの検索での「すべて強調表示」では、背景色と文字色を指定したspan要素を検索がヒットした箇所に動的に埋め込むという形で、ハイライト表示を実現している。これはinput要素やtextarea要素の場合でも全く同じ。実はFirefoxではテキスト入力欄もすべて、内部的には編集可能なHTMLとして実装されていて、それ故にspan要素の埋め込みも可能になっている。

ただ、この時span要素が埋め込まれる先のDOMツリーはchildNodesとかのプロパティでは辿れない場所にあって、アクセスするにはこんな風にする必要がある。

var editable = content.document.getElementsByTagName('textarea')[0];
var nodesInEditable = editable
     .QueryInterface(Components.interfaces.nsIDOMNSEditableElement)
     .editor
     .rootElement
     .childNodes;

textareaでこれをやってみると、改行が内部的にはbr要素で表現されているとかそういうのも見て取れる。nsIFindで検索する時はテキストフィールド内のこうした「隠しDOMツリー」も普通に検索対象になるようで、span要素を埋め込む時も特に変わったことはしなくていいようだ。

しかし、ここで一つ問題がある。こうしてテキストフィールド内に普通のspan要素を埋め込んでしまうと、その要素は、選択も内部の文字の編集もできない、ワープロでいえば埋め込まれた画像みたいな状態になってしまう。普段は特に意識せずに済むけど、常に強調表示を有効にするようにしていると当然テキストフィールド内でハイライト表示が行われることになる場合も多くなり、この問題が目につくようになってくる。というか僕自身がテスト用ドキュメントでテストしていて、いいかげんウザくなってきたのでなんとかしたかった。

理想的には、テキストフィールドにフォーカスされた時に自動的に強調表示を解除するという風な挙動にできるとよかったんだけど、試してみるとどうもなかなか大変そうだということが分かった。挿入されるspan要素にonclickなどの属性でイベントハンドラを設定してみたところ、getAttributeやdispatchEventなどのメソッド、あるいはparentNodeなどのプロパティを参照しようとするとパーミッションエラーが表示されてしまった。これではnode.parentNode.removeChild()という風なことができないし、独自イベントを発行してChrome領域のスクリプトに後の処理を任せるということもできない。

これについては幸いにも、createRangeなどの機能は使うことができるようだったので、deleteContentsやextractContentsを使って自分自身を削除させるようにはできた。強調表示された箇所を選択して「選択範囲のソース」を表示してみれば、こんな風になっていることが分かると思う。

<h1>B.B.S. <span class="sub"><span onmousedown="
  try {
    var xpathResult = this.ownerDocument.evaluate(
        'ancestor::*[contains(&quot; INPUT input TEXTAREA textarea &quot;, concat(&quot; &quot;, local-name(), &quot; &quot;))]',
        this,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
      );
    if (!xpathResult.singleNodeValue) return;
  }
  catch(e) {
    // permission denied, then this is in the input area!
  }
  var range = document.createRange();
  range.selectNodeContents(this);
  var contents = range.extractContents(true);
  range.selectNode(this);
  range.deleteContents();
  range.insertNode(contents);
  range.detach();
" id="__firefox-findbar-search-id" style="...">掲示板</span></span></h1>

無駄とは分かっていても一応、正攻法の判別処理も入れてある。テキストフィールド以外の部分に挿入されたspanにも全部このイベントハンドラが設定されてしまうのは、挿入先に応じて挿入する内容を変えるのがめんどかったから。まあとりあえず、これがあっても正常に動かなくなるわけではないし、別にいいかなと。

これだとキーボード操作に対して反応させることができない(そもそもカーソルをspanの中に移動できない)し、本当は特にクリック等の操作をしなくても、テキストフィールドにフォーカスが当たった時点で強調表示を解除するという風な挙動を実現したかったわけで、そこら辺まだまだ改善の余地はある。

XUL/MigemoでのSafari風ハイライト処理で、要素の下にあるリンクにクリックイベントを送る - May 04, 2008

一つ前のエントリの続き。

「すべて強調表示」の時に強調箇所以外を暗くするというSafari風ハイライト表示は、元はSafariHighlightを取り込ませてもらったものなんだけれども、基本的には、「画面の最全面に半透明の黒いボックスを表示して全体を覆う」「その上にz-indexを調整して強調箇所を浮かび上がらせる」という二つの操作が鍵になっている。で、このうち前者の方の操作のせいで、「暗くなった所にあるリンクをクリックしても、ハイライトが解除されるだけで、リンク先には飛べない」という問題が起こっていた。

まあ、問題というか実装上そうならざるを得ないという感じで、本家のSafariもこういう仕様だったと思うからまあいいじゃんと思わなくもないんだけど、要望はあるようなので対応しなきゃなーと思ってた。で、この度晴れて対応してみた。highlight.jsの最後の方に付け加えたresendClickEventメソッドがそれ。

resendClickEvent : function(aEvent) 
{
  var utils = aEvent.view
    .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
    .getInterface(Components.interfaces.nsIDOMWindowUtils);
  if ('sendMouseEvent' in utils) { // Firefox 3
    var flags = 0;
    const nsIDOMNSEvent = Components.interfaces.nsIDOMNSEvent;
    if (aEvent.altKey) flags |= nsIDOMNSEvent.ALT_MASK;
    if (aEvent.ctrlKey) flags |= nsIDOMNSEvent.CONTROL_MASK;
    if (aEvent.shiftKey) flags |= nsIDOMNSEvent.SHIFT_MASK;
    if (aEvent.metaKey) flags |= nsIDOMNSEvent.META_MASK;
    window.setTimeout(function(aX, aY, aButton) {
      if (ZoomManager.useFullZoom) { // Firefox 3のフルズームへの対応
        aX = aX * ZoomManager.zoom;
        aY = aY * ZoomManager.zoom;
      }
      utils.sendMouseEvent('mousedown', aX, aY, aButton, 1, flags);
      utils.sendMouseEvent('mouseup', aX, aY, aButton, 1, flags);
    }, 0, aEvent.clientX, aEvent.clientY, aEvent.button);
  }
  else { // Firefox 2, emulation
    var args = [
        'click',
        aEvent.bubbles,
        aEvent.cancelable,
        aEvent.view,
        1,
        aEvent.screenX,
        aEvent.screenY,
        aEvent.clientX,
        aEvent.clientY,
        aEvent.ctrlKey,
        aEvent.altKey,
        aEvent.shiftKey,
        aEvent.metaKey,
        aEvent.button
      ];
    window.setTimeout(function(aSelf, aFrame, aX, aY) {
      var node = aSelf.getClickableElementFromPoint(aFrame, aX, aY);
      if (!node) return;
      var event = aFrame.document.createEvent('MouseEvents');
      args.push(node);
      event.initMouseEvent.apply(event, args);
      node.dispatchEvent(event);
      if ('focus' in node) node.focus();
    }, 0, this, aEvent.view, aEvent.screenX, aEvent.screenY);
  }
},

getClickableElementFromPoint : function(aWindow, aScreenX, aScreenY) 
{
  var accNode;
  try {
    var accService = Components.classes['@mozilla.org/accessibilityService;1']
              .getService(Components.interfaces.nsIAccessibilityService);
    var acc = accService.getAccessibleFor(aWindow.document);
    var box = aWindow.document.getBoxObjectFor(aWindow.document.documentElement);
    accNode = acc.getChildAtPoint(aScreenX, aScreenY);
    accNode = accNode.QueryInterface(Components.interfaces.nsIAccessNode).DOMNode;
  }
  catch(e) {
  }

  var filter = function(aNode) {
    switch (aNode.localName.toUpperCase()) {
      case 'A':
        if (aNode.href)
          return NodeFilter.FILTER_ACCEPT;
        break;
      case 'INPUT':
      case 'TEXTAREA':
      case 'BUTTON':
      case 'SELECT':
        return NodeFilter.FILTER_ACCEPT;
        break;
    }
    return NodeFilter.FILTER_SKIP;
  };

  if (accNode &&
    accNode.nodeType == Node.ELEMENT_NODE &&
    filter(accNode) == NodeFilter.FILTER_ACCEPT)
    return accNode;

  var doc = aWindow.document;
  var startNode = accNode || doc;
  var walker = aWindow.document.createTreeWalker(startNode, NodeFilter.SHOW_ELEMENT, filter, false);
  for (var node = walker.firstChild(); node != null; node = walker.nextNode())
  {
    var box = doc.getBoxObjectFor(node);
    var l = box.screenX;
    var t = box.screenY;
    var r = l + box.width;
    var b = t + box.height;
    if (l <= aScreenX && aScreenX <= r && t <= aScreenY && aScreenY <= b)
      return node;
  }
  return null;
},

見ての通り、Firefox 3ではnsIDOMWindowUtilsの新機能を使ってイベントを発行するようにして根本的解決を図り、Firefox 2ではTabScope由来のコードにnsIAccessibleを組み合わせたものを使ってそれっぽいことをしている、という感じです。どっちもタイマーを使って処理を遅らせているのは、画面全体を覆っているスクリーンが消えてからイベントを発行しないと、もう一度スクリーンをクリックしたのと同じ事になってしまうから。

最初、nsIDOMWindowUtilsのsendMouseEventでclickイベントを発行しようとしてうまくいかなかったんだけど、どうやらこれはDOMのイベントをそのまま発行するわけではなく、本当の本当にユーザの操作をエミュレートするという物みたいだ。間を開けずmousedown→mouseupと実行するとクリックしたのと同じ事になる。

強調表示関係ではこのほかに、テキストフィールド内の強調を自動的に解除するようにするための改良もあります。これも長くなったから別エントリで。

XUL/Migemoのリファクタリングとか改善とか - May 04, 2008

pXMigemoFind(pIXMigemoFindの実装)、特にfindメソッドまわりを中心にだいぶ書き直した。といってもアルゴリズム的には変わってなくて、主にメンテナンス性を向上することを目的にした書き換えです。こういうのもリファクタリングと言っていいんでしょうか。

findInDocumentメソッドがかなり長くて中のループのネストも深くなってたので、これをだいぶ細かく分けた。

まず、フレームのツリーを辿る処理がループの最後の方にどかっとあったので、これを取り出してIteratorパターンのヘルパーオブジェクトとして実装することにした(最後の方にあるDocShellIteratorというやつ)。最初Iteratorパターンというのを知った時には「こんなの何の役に立つんだ?」とか「配列みたいに長さを取り出せた方が便利じゃね?」とか思ってたけど、こうして実際に書いてみると、「次に処理対象にする物を探す」部分だけに特化して作り込めるので便利だ、ということがよく分かった。

具体的には、今までは前方検索と後方検索それぞれの場合で「一つ前のフレーム」に処理を移すのか「一つ後のフレーム」に処理を移すのかをいちいち判別してたんだけど、その判別を行う部分まで含めてDocShellIteratorとして分離することで、findInDocumentの側では何も考えずにDocShellIteratorのiterateNextメソッドを呼べば適切な結果が帰ってきてウマーという風にできるようになった。

実際のコードで見ると、findInDocument側は

docShell = this.getDocShellForFrame(doc.defaultView)
  .QueryInterface(Components.interfaces.nsIDocShellTreeNode);

if (aFindFlag & this.FIND_BACK) { // back
  docShell = this.getPrevDocShell(docShell);
  if (!docShell) {
    if (!(aFindFlag & this.FIND_WRAP)) {
      docShell = this.getDocShellForFrame(doc.defaultView.top);
      docShell = this.getLastChildDocShell(docShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode));
      doc = docShell
        .QueryInterface(Components.interfaces.nsIDocShell)
        .QueryInterface(Components.interfaces.nsIWebNavigation)
        .document;
      this.document.commandDispatcher.focusedWindow = docShell
        .QueryInterface(Components.interfaces.nsIDocShell)
        .QueryInterface(Components.interfaces.nsIWebNavigation)
        .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
        .getInterface(Components.interfaces.nsIDOMWindow);
      if (
        !editableInOut ||
        findRange.sRange.startContainer == aDocument.body ||
        findRange.sRange.startContainer == aDocument.documentElement
        )
        aFindFlag |= this.FIND_WRAP;
      continue;
    }
    this.dispatchProgressEvent(found, aFindFlag);
    break doFind;
  }
}
else { // forward
  docShell = this.getNextDocShell(docShell);
  if (!docShell) {
    if (!(aFindFlag & this.FIND_WRAP)) {
      doc = Components.lookupMethod(doc.defaultView.top, 'document').call(doc.defaultView.top);
      this.document.commandDispatcher.focusedWindow = doc.defaultView.top;
      if (
        !editableInOut ||
        findRange.sRange.endContainer == aDocument.body ||
        findRange.sRange.endContainer == aDocument.documentElement
        )
        aFindFlag |= this.FIND_WRAP;
      continue;
    }
    this.dispatchProgressEvent(found, aFindFlag);
    break doFind;
  }
}
doc = docShell
    .QueryInterface(Components.interfaces.nsIDocShell)
    .QueryInterface(Components.interfaces.nsIWebNavigation)
    .document;
if (doc == aDocument) {
  this.dispatchProgressEvent(found, aFindFlag);
  break doFind;
}

こーんなだったのが

aDocShellIterator.iterateNext();

if (aDocShellIterator.wrapped) {
  if (!(aFindFlag & this.FIND_WRAP)) {
    this.document.commandDispatcher.focusedWindow = aDocShellIterator.view;
    if (
      !editableInOut ||
      !rangeSet ||
      aDocShellIterator.isRangeTopLevel(rangeSet.range)
      )
      aFindFlag |= this.FIND_WRAP;
    continue;
  }
  this.dispatchProgressEvent(aFindFlag, resultFlag);
  break;
}

if (aDocShellIterator.isInitial) {
  this.dispatchProgressEvent(aFindFlag, resultFlag);
  break;
}

ここまでスッキリしました。まあその代わり、フレームのツリーを辿る処理(DocShellIteratorの方がちょっと長くなってしまったんだけど、それぞれロジック的には綺麗に分かれているから、今後手を入れる時は「ツリーを辿ること」と「検索すること」のどっちか一方のことだけに集中して作業できるわけで、これこそがイテレータパターンの最も大きな意義だったんですね。デザインパターン万歳。

あと、findInDocumentの中でループの中でループを回していた部分をfindInDocumentInternalメソッドとして分離した。これはロジックを分離する効果はなくて、単にネストを浅くしてコードを見やすくするためだけの変更。のつもりだったんだけど、これをうまくやるために、他の部分も含めてだいぶ手直ししないといけなかった。

何故かというと、単純に内側のループだけをfindInDocumentInternalとして分離したところで、元のfindInDocumentとfindInDocumentInternalとの間でお互いに引き渡さないといけない情報(引数として引き渡す情報と、返り値として戻す情報の両方)が多すぎて、そのまま二つに分けただけだと却って分かりにくくなってしまったから。普通にreturnするだけじゃ情報を渡しきれないから、オブジェクトを引数で渡して、そのオブジェクトのプロパティとして返り値を設定して、変更されたプロパティを呼び出し元の側でまた参照して……という感じになってしまって、せっかくメソッドを分けたのにそこの所でガッツリ繋がってしまってて、これじゃ分けた意味がないよと。

というわけで最低限必要な情報だけに絞り込んでやりとりするように設計を検討したのに加えて、インターフェースの定義も見直して、今まであんまり有効活用してなかったビット演算を多用するようにしたことで、findInDocumentInternalからの返り値は最終的にビット列一つだけにまでまとめることができた。ビット演算いいよビット演算。

ビット演算を有効に利用するためには、どの意味をどのビットに与えるかというのをよく考えておかないといけない。とかなんとか大仰な言い方をしてみたけど、要するに、適当に定数プロパティを名前順に並べて先頭から0, 1, 2, 4, 8……と値を割り振っていくなんていいかげんな事してちゃ駄目だよってことですね。0.8.1まででは


const unsigned short FOUND             = 0;
const unsigned short NOTFOUND          = 1;
const unsigned short WRAPPED           = 2;
const unsigned short NOTLINK           = 4;
const unsigned short FOUND_IN_EDITABLE = 8;

こんなだったけど、これじゃあ実質的にはただの定数値が並んでるだけで単純比較にしか使えない。FOUNDが0だったり「検索はヒットしたけどリンクじゃないからヒット無しとみなす」という意味のNOTLINKなんてのがあったり。一つの値に複数の意味が割り当てられてるんじゃ、重複しないビットを割り当てても意味がないわけです。


const unsigned short NOTFOUND          = 0;
const unsigned short FOUND             = 1;
const unsigned short WRAPPED           = 2;
const unsigned short FOUND_IN_LINK     = 4;
const unsigned short FOUND_IN_EDITABLE = 8;

こーいう風にしておけば、NOTLINKに相当する場合は「FOUND | WRAPED」、そうでない場合には「FOUND | FOUND_IN_LINK」とかそんな風に書けるわけで、これなら「普通にヒットした」「普通にヒットしたけど一度ページの末尾まで検索してもう一度頭からやり直した」「入力欄の中でヒットした」などなどいろんな場合もひっくるめて全部「flag & FOUND」というビット演算いっこで判定できる。

あとSafari風強調表示の改善もあるんだけど、長くなるから別のエントリに分けます。

XUL/Migemoで正規表現検索をできるようにするにあたって、正規表現で後方検索する方法を考えてみたよ - May 02, 2008

好きなように正規表現で検索できるようになりました。XUL/Migemo 0.8.0からの新機能ということになります。というかAPIとしては持ってたのにUI上から利用する手段が今までなかった。

「普通の検索」と「正規表現」と「Migemo」の3モードが排他的な切り替えということになるので、UIをどうするか迷って、チェックボックスを増やしてみたりドロップダウンリストを置いたり色々試してみたけど、最終的にはラジオボタンを並べる形に落ち着いた。

使う側としてはややこしくなってしまったので、せめてちょっとでも使い勝手をよくしておきたいと思って、キーボード操作だけで検索モードを切り替えたり、入力内容が /ほげほげ/ みたいな正規表現リテラルっぽい形だったら自動的に正規表現検索に切り替えたり(判別にはNarcissus由来の正規表現を使わせてもらいました)、という風な工夫も入れてみたけど、実際の所どうだろう。

という外見でよく分かる変更の他に変わった所では、後方検索の仕組みを今までとガラッと変えてみた。

元々の作者のplus7さんはどうされていたかというと、正規表現のマッチングで後方検索ができないから、「正規表現の内容を前後逆にひっくり返して」「検索対象の範囲のテキストも全部反転して」その上でマッチングを行う、という方法を採っていて、僕が引き継いだ後もそこは変わりなかった。

var regexp = /(ほげ|hoge|foo|bar)/;
var text = 'ほげほげ、hogehoge。foo? bar。';
// ここで、「bar」「foo」「hoge」「hoge」
// 「ほげ」「ほげ」の順に検索したい

regexp = regexp.compile(
     regexp.source
          .split('')
          .reverse().join('')
          .replace(/\)/g, '[[')
          .replace(/\(/g, ')')
          .replace(/\[\[/g, '(')
   );
text = text.split('').reverse().join('');

var found;
while(text.match(regexp))
{
  found = RegExp.lastMatch.split('').reverse().join('');
  alert(found);
  text = RegExp.rightContext;
}

(なんでこんなややこしいことをするのかについてはplus7さんの技術解説を参照のこと)

しかし、この度正規表現検索機能を表に出すにあたって重大な問題が発覚した。元の「正規表現を反転する処理」は、反転しなきゃいけない正規表現がXUL/Migemo自身の生成したごく単純なパターンの物だけだから、かなり簡単な作りになっていた。でも、ユーザが手入力で凝った正規表現を入力すると、途端に処理がうまくいかなくなってしまう。

これは非常にまずい。ということでなんとかならないかと考えてみた。

まず、正規表現を反転する処理をもっと改善する事を考えてみた。でもこれは早々に諦めた。調べてみればみるほど、正規表現という物は複雑怪奇な書き方ができるようで、その全部の記法に対応するなんでのはとてもじゃないけど僕の頭じゃ無理。

となると残された道としては、正規表現も検索対象の範囲のテキストも反転せずにどうにかして後方からマッチングを行う方法を見つけるしかない。

んでJavaScriptのリファレンスを見ながらうんうん考えてたんだけど、ふと思いついた方法を試してみたらなんとバッチリうまくいった。そのやり方というのは、「g」フラグを使うというもの。

gフラグを付けてマッチングを行うと、「最後にヒットした箇所」はマッチングの結果の配列の最後の要素として取得できる、というのは当たり前。でもそれだけじゃなくて、実験して確認してみたら、この時のRegExp.leftContextRegExp.rightContextは、gフラグ無しの時は最初にマッチした箇所から見た「それより前」と「それより後」だったのが、今度は、最後にマッチした箇所に対する物になっていた(よく考えてみれば当たり前のことだけど、今まで全然気付いてなかった)。

ということは、このleftContextを次の検索のマッチング対象にし、そこでもまたgフラグを使って、さらにその時のleftContextを次の検索のマッチング対象にして……という風に繰り返していけば、「正規表現で後ろから検索」するのと同じ結果が得られるわけだ。

var regexp = /(ほげ|hoge|foo|bar)/;
var text = 'ほげほげ、hogehoge。foo? bar。';

regexp = regexp.compile(regexp.source, 'g');
var found;
while(text.match(regexp))
{
  found = RegExp.lastMatch;
  alert(found);
  text = RegExp.leftContext;
}

というわけで無事、どんな正規表現でも問題なく後方検索にかけることができるようになった次第です。しかも文字列の分割とか連結とか配列の反転とかいろんな処理がゴッソリ不要になったから、長いページでは後方検索がちょっと高速になったんじゃないかと思う。

油物 - May 01, 2008

  • はりきって油をドボッと出した後で、あくまで「油を多めに引いて焼いて下さい」であってそこまで大量に油を出す必要はなかったことに気がついた。
  • 油を吸い取らせるためのクッキングペーパーだと思って買って置いといた物を引っ張り出してきて開封してみたらなんか触り心地がパラフィン紙っぽくて「アルェー?」と思いながら皿に敷いて、焼き上がった(揚がった)物を載せても全然油を吸う様子がなくて「アルェー?」と思ってパッケージをよく見たら「下に油を通さないので調理器具や食器の片付けが楽チン!」という「クッキングシート」だった。

色々間違ってる。

ボーイズ・オン・ザ・ランと闇金ウシジマくんの連チャンは精神的にヤバイ。 - Apr 30, 2008

なんかこう、どうにもならない・どうにもできない閉塞感とか、うまくいかないイライラとか、すれ違いの無力感とか、将来への不安とか、目を背けているものを突きつけられるストレスとか、足掻いても勝てない惨めさとか、あることないこと色々なものが掘り起こされて増幅されて、リアルに震えが止まらないです。うん。このタイミングでこの連チャンはよくなかった。

しかしボーイズ・オン・ザ・ランは次で最終巻か……どういう所に落ち着くんだろう。

いたちごっこ - Apr 29, 2008

やっとBeta5準拠のデザインで巻き戻し/早送りボタンのアイコンを作り直したぜKeyholeにもばっちりたいおうだぜ、と思った瞬間にデザイン変更が入って僕涙目。

各ボタンの位置が2ピクセルずつ変わってるとかの地味な変更が。これはイジメだろう……

ウィンドウ全体を覆い隠してゴニョゴニョするのは実は簡単だった - Apr 28, 2008

Firefox 2まででは、position:fixedな要素はz-indexを適切に指定すれば内容領域の上にも普通に表示できた。なので、ウィンドウ全体を覆い尽くすというのも簡単だった。

でもFirefox 3では仕様が変わって、常にサブフレームの内容が上に表示されるようになったため、CSSのポジショニングだけでは任意の要素を任意の位置に表示することはできなくなってしまった。仮にposition:fixed; top:0; left:0なボックスを置いても、その上にブラウズ領域の中身が表示されてしまう。ウィンドウの内容の上に何かを重ねて表示したい時には汎用のポップアップであるpanel要素を使え、というのがFirefox 3流のやり方らしい。

しかし古い拡張機能をアップデートするにあたってそこらへんのつくりを全面的に書き換えるのは大変な労力を要するし、果てしないregressionの嵐が発生しかねない。できれば最小限の労力で、今までのやり方に一工夫加えるだけで正常に動作するようにしたい。

ということでウィンドウ全体を覆い隠してゴニョゴニョするアレの時に試行錯誤して、CSSのポジショニングをどーしても使いたければサブフレームを新たに生成するしかないっぽいという結論に辿り着いていただけれども、その方法をタブカタログで使おうとしたところDOMDocumentの違いとかで死にそうになったので、諦めて別の方法をいくつか探ってみた。

で、結論としては、visibility:hiddenになったサブフレームの上なら、普通にCSSのポジショニングで要素を重ねられるようです。display:noneとかvisibility:collapseとかをサブフレームに使うとDocument Shell(nsIDocShellのオブジェクト)がぶっ壊れてしまってまともに動かなくなるので、この辺のプロパティをいじるのはヤバイと思って今まで全く触ってなかったんだけど、少なくともFirefox 3ではFirefox 2でもFirefox 3でも、visibility:hidden/visibleの切り替えではDocument Shellは破壊されないようだということに、ああでもないこうでもないといじってるうちにたまたま気がついたのですよ。

でもこれだとタブカタログのように、一旦全面を覆い尽くしてその上に全然別のコンテンツを表示するといった機能は作れるけど、InterNoteのようにページ上に任意の要素を置くっていうことはできそうにない。そういう場合はページ内のDOMDocumentを編集するしかないようだ。

機動戦士ガンダムUC(4) パラオ攻略戦 - Apr 28, 2008

武器付きのアレが届きましたが、肝心の本体の方が昨年末の段階から一歩も進んでない件。

自分の駄目さをなぜ認められなかったのだろう - Apr 26, 2008

プログラムを書くことについて、自虐っぽい事をたくさん言ってきたけれども、そのくせどうして今までこんなことすら自覚できていなかったのか。

それは多分、何だかんだ言って、プログラムを書いて何かする・何かできる・何か作れるという事が、自分にとって唯一誇れそうな要素だったからなんだろうなと思う。それを否定してしまったら後には何も残らなくなってしまう(運動はできないし、異性にモテるわけでもないし、オシャレになれるわけでもないし、絵だって流行りの絵柄に追いつけてないし、かと言って他の誰も真似できない個性的な絵を描けるわけでもないし、Web制作の世界でも気付いたら取り残されてたし……)からなんだろうなと。自分にとって最後の砦だったから、ダメな所を直視しないようにしてたんだろう。

あと、逆に、「ダメでもいいや」って思ってる所もあったと思う。普通、この歳になると部下ができたり、部下じゃなくても後輩で新人が入ってきたりして、追いかけられたり追い抜かれたりする不安を抱えながらそれをバネにして「上司」とか「先輩」として成長していくものなんじゃないかと何となく思ってたけど、気がついてみたら、今まで自分より後から入ってきた若い人と仕事をするという事が全然無くて、基本的に自分が常に下っ端だったから、その現状に甘んじていた、そういうところがあったと思う。本当は歳相応の能力を身につけられていない、成長できてないのに、「下っ端でもそんだけできてんだから、大したもんだ」みたいに自分も他人も下駄を履かせて見ていたんじゃないか、って気がする。

なんとなくそれなりに順調に生きているような気がしていたけれども、色々な事がうまくいかなくなってメッキがはがれてきて、自分自身の事なんだけどなんというかとてもガッカリしている。失望している。

Page 89/248: « 85 86 87 88 89 90 91 92 93 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のコメント

最近のつぶやき