Now browsing the SCRAPBLOG weblog archives.

ツリーのブックマーク風アイコン表示

xul:treecell 要素へ properties=”Name” という属性を追加する、あるいはカスタムツリービューの基本的な使い方(その9~階層構造 – 表示)にあるように nsITreeView::getCellProperties でツリーセルのプロパティへ “Name” という nsIAtom を追加することで、そのツリーセルにブックマーク風のアイコン(フォルダやページ)が表示されるようになる。

というのは Firefox 3.0 までの話で、 Firefox 3.1 以上では Bug 464916 – Remove non-global rules from global tree stylesheet の影響で “Name” プロパティによるツリーのアイコン表示は不可となった。 Firefox 3.1 以上では、「chrome://browser/skin/places/places.css」を読み込ませ、 “Name” の代わりに “title” というプロパティを追加することでブックマーク風アイコン表示が可能となる。

TOP

Firefox 3ではじめる拡張機能開発|gihyo.jp … 技術評論社

特集:Firefox 3ではじめる拡張機能開発

第1回 最小構成でインストール
第2回 機能を実装する(前編)
第3回 機能を実装する(中編)
第4回 機能を実装する(後編)
第5回 localeパッケージによるローカライズ
第6回 skinパッケージによる装飾
第7回 インストーラの作成
第8回 電子署名済みアップデートマニフェストを用いた拡張機能の配布

TOP

nsITreeBoxObject::clearStyleAndImageCaches

ツリーのスタイル付け – MDC の方法によってツリーアイテムのアイコン画像を一時的にアニメーションPNGやアニメーションGIFにすると、アイコンを元に戻した後もCPU使用率が上昇したままとなる問題があった。

アイコンを元に戻した後に nsITreeBoxObject::clearStyleAndImageCaches を呼び出すことで、ツリーのアイコン画像のキャッシュが消去され、この問題が解消された。

nsITreeBoxObject.idl

TOP

Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その3~ドロップ処理)

その2~ドラッグ処理から引き続き、ドラッグ元をドロップ先へドロップ可能にするための処理と、実際にドロップしたときの処理を追加する。

ドロップ先へのドロップを可能にする

ドロップ先の要素では、テキストボックス (html:input 要素や xul:textbox 要素) などを除き、通常はいかなる形式の転送データでもドロップ不可となっている。ドロップ可能にするには、 dragenter と dragover の2つのイベントハンドラで event.preventDefault() を呼び出す。

はじめに、転送データの形式によらず常にドロップ可能にするには、 handleDropEvents 関数にて以下のような処理を追加する。

        case "dragenter": 
        case "dragover": 
            event.preventDefault();
            break;

別の書き方として、 XUL / HTML のイベントハンドラ側で ondragenter=”return false;” のようにする方法もある。

実際は特定の転送データ形式、例えばリンクのドロップのみを可能にする場合が多いので、 dragenter と dragover の2つのイベントハンドラで転送データの形式を調べ、条件付きで event.preventDefault() を呼び出すようにする。

