貼り付けて送信

(function() {
  var mi = document.createElement('menuitem');
  mi.setAttribute('label', '\u8cbc\u308a\u4ed8\u3051\u3066\u9001\u4fe1');
  mi.setAttribute('accesskey', 's');
  mi.setAttribute('id', 'context-pasteandsubmit');
  mi.setAttribute('oncommand', 'gContextMenu.pasteAndSubmit(event)');
  var pm = document.getElementById('context-paste');
  var cm = pm.parentNode;
  cm.insertBefore(mi, pm.nextSibling);
  cm.addEventListener('popupshown', function(aEvent) {
    mi.hidden = pm.hidden || !document.popupNode.form;
    mi.disabled = pm.disabled;
  }, false);
  nsContextMenu.prototype.pasteAndSubmit = function(aEvent) {
    var node = this.target || document.popupNode;
    if (node instanceof HTMLTextAreaElement &&
        !window.confirm('\u672c\u5f53\u306b\u9001\u4fe1\u3057\u307e\u3059\u304b\uff1f'))
      return;

    goDoCommand('cmd_paste');
    Components.lookupMethod(node.form, "submit").call(node.form);

    setTimeout(function() {
      document.getElementById('PopupAutoComplete').hidePopup();
    }, 100);
  };
})();

Visual C++ 2005 Express Edition で Firefox をビルドする

LinuxFirefox をビルドするのはわりと簡単で、Linux Prerequisites - Mozilla | MDN に書かれている必要なものをインストールすれば特に問題なくビルドできたのですが、Windows では何回かやってみたもののうまくいかず断念していました。ですが今回めでたくビルドできたので、備忘録もかねて行程を記そうと思います。

ビルド環境の構築

基本的には Building Firefox for Windows - Mozilla | MDN に書いてあることに従います。

Visual C++ 2005 Express Edition のインストール

VC2005EEは以前インストール済みだったので詳しいオプションとかは忘れてしまいました。標準インストールでたぶん問題ないと思います。

Microsoft Windows Vista SDK のインストール

すべてのバージョンの Visual Studio で必須になったらしいです。ディスク容量を節約したければ Sample Code や Documentation はインストールしなくても問題ありません。

Microsoft Windows Server 2003 R2 Platform SDK のインストール

