[Pkg-mozext-commits] [SCM] firegesture s extension branch, upstream, updated. upstream/1.6.7-1-gf738b3d

Andrea Veri av at src.gnome.org
Tue Feb 21 11:28:22 UTC 2012


The following commit has been merged in the upstream branch:
commit f738b3d80b4811e587a1efc74c431ef0ced9940a
Author: Andrea Veri <av at src.gnome.org>
Date:   Tue Feb 21 12:28:41 2012 +0100

    Upstream version 1.6.9

diff --git a/FireGestures.idl b/FireGestures.idl
index a25e825..74eb2bd 100644
--- a/FireGestures.idl
+++ b/FireGestures.idl
@@ -1,221 +1,221 @@
-#include "nsISupports.idl"
-
-interface nsIDOMElement;
-interface nsIDOMEvent;
-interface nsIDOMEventTarget;
-interface nsILocalFile;
-interface nsIVariant;
-interface mozIStorageConnection;
-
-interface xdIGestureService;
-interface xdIGestureHandler;
-interface xdIGestureObserver;
-interface xdIGestureMapping;
-interface xdIGestureCommand;
-
-
-[scriptable, uuid(1d26f3e7-d92e-4bcc-ac79-9624bb181308)]
-interface xdIGestureService : nsISupports
-{
-
-	/**
-	 * A wrapper function to create instance of xdIGestureObserver.
-	 */
-	xdIGestureHandler createHandler();
-
-	/**
-	 * Registers a mapping to service for later use.
-	 * @param aID   The id of the mapping to identify it. It must equal to 
-	 *              the database table name which preserves the user mapping.
-	 * @param aURI  The URI of RDF datasource which preserves the default mapping.
-	 * @param aName The name of the mapping.
-	 */
-	void registerMapping(in AUTF8String aID, in string aURI, in AUTF8String aName);
-
-	/**
-	 * Returns a registered mapping from service.
-	 * At the first-time calling, creates instance of xdIGestureMapping and initializes it.
-	 * @throws NS_ERROR_NOT_INITIALIZED if the mapping is not registered.
-	 */
-	xdIGestureMapping getMapping(in AUTF8String aID);
-
-	/**
-	 * A special version of getMapping, which returns the mapping for browser.
-	 */
-	xdIGestureMapping getMappingForBrowser();
-
-	/**
-	 * Returns meta data of all registered mappings.
-	 */
-	nsIVariant getMappingsInfo();
-
-	/**
-	 * Backups all user mappings to a file.
-	 */
-	void backupMappings(in nsILocalFile aFile);
-
-	/**
-	 * Restores all user mappings from a file.
-	 */
-	void restoreMappings(in nsILocalFile aFile);
-
-	/**
-	 * Returns database connection.
-	 * @param aForceOpen false: Returns null if database file doesn't exist.
-	 *                   true : Returns connection regardless of the file existence.
-	 */
-	mozIStorageConnection getDBConnection(in boolean aForceOpen);
-
-	/**
-	 * Returns localized string from string bundle.
-	 */
-	wstring getLocaleString(in wstring aName);
-
-};
-
-
-[scriptable, uuid(ca559550-8ab4-41c5-a72f-fd931322cc7e)]
-interface xdIGestureHandler : nsISupports
-{
-
-	/**
-	 * Get DOM element at the starting point of current mouse gesture.
-	 */
-	readonly attribute nsIDOMEventTarget sourceNode;
-
-	/**
-	 * This method starts to handle mouse gestures at |nsIDOMEventTarget|
-	 * and register |xdIGestureObserver| as a observer,
-	 * which responds to mouse gestures.
-	 * @param aDrawArea The element where an user can perform gestures.
-	 * @param aObserver The gesture observer which is called back from handler.
-	 */
-	void attach(in nsIDOMElement aDrawArea, in xdIGestureObserver aObserver);
-
-	/**
-	 * This method stops to handle mouse gestures.
-	 */
-	void detach();
-
-	/**
-	 * This method opens a popup at pointer and aborts the current mouse gesture.
-	 */
-	void openPopupAtPointer(in nsIDOMElement aPopup);
-
-};
-
-
-[scriptable, uuid(c0db6b26-01d2-4060-91ff-b54af54bdd92)]
-interface xdIGestureObserver : nsISupports
-{
-
-	/**
-	 * Called when the direction is changed in progress of mouse gesture.
-	 */
-	void onDirectionChanged(in nsIDOMEvent event, in ACString aDirectionChain);
-
-	/**
-	 * Called when user perform mouse gesture.
-	 */
-	void onMouseGesture(in nsIDOMEvent event, in ACString aDirectionChain);
-
-	/**
-	 * Called when user perform some extra gestures.
-	 */
-	void onExtraGesture(in nsIDOMEvent event, in ACString aGestureType);
-
-};
-
-
-[scriptable, uuid(d7018e80-d6da-4cbc-b77f-8dca4d95bbbf)]
-interface xdIGestureMapping : nsISupports
-{
-
-	/**
-	 * Types of commands.
-	 */
-	const unsigned short TYPE_CATEGORY = 0;
-	const unsigned short TYPE_NORMAL   = 1;
-	const unsigned short TYPE_SCRIPT   = 2;
-
-	/**
-	 * The id of mapping, which equals to the table name.
-	 */
-	readonly attribute AUTF8String id;
-
-	/**
-	 * The name of the mapping.
-	 */
-	readonly attribute AUTF8String name;
-
-	/**
-	 * Initializes mapping.
-	 */
-	void init(in AUTF8String aID, in string aURI, in AUTF8String aName);
-
-	/**
-	 * Finalizes mapping.
-	 */
-	void finalize();
-
-	/**
-	 * Returns xdIGestureCommand object for given direction.
-	 * Returns undefined if there are no definition for the given direction.
-	 */
-	xdIGestureCommand getCommandForDirection(in ACString aDirection);
-
-	/**
-	 * Opens options window to configure mapping.
-	 */
-	void configure();
-
-	/**
-	 * Returns 2D array of mapping.
-	 */
-	nsIVariant getMappingArray();
-
-	/**
-	 * Flushes user mapping to local disk.
-	 */
-	void saveUserMapping(in nsIVariant aItems);
-
-	/**
-	 * API for third-party extensions to add script-type commands.
-	 * Does not add command if there is one which has same script.
-	 * @param aItems Array of JavaScript object which has the following properties:
-	 *               name     : name of the command.
-	 *               script   : script of the command.
-	 *               direction: default normal gesture to be assigned to the command (e.g. "LRUD").
-	 */
-	void addScriptCommands(in nsIVariant aItems);
-
-};
-
-
-[scriptable, uuid(2a8d26ee-6b43-4e73-9352-7632c128b006)]
-interface xdIGestureCommand : nsISupports
-{
-
-	/**
-	 * Represents the type of the command.
-	 */
-	readonly attribute PRUint8 type;
-
-	/**
-	 * Represents the localized name of the command.
-	 */
-	readonly attribute AString name;
-
-	/**
-	 * Represents the value of the command.
-	 */
-	readonly attribute AString value;
-
-	/**
-	 * Represents the direction of the command.
-	 */
-	readonly attribute ACString direction;
-
-};
-
-
+#include "nsISupports.idl"
+
+interface nsIDOMElement;
+interface nsIDOMEvent;
+interface nsIDOMEventTarget;
+interface nsILocalFile;
+interface nsIVariant;
+interface mozIStorageConnection;
+
+interface xdIGestureService;
+interface xdIGestureHandler;
+interface xdIGestureObserver;
+interface xdIGestureMapping;
+interface xdIGestureCommand;
+
+
+[scriptable, uuid(1d26f3e7-d92e-4bcc-ac79-9624bb181308)]
+interface xdIGestureService : nsISupports
+{
+
+	/**
+	 * A wrapper function to create instance of xdIGestureObserver.
+	 */
+	xdIGestureHandler createHandler();
+
+	/**
+	 * Registers a mapping to service for later use.
+	 * @param aID   The id of the mapping to identify it. It must equal to 
+	 *              the database table name which preserves the user mapping.
+	 * @param aURI  The URI of RDF datasource which preserves the default mapping.
+	 * @param aName The name of the mapping.
+	 */
+	void registerMapping(in AUTF8String aID, in string aURI, in AUTF8String aName);
+
+	/**
+	 * Returns a registered mapping from service.
+	 * At the first-time calling, creates instance of xdIGestureMapping and initializes it.
+	 * @throws NS_ERROR_NOT_INITIALIZED if the mapping is not registered.
+	 */
+	xdIGestureMapping getMapping(in AUTF8String aID);
+
+	/**
+	 * A special version of getMapping, which returns the mapping for browser.
+	 */
+	xdIGestureMapping getMappingForBrowser();
+
+	/**
+	 * Returns meta data of all registered mappings.
+	 */
+	nsIVariant getMappingsInfo();
+
+	/**
+	 * Backups all user mappings to a file.
+	 */
+	void backupMappings(in nsILocalFile aFile);
+
+	/**
+	 * Restores all user mappings from a file.
+	 */
+	void restoreMappings(in nsILocalFile aFile);
+
+	/**
+	 * Returns database connection.
+	 * @param aForceOpen false: Returns null if database file doesn't exist.
+	 *                   true : Returns connection regardless of the file existence.
+	 */
+	mozIStorageConnection getDBConnection(in boolean aForceOpen);
+
+	/**
+	 * Returns localized string from string bundle.
+	 */
+	wstring getLocaleString(in wstring aName);
+
+};
+
+
+[scriptable, uuid(ca559550-8ab4-41c5-a72f-fd931322cc7e)]
+interface xdIGestureHandler : nsISupports
+{
+
+	/**
+	 * Get DOM element at the starting point of current mouse gesture.
+	 */
+	readonly attribute nsIDOMEventTarget sourceNode;
+
+	/**
+	 * This method starts to handle mouse gestures at |nsIDOMEventTarget|
+	 * and register |xdIGestureObserver| as a observer,
+	 * which responds to mouse gestures.
+	 * @param aDrawArea The element where an user can perform gestures.
+	 * @param aObserver The gesture observer which is called back from handler.
+	 */
+	void attach(in nsIDOMElement aDrawArea, in xdIGestureObserver aObserver);
+
+	/**
+	 * This method stops to handle mouse gestures.
+	 */
+	void detach();
+
+	/**
+	 * This method opens a popup at pointer and aborts the current mouse gesture.
+	 */
+	void openPopupAtPointer(in nsIDOMElement aPopup);
+
+};
+
+
+[scriptable, uuid(c0db6b26-01d2-4060-91ff-b54af54bdd92)]
+interface xdIGestureObserver : nsISupports
+{
+
+	/**
+	 * Called when the direction is changed in progress of mouse gesture.
+	 */
+	void onDirectionChanged(in nsIDOMEvent event, in ACString aDirectionChain);
+
+	/**
+	 * Called when user perform mouse gesture.
+	 */
+	void onMouseGesture(in nsIDOMEvent event, in ACString aDirectionChain);
+
+	/**
+	 * Called when user perform some extra gestures.
+	 */
+	void onExtraGesture(in nsIDOMEvent event, in ACString aGestureType);
+
+};
+
+
+[scriptable, uuid(d7018e80-d6da-4cbc-b77f-8dca4d95bbbf)]
+interface xdIGestureMapping : nsISupports
+{
+
+	/**
+	 * Types of commands.
+	 */
+	const unsigned short TYPE_CATEGORY = 0;
+	const unsigned short TYPE_NORMAL   = 1;
+	const unsigned short TYPE_SCRIPT   = 2;
+
+	/**
+	 * The id of mapping, which equals to the table name.
+	 */
+	readonly attribute AUTF8String id;
+
+	/**
+	 * The name of the mapping.
+	 */
+	readonly attribute AUTF8String name;
+
+	/**
+	 * Initializes mapping.
+	 */
+	void init(in AUTF8String aID, in string aURI, in AUTF8String aName);
+
+	/**
+	 * Finalizes mapping.
+	 */
+	void finalize();
+
+	/**
+	 * Returns xdIGestureCommand object for given direction.
+	 * Returns undefined if there are no definition for the given direction.
+	 */
+	xdIGestureCommand getCommandForDirection(in ACString aDirection);
+
+	/**
+	 * Opens options window to configure mapping.
+	 */
+	void configure();
+
+	/**
+	 * Returns 2D array of mapping.
+	 */
+	nsIVariant getMappingArray();
+
+	/**
+	 * Flushes user mapping to local disk.
+	 */
+	void saveUserMapping(in nsIVariant aItems);
+
+	/**
+	 * API for third-party extensions to add script-type commands.
+	 * Does not add command if there is one which has same script.
+	 * @param aItems Array of JavaScript object which has the following properties:
+	 *               name     : name of the command.
+	 *               script   : script of the command.
+	 *               direction: default normal gesture to be assigned to the command (e.g. "LRUD").
+	 */
+	void addScriptCommands(in nsIVariant aItems);
+
+};
+
+
+[scriptable, uuid(2a8d26ee-6b43-4e73-9352-7632c128b006)]
+interface xdIGestureCommand : nsISupports
+{
+
+	/**
+	 * Represents the type of the command.
+	 */
+	readonly attribute PRUint8 type;
+
+	/**
+	 * Represents the localized name of the command.
+	 */
+	readonly attribute AString name;
+
+	/**
+	 * Represents the value of the command.
+	 */
+	readonly attribute AString value;
+
+	/**
+	 * Represents the direction of the command.
+	 */
+	readonly attribute ACString direction;
+
+};
+
+
diff --git a/chrome/firegestures.jar!/content/firegestures/browser.js b/chrome/firegestures.jar!/content/firegestures/browser.js
index 24909a0..36d2f02 100644
--- a/chrome/firegestures.jar!/content/firegestures/browser.js
+++ b/chrome/firegestures.jar!/content/firegestures/browser.js
@@ -1,946 +1,957 @@
-
-var FireGestures = {
-
+
+var FireGestures = {
+
 	_gestureHandler: null,
-
+
 	_gestureMapping: null,
-
-	_getLocaleString: null,
-
-	_statusTextField: null,
-
-	_clearStatusTimer: null,
-
-	get _isMac() {
-		delete this._isMac;
-		return this._isMac = navigator.platform.indexOf("Mac") >= 0;
-	},
-
-	init: function() {
-		if ("aioGestTable" in window || "mozgestInit" in window || "ucjsMouseGestures" in window) {
-			Cu.reportError("Detected an extension or script which conflicts with FireGestures.");
-			toJavaScriptConsole();
-			return;
-		}
-		var gestureSvc = Cc["@xuldev.org/firegestures/service;1"].getService(Ci.xdIGestureService);
-		this._gestureHandler = gestureSvc.createHandler();
-		this._gestureHandler.attach(gBrowser.mPanelContainer, this);
-		this._gestureMapping = gestureSvc.getMappingForBrowser();
-		this._getLocaleString = gestureSvc.getLocaleString;
-		this._statusTextField = document.getElementById("statusbar-display");
-		if (!this._statusTextField) {
-			this.setStatusText = this._setStatusText2;
-			this.clearStatusText = this._clearStatusText2;
-		}
-	},
-
-	uninit: function() {
-		if (this._gestureHandler) {
-			this._gestureHandler.detach();
-			this._gestureHandler = null;
-		}
-		this._gestureMapping = null;
-		this._getLocaleString = null;
-		if (this._clearStatusTimer)
-			window.clearTimeout(this._clearStatusTimer);
-		this._statusTextField = null;
-	},
-
-
-
-	_statusDisplay: null,
-
-	onDirectionChanged: function(event, aDirectionChain) {
-		if (this._statusDisplay === null) {
-			const prefName = "extensions.firegestures.status_display";
-			this._statusDisplay = (gPrefService || Services.prefs).getIntPref(prefName);
-		}
-		if (this._statusDisplay === 0)
-			return;
-		var command = this._gestureMapping.getCommandForDirection(aDirectionChain);
-		var name = command ? " (" + command.name + ")" : "";
-		this.setStatusText(this._getLocaleString("GESTURE") + ": " + aDirectionChain + name);
-	},
-
-	onMouseGesture: function(event, aDirection) {
-		if (gInPrintPreviewMode)
-			return;
-		try {
-			var command = this._gestureMapping.getCommandForDirection(aDirection);
-			if (!command)
-				throw null;
-			if (command.type == this._gestureMapping.TYPE_SCRIPT)
-				(new Function("event", command.value))(event);
-			else
-				this._performAction(event, command.value);
-		}
-		catch(ex) {
-			this.setStatusText(
-				ex ? 
-				this._getLocaleString("GESTURE_FAILED")  + ": " + aDirection + " (" + ex + ")" :
-				this._getLocaleString("GESTURE_UNKNOWN") + ": " + aDirection
-			);
-		}
-		this.clearStatusText(this._statusDisplay);
-		this._statusDisplay = null;
-	},
-
-	onExtraGesture: function(event, aGesture) {
-		if (gInPrintPreviewMode)
-			return;
-		this.clearStatusText(0);
-		switch (aGesture) {
-			case "wheel-up": 
-			case "wheel-down": 
-			case "rocker-left": 
-			case "rocker-right": 
-			case "keypress-ctrl": 
-			case "keypress-shift": 
-				this.onMouseGesture(event, aGesture);
-				return;
-			case "keypress-start": 
-				this._linkURLs = [];
-				this._linkElts = [];
-				break;
-			case "keypress-progress": 
-				var linkURL = this.getLinkURL(event.target);
-				if (!this._linkURLs)
-					this._linkURLs = [];
-				if (linkURL && this._linkURLs.indexOf(linkURL) < 0) {
-					try {
-						this.checkURL(linkURL, event.target.ownerDocument);
-						this._linkURLs.push(linkURL);
-						this._linkElts.push(event.target);
-						event.target.style.MozOutline = "1px dashed darkorange";
-					}
+
+	_getLocaleString: null,
+
+	_statusTextField: null,
+
+	_clearStatusTimer: null,
+
+	get _isMac() {
+		delete this._isMac;
+		return this._isMac = navigator.platform.indexOf("Mac") >= 0;
+	},
+
+	init: function() {
+		if ("aioGestTable" in window || "mozgestInit" in window || "ucjsMouseGestures" in window) {
+			Cu.reportError("Detected an extension or script which conflicts with FireGestures.");
+			toJavaScriptConsole();
+			return;
+		}
+		var gestureSvc = Cc["@xuldev.org/firegestures/service;1"].getService(Ci.xdIGestureService);
+		this._gestureHandler = gestureSvc.createHandler();
+		this._gestureHandler.attach(gBrowser.mPanelContainer, this);
+		this._gestureMapping = gestureSvc.getMappingForBrowser();
+		this._getLocaleString = gestureSvc.getLocaleString;
+		this._statusTextField = document.getElementById("statusbar-display");
+		if (!this._statusTextField) {
+			this.setStatusText = this._setStatusText2;
+			this.clearStatusText = this._clearStatusText2;
+		}
+	},
+
+	uninit: function() {
+		if (this._gestureHandler) {
+			this._gestureHandler.detach();
+			this._gestureHandler = null;
+		}
+		this._gestureMapping = null;
+		this._getLocaleString = null;
+		if (this._clearStatusTimer)
+			window.clearTimeout(this._clearStatusTimer);
+		this._statusTextField = null;
+	},
+
+
+
+	_statusDisplay: null,
+
+	onDirectionChanged: function(event, aDirectionChain) {
+		if (this._statusDisplay === null) {
+			const prefName = "extensions.firegestures.status_display";
+			this._statusDisplay = (gPrefService || Services.prefs).getIntPref(prefName);
+		}
+		if (this._statusDisplay === 0)
+			return;
+		var command = this._gestureMapping.getCommandForDirection(aDirectionChain);
+		var name = command ? " (" + command.name + ")" : "";
+		this.setStatusText(this._getLocaleString("GESTURE") + ": " + aDirectionChain + name);
+	},
+
+	onMouseGesture: function(event, aDirection) {
+		if (gInPrintPreviewMode)
+			return;
+		try {
+			var command = this._gestureMapping.getCommandForDirection(aDirection);
+			if (!command)
+				throw null;
+			if (command.type == this._gestureMapping.TYPE_SCRIPT)
+				(new Function("event", command.value))(event);
+			else
+				this._performAction(event, command.value);
+		}
+		catch(ex) {
+			this.setStatusText(
+				ex ? 
+				this._getLocaleString("GESTURE_FAILED")  + ": " + aDirection + " (" + ex + ")" :
+				this._getLocaleString("GESTURE_UNKNOWN") + ": " + aDirection
+			);
+		}
+		this.clearStatusText(this._statusDisplay);
+		this._statusDisplay = null;
+	},
+
+	onExtraGesture: function(event, aGesture) {
+		if (gInPrintPreviewMode)
+			return;
+		this.clearStatusText(0);
+		switch (aGesture) {
+			case "wheel-up": 
+			case "wheel-down": 
+			case "rocker-left": 
+			case "rocker-right": 
+			case "keypress-ctrl": 
+			case "keypress-shift": 
+				this.onMouseGesture(event, aGesture);
+				return;
+			case "keypress-start": 
+				this._linkURLs = [];
+				this._linkElts = [];
+				break;
+			case "keypress-progress": 
+				var linkURL = this.getLinkURL(event.target);
+				if (!this._linkURLs)
+					this._linkURLs = [];
+				if (linkURL && this._linkURLs.indexOf(linkURL) < 0) {
+					try {
+						this.checkURL(linkURL, event.target.ownerDocument);
+						this._linkURLs.push(linkURL);
+						this._linkElts.push(event.target);
+						event.target.style.MozOutline = "1px dashed darkorange";
+					}
 					catch(ex) {}
-				}
-				break;
-			case "keypress-stop": 
-				for (var i = 0; i < this._linkURLs.length; i++) {
-					this._linkElts[i].style.MozOutline = "";
+				}
+				break;
+			case "keypress-stop": 
+				for (var i = 0; i < this._linkURLs.length; i++) {
+					this._linkElts[i].style.MozOutline = "";
 					this._linkElts[i] = null;
-				}
-				this._linkURLs = null;
-				this._linkElts = null;
-				break;
-			case "gesture-timeout": 
-				this.clearStatusText(0);
-				break;
-		}
-	},
-
-	_performAction: function(event, aCommand) {
-		switch (aCommand) {
-			case "FireGestures:GoUpperLevel": 
-				this.goUpperLevel();
-				break;
-			case "FireGestures:IncrementURL": 
-				this.goNumericURL(+1);
-				break;
-			case "FireGestures:DecrementURL": 
-				this.goNumericURL(-1);
-				break;
-			case "FireGestures:MinimizeWindow": 
-				event.preventDefault();
-				window.minimize();
-				break;
-			case "FireGestures:MaximizeWindow": 
-				window.windowState == window.STATE_MAXIMIZED ? window.restore() : window.maximize();
-				break;
-			case "cmd_close": 
-				if (gBrowser.mCurrentTab.pinned)
-					throw "Blocked closing app tab.";
-				gBrowser.removeCurrentTab({ animate: true });
-				break;
-			case "FireGestures:CloseTabOrWindow": 
-				if (gBrowser.mCurrentTab.pinned)
-					throw "Blocked closing app tab.";
-				if (gBrowser.mTabs.length > 1)
-					document.getElementById("cmd_close").doCommand();
-				else
-					document.getElementById("cmd_closeWindow").doCommand();
-				break;
-			case "FireGestures:UndoCloseTab": 
-				try { document.getElementById("History:UndoCloseTab").doCommand(); }
-				catch(ex) {
-					if ("undoRemoveTab" in gBrowser)
-						gBrowser.undoRemoveTab();
-					else
-						throw "Session Restore feature is disabled.";
-				}
-				break;
-			case "FireGestures:PreviousTab": 
-				gBrowser.mTabContainer.advanceSelectedTab(-1, true);
-				break;
-			case "FireGestures:NextTab": 
-				gBrowser.mTabContainer.advanceSelectedTab(+1, true);
-				break;
-			case "FireGestures:DuplicateTab": 
-				var orgTab = gBrowser.mCurrentTab;
-				var newTab = gBrowser.duplicateTab(orgTab);
-				gBrowser.moveTabTo(newTab, orgTab._tPos + 1);
-				break;
-			case "FireGestures:DetachTab": 
-				gBrowser.replaceTabWithWindow(gBrowser.mCurrentTab);
-				break;
-			case "FireGestures:TogglePinTab": 
-				var tab = gBrowser.mCurrentTab;
-				tab.pinned ? gBrowser.unpinTab(tab) : gBrowser.pinTab(tab);
-				break;
-			case "FireGestures:ReloadAllTabs": 
-				gBrowser.reloadAllTabs(gBrowser.mCurrentTab);
-				break;
-			case "FireGestures:CloseOtherTabs": 
-				gBrowser.removeAllTabsBut(gBrowser.mCurrentTab);
-				break;
-			case "FireGestures:CloseLeftTabs": 
-				this.closeMultipleTabs("left");
-				break;
-			case "FireGestures:CloseRightTabs": 
-				this.closeMultipleTabs("right");
-				break;
-			case "cmd_textZoomEnlarge": 
-			case "cmd_textZoomReduce": 
-				if ("FullZoom" in window && !ZoomManager.useFullZoom)
-					document.getElementById(aCommand.replace("text", "full")).doCommand();
-				else
-					gBrowser.markupDocumentViewer.textZoom += (aCommand == "cmd_textZoomEnlarge") ? 0.2 : -0.2;
-				break;
-			case "cmd_fullZoomEnlarge": 
-			case "cmd_fullZoomReduce": 
-				if (ZoomManager.useFullZoom)
-					document.getElementById(aCommand).doCommand();
-				else
-					gBrowser.markupDocumentViewer.fullZoom += (aCommand == "cmd_fullZoomEnlarge") ? 0.2 : -0.2;
-				break;
-			case "cmd_textZoomReset": 
-				if ("FullZoom" in window)
-					aCommand = aCommand.replace("text", "full");
-				document.getElementById(aCommand).doCommand();
-				break;
-			case "FireGestures:ScrollTop": 
-			case "FireGestures:ScrollBottom": 
-			case "FireGestures:ScrollPageUp": 
-			case "FireGestures:ScrollPageDown": 
-				switch (aCommand) {
-					case "FireGestures:ScrollTop"     : aCommand = "cmd_scrollTop"; break;
-					case "FireGestures:ScrollBottom"  : aCommand = "cmd_scrollBottom"; break;
-					case "FireGestures:ScrollPageUp"  : aCommand = "cmd_scrollPageUp"; break;
-					case "FireGestures:ScrollPageDown": aCommand = "cmd_scrollPageDown"; break;
-				}
-				try { goDoCommand(aCommand); }
-				catch (ex) {
-					var win = this.focusedWindow;
-					switch (aCommand) {
-						case "cmd_scrollTop"     : win.scroll(0, 0); break;
-						case "cmd_scrollBottom"  : win.scroll(0, 71582788); break;
-						case "cmd_scrollPageUp"  : win.scrollBy(0, win.outerHeight * -0.8); break;
-						case "cmd_scrollPageDown": win.scrollBy(0, win.outerHeight * 0.8); break;
-					}
-				}
-				break;
-			case "FireGestures:ShowOnlyThisFrame": 
-				var docURL = this.sourceNode.ownerDocument.location.href;
-				this.checkURL(docURL, gBrowser.contentDocument, Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-				gBrowser.loadURI(docURL);
-				break;
-			case "FireGestures:OpenFrame": 
-			case "FireGestures:OpenFrameInTab": 
-			case "FireGestures:ReloadFrame": 
-			case "FireGestures:AddBookmarkForFrame": 
-			case "FireGestures:SaveFrame": 
-			case "FireGestures:ViewFrameSource": 
-			case "FireGestures:ViewFrameInfo": 
-				var funcName = aCommand.substr("FireGestures:".length);
-				funcName = funcName.charAt(0).toLowerCase() + funcName.substr(1);
-				nsContextMenu.prototype.target = this.sourceNode;
-				try { nsContextMenu.prototype[funcName](); }
-				finally { nsContextMenu.prototype.target = null; }
-				break;
-			case "FireGestures:OpenLink": 
-				var linkURL = this.getLinkURL();
-				if (!linkURL)
-					throw this._getLocaleString("ERROR_NOT_ON_LINK");
-				openNewWindowWith(linkURL, this.sourceNode.ownerDocument, null, false);
-				break;
-			case "FireGestures:OpenLinkInBgTab": 
-			case "FireGestures:OpenLinkInFgTab": 
-				var linkURL = this.getLinkURL();
-				if (!linkURL)
-					throw this._getLocaleString("ERROR_NOT_ON_LINK");
-				var doc = this.sourceNode.ownerDocument;
-				this.checkURL(linkURL, doc);
-				var charset = window.content.document.characterSet;
-				var referer = makeURI(doc.location.href);
-				var background = aCommand == "FireGestures:OpenLinkInBgTab";
-				gBrowser.loadOneTab(linkURL, referer, charset, null, background, false);
-				break;
-			case "FireGestures:AddBookmarkForLink": 
-				var linkURL = this.getLinkURL();
-				if (!linkURL)
-					throw this._getLocaleString("ERROR_NOT_ON_LINK");
-				PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId, linkURL, this.getLinkText());
-				break;
-			case "FireGestures:SaveLink": 
-				var linkURL = this.getLinkURL();
-				if (!linkURL)
-					throw this._getLocaleString("ERROR_NOT_ON_LINK");
-				var doc = this.sourceNode.ownerDocument;
-				this.checkURL(linkURL, doc);
-				saveURL(linkURL, this.getLinkText(), null, true, false,
-				        makeURI(doc.location.href, doc.characterSet));
-				break;
-			case "FireGestures:ViewImage": 
-				var imageURL = this.getImageURL();
-				if (!imageURL)
-					throw this._getLocaleString("ERROR_NOT_ON_IMAGE");
-				var onCanvas = this.sourceNode instanceof HTMLCanvasElement;
-				if (onCanvas)
-					this.checkURL(imageURL, gBrowser.contentDocument, Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-				openUILink(imageURL, event);
-				break;
-			case "FireGestures:SaveImage": 
-			case "FireGestures:SaveImageNow": 
-				var mediaURL = this.getMediaURL();
-				if (!mediaURL)
-					throw this._getLocaleString("ERROR_NOT_ON_IMAGE");
-				var doc = this.sourceNode.ownerDocument;
-				var onCanvas = this.sourceNode instanceof HTMLCanvasElement;
-				if (onCanvas)
-					this.checkURL(mediaURL, doc);
-				var skipPrompt = aCommand == "FireGestures:SaveImageNow";
-				saveImageURL(mediaURL, onCanvas ? "canvas.png" : null, "SaveImageTitle", 
-				             false, skipPrompt, doc.documentURIObject);
-				break;
-			case "FireGestures:WebSearch": 
-				BrowserSearch.loadSearch(getBrowserSelection(), true);
-				break;
-			case "FireGestures:OpenLinksInSelection": 
-				var linkURLs = this.gatherLinkURLsInSelection();
-				if (!linkURLs || linkURLs.length == 0)
-					throw "No valid links in selection";
-				var doc = this.sourceNode.ownerDocument;
-				var referer = makeURI(doc.location.href);
-				var charset = window.content.document.characterSet;
-				this.openURLs(linkURLs, referer, charset);
-				break;
-			case "FireGestures:OpenURLsInSelection": 
-				this.openURLsInSelection();
-				break;
-			case "FireGestures:ErrorConsole": 
-				toJavaScriptConsole();
-				break;
-			case "FireGestures:WebConsole": 
-				HUDConsoleUI.toggleHUD();
-				break;
-			case "FireGestures:BookmarksSidebar": 
-				toggleSidebar("viewBookmarksSidebar");
-				break;
-			case "FireGestures:HistorySidebar": 
-				toggleSidebar("viewHistorySidebar");
-				break;
-			case "FireGestures:FindBar": 
-				gFindBar.hidden ? gFindBar.onFindCommand() : gFindBar.close();
-				break;
-			case "FireGestures:RestartApp": 
-				Application.restart();
-				break;
-			case "FireGestures:Preferences": 
-				this._gestureMapping.configure();
-				break;
-			case "FireGestures:HybridSave": 
-			case "FireGestures:HybridBookmark": 
-				var onLink  = this.getLinkURL()  != null;
-				var onMedia = this.getMediaURL() != null;
-				var inFrame = this.sourceNode.ownerDocument != window.content.document;
-				if (aCommand == "FireGestures:HybridSave") {
-					if (onLink)       aCommand = "FireGestures:SaveLink";
-					else if (onMedia) aCommand = "FireGestures:SaveImage";
-					else if (inFrame) aCommand = "FireGestures:SaveFrame";
-					else              aCommand = "Browser:SavePage";
-				}
-				else {
-					if (onLink)       aCommand = "FireGestures:AddBookmarkForLink";
-					else if (inFrame) aCommand = "FireGestures:AddBookmarkForFrame";
-					else              aCommand = "Browser:AddBookmarkAs";
-				}
-				this._performAction(event, aCommand);
-				break;
-			case "FireGestures:HybridSendURL": 
-				var url = this.getLinkURL() || this.getImageURL();
-				if (url)
-					MailIntegration.sendMessage(url, "");
-				else
-					MailIntegration.sendLinkForWindow(window.content);
-				break;
-			case "FireGestures:HybridCopyURL": 
-				var url = this.getLinkURL() || this.getImageURL() || 
-				          this.sourceNode.ownerDocument.location.href;
-				var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
-				clipboard.copyString(url);
-				break;
-			case "FireGestures:HybridMetaData": 
-				if (this.getLinkURL() || this.getImageURL())
-					window.openDialog(
-						"chrome://browser/content/metaData.xul", "_blank", 
-						"scrollbars,resizable,chrome,dialog=no", this.sourceNode
-					);
-				else
-					BrowserPageInfo(this.sourceNode.ownerDocument);
-				break;
-			case "FireGestures:HybridViewSource": 
-				if (this.getSelectedText())
-					nsContextMenu.prototype.viewPartialSource("selection");
-				else
-					BrowserViewSourceOfDocument(this.sourceNode.ownerDocument);
-				break;
-			case "FireGestures:AllTabsPopup": 
-			case "FireGestures:BFHistoryPopup": 
-			case "FireGestures:ClosedTabsPopup": 
-			case "FireGestures:WebSearchPopup": 
-				this._buildPopup(aCommand, event.type == "DOMMouseScroll");
-				break;
-			case "FireGestures:OpenHoveredLinks": 
-				var doc = this.sourceNode.ownerDocument;
-				var referer = makeURI(doc.location.href);
-				var charset = window.content.document.characterSet;
-				this.openURLs(this._linkURLs, referer, charset);
-				break;
-			case "FireGestures:SaveHoveredLinks": 
-				var delay = 0;
-				var doc = this.sourceNode.ownerDocument;
-				var ref = 'makeURI("' + doc.location.href + '", "' + doc.characterSet + '")';
-				this._linkURLs.forEach(function(aURL) {
-					window.setTimeout(
-						'saveURL("' + aURL + '", null, null, false, true, ' + ref + ');', delay
-					);
-					delay += 1000;
-				});
-				break;
-			case "FireGestures:CopyHoveredLinks": 
-				if (this._linkURLs.length < 1)
+				}
+				this._linkURLs = null;
+				this._linkElts = null;
+				break;
+			case "gesture-timeout": 
+				this.clearStatusText(0);
+				break;
+		}
+	},
+
+	_performAction: function(event, aCommand) {
+		switch (aCommand) {
+			case "FireGestures:GoUpperLevel": 
+				this.goUpperLevel();
+				break;
+			case "FireGestures:IncrementURL": 
+				this.goNumericURL(+1);
+				break;
+			case "FireGestures:DecrementURL": 
+				this.goNumericURL(-1);
+				break;
+			case "FireGestures:MinimizeWindow": 
+				event.preventDefault();
+				window.minimize();
+				break;
+			case "FireGestures:MaximizeWindow": 
+				window.windowState == window.STATE_MAXIMIZED ? window.restore() : window.maximize();
+				break;
+			case "cmd_close": 
+				if (gBrowser.mCurrentTab.pinned)
+					throw "Blocked closing app tab.";
+				gBrowser.removeCurrentTab({ animate: true });
+				break;
+			case "FireGestures:CloseTabOrWindow": 
+				if (gBrowser.mCurrentTab.pinned)
+					throw "Blocked closing app tab.";
+				if (gBrowser.mTabs.length > 1)
+					document.getElementById("cmd_close").doCommand();
+				else
+					document.getElementById("cmd_closeWindow").doCommand();
+				break;
+			case "FireGestures:UndoCloseTab": 
+				try { document.getElementById("History:UndoCloseTab").doCommand(); }
+				catch(ex) {
+					if ("undoRemoveTab" in gBrowser)
+						gBrowser.undoRemoveTab();
+					else
+						throw "Session Restore feature is disabled.";
+				}
+				break;
+			case "FireGestures:PreviousTab": 
+				gBrowser.mTabContainer.advanceSelectedTab(-1, true);
+				break;
+			case "FireGestures:NextTab": 
+				gBrowser.mTabContainer.advanceSelectedTab(+1, true);
+				break;
+			case "FireGestures:DuplicateTab": 
+				var orgTab = gBrowser.mCurrentTab;
+				var newTab = gBrowser.duplicateTab(orgTab);
+				gBrowser.moveTabTo(newTab, orgTab._tPos + 1);
+				break;
+			case "FireGestures:DetachTab": 
+				gBrowser.replaceTabWithWindow(gBrowser.mCurrentTab);
+				break;
+			case "FireGestures:TogglePinTab": 
+				var tab = gBrowser.mCurrentTab;
+				tab.pinned ? gBrowser.unpinTab(tab) : gBrowser.pinTab(tab);
+				break;
+			case "FireGestures:ReloadAllTabs": 
+				gBrowser.reloadAllTabs(gBrowser.mCurrentTab);
+				break;
+			case "FireGestures:CloseOtherTabs": 
+				gBrowser.removeAllTabsBut(gBrowser.mCurrentTab);
+				break;
+			case "FireGestures:CloseLeftTabs": 
+				this.closeMultipleTabs("left");
+				break;
+			case "FireGestures:CloseRightTabs": 
+				this.closeMultipleTabs("right");
+				break;
+			case "cmd_textZoomEnlarge": 
+			case "cmd_textZoomReduce": 
+				if ("FullZoom" in window && !ZoomManager.useFullZoom)
+					document.getElementById(aCommand.replace("text", "full")).doCommand();
+				else
+					gBrowser.markupDocumentViewer.textZoom += (aCommand == "cmd_textZoomEnlarge") ? 0.2 : -0.2;
+				break;
+			case "cmd_fullZoomEnlarge": 
+			case "cmd_fullZoomReduce": 
+				if (ZoomManager.useFullZoom)
+					document.getElementById(aCommand).doCommand();
+				else
+					gBrowser.markupDocumentViewer.fullZoom += (aCommand == "cmd_fullZoomEnlarge") ? 0.2 : -0.2;
+				break;
+			case "cmd_textZoomReset": 
+				if ("FullZoom" in window)
+					aCommand = aCommand.replace("text", "full");
+				document.getElementById(aCommand).doCommand();
+				break;
+			case "FireGestures:ScrollTop": 
+				if (gBrowser.mPrefs.getBoolPref("accessibility.browsewithcaret"))
+					goDoCommand("cmd_scrollTop");
+				else
+					this.sendKeyEvent({ keyCode: "DOM_VK_HOME" });
+				break;
+			case "FireGestures:ScrollBottom": 
+				if (gBrowser.mPrefs.getBoolPref("accessibility.browsewithcaret"))
+					goDoCommand("cmd_scrollBottom");
+				else
+					this.sendKeyEvent({ keyCode: "DOM_VK_END" });
+				break;
+			case "FireGestures:ScrollPageUp": 
+				this.sendKeyEvent({ keyCode: "DOM_VK_PAGE_UP" });
+				break;
+			case "FireGestures:ScrollPageDown": 
+				this.sendKeyEvent({ keyCode: "DOM_VK_PAGE_DOWN" });
+				break;
+			case "FireGestures:ShowOnlyThisFrame": 
+				var docURL = this.sourceNode.ownerDocument.location.href;
+				this.checkURL(docURL, gBrowser.contentDocument, Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+				gBrowser.loadURI(docURL);
+				break;
+			case "FireGestures:OpenFrame": 
+			case "FireGestures:OpenFrameInTab": 
+			case "FireGestures:ReloadFrame": 
+			case "FireGestures:AddBookmarkForFrame": 
+			case "FireGestures:SaveFrame": 
+			case "FireGestures:ViewFrameSource": 
+			case "FireGestures:ViewFrameInfo": 
+				var funcName = aCommand.substr("FireGestures:".length);
+				funcName = funcName.charAt(0).toLowerCase() + funcName.substr(1);
+				nsContextMenu.prototype.target = this.sourceNode;
+				try { nsContextMenu.prototype[funcName](); }
+				finally { nsContextMenu.prototype.target = null; }
+				break;
+			case "FireGestures:OpenLink": 
+				var linkURL = this.getLinkURL();
+				if (!linkURL)
+					throw this._getLocaleString("ERROR_NOT_ON_LINK");
+				openNewWindowWith(linkURL, this.sourceNode.ownerDocument, null, false);
+				break;
+			case "FireGestures:OpenLinkInBgTab": 
+			case "FireGestures:OpenLinkInFgTab": 
+				var linkURL = this.getLinkURL();
+				if (!linkURL)
+					throw this._getLocaleString("ERROR_NOT_ON_LINK");
+				var doc = this.sourceNode.ownerDocument;
+				this.checkURL(linkURL, doc);
+				var charset = window.content.document.characterSet;
+				var referer = makeURI(doc.location.href);
+				var background = aCommand == "FireGestures:OpenLinkInBgTab";
+				gBrowser.loadOneTab(linkURL, referer, charset, null, background, false);
+				break;
+			case "FireGestures:AddBookmarkForLink": 
+				var linkURL = this.getLinkURL();
+				if (!linkURL)
+					throw this._getLocaleString("ERROR_NOT_ON_LINK");
+				PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId, linkURL, this.getLinkText());
+				break;
+			case "FireGestures:SaveLink": 
+				var linkURL = this.getLinkURL();
+				if (!linkURL)
+					throw this._getLocaleString("ERROR_NOT_ON_LINK");
+				var doc = this.sourceNode.ownerDocument;
+				this.checkURL(linkURL, doc);
+				saveURL(linkURL, this.getLinkText(), null, true, false,
+				        makeURI(doc.location.href, doc.characterSet));
+				break;
+			case "FireGestures:ViewImage": 
+				var imageURL = this.getImageURL();
+				if (!imageURL)
+					throw this._getLocaleString("ERROR_NOT_ON_IMAGE");
+				var onCanvas = this.sourceNode instanceof HTMLCanvasElement;
+				if (onCanvas)
+					this.checkURL(imageURL, gBrowser.contentDocument, Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+				openUILink(imageURL, event);
+				break;
+			case "FireGestures:SaveImage": 
+			case "FireGestures:SaveImageNow": 
+				var mediaURL = this.getMediaURL();
+				if (!mediaURL)
+					throw this._getLocaleString("ERROR_NOT_ON_IMAGE");
+				var doc = this.sourceNode.ownerDocument;
+				var onCanvas = this.sourceNode instanceof HTMLCanvasElement;
+				if (onCanvas)
+					this.checkURL(mediaURL, doc);
+				var skipPrompt = aCommand == "FireGestures:SaveImageNow";
+				saveImageURL(mediaURL, onCanvas ? "canvas.png" : null, "SaveImageTitle", 
+				             false, skipPrompt, doc.documentURIObject);
+				break;
+			case "FireGestures:WebSearch": 
+				BrowserSearch.loadSearch(getBrowserSelection(), true);
+				break;
+			case "FireGestures:OpenLinksInSelection": 
+				var linkURLs = this.gatherLinkURLsInSelection();
+				if (!linkURLs || linkURLs.length == 0)
+					throw "No valid links in selection";
+				var doc = this.sourceNode.ownerDocument;
+				var referer = makeURI(doc.location.href);
+				var charset = window.content.document.characterSet;
+				this.openURLs(linkURLs, referer, charset);
+				break;
+			case "FireGestures:OpenURLsInSelection": 
+				this.openURLsInSelection();
+				break;
+			case "FireGestures:ErrorConsole": 
+				toJavaScriptConsole();
+				break;
+			case "FireGestures:WebConsole": 
+				HUDConsoleUI.toggleHUD();
+				break;
+			case "FireGestures:BookmarksSidebar": 
+				toggleSidebar("viewBookmarksSidebar");
+				break;
+			case "FireGestures:HistorySidebar": 
+				toggleSidebar("viewHistorySidebar");
+				break;
+			case "FireGestures:FindBar": 
+				gFindBar.hidden ? gFindBar.onFindCommand() : gFindBar.close();
+				break;
+			case "FireGestures:RestartApp": 
+				Application.restart();
+				break;
+			case "FireGestures:Preferences": 
+				this._gestureMapping.configure();
+				break;
+			case "FireGestures:HybridSave": 
+			case "FireGestures:HybridBookmark": 
+				var onLink  = this.getLinkURL()  != null;
+				var onMedia = this.getMediaURL() != null;
+				var inFrame = this.sourceNode.ownerDocument != window.content.document;
+				if (aCommand == "FireGestures:HybridSave") {
+					if (onLink)       aCommand = "FireGestures:SaveLink";
+					else if (onMedia) aCommand = "FireGestures:SaveImage";
+					else if (inFrame) aCommand = "FireGestures:SaveFrame";
+					else              aCommand = "Browser:SavePage";
+				}
+				else {
+					if (onLink)       aCommand = "FireGestures:AddBookmarkForLink";
+					else if (inFrame) aCommand = "FireGestures:AddBookmarkForFrame";
+					else              aCommand = "Browser:AddBookmarkAs";
+				}
+				this._performAction(event, aCommand);
+				break;
+			case "FireGestures:HybridSendURL": 
+				var url = this.getLinkURL() || this.getImageURL();
+				if (url)
+					MailIntegration.sendMessage(url, "");
+				else
+					MailIntegration.sendLinkForWindow(window.content);
+				break;
+			case "FireGestures:HybridCopyURL": 
+				var url = this.getLinkURL() || this.getImageURL() || 
+				          this.sourceNode.ownerDocument.location.href;
+				var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
+				clipboard.copyString(url);
+				break;
+			case "FireGestures:HybridMetaData": 
+				if (this.getLinkURL() || this.getImageURL())
+					window.openDialog(
+						"chrome://browser/content/metaData.xul", "_blank", 
+						"scrollbars,resizable,chrome,dialog=no", this.sourceNode
+					);
+				else
+					BrowserPageInfo(this.sourceNode.ownerDocument);
+				break;
+			case "FireGestures:HybridViewSource": 
+				if (this.getSelectedText())
+					nsContextMenu.prototype.viewPartialSource("selection");
+				else
+					BrowserViewSourceOfDocument(this.sourceNode.ownerDocument);
+				break;
+			case "FireGestures:AllTabsPopup": 
+			case "FireGestures:BFHistoryPopup": 
+			case "FireGestures:ClosedTabsPopup": 
+			case "FireGestures:WebSearchPopup": 
+				this._buildPopup(aCommand, event.type == "DOMMouseScroll");
+				break;
+			case "FireGestures:OpenHoveredLinks": 
+				var doc = this.sourceNode.ownerDocument;
+				var referer = makeURI(doc.location.href);
+				var charset = window.content.document.characterSet;
+				this.openURLs(this._linkURLs, referer, charset);
+				break;
+			case "FireGestures:SaveHoveredLinks": 
+				var delay = 0;
+				var doc = this.sourceNode.ownerDocument;
+				var ref = 'makeURI("' + doc.location.href + '", "' + doc.characterSet + '")';
+				this._linkURLs.forEach(function(aURL) {
+					window.setTimeout(
+						'saveURL("' + aURL + '", null, null, false, true, ' + ref + ');', delay
+					);
+					delay += 1000;
+				});
+				break;
+			case "FireGestures:CopyHoveredLinks": 
+				if (this._linkURLs.length < 1)
+					return;
+				var newLine = this._isMac ? "\n" : "\r\n";
+				var urls = this._linkURLs.join(newLine);
+				if (this._linkURLs.length > 1)
+					urls += newLine;
+				var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
+				clipboard.copyString(urls);
+				break;
+			default: 
+				var cmd = document.getElementById(aCommand);
+				if (cmd && cmd.getAttribute("disabled") != "true")
+					cmd.doCommand();
+		}
+	},
+
+	get sourceNode() {
+		return this._gestureHandler.sourceNode;
+	},
+
+	get focusedWindow() {
+		var win = document.commandDispatcher.focusedWindow;
+		if (win == window)
+			win = window.content;
+		return win;
+	},
+
+	getLinkURL: function(aNode) {
+		if (!aNode)
+			aNode = this.sourceNode;
+		while (aNode) {
+			if ((aNode instanceof HTMLAnchorElement || aNode instanceof HTMLAreaElement) && aNode.href)
+				return aNode.href;
+			aNode = aNode.parentNode;
+		}
+		return null;
+	},
+
+	getLinkText: function(aNode) {
+		if (!aNode)
+			aNode = this.sourceNode;
+		var text = gatherTextUnder(aNode);
+		if (!text || !text.match(/\S/)) {
+			text = aNode.getAttribute("title");
+			if (!text || !text.match(/\S/)) {
+				text = aNode.getAttribute("alt");
+				if (!text || !text.match(/\S/)) {
+					text = this.getLinkURL(aNode);
+				}
+			}
+		}
+		return text;
+	},
+
+	getImageURL: function(aNode) {
+		if (!aNode)
+			aNode = this.sourceNode;
+		if (aNode instanceof HTMLImageElement && aNode.src)
+			return aNode.src;
+		else if (aNode instanceof HTMLCanvasElement)
+			return aNode.toDataURL();
+		return null;
+	},
+
+	getMediaURL: function(aNode) {
+		if (!aNode)
+			aNode = this.sourceNode;
+		var imageURL = this.getImageURL(aNode);
+		if (imageURL)
+			return imageURL;
+		else if (aNode instanceof HTMLVideoElement || aNode instanceof HTMLAudioElement)
+			return aNode.currentSrc || aNode.src;
+		else
+			return null;
+	},
+
+	getSelectedText: function() {
+		return this.focusedWindow.getSelection().toString();
+	},
+
+	gatherLinkURLsInSelection: function() {
+		var win = this.focusedWindow;
+		var sel = win.getSelection();
+		if (!sel || sel.isCollapsed)
+			return null;
+		var doc = win.document;
+		var ret = [];
+		for (var i = 0; i < sel.rangeCount; i++) {
+			var range = sel.getRangeAt(i);
+			var fragment = range.cloneContents();
+			var treeWalker = fragment.ownerDocument.createTreeWalker(fragment, NodeFilter.SHOW_ELEMENT, null, true);
+			while (treeWalker.nextNode()) {
+				var node = treeWalker.currentNode;
+				if ((node instanceof HTMLAnchorElement || node instanceof HTMLAreaElement) && node.href) {
+					try {
+						this.checkURL(node.href, doc, Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+						ret.push(node.href);
+					}
+					catch(ex) {
+					}
+				}
+			}
+		}
+		return ret;
+	},
+
+	checkURL: function(aURL, aDoc, aFlags) {
+		urlSecurityCheck(aURL, aDoc.nodePrincipal, aFlags);
+	},
+
+	openURLs: function(aURLs, aReferer, aCharset, aNextToCurrent) {
+		if ("TreeStyleTabService" in window)
+			TreeStyleTabService.readyToOpenChildTab(gBrowser.selectedTab, true);
+		var pos = gBrowser.mCurrentTab._tPos;
+		for each (aURL in aURLs) {
+			var tab = gBrowser.loadOneTab(aURL, aReferer, aCharset, null, true, false);
+			if (aNextToCurrent)
+				gBrowser.moveTabTo(tab, ++pos);
+		}
+		if ("TreeStyleTabService" in window)
+			TreeStyleTabService.stopToOpenChildTab(gBrowser.selectedTab);
+	},
+
+	goUpperLevel: function() {
+		var uri = gBrowser.currentURI;
+		if (uri.path == "/")
+			return;
+		var pathList = uri.path.split("/");
+		if (!pathList.pop())
+			pathList.pop();
+		loadURI(uri.prePath + pathList.join("/") + "/");
+	},
+
+	goNumericURL: function(aIncrement) {
+		var url = gBrowser.currentURI.spec;
+		if (!url.match(/(\d+)(\D*)$/))
+			throw "No numeric value in URL";
+		var num = RegExp.$1;
+		var digit = (num.charAt(0) == "0") ? num.length : null;
+		num = parseInt(num, 10) + aIncrement;
+		if (num < 0)
+			throw "Cannot decrement number in URL anymore";
+		num = num.toString();
+		digit = digit - num.length;
+		for (var i = 0; i < digit; i++)
+			num = "0" + num;
+		loadURI(RegExp.leftContext + num + RegExp.$2);
+	},
+
+	openURLsInSelection: function() {
+		var sel = this.getSelectedText();
+		if (!sel)
+			throw "No selection";
+		var URLs = [];
+		sel = sel.split("\n");
+		sel.forEach(function(str) {
+			str = str.match(/([\w\+\-\=\$;:\?\.%,!#~\*\/@&]{8,})/);
+			if (!str || str[1].indexOf(".") < 0)
+				return;
+			if (str[1].split("/").length < 3 && str[1].split(".").length < 3)
+				return;
+			str = str[1];
+			if (str.indexOf("ttp://") == 0 || str.indexOf("ttps://") == 0)
+				str = "h" + str;
+			URLs.push(str);
+		});
+		if (URLs.length > 0)
+			this.openURLs(URLs);
+		else
+			BrowserSearch.loadSearch(sel, true);
+	},
+
+	closeMultipleTabs: function(aLeftRight) {
+		if ("closeLeftTabs" in gBrowser) {
+			if (aLeftRight == "left")
+				gBrowser.closeLeftTabs(gBrowser.mCurrentTab);
+			else
+				gBrowser.closeRightTabs(gBrowser.mCurrentTab);
+			return;
+		}
+		if ("warnAboutClosingTabs2" in gBrowser == false)
+			window.eval(
+				"gBrowser.warnAboutClosingTabs2 = " + 
+				gBrowser.warnAboutClosingTabs.toString()
+				.replace("(aAll)", "(aAll, aTabsToClose)")
+				.replace(/var numTabs = [^;]+;/, "var numTabs = aTabsToClose;")
+				.replace("--tabsToClose;", "")
+				.replace(/var tabsToClose = [^;]+;/, "var tabsToClose = aTabsToClose;")
+			);
+		var tabs = Array.slice(gBrowser.mTabs);
+		var pos = gBrowser.mCurrentTab._tPos;
+		var start = aLeftRight == "left" ? 0   : pos + 1;
+		var stop  = aLeftRight == "left" ? pos : tabs.length;
+		tabs = tabs.slice(start, stop).filter(function(tab) !tab.pinned && !tab.hidden);
+		if (!gBrowser.warnAboutClosingTabs2(false, tabs.length))
+			return;
+		tabs.reverse().forEach(function(tab) gBrowser.removeTab(tab));
+	},
+
+	sendKeyEvent: function(aOptions) {
+		var evt = this.sourceNode.ownerDocument.createEvent("KeyEvents");
+		evt.initKeyEvent(
+			"keypress", true, true, null, 
+			aOptions.ctrl  || false, 
+			aOptions.alt   || false, 
+			aOptions.shift || false, 
+			aOptions.meta  || false, 
+			aOptions.keyCode ? evt[aOptions.keyCode] : null, 
+			aOptions.key ? aOptions.key.charCodeAt(0) : null
+		);
+		this.sourceNode.dispatchEvent(evt);
+	},
+
+
+
+	setStatusText: function(aText) {
+		this._statusTextField.label = aText;
+	},
+
+	clearStatusText: function(aMillisec) {
+		if (this._clearStatusTimer) {
+			window.clearTimeout(this._clearStatusTimer);
+			this._clearStatusTimer = null;
+		}
+		var text = this._statusTextField.label;
+		var callback = function(self) {
+			self._clearStatusTimer = null;
+			if (self._statusTextField.label == text)
+				self.setStatusText("");
+		};
+		this._clearStatusTimer = window.setTimeout(callback, aMillisec, this);
+	},
+
+	_setStatusText2: function(aText) {
+		if (!this._statusTextField) {
+			this._statusTextField = document.createElement("hbox");
+			this._statusTextField.id = "firegestures-status";
+			this._statusTextField.setAttribute("style", 
+				"position: fixed; border: 1px solid ThreeDShadow; " + 
+				"background-color: -moz-dialog; color: -moz-dialogtext; " + 
+				"margin: 4px; box-shadow: 2px 2px 2px rgba(0,0,0,0.5); " + 
+				"-moz-transition-property: opacity; opacity: 0; "
+			);
+			this._statusTextField.setAttribute("onmouseover", "this.hidden = true;");
+			this._statusTextField.appendChild(document.createElement("label"));
+			document.documentElement.appendChild(this._statusTextField);
+		}
+		if (this._statusTextField.hidden)
+			this._statusTextField.hidden = false;
+		if (this._statusTextField.style.opacity == 0) {
+			var box = gBrowser.mPanelContainer.boxObject;
+			var rootBox = document.documentElement.boxObject;
+			var left = box.x - rootBox.x;
+			var bottom = rootBox.y + rootBox.height - box.y - box.height;
+			this._statusTextField.style.left = left.toString() + "px";
+			this._statusTextField.style.bottom = bottom.toString() + "px";
+		}
+		this._statusTextField.firstChild.value = aText;
+		this._statusTextField.style.MozTransitionDuration = "0s";
+		this._statusTextField.style.MozTransitionDelay = "0s";
+		this._statusTextField.style.opacity = 1;
+	},
+
+	_clearStatusText2: function(aMillisec) {
+		if (!this._statusTextField)
+			return;
+		this._statusTextField.style.MozTransitionDuration = "0.5s";
+		this._statusTextField.style.MozTransitionDelay = (aMillisec / 1000).toString() + "s";
+		this._statusTextField.style.opacity = 0;
+	},
+
+
+
+	_popupActiveItem: null,
+
+	generatePopup: function(event, aAttrsList) {
+		this._buildPopup("FireGestures:CustomPopup", event.type == "DOMMouseScroll", aAttrsList);
+	},
+
+	_buildPopup: function(aCommand, aWheelGesture, aAttrsList) {
+		const POPUP_ID = "FireGesturesPopup";
+		var popup = document.getElementById(POPUP_ID);
+		if (!popup) {
+			if (this._isMac) {
+				popup = document.createElement("panel");
+				popup.setAttribute("noautohide", "true");
+			}
+			else {
+				popup = document.createElement("menupopup");
+			}
+			popup.id = POPUP_ID;
+			document.getElementById("mainPopupSet").appendChild(popup);
+		}
+		popup.setAttribute("_moz-gesturecommand", aCommand);
+		var activeItem = null;
+		switch (aCommand) {
+			case "FireGestures:AllTabsPopup": 
+				var tabs = gBrowser.mTabs;
+				if (tabs.length < 1)
 					return;
-				var newLine = this._isMac ? "\n" : "\r\n";
-				var urls = this._linkURLs.join(newLine);
-				if (this._linkURLs.length > 1)
-					urls += newLine;
-				var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
-				clipboard.copyString(urls);
-				break;
-			default: 
-				var cmd = document.getElementById(aCommand);
-				if (cmd && cmd.getAttribute("disabled") != "true")
-					cmd.doCommand();
-		}
-	},
-
-	get sourceNode() {
-		return this._gestureHandler.sourceNode;
-	},
-
-	get focusedWindow() {
-		var win = document.commandDispatcher.focusedWindow;
-		if (win == window)
-			win = window.content;
-		return win;
-	},
-
-	getLinkURL: function(aNode) {
-		if (!aNode)
-			aNode = this.sourceNode;
-		while (aNode) {
-			if ((aNode instanceof HTMLAnchorElement || aNode instanceof HTMLAreaElement) && aNode.href)
-				return aNode.href;
-			aNode = aNode.parentNode;
-		}
-		return null;
-	},
-
-	getLinkText: function(aNode) {
-		if (!aNode)
-			aNode = this.sourceNode;
-		var text = gatherTextUnder(aNode);
-		if (!text || !text.match(/\S/)) {
-			text = aNode.getAttribute("title");
-			if (!text || !text.match(/\S/)) {
-				text = aNode.getAttribute("alt");
-				if (!text || !text.match(/\S/)) {
-					text = this.getLinkURL(aNode);
-				}
-			}
-		}
-		return text;
-	},
-
-	getImageURL: function(aNode) {
-		if (!aNode)
-			aNode = this.sourceNode;
-		if (aNode instanceof HTMLImageElement && aNode.src)
-			return aNode.src;
-		else if (aNode instanceof HTMLCanvasElement)
-			return aNode.toDataURL();
-		return null;
-	},
-
-	getMediaURL: function(aNode) {
-		if (!aNode)
-			aNode = this.sourceNode;
-		var imageURL = this.getImageURL(aNode);
-		if (imageURL)
-			return imageURL;
-		else if (aNode instanceof HTMLVideoElement || aNode instanceof HTMLAudioElement)
-			return aNode.currentSrc || aNode.src;
-		else
-			return null;
-	},
-
-	getSelectedText: function() {
-		return this.focusedWindow.getSelection().toString();
-	},
-
-	gatherLinkURLsInSelection: function() {
-		var win = this.focusedWindow;
-		var sel = win.getSelection();
-		if (!sel || sel.isCollapsed)
-			return null;
-		var doc = win.document;
-		var ret = [];
-		for (var i = 0; i < sel.rangeCount; i++) {
-			var range = sel.getRangeAt(i);
-			var fragment = range.cloneContents();
-			var treeWalker = fragment.ownerDocument.createTreeWalker(fragment, NodeFilter.SHOW_ELEMENT, null, true);
-			while (treeWalker.nextNode()) {
-				var node = treeWalker.currentNode;
-				if ((node instanceof HTMLAnchorElement || node instanceof HTMLAreaElement) && node.href) {
-					try {
-						this.checkURL(node.href, doc, Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-						ret.push(node.href);
-					}
-					catch(ex) {
-					}
-				}
-			}
-		}
-		return ret;
-	},
-
-	checkURL: function(aURL, aDoc, aFlags) {
-		urlSecurityCheck(aURL, aDoc.nodePrincipal, aFlags);
-	},
-
-	openURLs: function(aURLs, aReferer, aCharset, aNextToCurrent) {
-		if ("TreeStyleTabService" in window)
-			TreeStyleTabService.readyToOpenChildTab(gBrowser.selectedTab, true);
-		var pos = gBrowser.mCurrentTab._tPos;
-		for each (aURL in aURLs) {
-			var tab = gBrowser.loadOneTab(aURL, aReferer, aCharset, null, true, false);
-			if (aNextToCurrent)
-				gBrowser.moveTabTo(tab, ++pos);
-		}
-		if ("TreeStyleTabService" in window)
-			TreeStyleTabService.stopToOpenChildTab(gBrowser.selectedTab);
-	},
-
-	goUpperLevel: function() {
-		var uri = gBrowser.currentURI;
-		if (uri.path == "/")
-			return;
-		var pathList = uri.path.split("/");
-		if (!pathList.pop())
-			pathList.pop();
-		loadURI(uri.prePath + pathList.join("/") + "/");
-	},
-
-	goNumericURL: function(aIncrement) {
-		var url = gBrowser.currentURI.spec;
-		if (!url.match(/(\d+)(\D*)$/))
-			throw "No numeric value in URL";
-		var num = RegExp.$1;
-		var digit = (num.charAt(0) == "0") ? num.length : null;
-		num = parseInt(num, 10) + aIncrement;
-		if (num < 0)
-			throw "Cannot decrement number in URL anymore";
-		num = num.toString();
-		digit = digit - num.length;
-		for (var i = 0; i < digit; i++)
-			num = "0" + num;
-		loadURI(RegExp.leftContext + num + RegExp.$2);
-	},
-
-	openURLsInSelection: function() {
-		var sel = this.getSelectedText();
-		if (!sel)
-			throw "No selection";
-		var URLs = [];
-		sel = sel.split("\n");
-		sel.forEach(function(str) {
-			str = str.match(/([\w\+\-\=\$;:\?\.%,!#~\*\/@&]{8,})/);
-			if (!str || str[1].indexOf(".") < 0)
-				return;
-			if (str[1].split("/").length < 3 && str[1].split(".").length < 3)
-				return;
-			str = str[1];
-			if (str.indexOf("ttp://") == 0 || str.indexOf("ttps://") == 0)
-				str = "h" + str;
-			URLs.push(str);
-		});
-		if (URLs.length > 0)
-			this.openURLs(URLs);
-		else
-			BrowserSearch.loadSearch(sel, true);
-	},
-
-	closeMultipleTabs: function(aLeftRight) {
-		if ("closeLeftTabs" in gBrowser) {
-			if (aLeftRight == "left")
-				gBrowser.closeLeftTabs(gBrowser.mCurrentTab);
-			else
-				gBrowser.closeRightTabs(gBrowser.mCurrentTab);
-			return;
-		}
-		if ("warnAboutClosingTabs2" in gBrowser == false)
-			window.eval(
-				"gBrowser.warnAboutClosingTabs2 = " + 
-				gBrowser.warnAboutClosingTabs.toString()
-				.replace("(aAll)", "(aAll, aTabsToClose)")
-				.replace(/var numTabs = [^;]+;/, "var numTabs = aTabsToClose;")
-				.replace("--tabsToClose;", "")
-				.replace(/var tabsToClose = [^;]+;/, "var tabsToClose = aTabsToClose;")
-			);
-		var tabs = Array.slice(gBrowser.mTabs);
-		var pos = gBrowser.mCurrentTab._tPos;
-		var start = aLeftRight == "left" ? 0   : pos + 1;
-		var stop  = aLeftRight == "left" ? pos : tabs.length;
-		tabs = tabs.slice(start, stop).filter(function(tab) !tab.pinned && !tab.hidden);
-		if (!gBrowser.warnAboutClosingTabs2(false, tabs.length))
-			return;
-		tabs.reverse().forEach(function(tab) gBrowser.removeTab(tab));
-	},
-
-
-
-	setStatusText: function(aText) {
-		this._statusTextField.label = aText;
-	},
-
-	clearStatusText: function(aMillisec) {
-		if (this._clearStatusTimer) {
-			window.clearTimeout(this._clearStatusTimer);
-			this._clearStatusTimer = null;
-		}
-		var text = this._statusTextField.label;
-		var callback = function(self) {
-			self._clearStatusTimer = null;
-			if (self._statusTextField.label == text)
-				self.setStatusText("");
-		};
-		this._clearStatusTimer = window.setTimeout(callback, aMillisec, this);
-	},
-
-	_setStatusText2: function(aText) {
-		if (!this._statusTextField) {
-			this._statusTextField = document.createElement("hbox");
-			this._statusTextField.id = "firegestures-status";
-			this._statusTextField.setAttribute("style", 
-				"position: fixed; border: 1px solid ThreeDShadow; " + 
-				"background-color: -moz-dialog; color: -moz-dialogtext; " + 
-				"margin: 4px; box-shadow: 2px 2px 2px rgba(0,0,0,0.5); " + 
-				"-moz-transition-property: opacity; opacity: 0; "
-			);
-			this._statusTextField.setAttribute("onmouseover", "this.hidden = true;");
-			this._statusTextField.appendChild(document.createElement("label"));
-			document.documentElement.appendChild(this._statusTextField);
-		}
-		if (this._statusTextField.hidden)
-			this._statusTextField.hidden = false;
-		if (this._statusTextField.style.opacity == 0) {
-			var box = gBrowser.mPanelContainer.boxObject;
-			var rootBox = document.documentElement.boxObject;
-			var left = box.x - rootBox.x;
-			var bottom = rootBox.y + rootBox.height - box.y - box.height;
-			this._statusTextField.style.left = left.toString() + "px";
-			this._statusTextField.style.bottom = bottom.toString() + "px";
-		}
-		this._statusTextField.firstChild.value = aText;
-		this._statusTextField.style.MozTransitionDuration = "0s";
-		this._statusTextField.style.MozTransitionDelay = "0s";
-		this._statusTextField.style.opacity = 1;
-	},
-
-	_clearStatusText2: function(aMillisec) {
-		if (!this._statusTextField)
-			return;
-		this._statusTextField.style.MozTransitionDuration = "0.5s";
-		this._statusTextField.style.MozTransitionDelay = (aMillisec / 1000).toString() + "s";
-		this._statusTextField.style.opacity = 0;
-	},
-
-
-
-	_popupActiveItem: null,
-
-	generatePopup: function(event, aAttrsList) {
-		this._buildPopup("FireGestures:CustomPopup", event.type == "DOMMouseScroll", aAttrsList);
-	},
-
-	_buildPopup: function(aCommand, aWheelGesture, aAttrsList) {
-		const POPUP_ID = "FireGesturesPopup";
-		var popup = document.getElementById(POPUP_ID);
-		if (!popup) {
-			if (this._isMac) {
-				popup = document.createElement("panel");
-				popup.setAttribute("noautohide", "true");
-			}
-			else {
-				popup = document.createElement("menupopup");
-			}
-			popup.id = POPUP_ID;
-			document.getElementById("mainPopupSet").appendChild(popup);
-		}
-		popup.setAttribute("_moz-gesturecommand", aCommand);
-		var activeItem = null;
-		switch (aCommand) {
-			case "FireGestures:AllTabsPopup": 
-				var tabs = gBrowser.mTabs;
-				if (tabs.length < 1)
-					return;
-				for (var i = 0; i < tabs.length; i++) {
-					var tab = tabs[i];
-					if (tab.hidden)
-						continue;
-					var menuitem = popup.appendChild(document.createElement("menuitem"));
-					menuitem.setAttribute("class", "menuitem-iconic bookmark-item");
-					menuitem.setAttribute("label", tab.label);
-					menuitem.setAttribute("crop", tab.getAttribute("crop"));
-					menuitem.setAttribute("image", tab.getAttribute("image"));
-					menuitem.setAttribute("statustext", tab.linkedBrowser.currentURI.spec);
-					menuitem.index = i;
-					if (tab.selected)
-						activeItem = menuitem;
-				}
-				break;
-			case "FireGestures:BFHistoryPopup": 
-				var sessionHistory = gBrowser.webNavigation.sessionHistory;
-				if (sessionHistory.count < 1)
-					throw "No back/forward history for this tab.";
-				var curIdx = sessionHistory.index;
-				for (var i = 0; i < sessionHistory.count; i++) {
-					var entry = sessionHistory.getEntryAtIndex(i, false);
-					if (!entry)
-						continue;
-					var menuitem = document.createElement("menuitem");
-					popup.insertBefore(menuitem, popup.firstChild);
-					menuitem.setAttribute("label", entry.title);
-					menuitem.setAttribute("statustext", entry.URI.spec);
-					try {
-						var iconURL = Cc["@mozilla.org/browser/favicon-service;1"]
-						              .getService(Ci.nsIFaviconService)
-						              .getFaviconForPage(entry.URI).spec;
-						menuitem.style.listStyleImage = "url(" + iconURL + ")";
-					}
-					catch (ex) {}
-					menuitem.index = i;
-					if (i == curIdx) {
-						menuitem.style.listStyleImage = "";
-						menuitem.setAttribute("type", "radio");
-						menuitem.setAttribute("checked", "true");
-						menuitem.className = "unified-nav-current";
-						activeItem = menuitem;
-					}
-					else {
-						menuitem.className = i < curIdx
-						                   ? "unified-nav-back menuitem-iconic"
-						                   : "unified-nav-forward menuitem-iconic";
-					}
-				}
-				break;
-			case "FireGestures:ClosedTabsPopup": 
-				var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
-				if (ss.getClosedTabCount(window) == 0)
-					throw "No restorable tabs in this window.";
-				var undoItems = eval("(" + ss.getClosedTabData(window) + ")");
-				for (var i = 0; i < undoItems.length; i++) {
-					var menuitem = popup.appendChild(document.createElement("menuitem"));
-					menuitem.setAttribute("label", undoItems[i].title);
-					menuitem.setAttribute("class", "menuitem-iconic bookmark-item");
-					menuitem.index = i;
-					var iconURL = undoItems[i].image;
-					if (iconURL)
-						menuitem.setAttribute("image", iconURL);
-				}
-				break;
-			case "FireGestures:WebSearchPopup": 
-				var searchSvc = Cc["@mozilla.org/browser/search-service;1"].getService(Ci.nsIBrowserSearchService);
-				var engines = searchSvc.getVisibleEngines({});
-				if (engines.length < 1)
-					throw "No search engines installed.";
-				for (var i = engines.length - 1; i >= 0; --i) {
-					var menuitem = document.createElement("menuitem");
-					menuitem.setAttribute("label", engines[i].name);
-					menuitem.setAttribute("class", "menuitem-iconic");
-					if (engines[i].iconURI)
-						menuitem.setAttribute("src", engines[i].iconURI.spec);
-					popup.insertBefore(menuitem, popup.firstChild);
-					menuitem.engine = engines[i];
-				}
-				popup.setAttribute("_moz-selectedtext", getBrowserSelection());
-				break;
-			case "FireGestures:CustomPopup": 
-				for each (var aAttrs in aAttrsList) {
-					var menuitem;
-					if (!aAttrs) {
-						menuitem = document.createElement("menuseparator");
-					}
-					else {
-						menuitem = document.createElement("menuitem");
-						for (var [name, val] in Iterator(aAttrs)) {
-							menuitem.setAttribute(name, val);
-						}
-					}
-					popup.appendChild(menuitem);
-				}
-				break;
-		}
-		if (activeItem)
-			activeItem.setAttribute("default", "true");
-		else
-			activeItem = popup.firstChild;
-		if (aWheelGesture) {
-			this._popupActiveItem = activeItem;
-			popup.addEventListener("popupshown", this, true);
-		}
-		document.popupNode = null;
-		document.tooltipNode = null;
-		popup.addEventListener("popupshowing", this, true);
-		popup.addEventListener("popuphiding", this, true);
-		popup.addEventListener("DOMMenuItemActive", this, false);
-		popup.addEventListener("DOMMenuItemInactive", this, false);
-		this._gestureHandler.openPopupAtPointer(popup);
-		document.documentElement.addEventListener("mouseup", this, true);
-		if (aWheelGesture)
-			document.documentElement.addEventListener("DOMMouseScroll", this, true);
-	},
-
-	handleEvent: function(event) {
-		var popup = document.getElementById("FireGesturesPopup");
-		switch (event.type) {
-			case "DOMMouseScroll": 
-				event.preventDefault();
-				event.stopPropagation();
-				this._activateMenuItem(false);
-				var activeItem = this._popupActiveItem;
-				activeItem = event.detail > 0 ? activeItem.nextSibling : activeItem.previousSibling;
-				if (!activeItem)
-					activeItem = event.detail > 0 ? popup.firstChild : popup.lastChild;
-				this._popupActiveItem = activeItem;
-				this._activateMenuItem(true);
-				var scrollbox = document.getAnonymousNodes(popup)[0];
-				scrollbox.ensureElementIsVisible(activeItem);
-				break;
-			case "DOMMenuItemActive": 
-				var statusText = event.target.getAttribute("statustext");
-				if (statusText == "about:blank")
+				for (var i = 0; i < tabs.length; i++) {
+					var tab = tabs[i];
+					if (tab.hidden)
+						continue;
+					var menuitem = popup.appendChild(document.createElement("menuitem"));
+					menuitem.setAttribute("class", "menuitem-iconic bookmark-item");
+					menuitem.setAttribute("label", tab.label);
+					menuitem.setAttribute("crop", tab.getAttribute("crop"));
+					menuitem.setAttribute("image", tab.getAttribute("image"));
+					menuitem.setAttribute("statustext", tab.linkedBrowser.currentURI.spec);
+					menuitem.index = i;
+					if (tab.selected)
+						activeItem = menuitem;
+				}
+				break;
+			case "FireGestures:BFHistoryPopup": 
+				var sessionHistory = gBrowser.webNavigation.sessionHistory;
+				if (sessionHistory.count < 1)
+					throw "No back/forward history for this tab.";
+				var curIdx = sessionHistory.index;
+				for (var i = 0; i < sessionHistory.count; i++) {
+					var entry = sessionHistory.getEntryAtIndex(i, false);
+					if (!entry)
+						continue;
+					var menuitem = document.createElement("menuitem");
+					popup.insertBefore(menuitem, popup.firstChild);
+					menuitem.setAttribute("label", entry.title);
+					menuitem.setAttribute("statustext", entry.URI.spec);
+					try {
+						var iconURL = Cc["@mozilla.org/browser/favicon-service;1"]
+						              .getService(Ci.nsIFaviconService)
+						              .getFaviconForPage(entry.URI).spec;
+						menuitem.style.listStyleImage = "url(" + iconURL + ")";
+					}
+					catch (ex) {}
+					menuitem.index = i;
+					if (i == curIdx) {
+						menuitem.style.listStyleImage = "";
+						menuitem.setAttribute("type", "radio");
+						menuitem.setAttribute("checked", "true");
+						menuitem.className = "unified-nav-current";
+						activeItem = menuitem;
+					}
+					else {
+						menuitem.className = i < curIdx
+						                   ? "unified-nav-back menuitem-iconic"
+						                   : "unified-nav-forward menuitem-iconic";
+					}
+				}
+				break;
+			case "FireGestures:ClosedTabsPopup": 
+				var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+				if (ss.getClosedTabCount(window) == 0)
+					throw "No restorable tabs in this window.";
+				var undoItems = eval("(" + ss.getClosedTabData(window) + ")");
+				for (var i = 0; i < undoItems.length; i++) {
+					var menuitem = popup.appendChild(document.createElement("menuitem"));
+					menuitem.setAttribute("label", undoItems[i].title);
+					menuitem.setAttribute("class", "menuitem-iconic bookmark-item");
+					menuitem.index = i;
+					var iconURL = undoItems[i].image;
+					if (iconURL)
+						menuitem.setAttribute("image", iconURL);
+				}
+				break;
+			case "FireGestures:WebSearchPopup": 
+				var searchSvc = Cc["@mozilla.org/browser/search-service;1"].getService(Ci.nsIBrowserSearchService);
+				var engines = searchSvc.getVisibleEngines({});
+				if (engines.length < 1)
+					throw "No search engines installed.";
+				for (var i = engines.length - 1; i >= 0; --i) {
+					var menuitem = document.createElement("menuitem");
+					menuitem.setAttribute("label", engines[i].name);
+					menuitem.setAttribute("class", "menuitem-iconic");
+					if (engines[i].iconURI)
+						menuitem.setAttribute("src", engines[i].iconURI.spec);
+					popup.insertBefore(menuitem, popup.firstChild);
+					menuitem.engine = engines[i];
+				}
+				popup.setAttribute("_moz-selectedtext", getBrowserSelection());
+				break;
+			case "FireGestures:CustomPopup": 
+				for each (var aAttrs in aAttrsList) {
+					var menuitem;
+					if (!aAttrs) {
+						menuitem = document.createElement("menuseparator");
+					}
+					else {
+						menuitem = document.createElement("menuitem");
+						for (var [name, val] in Iterator(aAttrs)) {
+							menuitem.setAttribute(name, val);
+						}
+					}
+					popup.appendChild(menuitem);
+				}
+				break;
+		}
+		if (activeItem)
+			activeItem.setAttribute("default", "true");
+		else
+			activeItem = popup.firstChild;
+		if (aWheelGesture) {
+			this._popupActiveItem = activeItem;
+			popup.addEventListener("popupshown", this, true);
+		}
+		document.popupNode = null;
+		document.tooltipNode = null;
+		popup.addEventListener("popupshowing", this, true);
+		popup.addEventListener("popuphiding", this, true);
+		popup.addEventListener("DOMMenuItemActive", this, false);
+		popup.addEventListener("DOMMenuItemInactive", this, false);
+		this._gestureHandler.openPopupAtPointer(popup);
+		document.documentElement.addEventListener("mouseup", this, true);
+		if (aWheelGesture)
+			document.documentElement.addEventListener("DOMMouseScroll", this, true);
+	},
+
+	handleEvent: function(event) {
+		var popup = document.getElementById("FireGesturesPopup");
+		switch (event.type) {
+			case "DOMMouseScroll": 
+				event.preventDefault();
+				event.stopPropagation();
+				this._activateMenuItem(false);
+				var activeItem = this._popupActiveItem;
+				activeItem = event.detail > 0 ? activeItem.nextSibling : activeItem.previousSibling;
+				if (!activeItem)
+					activeItem = event.detail > 0 ? popup.firstChild : popup.lastChild;
+				this._popupActiveItem = activeItem;
+				this._activateMenuItem(true);
+				var scrollbox = document.getAnonymousNodes(popup)[0];
+				scrollbox.ensureElementIsVisible(activeItem);
+				break;
+			case "DOMMenuItemActive": 
+				var statusText = event.target.getAttribute("statustext");
+				if (statusText == "about:blank")
 					statusText = " ";
-				if (statusText)
-					XULBrowserWindow.setOverLink(statusText, null);
-				break;
-			case "DOMMenuItemInactive": 
-				XULBrowserWindow.setOverLink("", null);
-				break;
-			case "mouseup": 
-				var activeItem = this._popupActiveItem || event.target;
-				if (activeItem.localName == "menuitem" && !activeItem.hasAttribute("default")) {
-					switch (popup.getAttribute("_moz-gesturecommand")) {
-						case "FireGestures:AllTabsPopup": 
-							gBrowser.selectedTab = gBrowser.mTabs[activeItem.index];
-							break;
-						case "FireGestures:BFHistoryPopup": 
-							gBrowser.webNavigation.gotoIndex(activeItem.index);
-							break;
-						case "FireGestures:ClosedTabsPopup": 
-							undoCloseTab(activeItem.index);
-							break;
-						case "FireGestures:WebSearchPopup": 
-							var selText = popup.getAttribute("_moz-selectedtext");
-							var engine = activeItem.engine;
-							if (!engine)
-								break;
-							var submission = engine.getSubmission(selText, null);
-							if (!submission)
-								break;
-							if ("LightWeightThemeWebInstaller" in window)
-								gBrowser.loadOneTab(submission.uri.spec, {
-									postData: submission.postData,
-									relatedToCurrent: true
-								});
-							else
-								gBrowser.loadOneTab(submission.uri.spec, null, null, submission.postData, null, false);
-							break;
-						default: 
-							eval(activeItem.getAttribute("oncommand"));
-					}
-				}
-				popup.hidePopup();
-				break;
-			case"popupshowing": 
-				var boxObj = popup.popupBoxObject;
-				if ("setConsumeRollupEvent" in boxObj) {
-					boxObj.setConsumeRollupEvent(boxObj.ROLLUP_NO_CONSUME);
-				}
-				break;
-			case "popupshown": 
-				this._activateMenuItem(true);
-				break;
-			case "popuphiding": 
-				this._activateMenuItem(false);
-				this._popupActiveItem = null;
-				popup.removeEventListener("popupshowing", this, true);
-				popup.removeEventListener("popupshown", this, true);
-				popup.removeEventListener("popuphiding", this, true);
-				document.documentElement.removeEventListener("mouseup", this, true);
-				document.documentElement.removeEventListener("DOMMouseScroll", this, true);
-				while (popup.hasChildNodes())
-					popup.removeChild(popup.lastChild);
-				break;
-		}
-	},
-
-	_activateMenuItem: function(aActive) {
-		if (!this._popupActiveItem)
-			return;
-		if (aActive)
-			this._popupActiveItem.setAttribute("_moz-menuactive", "true");
-		else
-			this._popupActiveItem.removeAttribute("_moz-menuactive");
-		if (this._isMac) {
-			if (aActive) {
-				var cssText = "background-color: -moz-menuhover; color: -moz-menuhovertext;";
-				this._popupActiveItem.setAttribute("style", cssText);
-			}
-			else
-				this._popupActiveItem.removeAttribute("style");
-		}
-		var evt = document.createEvent("Events");
-		evt.initEvent(aActive ? "DOMMenuItemActive" : "DOMMenuItemInactive", true, true);
-		this._popupActiveItem.dispatchEvent(evt);
-	},
-
-
-
-	QueryInterface: function(aIID) {
-		if (!aIID.equals(Ci.nsISupports) && 
-		    !aIID.equals(Ci.nsIDOMEventListener) &&
-		    !aIID.equals(Ci.xdIGestureObserver)) {
-			throw Cr.NS_ERROR_NO_INTERFACE;
-		}
-		return this;
-	}
-
-};
-
-
-window.addEventListener("load",   function() { FireGestures.init(); },   false);
-window.addEventListener("unload", function() { FireGestures.uninit(); }, false);
-
-
+				if (statusText)
+					XULBrowserWindow.setOverLink(statusText, null);
+				break;
+			case "DOMMenuItemInactive": 
+				XULBrowserWindow.setOverLink("", null);
+				break;
+			case "mouseup": 
+				var activeItem = this._popupActiveItem || event.target;
+				if (activeItem.localName == "menuitem" && !activeItem.hasAttribute("default")) {
+					switch (popup.getAttribute("_moz-gesturecommand")) {
+						case "FireGestures:AllTabsPopup": 
+							gBrowser.selectedTab = gBrowser.mTabs[activeItem.index];
+							break;
+						case "FireGestures:BFHistoryPopup": 
+							gBrowser.webNavigation.gotoIndex(activeItem.index);
+							break;
+						case "FireGestures:ClosedTabsPopup": 
+							undoCloseTab(activeItem.index);
+							break;
+						case "FireGestures:WebSearchPopup": 
+							var selText = popup.getAttribute("_moz-selectedtext");
+							var engine = activeItem.engine;
+							if (!engine)
+								break;
+							var submission = engine.getSubmission(selText, null);
+							if (!submission)
+								break;
+							if ("LightWeightThemeWebInstaller" in window)
+								gBrowser.loadOneTab(submission.uri.spec, {
+									postData: submission.postData,
+									relatedToCurrent: true
+								});
+							else
+								gBrowser.loadOneTab(submission.uri.spec, null, null, submission.postData, null, false);
+							break;
+						default: 
+							eval(activeItem.getAttribute("oncommand"));
+					}
+				}
+				popup.hidePopup();
+				break;
+			case"popupshowing": 
+				var boxObj = popup.popupBoxObject;
+				if ("setConsumeRollupEvent" in boxObj) {
+					boxObj.setConsumeRollupEvent(boxObj.ROLLUP_NO_CONSUME);
+				}
+				break;
+			case "popupshown": 
+				this._activateMenuItem(true);
+				break;
+			case "popuphiding": 
+				this._activateMenuItem(false);
+				this._popupActiveItem = null;
+				popup.removeEventListener("popupshowing", this, true);
+				popup.removeEventListener("popupshown", this, true);
+				popup.removeEventListener("popuphiding", this, true);
+				document.documentElement.removeEventListener("mouseup", this, true);
+				document.documentElement.removeEventListener("DOMMouseScroll", this, true);
+				while (popup.hasChildNodes())
+					popup.removeChild(popup.lastChild);
+				break;
+		}
+	},
+
+	_activateMenuItem: function(aActive) {
+		if (!this._popupActiveItem)
+			return;
+		if (aActive)
+			this._popupActiveItem.setAttribute("_moz-menuactive", "true");
+		else
+			this._popupActiveItem.removeAttribute("_moz-menuactive");
+		if (this._isMac) {
+			if (aActive) {
+				var cssText = "background-color: -moz-menuhover; color: -moz-menuhovertext;";
+				this._popupActiveItem.setAttribute("style", cssText);
+			}
+			else
+				this._popupActiveItem.removeAttribute("style");
+		}
+		var evt = document.createEvent("Events");
+		evt.initEvent(aActive ? "DOMMenuItemActive" : "DOMMenuItemInactive", true, true);
+		this._popupActiveItem.dispatchEvent(evt);
+	},
+
+
+
+	QueryInterface: function(aIID) {
+		if (!aIID.equals(Ci.nsISupports) && 
+		    !aIID.equals(Ci.nsIDOMEventListener) &&
+		    !aIID.equals(Ci.xdIGestureObserver)) {
+			throw Cr.NS_ERROR_NO_INTERFACE;
+		}
+		return this;
+	}
+
+};
+
+
+window.addEventListener("load",   function() { FireGestures.init(); },   false);
+window.addEventListener("unload", function() { FireGestures.uninit(); }, false);
+
+
diff --git a/components/xdGestureHandler.js b/components/xdGestureHandler.js
index bfb7755..a5e5f94 100644
--- a/components/xdGestureHandler.js
+++ b/components/xdGestureHandler.js
@@ -1,563 +1,563 @@
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-const PREFS_DOMAIN = "extensions.firegestures.";
-const HTML_NS = "http://www.w3.org/1999/xhtml";
-
-const STATE_READY    = 0;
-const STATE_GESTURE  = 1;
-const STATE_ROCKER   = 2;
-const STATE_WHEEL    = 3;
-const STATE_KEYPRESS = 4;
-
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-
-
-
-function xdGestureHandler() {}
-
-
-xdGestureHandler.prototype = {
-
-	classDescription: "Mouse Gesture Handler",
-	contractID: "@xuldev.org/firegestures/handler;1",
-	classID: Components.ID("{ca559550-8ab4-41c5-a72f-fd931322cc7e}"),
-	QueryInterface: XPCOMUtils.generateQI([
-		Ci.nsISupports,
-		Ci.nsIObserver,
-		Ci.nsISupportsWeakReference,
-		Ci.nsIDOMEventListener,
-		Ci.nsITimerCallback,
-		Ci.xdIGestureHandler
-	]),
-
-	sourceNode: null,
-
-	_drawArea: null,
-
-	_lastX: null,
-	_lastY: null,
-
-	_directionChain: "",
-
-	_gestureObserver: null,
-
-	_isFx4: false,
-
-	attach: function FGH_attach(aDrawArea, aObserver) {
-		var appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
-		this._isFx4 = parseFloat(appInfo.version) >= 4.0;
-		this._drawArea = aDrawArea;
-		this._gestureObserver = aObserver;
-		this._drawArea.addEventListener("mousedown", this, true);
-		if (this._isFx4) {
-			var root = this._drawArea.ownerDocument.defaultView.document.documentElement;
-			root.addEventListener("mousemove", this, true);
-			root.addEventListener("mouseup", this, true);
-		}
-		else {
-			this._drawArea.addEventListener("mousemove", this, true);
-			this._drawArea.addEventListener("mouseup", this, true);
-		}
-		this._drawArea.addEventListener("contextmenu", this, true);
-		this._drawArea.addEventListener("draggesture", this, true);
-		this._reloadPrefs();
-		var prefBranch2 = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch2);
-		prefBranch2.addObserver(PREFS_DOMAIN, this, true);
-	},
-
-	detach: function FGH_detach() {
-		this._drawArea.removeEventListener("mousedown", this, true);
-		if (this._isFx4) {
-			var root = this._drawArea.ownerDocument.defaultView.document.documentElement;
-			root.removeEventListener("mousemove", this, true);
-			root.removeEventListener("mouseup", this, true);
-		}
-		else {
-			this._drawArea.removeEventListener("mousemove", this, true);
-			this._drawArea.removeEventListener("mouseup", this, true);
-		}
-		this._drawArea.removeEventListener("contextmenu", this, true);
-		this._drawArea.removeEventListener("draggesture", this, true);
-		var prefBranch2 = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch2);
-		prefBranch2.removeObserver(PREFS_DOMAIN, this);
-		this._clearTimeout();
-		this.sourceNode = null;
-		this._drawArea = null;
-		this._gestureObserver = null;
-		this._prefs = null;
-	},
-
-	_reloadPrefs: function FGH__reloadPrefs() {
-		var prefBranch = Cc["@mozilla.org/preferences-service;1"]
-		                 .getService(Ci.nsIPrefService)
-		                 .getBranch(PREFS_DOMAIN);
-		var getPref = function(aName) {
-			try {
-				switch (prefBranch.getPrefType(aName)) {
-					case prefBranch.PREF_STRING:
-						return prefBranch.getCharPref(aName);
-					case prefBranch.PREF_BOOL:
-						return prefBranch.getBoolPref(aName);
-					case prefBranch.PREF_INT:
-						return prefBranch.getIntPref(aName);
-					default:
-						throw null;
-				}
-			}
-			catch(ex) {
-			}
-		};
-		this._triggerButton  = getPref("trigger_button");
-		this._suppressAlt    = getPref("suppress.alt");
-		this._trailEnabled   = getPref("mousetrail");
-		this._trailSize      = getPref("mousetrail.size");
-		this._trailColor     = getPref("mousetrail.color");
-		this._gestureTimeout = getPref("gesture_timeout");
-		this._mouseGestureEnabled    = getPref("mousegesture");
-		this._wheelGestureEnabled    = getPref("wheelgesture");
-		this._rockerGestureEnabled   = getPref("rockergesture");
-		this._keypressGestureEnabled = getPref("keypressgesture");
-		this._drawArea.removeEventListener("DOMMouseScroll", this, true);
-		this._drawArea.removeEventListener("click", this, true);
-		if (this._wheelGestureEnabled)
-			this._drawArea.addEventListener("DOMMouseScroll", this, true);
-		if (this._rockerGestureEnabled)
-			this._drawArea.addEventListener("click", this, true);
-		var tabbrowser = this._drawArea.ownerDocument.getBindingParent(this._drawArea);
-		if (tabbrowser && tabbrowser.localName == "tabbrowser") {
-			tabbrowser.mStrip.removeEventListener("DOMMouseScroll", this._wheelOnTabBar, true);
-			if (getPref("tabwheelgesture"))
-				tabbrowser.mStrip.addEventListener("DOMMouseScroll", this._wheelOnTabBar, true);
-		}
-		if (this._triggerButton == 1) {
-			var prefSvc = Cc["@mozilla.org/preferences-service;1"]
-			              .getService(Ci.nsIPrefBranch2)
-			              .QueryInterface(Ci.nsIPrefService);
-			prefSvc.setBoolPref("middlemouse.contentLoadURL", false);
-		}
-	},
-
-	_state: STATE_READY,
-	_isMouseDownL: false,
-	_isMouseDownM: false,
-	_isMouseDownR: false,
-	_suppressContext: false,
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const PREFS_DOMAIN = "extensions.firegestures.";
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+
+const STATE_READY    = 0;
+const STATE_GESTURE  = 1;
+const STATE_ROCKER   = 2;
+const STATE_WHEEL    = 3;
+const STATE_KEYPRESS = 4;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+
+
+
+function xdGestureHandler() {}
+
+
+xdGestureHandler.prototype = {
+
+	classDescription: "Mouse Gesture Handler",
+	contractID: "@xuldev.org/firegestures/handler;1",
+	classID: Components.ID("{ca559550-8ab4-41c5-a72f-fd931322cc7e}"),
+	QueryInterface: XPCOMUtils.generateQI([
+		Ci.nsISupports,
+		Ci.nsIObserver,
+		Ci.nsISupportsWeakReference,
+		Ci.nsIDOMEventListener,
+		Ci.nsITimerCallback,
+		Ci.xdIGestureHandler
+	]),
+
+	sourceNode: null,
+
+	_drawArea: null,
+
+	_lastX: null,
+	_lastY: null,
+
+	_directionChain: "",
+
+	_gestureObserver: null,
+
+	_isFx4: false,
+
+	attach: function FGH_attach(aDrawArea, aObserver) {
+		var appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
+		this._isFx4 = parseFloat(appInfo.version) >= 4.0;
+		this._drawArea = aDrawArea;
+		this._gestureObserver = aObserver;
+		this._drawArea.addEventListener("mousedown", this, true);
+		if (this._isFx4) {
+			var root = this._drawArea.ownerDocument.defaultView.document.documentElement;
+			root.addEventListener("mousemove", this, true);
+			root.addEventListener("mouseup", this, true);
+		}
+		else {
+			this._drawArea.addEventListener("mousemove", this, true);
+			this._drawArea.addEventListener("mouseup", this, true);
+		}
+		this._drawArea.addEventListener("contextmenu", this, true);
+		this._drawArea.addEventListener("draggesture", this, true);
+		this._reloadPrefs();
+		var prefBranch2 = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch2);
+		prefBranch2.addObserver(PREFS_DOMAIN, this, true);
+	},
+
+	detach: function FGH_detach() {
+		this._drawArea.removeEventListener("mousedown", this, true);
+		if (this._isFx4) {
+			var root = this._drawArea.ownerDocument.defaultView.document.documentElement;
+			root.removeEventListener("mousemove", this, true);
+			root.removeEventListener("mouseup", this, true);
+		}
+		else {
+			this._drawArea.removeEventListener("mousemove", this, true);
+			this._drawArea.removeEventListener("mouseup", this, true);
+		}
+		this._drawArea.removeEventListener("contextmenu", this, true);
+		this._drawArea.removeEventListener("draggesture", this, true);
+		var prefBranch2 = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch2);
+		prefBranch2.removeObserver(PREFS_DOMAIN, this);
+		this._clearTimeout();
+		this.sourceNode = null;
+		this._drawArea = null;
+		this._gestureObserver = null;
+		this._prefs = null;
+	},
+
+	_reloadPrefs: function FGH__reloadPrefs() {
+		var prefBranch = Cc["@mozilla.org/preferences-service;1"]
+		                 .getService(Ci.nsIPrefService)
+		                 .getBranch(PREFS_DOMAIN);
+		var getPref = function(aName) {
+			try {
+				switch (prefBranch.getPrefType(aName)) {
+					case prefBranch.PREF_STRING:
+						return prefBranch.getCharPref(aName);
+					case prefBranch.PREF_BOOL:
+						return prefBranch.getBoolPref(aName);
+					case prefBranch.PREF_INT:
+						return prefBranch.getIntPref(aName);
+					default:
+						throw null;
+				}
+			}
+			catch(ex) {
+			}
+		};
+		this._triggerButton  = getPref("trigger_button");
+		this._suppressAlt    = getPref("suppress.alt");
+		this._trailEnabled   = getPref("mousetrail");
+		this._trailSize      = getPref("mousetrail.size");
+		this._trailColor     = getPref("mousetrail.color");
+		this._gestureTimeout = getPref("gesture_timeout");
+		this._mouseGestureEnabled    = getPref("mousegesture");
+		this._wheelGestureEnabled    = getPref("wheelgesture");
+		this._rockerGestureEnabled   = getPref("rockergesture");
+		this._keypressGestureEnabled = getPref("keypressgesture");
+		this._drawArea.removeEventListener("DOMMouseScroll", this, true);
+		this._drawArea.removeEventListener("click", this, true);
+		if (this._wheelGestureEnabled)
+			this._drawArea.addEventListener("DOMMouseScroll", this, true);
+		if (this._rockerGestureEnabled)
+			this._drawArea.addEventListener("click", this, true);
+		var tabbrowser = this._drawArea.ownerDocument.getBindingParent(this._drawArea);
+		if (tabbrowser && tabbrowser.localName == "tabbrowser") {
+			tabbrowser.mStrip.removeEventListener("DOMMouseScroll", this._wheelOnTabBar, true);
+			if (getPref("tabwheelgesture"))
+				tabbrowser.mStrip.addEventListener("DOMMouseScroll", this._wheelOnTabBar, true);
+		}
+		if (this._triggerButton == 1) {
+			var prefSvc = Cc["@mozilla.org/preferences-service;1"]
+			              .getService(Ci.nsIPrefBranch2)
+			              .QueryInterface(Ci.nsIPrefService);
+			prefSvc.setBoolPref("middlemouse.contentLoadURL", false);
+		}
+	},
+
+	_state: STATE_READY,
+	_isMouseDownL: false,
+	_isMouseDownM: false,
+	_isMouseDownR: false,
+	_suppressContext: false,
 	_shouldFireContext: false,
-
-	handleEvent: function FGH_handleEvent(event) {
-		switch (event.type) {
-			case "mousedown": 
-				if (event.button == 0) {
-					var targetName = event.target.localName.toUpperCase();
-					if (targetName == "INPUT" || targetName == "TEXTAREA") {
-						break;
-					}
-					targetName = event.originalTarget.localName;
-					if (targetName == "scrollbarbutton" || targetName == "slider" || targetName == "thumb") {
-						break;
-					}
-					this._isMouseDownL = true;
+
+	handleEvent: function FGH_handleEvent(event) {
+		switch (event.type) {
+			case "mousedown": 
+				if (event.button == 0) {
+					var targetName = event.target.localName.toUpperCase();
+					if (targetName == "INPUT" || targetName == "TEXTAREA") {
+						break;
+					}
+					targetName = event.originalTarget.localName;
+					if (targetName == "scrollbarbutton" || targetName == "slider" || targetName == "thumb") {
+						break;
+					}
+					this._isMouseDownL = true;
 					this._isMouseDownM = false;
-					if (this._triggerButton == 0 && !this._isMouseDownM && !this._isMouseDownR && !this._altKey(event)) {
-						this._state = STATE_GESTURE;
-						this._startGesture(event);
-						if (this._mouseGestureEnabled)
-							event.preventDefault();
-					}
-					else if (this._rockerGestureEnabled && this._isMouseDownR) {
-						this._state = STATE_ROCKER;
-						this._invokeExtraGesture(event, "rocker-left");
-					}
-				}
-				else if (event.button == 1) {
-					this._isMouseDownM = true;
-					if (this._triggerButton == 1 && !this._isMouseDownL && !this._isMouseDownR && !this._altKey(event)) {
-						this._state = STATE_GESTURE;
-						this._startGesture(event);
-						event.stopPropagation();
-					}
-				}
-				else if (event.button == 2) {
-					var targetName = event.target.localName.toUpperCase();
-					if (targetName == "OBJECT" || targetName == "EMBED") {
-						break;
-					}
-					this._isMouseDownR = true;
+					if (this._triggerButton == 0 && !this._isMouseDownM && !this._isMouseDownR && !this._altKey(event)) {
+						this._state = STATE_GESTURE;
+						this._startGesture(event);
+						if (this._mouseGestureEnabled)
+							event.preventDefault();
+					}
+					else if (this._rockerGestureEnabled && this._isMouseDownR) {
+						this._state = STATE_ROCKER;
+						this._invokeExtraGesture(event, "rocker-left");
+					}
+				}
+				else if (event.button == 1) {
+					this._isMouseDownM = true;
+					if (this._triggerButton == 1 && !this._isMouseDownL && !this._isMouseDownR && !this._altKey(event)) {
+						this._state = STATE_GESTURE;
+						this._startGesture(event);
+						event.stopPropagation();
+					}
+				}
+				else if (event.button == 2) {
+					var targetName = event.target.localName.toUpperCase();
+					if (targetName == "OBJECT" || targetName == "EMBED") {
+						break;
+					}
+					this._isMouseDownR = true;
 					this._isMouseDownM = false;
 					this._suppressContext = false;
-					this._enableContextMenu(true);
-					if (this._triggerButton == 2 && !this._isMouseDownL && !this._isMouseDownM && !this._altKey(event)) {
-						this._state = STATE_GESTURE;
-						this._startGesture(event);
-					}
-					else if (this._rockerGestureEnabled && this._isMouseDownL) {
-						this._state = STATE_ROCKER;
-						this._invokeExtraGesture(event, "rocker-right");
-					}
-				}
-				break;
-			case "mousemove": 
-				if (this._state == STATE_GESTURE || this._state == STATE_KEYPRESS) {
-					if (this._mouseGestureEnabled) {
-						if (this._keypressGestureEnabled && (event.ctrlKey || event.shiftKey)) {
-							var type = this._state == STATE_GESTURE ? "keypress-start" : "keypress-progress";
-							this._state = STATE_KEYPRESS;
-							this._invokeExtraGesture(event, type);
-						}
-						this._progressGesture(event);
-					}
-				}
-				else if (this._state == STATE_WHEEL || this._state == STATE_ROCKER) {
-					this._lastX = event.screenX;
-					this._lastY = event.screenY;
-					if (Math.abs(this._lastX - this._lastExtraX) > 10 || 
-					    Math.abs(this._lastY - this._lastExtraY) > 10) {
-						this._stopGesture();
-					}
-				}
-				break;
-			case "mouseup": 
-				if (event.button == 0)
-					this._isMouseDownL = false;
-				else if (event.button == 1)
-					this._isMouseDownM = false;
-				else if (event.button == 2)
-					this._isMouseDownR = false;
-				if (!this._isMouseDownL && !this._isMouseDownM && !this._isMouseDownR) {
-					if (this._state == STATE_KEYPRESS) {
-						this._state = STATE_READY;
-						if (event.ctrlKey)
-							this._invokeExtraGesture(event, "keypress-ctrl");
-						else if (event.shiftKey)
-							this._invokeExtraGesture(event, "keypress-shift");
-						this._invokeExtraGesture(event, "keypress-stop");
-					}
-					this._stopGesture(event);
-					if (this._shouldFireContext) {
-						this._shouldFireContext = false;
-						this._displayContextMenu(event);
-					}
-				}
-				break;
-			case "contextmenu": 
-				if (!this._isMouseDownL && this._isMouseDownR) {
-					this._suppressContext = true;
-					this._shouldFireContext = true;
-				}
-				if (event.button == 0) {
-					this._enableContextMenu(true);
-				}
-				if (this._suppressContext) {
-					this._suppressContext = false;
-					event.preventDefault();
-					event.stopPropagation();
-					this._enableContextMenu(false);
-				}
-				break;
-			case "DOMMouseScroll": 
-				if (this._state == STATE_GESTURE || this._state == STATE_WHEEL) {
-					this._state = STATE_WHEEL;
-					this._invokeExtraGesture(event, event.detail < 0 ? "wheel-up" : "wheel-down");
-					event.preventDefault();
-					event.stopPropagation();
-				}
-				break;
-			case "click": 
-				if (this._state == STATE_ROCKER) {
-					event.preventDefault();
-					event.stopPropagation();
-				}
-				break;
-			case "draggesture": 
-				if (this._state != STATE_ROCKER)
-					this._isMouseDownL = false;
-				break;
-		}
-	},
-
-	_altKey: function(event) {
-		return this._suppressAlt ? event.altKey : false;
-	},
-
-	_displayContextMenu: function FGH__displayContextMenu(event) {
-		with (this._drawArea.ownerDocument.defaultView) {
-			if (!nsContextMenu.prototype._setTargetInternal) {
-				nsContextMenu.prototype._setTargetInternal = nsContextMenu.prototype.setTarget;
-				nsContextMenu.prototype.setTarget = function(aNode, aRangeParent, aRangeOffset) {
-					this._setTargetInternal(aNode, aRangeParent, this._rangeOffset);
-				};
-			}
-			nsContextMenu.prototype._rangeOffset = event.rangeOffset;
-		}
-		this._enableContextMenu(true);
-		var evt = event.originalTarget.ownerDocument.createEvent("MouseEvents");
-		evt.initMouseEvent(
-			"contextmenu", true, true, event.originalTarget.ownerDocument.defaultView, 0,
-			event.screenX, event.screenY, event.clientX, event.clientY,
-			false, false, false, false, 2, null
-		);
-		event.originalTarget.dispatchEvent(evt);
-	},
-
-	_enableContextMenu: function FGH__enableContextMenu(aEnable) {
-		var elt = this._drawArea.ownerDocument.getElementById("contentAreaContextMenu");
-		if (!elt)
-			elt = this._drawArea.ownerDocument.getElementById("viewSourceContextMenu");
-		if (!elt)
-			return;
-		if (aEnable)
-			elt.removeAttribute("hidden");
-		else
-			elt.setAttribute("hidden", "true");
-	},
-
-	_wheelOnTabBar: function FGH__wheelOnTabBar(event) {
-		var tabbar = null;
-		if (event.target.localName == "tab")
-			tabbar = event.target.parentNode;
-		else if (event.target.localName == "tabs" && event.originalTarget.localName != "menuitem")
-			tabbar = event.target;
-		else
-			return;
-		event.preventDefault();
-		event.stopPropagation();
-		tabbar.advanceSelectedTab(event.detail < 0 ? -1 : 1, true);
-	},
-
-	_startGesture: function FGH__startGesture(event) {
-		this.sourceNode = event.target;
-		this._lastX = event.screenX;
-		this._lastY = event.screenY;
-		this._directionChain = "";
-		this._shouldFireContext = false;
-		if (this._trailEnabled)
-			this.createTrail(event);
-	},
-
-	_progressGesture: function FGH__progressGesture(event) {
-		var x = event.screenX;
-		var y = event.screenY;
-		var dx = Math.abs(x - this._lastX);
-		var dy = Math.abs(y - this._lastY);
-		if (dx < 10 && dy < 10)
-			return;
-		var direction;
-		if (dx > dy)
-			direction = x < this._lastX ? "L" : "R";
-		else
-			direction = y < this._lastY ? "U" : "D";
-		if (this._trailEnabled)
-			this.drawTrail(this._lastX, this._lastY, x, y);
-		this._lastX = x;
-		this._lastY = y;
-		if (this._state == STATE_KEYPRESS)
-			return;
-		var lastDirection = this._directionChain.charAt(this._directionChain.length - 1);
-		if (direction != lastDirection) {
-			this._directionChain += direction;
-			this._gestureObserver.onDirectionChanged(event, this._directionChain);
-		}
-		if (this._gestureTimeout > 0)
-			this._setTimeout(this._gestureTimeout);
-	},
-
-	_invokeExtraGesture: function FGH__invokeExtraGesture(event, aGestureType) {
-		if (this._state == STATE_WHEEL || this._state == STATE_ROCKER) {
-			this._lastExtraX = event.screenX;
-			this._lastExtraY = event.screenY;
-		}
-		if (this._state != STATE_KEYPRESS && this._trailEnabled)
-			this.eraseTrail();
-		if (!this.sourceNode)
-			this.sourceNode = event.target;
-		this._gestureObserver.onExtraGesture(event, aGestureType);
-		this._suppressContext = true;
-		this._shouldFireContext = false;
-		this._directionChain = "";
-		if (this._state == STATE_WHEEL || this._state == STATE_ROCKER) {
-			if (this._gestureTimeout > 0)
-				this._setTimeout(this._gestureTimeout);
-		}
-	},
-
-	_stopGesture: function FGH__stopGesture(event) {
-		this._state = STATE_READY;
-		this._isMouseDownL = false;
-		this._isMouseDownM = false;
-		this._isMouseDownR = false;
-		if (this._trailEnabled)
-			this.eraseTrail();
-		if (this._directionChain) {
-			this._gestureObserver.onMouseGesture(event, this._directionChain);
-			this._suppressContext = true;
-			this._shouldFireContext = false;
-		}
-		this.sourceNode = null;
-		this._directionChain = "";
-		this._clearTimeout();
-	},
-
-
-
-	observe: function FGH_observe(aSubject, aTopic, aData) {
-		if (aTopic == "nsPref:changed")
-			this._reloadPrefs();
-	},
-
-
-
-	_gestureTimer: null,
-
-	_setTimeout: function FGH__setTimeout(aMsec) {
-		this._clearTimeout();
-		this._gestureTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-		this._gestureTimer.initWithCallback(this, aMsec, Ci.nsITimer.TYPE_ONE_SHOT);
-	},
-
-	_clearTimeout: function FGH__clearTimeout() {
-		if (this._gestureTimer) {
-			this._gestureTimer.cancel();
-			this._gestureTimer = null;
-		}
-	},
-
-	notify: function(aTimer) {
-		this._suppressContext = true;
-		this._shouldFireContext = false;
-		this._directionChain = "";
-		this._stopGesture();
-		this._gestureObserver.onExtraGesture(null, "gesture-timeout");
-	},
-
-	openPopupAtPointer: function FGH_openPopupAtPointer(aPopup) {
-		aPopup.openPopupAtScreen(this._lastX, this._lastY, false);
-		this._directionChain = "";
-		this._stopGesture();
-	},
-
-
-
-	_trailDot: null,
-	_trailArea: null,
-	_trailLastDot: null,
-	_trailOffsetX: 0,
-	_trailOffsetY: 0,
-	_trailZoom: 1,
-
-	createTrail: function FGH_createTrail(event) {
-		var doc;
-		if (event.view.top.document instanceof Ci.nsIDOMHTMLDocument)
-			doc = event.view.top.document;
-		else if (event.view.document instanceof Ci.nsIDOMHTMLDocument)
-			doc = event.view.document;
-		else
-			return;
-		var insertionNode = doc.documentElement ? doc.documentElement : doc;
-		if (doc.getBoxObjectFor) {
-			var box = doc.getBoxObjectFor(insertionNode);
-			this._trailOffsetX = box.screenX;
-			this._trailOffsetY = box.screenY;
-			this._trailZoom = 1;
-			var tabbrowser = this._drawArea.ownerDocument.defaultView.gBrowser;
-			if (tabbrowser && (tabbrowser.mCurrentBrowser || tabbrowser).markupDocumentViewer.fullZoom != 1) {
-				var dot = doc.createElementNS(HTML_NS, "xdTrailDot");
+					this._enableContextMenu(true);
+					if (this._triggerButton == 2 && !this._isMouseDownL && !this._isMouseDownM && !this._altKey(event)) {
+						this._state = STATE_GESTURE;
+						this._startGesture(event);
+					}
+					else if (this._rockerGestureEnabled && this._isMouseDownL) {
+						this._state = STATE_ROCKER;
+						this._invokeExtraGesture(event, "rocker-right");
+					}
+				}
+				break;
+			case "mousemove": 
+				if (this._state == STATE_GESTURE || this._state == STATE_KEYPRESS) {
+					if (this._mouseGestureEnabled) {
+						if (this._keypressGestureEnabled && (event.ctrlKey || event.shiftKey)) {
+							var type = this._state == STATE_GESTURE ? "keypress-start" : "keypress-progress";
+							this._state = STATE_KEYPRESS;
+							this._invokeExtraGesture(event, type);
+						}
+						this._progressGesture(event);
+					}
+				}
+				else if (this._state == STATE_WHEEL || this._state == STATE_ROCKER) {
+					this._lastX = event.screenX;
+					this._lastY = event.screenY;
+					if (Math.abs(this._lastX - this._lastExtraX) > 10 || 
+					    Math.abs(this._lastY - this._lastExtraY) > 10) {
+						this._stopGesture();
+					}
+				}
+				break;
+			case "mouseup": 
+				if (event.button == 0)
+					this._isMouseDownL = false;
+				else if (event.button == 1)
+					this._isMouseDownM = false;
+				else if (event.button == 2)
+					this._isMouseDownR = false;
+				if (!this._isMouseDownL && !this._isMouseDownM && !this._isMouseDownR) {
+					if (this._state == STATE_KEYPRESS) {
+						this._state = STATE_READY;
+						if (event.ctrlKey)
+							this._invokeExtraGesture(event, "keypress-ctrl");
+						else if (event.shiftKey)
+							this._invokeExtraGesture(event, "keypress-shift");
+						this._invokeExtraGesture(event, "keypress-stop");
+					}
+					this._stopGesture(event);
+					if (this._shouldFireContext) {
+						this._shouldFireContext = false;
+						this._displayContextMenu(event);
+					}
+				}
+				break;
+			case "contextmenu": 
+				if (!this._isMouseDownL && this._isMouseDownR) {
+					this._suppressContext = true;
+					this._shouldFireContext = true;
+				}
+				if (event.button == 0) {
+					this._enableContextMenu(true);
+				}
+				if (this._suppressContext) {
+					this._suppressContext = false;
+					event.preventDefault();
+					event.stopPropagation();
+					this._enableContextMenu(false);
+				}
+				break;
+			case "DOMMouseScroll": 
+				if (this._state == STATE_GESTURE || this._state == STATE_WHEEL) {
+					this._state = STATE_WHEEL;
+					this._invokeExtraGesture(event, event.detail < 0 ? "wheel-up" : "wheel-down");
+					event.preventDefault();
+					event.stopPropagation();
+				}
+				break;
+			case "click": 
+				if (this._state == STATE_ROCKER) {
+					event.preventDefault();
+					event.stopPropagation();
+				}
+				break;
+			case "draggesture": 
+				if (this._state != STATE_ROCKER)
+					this._isMouseDownL = false;
+				break;
+		}
+	},
+
+	_altKey: function(event) {
+		return this._suppressAlt ? event.altKey : false;
+	},
+
+	_displayContextMenu: function FGH__displayContextMenu(event) {
+		with (this._drawArea.ownerDocument.defaultView) {
+			if (!nsContextMenu.prototype._setTargetInternal) {
+				nsContextMenu.prototype._setTargetInternal = nsContextMenu.prototype.setTarget;
+				nsContextMenu.prototype.setTarget = function(aNode, aRangeParent, aRangeOffset) {
+					this._setTargetInternal(aNode, aRangeParent, this._rangeOffset);
+				};
+			}
+			nsContextMenu.prototype._rangeOffset = event.rangeOffset;
+		}
+		this._enableContextMenu(true);
+		var evt = event.originalTarget.ownerDocument.createEvent("MouseEvents");
+		evt.initMouseEvent(
+			"contextmenu", true, true, event.originalTarget.ownerDocument.defaultView, 0,
+			event.screenX, event.screenY, event.clientX, event.clientY,
+			false, false, false, false, 2, null
+		);
+		event.originalTarget.dispatchEvent(evt);
+	},
+
+	_enableContextMenu: function FGH__enableContextMenu(aEnable) {
+		var elt = this._drawArea.ownerDocument.getElementById("contentAreaContextMenu");
+		if (!elt)
+			elt = this._drawArea.ownerDocument.getElementById("viewSourceContextMenu");
+		if (!elt)
+			return;
+		if (aEnable)
+			elt.removeAttribute("hidden");
+		else
+			elt.setAttribute("hidden", "true");
+	},
+
+	_wheelOnTabBar: function FGH__wheelOnTabBar(event) {
+		var tabbar = null;
+		if (event.target.localName == "tab")
+			tabbar = event.target.parentNode;
+		else if (event.target.localName == "tabs" && event.originalTarget.localName != "menuitem")
+			tabbar = event.target;
+		else
+			return;
+		event.preventDefault();
+		event.stopPropagation();
+		tabbar.advanceSelectedTab(event.detail < 0 ? -1 : 1, true);
+	},
+
+	_startGesture: function FGH__startGesture(event) {
+		this.sourceNode = event.target;
+		this._lastX = event.screenX;
+		this._lastY = event.screenY;
+		this._directionChain = "";
+		this._shouldFireContext = false;
+		if (this._trailEnabled)
+			this.createTrail(event);
+	},
+
+	_progressGesture: function FGH__progressGesture(event) {
+		var x = event.screenX;
+		var y = event.screenY;
+		var dx = Math.abs(x - this._lastX);
+		var dy = Math.abs(y - this._lastY);
+		if (dx < 10 && dy < 10)
+			return;
+		var direction;
+		if (dx > dy)
+			direction = x < this._lastX ? "L" : "R";
+		else
+			direction = y < this._lastY ? "U" : "D";
+		if (this._trailEnabled)
+			this.drawTrail(this._lastX, this._lastY, x, y);
+		this._lastX = x;
+		this._lastY = y;
+		if (this._state == STATE_KEYPRESS)
+			return;
+		var lastDirection = this._directionChain.charAt(this._directionChain.length - 1);
+		if (direction != lastDirection) {
+			this._directionChain += direction;
+			this._gestureObserver.onDirectionChanged(event, this._directionChain);
+		}
+		if (this._gestureTimeout > 0)
+			this._setTimeout(this._gestureTimeout);
+	},
+
+	_invokeExtraGesture: function FGH__invokeExtraGesture(event, aGestureType) {
+		if (this._state == STATE_WHEEL || this._state == STATE_ROCKER) {
+			this._lastExtraX = event.screenX;
+			this._lastExtraY = event.screenY;
+		}
+		if (this._state != STATE_KEYPRESS && this._trailEnabled)
+			this.eraseTrail();
+		if (!this.sourceNode)
+			this.sourceNode = event.target;
+		this._gestureObserver.onExtraGesture(event, aGestureType);
+		this._suppressContext = true;
+		this._shouldFireContext = false;
+		this._directionChain = "";
+		if (this._state == STATE_WHEEL || this._state == STATE_ROCKER) {
+			if (this._gestureTimeout > 0)
+				this._setTimeout(this._gestureTimeout);
+		}
+	},
+
+	_stopGesture: function FGH__stopGesture(event) {
+		this._state = STATE_READY;
+		this._isMouseDownL = false;
+		this._isMouseDownM = false;
+		this._isMouseDownR = false;
+		if (this._trailEnabled)
+			this.eraseTrail();
+		if (this._directionChain) {
+			this._gestureObserver.onMouseGesture(event, this._directionChain);
+			this._suppressContext = true;
+			this._shouldFireContext = false;
+		}
+		this.sourceNode = null;
+		this._directionChain = "";
+		this._clearTimeout();
+	},
+
+
+
+	observe: function FGH_observe(aSubject, aTopic, aData) {
+		if (aTopic == "nsPref:changed")
+			this._reloadPrefs();
+	},
+
+
+
+	_gestureTimer: null,
+
+	_setTimeout: function FGH__setTimeout(aMsec) {
+		this._clearTimeout();
+		this._gestureTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+		this._gestureTimer.initWithCallback(this, aMsec, Ci.nsITimer.TYPE_ONE_SHOT);
+	},
+
+	_clearTimeout: function FGH__clearTimeout() {
+		if (this._gestureTimer) {
+			this._gestureTimer.cancel();
+			this._gestureTimer = null;
+		}
+	},
+
+	notify: function(aTimer) {
+		this._suppressContext = true;
+		this._shouldFireContext = false;
+		this._directionChain = "";
+		this._stopGesture();
+		this._gestureObserver.onExtraGesture(null, "gesture-timeout");
+	},
+
+	openPopupAtPointer: function FGH_openPopupAtPointer(aPopup) {
+		aPopup.openPopupAtScreen(this._lastX, this._lastY, false);
+		this._directionChain = "";
+		this._stopGesture();
+	},
+
+
+
+	_trailDot: null,
+	_trailArea: null,
+	_trailLastDot: null,
+	_trailOffsetX: 0,
+	_trailOffsetY: 0,
+	_trailZoom: 1,
+
+	createTrail: function FGH_createTrail(event) {
+		var doc;
+		if (event.view.top.document instanceof Ci.nsIDOMHTMLDocument)
+			doc = event.view.top.document;
+		else if (event.view.document instanceof Ci.nsIDOMHTMLDocument)
+			doc = event.view.document;
+		else
+			return;
+		var insertionNode = doc.documentElement ? doc.documentElement : doc;
+		if (doc.getBoxObjectFor) {
+			var box = doc.getBoxObjectFor(insertionNode);
+			this._trailOffsetX = box.screenX;
+			this._trailOffsetY = box.screenY;
+			this._trailZoom = 1;
+			var tabbrowser = this._drawArea.ownerDocument.defaultView.gBrowser;
+			if (tabbrowser && (tabbrowser.mCurrentBrowser || tabbrowser).markupDocumentViewer.fullZoom != 1) {
+				var dot = doc.createElementNS(HTML_NS, "xdTrailDot");
 				dot.style.top = "1048576px";
-				dot.style.position = "absolute";
-				insertionNode.appendChild(dot);
-				this._trailZoom = (doc.getBoxObjectFor(dot).screenY - this._trailOffsetY) / dot.offsetTop;
-				insertionNode.removeChild(dot);
-			}
-		}
-		else {
-			var win = doc.defaultView;
-			this._trailZoom = win.QueryInterface(Ci.nsIInterfaceRequestor).
-			                  getInterface(Ci.nsIDOMWindowUtils).screenPixelsPerCSSPixel;
-			this._trailOffsetX = (win.mozInnerScreenX - win.scrollX) * this._trailZoom;
-			this._trailOffsetY = (win.mozInnerScreenY - win.scrollY) * this._trailZoom;
-		}
-		this._trailArea = doc.createElementNS(HTML_NS, "xdTrailArea");
-		insertionNode.appendChild(this._trailArea);
-		this._trailDot = doc.createElementNS(HTML_NS, "xdTrailDot");
-		this._trailDot.style.width = this._trailSize + "px";
-		this._trailDot.style.height = this._trailSize + "px";
-		this._trailDot.style.background = this._trailColor;
-		this._trailDot.style.border = "0px";
-		this._trailDot.style.position = "absolute";
-		this._trailDot.style.zIndex = 2147483647;
-	},
-
-	drawTrail: function FGH_drawTrail(x1, y1, x2, y2) {
-		if (!this._trailArea)
-			return;
-		var xMove = x2 - x1;
-		var yMove = y2 - y1;
-		var xDecrement = xMove < 0 ? 1 : -1;
-		var yDecrement = yMove < 0 ? 1 : -1;
-		x2 -= this._trailOffsetX;
-		y2 -= this._trailOffsetY;
-		if (Math.abs(xMove) >= Math.abs(yMove))
-			for (var i = xMove; i != 0; i += xDecrement)
-				this._strokeDot(x2 - i, y2 - Math.round(yMove * i / xMove));
-		else
-			for (var i = yMove; i != 0; i += yDecrement)
-				this._strokeDot(x2 - Math.round(xMove * i / yMove), y2 - i);
-	},
-
-	eraseTrail: function FGH_eraseTrail() {
-		if (this._trailArea && this._trailArea.parentNode) {
-			while (this._trailArea.lastChild)
-				this._trailArea.removeChild(this._trailArea.lastChild);
-			this._trailArea.parentNode.removeChild(this._trailArea);
-		}
-		this._trailDot = null;
-		this._trailArea = null;
-		this._trailLastDot = null;
-	},
-
-	_strokeDot: function FGH__strokeDot(x, y) {
-		if (this._trailArea.y == y && this._trailArea.h == this._trailSize) {
-			var newX = Math.min(this._trailArea.x, x);
-			var newW = Math.max(this._trailArea.x + this._trailArea.w, x + this._trailSize) - newX;
-			this._trailArea.x = newX;
-			this._trailArea.w = newW;
-			this._trailLastDot.style.left  = newX.toString() + "px";
-			this._trailLastDot.style.width = newW.toString() + "px";
-			return;
-		}
-		else if (this._trailArea.x == x && this._trailArea.w == this._trailSize) {
-			var newY = Math.min(this._trailArea.y, y);
-			var newH = Math.max(this._trailArea.y + this._trailArea.h, y + this._trailSize) - newY;
-			this._trailArea.y = newY;
-			this._trailArea.h = newH;
-			this._trailLastDot.style.top    = newY.toString() + "px";
-			this._trailLastDot.style.height = newH.toString() + "px";
-			return;
-		}
-		if (this._trailZoom != 1) {
-			x = Math.floor(x / this._trailZoom);
-			y = Math.floor(y / this._trailZoom);
-		}
-		var dot = this._trailDot.cloneNode(true);
-		dot.style.left = x + "px";
-		dot.style.top = y + "px";
-		this._trailArea.x = x;
-		this._trailArea.y = y;
-		this._trailArea.w = this._trailSize;
-		this._trailArea.h = this._trailSize;
-		this._trailArea.appendChild(dot);
-		this._trailLastDot = dot;
-	},
-
-};
-
-
-
-if (XPCOMUtils.generateNSGetFactory)
-	var NSGetFactory = XPCOMUtils.generateNSGetFactory([xdGestureHandler]);
-else
-	var NSGetModule = XPCOMUtils.generateNSGetModule([xdGestureHandler]);
-
-
+				dot.style.position = "absolute";
+				insertionNode.appendChild(dot);
+				this._trailZoom = (doc.getBoxObjectFor(dot).screenY - this._trailOffsetY) / dot.offsetTop;
+				insertionNode.removeChild(dot);
+			}
+		}
+		else {
+			var win = doc.defaultView;
+			this._trailZoom = win.QueryInterface(Ci.nsIInterfaceRequestor).
+			                  getInterface(Ci.nsIDOMWindowUtils).screenPixelsPerCSSPixel;
+			this._trailOffsetX = (win.mozInnerScreenX - win.scrollX) * this._trailZoom;
+			this._trailOffsetY = (win.mozInnerScreenY - win.scrollY) * this._trailZoom;
+		}
+		this._trailArea = doc.createElementNS(HTML_NS, "xdTrailArea");
+		insertionNode.appendChild(this._trailArea);
+		this._trailDot = doc.createElementNS(HTML_NS, "xdTrailDot");
+		this._trailDot.style.width = this._trailSize + "px";
+		this._trailDot.style.height = this._trailSize + "px";
+		this._trailDot.style.background = this._trailColor;
+		this._trailDot.style.border = "0px";
+		this._trailDot.style.position = "absolute";
+		this._trailDot.style.zIndex = 2147483647;
+	},
+
+	drawTrail: function FGH_drawTrail(x1, y1, x2, y2) {
+		if (!this._trailArea)
+			return;
+		var xMove = x2 - x1;
+		var yMove = y2 - y1;
+		var xDecrement = xMove < 0 ? 1 : -1;
+		var yDecrement = yMove < 0 ? 1 : -1;
+		x2 -= this._trailOffsetX;
+		y2 -= this._trailOffsetY;
+		if (Math.abs(xMove) >= Math.abs(yMove))
+			for (var i = xMove; i != 0; i += xDecrement)
+				this._strokeDot(x2 - i, y2 - Math.round(yMove * i / xMove));
+		else
+			for (var i = yMove; i != 0; i += yDecrement)
+				this._strokeDot(x2 - Math.round(xMove * i / yMove), y2 - i);
+	},
+
+	eraseTrail: function FGH_eraseTrail() {
+		if (this._trailArea && this._trailArea.parentNode) {
+			while (this._trailArea.lastChild)
+				this._trailArea.removeChild(this._trailArea.lastChild);
+			this._trailArea.parentNode.removeChild(this._trailArea);
+		}
+		this._trailDot = null;
+		this._trailArea = null;
+		this._trailLastDot = null;
+	},
+
+	_strokeDot: function FGH__strokeDot(x, y) {
+		if (this._trailArea.y == y && this._trailArea.h == this._trailSize) {
+			var newX = Math.min(this._trailArea.x, x);
+			var newW = Math.max(this._trailArea.x + this._trailArea.w, x + this._trailSize) - newX;
+			this._trailArea.x = newX;
+			this._trailArea.w = newW;
+			this._trailLastDot.style.left  = newX.toString() + "px";
+			this._trailLastDot.style.width = newW.toString() + "px";
+			return;
+		}
+		else if (this._trailArea.x == x && this._trailArea.w == this._trailSize) {
+			var newY = Math.min(this._trailArea.y, y);
+			var newH = Math.max(this._trailArea.y + this._trailArea.h, y + this._trailSize) - newY;
+			this._trailArea.y = newY;
+			this._trailArea.h = newH;
+			this._trailLastDot.style.top    = newY.toString() + "px";
+			this._trailLastDot.style.height = newH.toString() + "px";
+			return;
+		}
+		if (this._trailZoom != 1) {
+			x = Math.floor(x / this._trailZoom);
+			y = Math.floor(y / this._trailZoom);
+		}
+		var dot = this._trailDot.cloneNode(true);
+		dot.style.left = x + "px";
+		dot.style.top = y + "px";
+		this._trailArea.x = x;
+		this._trailArea.y = y;
+		this._trailArea.w = this._trailSize;
+		this._trailArea.h = this._trailSize;
+		this._trailArea.appendChild(dot);
+		this._trailLastDot = dot;
+	},
+
+};
+
+
+
+if (XPCOMUtils.generateNSGetFactory)
+	var NSGetFactory = XPCOMUtils.generateNSGetFactory([xdGestureHandler]);
+else
+	var NSGetModule = XPCOMUtils.generateNSGetModule([xdGestureHandler]);
+
+
diff --git a/install.rdf b/install.rdf
index d2a83ea..4dab41f 100644
--- a/install.rdf
+++ b/install.rdf
@@ -7,7 +7,7 @@
 		<em:id>firegestures at xuldev.org</em:id>
 		<em:type>2</em:type>
 		<em:name>FireGestures</em:name>
-		<em:version>1.6.7</em:version>
+		<em:version>1.6.9</em:version>
 		<em:description>Executes various commands with mouse gestures.</em:description>
 		<em:creator>Gomita</em:creator>
 		<em:localized>
@@ -309,7 +309,7 @@
 			<Description>
 				<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
 				<em:minVersion>4.0</em:minVersion>
-				<em:maxVersion>10.*</em:maxVersion>
+				<em:maxVersion>12.*</em:maxVersion>
 			</Description>
 		</em:targetApplication>
 	</Description>

-- 
firegesture s extension



More information about the Pkg-mozext-commits mailing list