転送データの形式を調べるには、 DataTransfer オブジェクトの types プロパティを使用する。
ひとまずは、 dragenter と dragover イベント発生時に転送データに含まれるすべての形式を列挙してみる。

        case "dragenter": 
        case "dragover": 
            for (var i = 0; i < event.dataTransfer.types.length; i++) {
                dump("    " + event.dataTransfer.types.item(i) + "
");
            }
            break;

DataTransfer の types プロパティに特定のデータ形式が含まれるかどうかは、以下のように contains メソッドを使用すると良い。

        case "dragenter": 
        case "dragover": 
            if (event.dataTransfer.types.contains("text/url-list") || 
                event.dataTransfer.types.contains("text/plain"))
                event.preventDefault();
            break;

動作確認 (1)

ドラッグ元をドラッグし、マウスの右クリックを放さずにドロップ先の枠内へ入ったり出たりすると dragenter, dragover, dragleave の3つのイベントが発生することを確認してください。
なお、 XUL / HTML をブラウザタブで開いている場合、マウスの右クリックを放してドロップすると、ブラウザタブ側でドロップイベントが発生して URL を開く動作となります。

ドロップされたデータを取得する

次に、 drop イベント発生時にドロップされた転送データを取得する処理を追加する。
ドロップされた転送データを取得するには、まず dragenter, dragover イベントハンドラでやったように DataTransfer オブジェクトの types プロパティから転送データに目的の形式が含まれることをチェックした上で、DataTransfer オブジェクトの getData メソッドで指定した形式の転送データを取得する。
なお、下記のサンプルコードでは、 XUL / HTML をブラウザタブで開いている場合を考慮し、 event.preventDefault() を呼び出して、ブラウザタブ側でドロップイベントが発生するのを阻止している。

        case "drop": 
            event.preventDefault();
            var data = null;
            if (event.dataTransfer.types.contains("text/url-list"))
                data = event.dataTransfer.getData("text/url-list");
            else if (event.dataTransfer.types.contains("text/plain"))
                data = event.dataTransfer.getData("text/plain");
            alert("Dropped URL: " + data);
            break;

以下のように、あらかじめドロップ可能なデータ形式を配列で定義しておいたほうが見通しの良いソースコードとなるかもしれない。

        case "drop": 
            event.preventDefault();
            var data = null;
            var supportedTypes = ["text/url-list", "text/plain"];
            for each (type in supportedTypes) {
                if (event.dataTransfer.types.contains(type)) {
                    data = event.dataTransfer.getData(type);
                    break;
                }
            }
            alert("Dropped URL: " + data);
            break;

動作確認 (2)

ドラッグ元をドラッグしてドロップ先へドロップすると drop イベントが発生することを確認してください。
また、ドロップされたURLがメッセージボックスで表示されることを確認してください。

参考

Drag Operations – MDC

関連記事

Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その1~イベントハンドラの追加)
Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その2~ドラッグ処理)
Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その3~ドロップ処理)

TOP

Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その2~ドラッグ処理)

その1~イベントハンドラの追加から引き続き、ドラッグ元の要素をドラッグ開始した際に、転送データをセットする処理を追加する。

DataTransfer オブジェクト

ドラッグした際に転送データをセットする処理や、ドロップした際に転送データを取得する処理は、 DataTransfer オブジェクト (event.dataTransfer) によって行う。

ドラッグ元をドラッグ開始時、つまり dragstart イベント発生時、転送データをセットするには、 DataTransfer オブジェクトの setData メソッドを使用する。 setData メソッドの第1引数は転送データの形式、第2引数は転送データの値(文字列に限る)である。転送データの形式は、単純な文字列であれば「text/plain」、URL(複数も可)であれば「text/url-list」といった値を用いる。もちろん、一度のドラッグで複数の形式の転送データをセットすることも可能である。

サンプルコード (JavaScript)

handleDragEvents 関数へ以下のような処理を追加する。

        case "dragstart": 
            // 転送データをセットする
            event.dataTransfer.setData("text/url-list", "http://www.mozilla.org/");
            event.dataTransfer.setData("text/plain", "http://www.mozilla.org/");
            break;

動作確認

ドラッグ元をドラッグして dragstart, drag, dragend の3つのイベントが発生することを確認してください。
現段階ではドロップ先の処理が未完ですので、ドラッグ時の転送データが正しくセットされていることを確認するために、 Firefox のロケーションバーなどにドロップするか、メモ帳などの別アプリへドロップして、「http://www.mozilla.org/」という文字列が貼り付けされることを確認してください。

参考

DataTransfer オブジェクトの詳細:
DataTransfer – MDC
nsIDOMDataTransfer.idl

転送データの形式の詳細:
Recommended Drag Types – MDC

関連記事

Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その1~イベントハンドラの追加)
Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その2~ドラッグ処理)
Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その3~ドロップ処理)

TOP

Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その1~イベントハンドラの追加)

Firefox 3.1 (b1pre) にて HTML 5 のドラッグ&ドロップ API が実装され、 HTML の Web アプリ、 XUL の拡張機能のいずれからも同じように利用可能となった。

拡張機能開発者としては、ドラッグ&ドロップに関する各種処理を、 XPCOM サービスの nsIDragService や面倒くさい nsDragAndDrop.js を使わずに、単純な DOM の API のみで記述できるようになったので、非常にありがたい。

ドラッグ&ドロップのイベント

ドラッグ&ドロップ操作時、ドラッグ元の要素とドロップ先の要素において、以下のようなイベントが発生する。

イベント名 (event.type) イベント発生対象 (event.target) イベント発生のタイミング
dragstart ドラッグ元 ドラッグ開始時
drag ドラッグ元 ドラッグ中
dragend ドラッグ元 ドラッグ終了時
dragenter ドロップ先 ドラッグオーバー開始時
dragover ドロップ先 ドラッグオーバー中
dragleave ドロップ先 ドラッグオーバー終了時
drop ドロップ先 ドロップ時

