Index: /lang/javascript/userchrome/chromeconsole.uc.js
===================================================================
--- /lang/javascript/userchrome/chromeconsole.uc.js (revision 3608)
+++ /lang/javascript/userchrome/chromeconsole.uc.js (revision 3608)
@@ -0,0 +1,202 @@
+(function (Global) {
+
+const NAME = "ChromeConsole";
+
+if (typeof Global[NAME] != "function") {
+	Global[NAME] = function () {
+		if (arguments.callee.instance) {
+			// some complex process for reloading
+			for (prop in arguments.callee.prototype) {
+				if (arguments.callee.prototype.hasOwnProperty(prop)) {
+					arguments.callee.instance[prop] = arguments.callee.prototype[prop];
+				}
+			}
+			return arguments.callee.instance;
+		} else {
+			this.initialize.apply(this, arguments);
+			arguments.callee.instance = this;
+		}
+	};
+}
+Global[NAME].prototype = {
+	initialize : function () {
+		this.registerMenu();
+	},
+
+	registerMenu : function () {
+		var self = this;
+		var menup = document.getElementById("userChrome.js");
+		if (!menup) {
+			var ref  = document.getElementById("sanitizeSeparator");
+			var menu = document.createElementNS(kXULNS, "menu");
+			menu.setAttribute("label", "userChrome.js");
+			var menup = document.createElementNS(kXULNS, "menupopup");
+			menup.setAttribute("id", "userChrome.js");
+			ref.parentNode.insertBefore(menu, ref);
+			menu.appendChild(menup);
+		}
+
+		var menuItem = document.createElementNS(kXULNS, "menuitem");
+		menuItem.setAttribute("label", NAME);
+		menuItem.addEventListener("command", function () self.open(), false);
+		menup.appendChild(menuItem);
+	},
+
+	open : function () {
+		var xul = Namespace("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+		var h   = Namespace("http://www.w3.org/1999/xhtml");
+		default xml namespace = xul;
+		XML.ignoreProcessingInstructions = false;
+		openChromeWindow(<>
+			<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+			<?xml-stylesheet href="data:text/css,
+
+			#input {
+				font-size: 14px;
+				font-family: monospace;
+			}
+
+			#results {
+				min-height: 20px;
+			}
+
+			.error {
+				background: #fcc;
+			}
+
+			" type="text/css"?>
+			<window title="Chrome Console">
+				<listbox rows="3" flex="1" id="results">
+					<listitem label="Foobar"/>
+				</listbox>
+				<splitter collapse="none" ></splitter>
+				<textbox multiline="true" id="input" onkeypress="keypress.call(this, event)"></textbox>
+
+				<script type="application/javascript"><![CDATA[
+					const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+					function keypress (e) {
+						if (e.keyCode == 13 && e.ctrlKey) {
+							try {
+								var input = document.getElementById("input").value;
+								var ret = eval(input);
+								puts(ret);
+							} catch (e) {
+								puts(e, "error");
+							}
+						}
+					}
+
+					function puts (msg, type) {
+						var parentNode = document.getElementById("results");
+						var element   = document.createElementNS(XULNS, "listitem");
+						element.setAttribute("label", String(msg));
+						element.setAttribute("class", type || "normal");
+						parentNode.appendChild(element);
+					}
+				]]></script>
+			</window>
+		</>, {name:"Chrome Console", fun:"open", opts:"chrome,resizable=yes,width=600,height=300"});
+	}
+
+};
+
+new Global[NAME];
+
+function log () {
+	var message = Array.prototype.slice.call(arguments).map(function (i) {
+		if (typeof i == "object") return String(i);
+		return uneval(i);
+	});
+	var console = Components.classes["@mozilla.org/consoleservice;1"]
+	                        .getService(Components.interfaces.nsIConsoleService);
+	try {
+		console.logStringMessage(message);
+	} catch(e) {
+		Components.utils.reportError(message);
+	}
+};
+
+function pref (name, value) {
+	if (!pref.data) {
+		pref.data = {};
+		pref.observer = {
+			observe: function(aSubject, aTopic, aData) {
+				if (aTopic != "nsPref:changed") return;
+				pref.data[aData] = pref.pref.getCharPref(aData);
+			}
+		};
+		pref.clear = function () {
+			for (var k in pref.data) if (pref.data.hasOwnProperty(k))
+				pref.pref.clearUserPref(k);
+		};
+		pref.pref = Components.classes["@mozilla.org/preferences-service;1"]
+		                      .getService(Components.interfaces.nsIPrefService)
+		                      .getBranch("extensions.userchromejs."+pref.NAME+".");
+		pref.pref.QueryInterface(Components.interfaces.nsIPrefBranch2);
+		pref.pref.addObserver("", pref.observer, false);
+		window.addEventListener("unload", function () {
+			pref.pref.removeObserver("", pref.observer, false);
+		}, false);
+	}
+	if (typeof value != "undefined") {
+		pref.data[name] = String(value);
+		pref.pref.setCharPref(name, pref.data[name]);
+	}
+	if (!pref.data.hasOwnProperty(name)) {
+		try { pref.data[name] = pref.pref.getCharPref(name) } catch (e) {};
+	}
+	return pref.data[name];
+}
+pref.NAME = NAME;
+
+function openChromeWindow (xml, opts) {
+	// create temporary content dir.
+	var t = IO.getFile("Temp", "content" + Math.random() * 0xffff);
+	t.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0700);
+
+	// write manifest
+	var f = IO.getFile("PrefD", "extensions");
+	f.append("{1280606b-2510-4fe0-97ef-9b5a22eafe64}"); // userchromejs
+	f.append("chrome.manifest");
+	var prev = read(f);
+	write(f, "content userchromejs file://" + t.path + "/\n");
+
+	// write chrome xul
+	var c = t.clone();
+	c.append("temp.xul");
+	c.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0700);
+	write(c, xml.toXMLString());
+
+	// refresh chrome registry
+	Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+	          .getService(Components.interfaces.nsIChromeRegistry)
+	          .checkForNewChrome();
+
+	// open
+	window[opts.fun || "openDialog"]("chrome://userchromejs/content/temp.xul", opts.name || "temp", opts.opts || "chrome");
+
+	// remove temp files and restore original
+	t.remove(true);
+	write(f, prev);
+
+	// refresh chrome registry
+	Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+	          .getService(Components.interfaces.nsIChromeRegistry)
+	          .checkForNewChrome();
+
+
+	function read (f) {
+		var res, str, strm = IO.newInputStream(f, "text");
+		while (str = strm.readString(4096)) res += str;
+		strm.close();
+		return res;
+	}
+
+	function write (f, str) {
+		var strm  = IO.newOutputStream(f, "text");
+		strm.writeString(str);
+		strm.close();
+	}
+}
+
+})(this);