Firefox をビルドするには最低限 "Windows Core SDK" と "Web Workshop (IE) SDK" と "Data Access Services (MDAC) SDK" があればいいとのこと。ディスク容量を節約したければ Sample Code や(略

MozillaBuildのインストール

ビルドに必要なさまざまなツールが入った MozillaBuild というパッケージをインストールします。標準のディレクトリ (c:\mozilla-build) にインストールした方がいいらしいです。

Express Edition 用のバッチファイル

MDC によると、Express Edition でビルドするには標準のバッチファイルでは駄目らしいです。guess-msvc-v2.batstart-msvc8-v2.bat をそれぞれダウンロードし、C:\mozilla-build に置きます。元の guess-msvc.batstart-msvc8.bat を削除する必要はありません。また、ダウンロードした guess-msvc-v2.batstart-msvc8-v2.bat はともにファイル名を変えずにそのまま置いてください。リネームするとエラーが出て起動しません。

profile-sshagent.sh のアップデート

この状態で start-msvc8-v2.bat を実行すると、

bash: ${SSH_ENV}: ambiguous redirect
chmod: cannot access `/c/Documents': No such file or directory
chmod: cannot access `and': No such file or directory
chmod: cannot access `Settings//.ssh/environment': No such file or directory
bash: /c/Documents: No such file or directory
Could not open a connection to your authentication agent.

とかいうエラーメッセージが出ます。
エラーメッセージで検索してみるとGoogle グループが引っかかりました。どうやら profile-sshagent.sh を最新のものにするとエラーが出なくなるようです。最新の profile-sshagent.sh をダウンロードして、C:\mozilla-build\msys\etc\profile.d にあるものと置き換えます。

cvs.exe のアップデート

検索しても同じトラブルに会った人がいないようなので環境依存なのかもしれませんが、うちの環境ではこの後 start-msvc8-v2.bat を実行して CVS でチェックアウトしようとしても

cvs [checkout aborted]: reading from server: Connection reset by peer

といったメッセージとともにタイムアウトしてしまいました。以前 Windows でのビルドを断念したのもこれが原因だったのですが、調べてみると CVS のバージョンが古い (1.11) せいで起きる現象らしかったので、http://ftp.gnu.org/non-gnu/cvs/binary/feature/x86-woe/ から新しい CVS をダウンロードしました。今回はとりあえず最新の 1.12.13a にしてみましたが、どのバージョンが安定しているのかはわかりません。ダウンロードした zip ファイルの中の cvs.exe を、C:\mozilla-build\msys\bin にあるものと置き換えます。

コンソールウィンドウの起動

C:\mozilla-build\ にある start-msvc8-v2.bat をダブルクリックで起動すると、白い背景のコンソールが起動します。以降はこのコンソールにコマンドを打ち込んでいきます。

カレントディレクトリの移動

コンソールが起動した初期状態では、カレントディレクトリは C:\Documents and Settings\<ユーザ名> になっています。長いし深いしスペースが入っていてなんか嫌なので、カレントディレクトリを移動します。今回は D:\source にしました。

cd d:/source

チェックアウトするとこのフォルダの下に mozilla というフォルダができ、そこに大量のソースコードがダウンロードされることになります。チェックアウトしてビルドしてパッケージングした時点でフォルダ容量が 1 GB 近くになったので、余裕のあるパーティションを選択しましょう。

チェックアウト

まずは他のファイルをチェックアウトするのに必要な client.mk ファイルをチェックアウトします。

cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot co mozilla/client.mk

client.mk を落としたら次は他のソースファイルをチェックアウトすればいいのですが、それをやろうとすると

client.mk:480: *** This source tree appears to have Windows-style line endings. To convert it to Unix-style line endings, run "python mozilla/build/win32/mozilla-dos2unix.py". Stop.

と怒られてしまいました。
仕方ないのでまず mozilla/build/win32/mozilla-dos2unix.py を落とします。

cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot co mozilla/build/win32/mozilla-dos2unix.py

それから実行します。

python mozilla/build/win32/mozilla-dos2unix.py

すると

This command will convert the source tree at
d:\source\mozilla
to an MSYS-compatible (unix mode) source tree. You can run this
command multiple times safely. Are you sure you want to continue (Y/N)?

と聞かれるので y と入力して Enter を押します。
なにやら処理が完了したら、カレントディレクトリを mozilla/ に移動します。

cd mozilla/

残りのソースファイルをチェックアウトします。

make -f client.mk checkout MOZ_CO_PROJECT=browser

browser のところを変えると ThunderbirdSeaMonkey をチェックアウトできるらしいです。詳しくはGetting Mozilla Source Code Using CVS - Mozilla | MDNを見てください。

すべてのファイルをチェックアウトするにはしばらく時間がかかるので、何か他のことをして時間をつぶしましょう。コンソールを眺めていると時々

cvs.exe [checkout aborted]: reading from server: Connection reset by peer

とか表示されてドキッとしますが、後でビルドしたら特に問題なくビルドできたのでスルーして構わないと思います。

ビルドオプションの設定

ビルドオプションは mozilla ディレクトリに .mozconfig というファイルを作成してそこに記載します。いろんなオプションがあるのですが今回はとりあえずビルドするのが目的だったので必要な最小限 (多分) のオプションだけ指定して後はデフォルト設定にしました。詳しいオプションの種類や設定の仕方はビルドオプションの設定 - Mozilla | MDNを見てください。

. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@

mozilla ディレクトリに .mozconfig というファイルを作り、その中に上記の内容をコピペして保存します。

ビルド

いよいよビルドです。

make -f client.mk build

を実行するとビルドが開始されます。ビルドにはチェックアウトよりもさらに時間がかかるはずなので、外出するなりご飯を食べるなり風呂に入るなり寝るなりして終わるのを待ちましょう。

パッケージング

ビルドが終了すると、mozilla ディレクトリに obj- で始まるディレクトリができているはずです。このディレクトリはオブジェクトディレクトリとか Objdir とか言ったりするらしいです。うちの場合は obj-i686-pc-mingw32 という名前でした。ビルドしてできたバイナリなどはここに格納されます。
カレントディレクトリをこのフォルダに移動

cd obj-i686-pc-mingw32/

して、

make package

とすると、オブジェクトディレクトリの dist フォルダに firefox-3.0pre.en-US.win32.zip のような ZIP パッケージが、

make installer

とするとオブジェクトディレクトリの dist\install\sea フォルダに firefox-3.0pre.en-US.win32.installer.exe のようなインストーラが作成されます。

起動確認

できた ZIP ファイルを解凍するか、インストーラを起動して Firefox を起動して、正常に動作するのが確認できればビルドは成功です。

まとめ

Windowsでビルドできた勢いで書いたエントリなので、間違いとかもっと効率的にやる方法とかあるかもしれません。もしあったらコメントとかで教えてくれると幸いです。バッチファイルの書き方とかも勉強しなきゃいけないと思いました。まる。

Firefox3beta4のバグを回避するためのAutoPagerize0.0.25用パッチ

--- autopagerize.user.js.orig	2008-03-15 03:19:35.270184000 +0900
+++ autopagerize.user.js	2008-03-15 21:54:38.877256000 +0900
@@ -473,11 +473,16 @@
 // utility functions.
 function createHTMLDocumentByString(str) {
     var html = str.replace(/<!DOCTYPE.*?>/, '').replace(/<html.*?>/, '').replace(/<\/html>.*/, '')
     var htmlDoc  = document.implementation.createDocument(null, 'html', null)
     var fragment = createDocumentFragmentByString(html)
-    htmlDoc.documentElement.appendChild(htmlDoc.importNode(fragment, true))
+    try {
+      fragment = htmlDoc.adoptNode(fragment)
+    } catch(e) {
+      fragment = htmlDoc.importNode(fragment, true)
+    }
+    htmlDoc.documentElement.appendChild(fragment)
     return htmlDoc
 }
 
 function getElementsByXPath(xpath, node) {
     var node = node || document

とりあえず。

Firefox3beta4とAutoPagerize0.0.24で起きる問題の修正patch - ZeroMemoryのでもいいけど、clear cache したときとかに変なことになると思います。

昨日の続き。

どうやらこのバグのregressionっぽい。

Bug 386769 – Make setting innerHTML faster

C++はぜんぜんわからんけど。

このバグはキャッシュを利用してCreateContextualFragmentを高速化しようとしてるみたいだけど、初期化処理がうまくいってないのかな?createContextualFragmentするたびにその内容が蓄積されていってる。

var range = document.createRange();
range.setStartAfter(document.body);
var df = range.createContextualFragment("<h1>h1</h1>");
var df2 = range.createContextualFragment("<h2>h2</h2>");

とかやると、df2のchildNodesにはh1とh2の両方が入ってしまっている。で、innerHTMLに値を設定する時には実は内部でCreateContextualFragmentを呼び出している。それでなんかのinnerHTMLに文字列を代入するといままでにcreateContextualFragmentされた分がまとめてその中に入っちゃう。

DeaRさんのコメントにもあるように、一度innerHTMLに何らかの値を設定すればこの状態はクリアされるようなので、こんな感じにすればいいかも。

// copied from Pagerization (c) id:ofk
function parseHTML(str) {
  str = str.replace(/^([\n\r]|.)*?<html.*?>|<\/html>([\n\r]|.)*$/ig, '')
  var res = document.implementation.createDocument(null, 'html', null)
  var range = document.createRange();
  range.setStartAfter(document.body);
  res.documentElement.appendChild(
  res.importNode(range.createContextualFragment(str), true)
  );
  document.createElement('span').innerHTML = '';
  return res;
}

なんか気持ち悪いですが。

Firefox3はまだ不安定ですね。そろそろ移行しなきゃなとは思ってるんですが。

なんかimportNodeをadoptNodeにするだけでも回避できた。こっちのほうがいいかも

// copied from Pagerization (c) id:ofk
function parseHTML(str) {
  str = str.replace(/^([\n\r]|.)*?<html.*?>|<\/html>([\n\r]|.)*$/ig, '')
  var res = document.implementation.createDocument(null, 'html', null)
  var range = res.createRange();
  range.setStartAfter(document.body);
  res.documentElement.appendChild(
  res.adoptNode(range.createContextualFragment(str))
  );
  return res;
}

とおもったらadoptNodeはFirefox2が対応していないのでした。

// copied from Pagerization (c) id:ofk
function parseHTML(str) {
  str = str.replace(/^([\n\r]|.)*?<html.*?>|<\/html>([\n\r]|.)*$/ig, '')
  var res = document.implementation.createDocument(null, 'html', null)
  var range = document.createRange();
  range.setStartAfter(document.body);
  var df = range.createContextualFragment(str);
  var dfc;
  try {
    dfc = res.adoptNode(df);
  } catch (e) {
    dfc = res.importNode(df, true);
  }
  res.documentElement.appendChild(dfc);
  return res;
}

d:id:Constellation:20080302:1204467538
どうもrange.createContextualFragmentしたあとに要素のinnerHTMLに何か代入するとその要素にdocumentfragmentが入ってしまうようです。Firefox3のバグでしょうか?

とりあえずcreateContextualFragmentを使わないようにすれば回避できると思います。