サンプルコード (HTML)

2つの div 要素を配置し、一方をドラッグ元、もう一方をドロップ先とする。
ドラッグ元となる div 要素には、「draggable=”true”」属性をセットしなければならない。ただし、リンク(a 要素)や画像 (img 要素)などは、「draggable=”true”」を指定しなくても自動的にドラッグ可能となる。

ドラッグ元の要素には ondragstart, ondrag, ondragend の3つのイベントハンドラを追加し、ドロップ先の要素には ondragenter, ondragover, ondragleave, ondrop の4つのイベントハンドラを追加する。ただし、必要最低限のドラッグ&ドロップを実装するのであれば、 ondrag, ondragend, ondragleave は省略しても問題ない。

<html>
<head>
    <title>Drag and Drop Test</title>
    <script type="text/javascript" src="dragdrop.js"></script>
</head>
<body>
    <div id="DragSource"
         draggable="true"
         ondragstart="handleDragEvents(event);"
         ondrag="handleDragEvents(event);"
         ondragend="handleDragEvents(event);"
         style="border: 1px solid black; padding: 50px; margin: 10px;">Drag Source</div>
    <div id="DropTarget"
         ondragenter="handleDropEvents(event);"
         ondragover="handleDropEvents(event);"
         ondragleave="handleDropEvents(event);"
         ondrop="handleDropEvents(event);"
         style="border: 1px solid black; padding: 50px; margin: 10px;">Drop Target</div>
    <a href="http://www.mozilla.org/">http://www.mozilla.org/</a>
    <img src="http://www.mozilla.org/images/poweredby_200.gif">
</body>
</html>

サンプルコード (XUL)

2つの xul:label 要素を配置し、一方をドラッグ元、もう一方をドロップ先とする。 HTML の場合とほぼ同じであるが、すべての XUL 要素はドラッグ可能となりうるため、「draggable=”true”」属性を指定する必要は無い。

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window title="Drag and Drop Test"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
    <script type="application/x-javascript" src="dragdrop.js" />
    <label id="DragSource"
           value="Drag Source"
           ondragstart="handleDragEvents(event);"
           ondrag="handleDragEvents(event);"
           ondragend="handleDragEvents(event);"
           style="border: 1px solid black; padding: 50px; margin: 10px;" />
    <label id="DropTarget"
           value="Drop Target"
           ondragenter="handleDropEvents(event);"
           ondragover="handleDropEvents(event);"
           ondragleave="handleDropEvents(event);"
           ondrop="handleDropEvents(event);"
           style="border: 1px solid black; padding: 50px; margin: 10px;" />
</window>

サンプルコード (JavaScript)

JavaScript のソースコードは、 HTML と XUL の両者に共通となる。また、動作させるために chrome 権限は必要ない。
今回はとりあえず以下のような雛形的なイベントハンドラの処理を作っておき、あとで処理を追加する。
なお、サンプルコードでは window.dump メソッドを使用する。

function handleDragEvents(event) {
    dump("[" + event.target.id + "] " + event.type + "
");
    switch (event.type) {
        case "dragstart": 
            break;
        case "drag": 
            break;
        case "dragend": 
            break;
    }
}

function handleDropEvents(event) {
    dump("[" + event.target.id + "] " + event.type + "
");
    switch (event.type) {
        case "dragenter": 
        case "dragover": 
            break;
        case "dragleave": 
            break;
        case "drop": 
            break;
    }
}

動作確認

現段階ではドラッグ元をドラッグ開始した際の dragstart イベントしか発生しませんが、実際にドラッグしてイベントが発生することを確認してください。

参考

Drag and Drop – MDC

関連記事

Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その1~イベントハンドラの追加)
Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その2~ドラッグ処理)
Firefox 3.1 の新しいドラッグ&ドロップ API の基本的な使い方 (その3~ドロップ処理)

TOP

nsIConsoleService

// サービス取得
var consoleSvc = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);

// エラーコンソールへの出力内容を消去
consoleSvc.reset();

// #1 Components.utils.reportErrorによるのエラーメッセージ出力
Components.utils.reportError("error #1");

// #2 nsIConsoleService.logStringMessageによるエラーメッセージ出力
consoleSvc.logStringMessage("error #2");

// #3 nsIConsoleService.logMessageによるエラーメッセージ出力
var err = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
err.init("error #3", null, null, null, null, err.errorFlag, null);
consoleSvc.logMessage(err);

// エラーコンソールへの出力内容を列挙
var msgs = {};
consoleSvc.getMessageArray(msgs, {});
msgs.value.forEach(function(msg) {
    dump("---
");
    if (msg instanceof Ci.nsIScriptError) {
        // [JavaScript Error]
        msg.QueryInterface(Ci.nsIScriptError);
        dump(
            msg + "
" + 
            "errorMessage => " + msg.errorMessage + "
" + 
            "sourceName   => " + msg.sourceName   + "
" + 
            "sourceLine   => " + msg.sourceLine   + "
" + 
            "lineNumber   => " + msg.lineNumber   + "
" + 
            "columnNumber => " + msg.columnNumber + "
" + 
            "flags        => " + msg.flags        + "
" + 
            "category     => " + msg.category     + "
"
        );
    }
    else {
        // [xpconnect wrapped nsIConsoleMessage]
        dump(
            msg + "
" + 
            "message     => " + msg.message + "
"
        );
    }
});

リファレンス

nsIConsoleService – MDC
nsIConsoleService.idl
nsIConsoleMessage.idl
nsIScriptError.idl

TOP

Gmail の設定で Always use https を有効にすると Gmail Notifier で新着チェック不可

Gmail で最近追加されたオプション「Always use https」を有効にすると、 Gmail Notifier v1.0.25.0 にてメールの新着チェックができなくなった。 Gmail Notifier といっても、 Firefox 用のアドオンの方ではなく、 Google が公式に配布している方の話。

「gnotify.exe」をバイナリエディタで開き、「http://」の箇所を無理やり「https://」に変え、x00を一個削って全体のバイト長をそろえたら、無事に新着チェックできるようになった。

と思ったら、すでに Google から対策用パッチが配布されていた。

TOP

[userChrome.css] Places Style Throbber

Firefox 3 のスマートロケーションバー入力中にまれに一瞬だけ表示される回転するアイコンがかっこいいので、読み込み中のタブやアドオンマネージャの更新チェック中などの Throbber アイコンに転用するための userChrome.css を作った。ただし、回転が停止しているバージョンのアイコンは今までどおりです。

Places Style Throbber Places Style Throbber

userChrome.css

/* ::::: Places Style Throbber ::::: */

#navigator-throbber[busy="true"],
toolbar[iconsize="small"] #navigator-throbber[busy="true"],
toolbar[mode="text"] #navigator-throbber[busy="true"],
.tabbrowser-tab[busy] > .tab-icon-image,
.alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon,
#sidebar-throbber[loading="true"],
#checkForUpdates[loading="true"],
#extensionsManager richlistitem[loading="true"] .updateBadge,
#extensionsManager .addonThrobber,
#extensionsManager .throbber {
    list-style-image: url("chrome://browser/skin/places/searching_16.png") !important;
}

TOP

nsISafeOutputStream で安全なファイルの書き込み

(1) 通常のファイルの書き込み

function writeFile(aFile, aText) {
    var stream = Cc["@mozilla.org/network/file-output-stream;1"].
                 createInstance(Ci.nsIFileOutputStream);
    stream.init(aFile, 0x02 | 0x08 | 0x20, 0644, 0);
    stream.write(aText, aText.length);
    stream.close();
}

(2) nsISafeOutputStream を使った安全なファイルの書き込み

function writeFileSafely(aFile, aText) {
    var stream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
                 createInstance(Ci.nsIFileOutputStream);
    stream.init(aFile, 0x02 | 0x08 | 0x20, 0644, 0);
    stream.write(aText, aText.length);
    stream.QueryInterface(Ci.nsISafeOutputStream);
    stream.finish();
}

(1) の方式は多分ファイル書き込み時にいったん0バイトにしてから書き込みが行われるため、ファイルの書き込み中の強制終了などにより破損する可能性が高い。
一方、(2) の方式は “test-1.txt” のような別名でいったんファイルの書き出しが行われた後、その内容を本来のファイル “test.txt” へ上書きコピーするため、破損の可能性が低いと思われる。
パフォーマンスの面では、簡単なベンチマークを試したところ (2) が劣るようであったが、書き込むデータサイズが大きい(数MB以上)場合はほとんど差異がなくなるようだった。

TOP