/*******************************************************************************
 * Copyright (c) 2003, 2010 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.browser;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;

import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.mozilla.*;
import org.eclipse.swt.internal.mozilla.init.*;
import org.eclipse.swt.layout.*;

class Mozilla extends WebBrowser {
	int /*long*/ embedHandle;
	nsIWebBrowser webBrowser;
	Object webBrowserObject;
	MozillaDelegate delegate;

	/* Interfaces for this Mozilla embedding notification */
	XPCOMObject supports;
	XPCOMObject weakReference;
	XPCOMObject webProgressListener;
	XPCOMObject	webBrowserChrome;
	XPCOMObject webBrowserChromeFocus;
	XPCOMObject embeddingSiteWindow;
	XPCOMObject interfaceRequestor;
	XPCOMObject supportsWeakReference;
	XPCOMObject contextMenuListener;	
	XPCOMObject uriContentListener;
	XPCOMObject tooltipListener;
	XPCOMObject domEventListener;
	XPCOMObject badCertListener;

	int chromeFlags = nsIWebBrowserChrome.CHROME_DEFAULT;
	int registerFunctionsOnState = 0;
	int refCount, lastKeyCode, lastCharCode, authCount;
	int /*long*/ request;
	Point location, size;
	boolean visible, isChild, ignoreDispose, isRetrievingBadCert, isViewingErrorPage, ignoreAllMessages, untrustedText;
	boolean updateLastNavigateUrl;
	Shell tip = null;
	Listener listener;
	Vector unhookedDOMWindows = new Vector ();
	String lastNavigateURL;
	byte[] htmlBytes;

	static nsIAppShell AppShell;
	static AppFileLocProvider LocationProvider;
	static WindowCreator2 WindowCreator;
	static int BrowserCount, NextJSFunctionIndex = 1;
	static Hashtable AllFunctions = new Hashtable ();
	static Listener DisplayListener;
	static boolean Initialized, IsPre_1_8, IsPre_1_9, PerformedVersionCheck, XPCOMWasGlued, XPCOMInitWasGlued;
	static String oldProxyHostFTP, oldProxyHostHTTP, oldProxyHostSSL;
	static int oldProxyPortFTP = -1, oldProxyPortHTTP = -1, oldProxyPortSSL = -1, oldProxyType = -1;
	static byte[] pathBytes_JSEvaluateUCScriptForPrincipals;
	static byte[] pathBytes_NSFree;

	/* XULRunner detect constants */
	static final String GRERANGE_LOWER = "1.8.1.2"; //$NON-NLS-1$
	static final String GRERANGE_LOWER_FALLBACK = "1.8"; //$NON-NLS-1$
	static final boolean LowerRangeInclusive = true;
	static final String GRERANGE_UPPER = "1.9.*"; //$NON-NLS-1$
	static final boolean UpperRangeInclusive = true;

	static final int MAX_PORT = 65535;
	static final String DEFAULTVALUE_STRING = "default"; //$NON-NLS-1$
	static final String SEPARATOR_OS = System.getProperty ("file.separator"); //$NON-NLS-1$
	static final String ABOUT_BLANK = "about:blank"; //$NON-NLS-1$
	static final String DISPOSE_LISTENER_HOOKED = "org.eclipse.swt.browser.Mozilla.disposeListenerHooked"; //$NON-NLS-1$
	static final String HEADER_CONTENTTYPE = "Content-Type"; //$NON-NLS-1
	static final String MIMETYPE_FORMURLENCODED = "application/x-www-form-urlencoded"; //$NON-NLS-1$
	static final String PREFIX_JAVASCRIPT = "javascript:"; //$NON-NLS-1$
	static final String PREFERENCE_CHARSET = "intl.charset.default"; //$NON-NLS-1$
	static final String PREFERENCE_DISABLEOPENDURINGLOAD = "dom.disable_open_during_load"; //$NON-NLS-1$
	static final String PREFERENCE_DISABLEOPENWINDOWSTATUSHIDE = "dom.disable_window_open_feature.status"; //$NON-NLS-1$
	static final String PREFERENCE_DISABLEWINDOWSTATUSCHANGE = "dom.disable_window_status_change"; //$NON-NLS-1$
	static final String PREFERENCE_JAVASCRIPTENABLED = "javascript.enabled"; //$NON-NLS-1$
	static final String PREFERENCE_LANGUAGES = "intl.accept_languages"; //$NON-NLS-1$
	static final String PREFERENCE_PROXYHOST_FTP = "network.proxy.ftp"; //$NON-NLS-1$
	static final String PREFERENCE_PROXYPORT_FTP = "network.proxy.ftp_port"; //$NON-NLS-1$
	static final String PREFERENCE_PROXYHOST_HTTP = "network.proxy.http"; //$NON-NLS-1$
	static final String PREFERENCE_PROXYPORT_HTTP = "network.proxy.http_port"; //$NON-NLS-1$
	static final String PREFERENCE_PROXYHOST_SSL = "network.proxy.ssl"; //$NON-NLS-1$
	static final String PREFERENCE_PROXYPORT_SSL = "network.proxy.ssl_port"; //$NON-NLS-1$
	static final String PREFERENCE_PROXYTYPE = "network.proxy.type"; //$NON-NLS-1$
	static final String PROFILE_AFTER_CHANGE = "profile-after-change"; //$NON-NLS-1$
	static final String PROFILE_BEFORE_CHANGE = "profile-before-change"; //$NON-NLS-1$
	static final String PROFILE_DIR = SEPARATOR_OS + "eclipse" + SEPARATOR_OS; //$NON-NLS-1$
	static final String PROFILE_DO_CHANGE = "profile-do-change"; //$NON-NLS-1$
	static final String PROPERTY_PROXYPORT = "network.proxy_port"; //$NON-NLS-1$
	static final String PROPERTY_PROXYHOST = "network.proxy_host"; //$NON-NLS-1$
	static final String SEPARATOR_LOCALE = "-"; //$NON-NLS-1$
	static final String SHUTDOWN_PERSIST = "shutdown-persist"; //$NON-NLS-1$
	static final String STARTUP = "startup"; //$NON-NLS-1$
	static final String TOKENIZER_LOCALE = ","; //$NON-NLS-1$
	static final String URI_FILEROOT = "file:///"; //$NON-NLS-1$
	static final String XULRUNNER_PATH = "org.eclipse.swt.browser.XULRunnerPath"; //$NON-NLS-1$

	// TEMPORARY CODE
	static final String GRE_INITIALIZED = "org.eclipse.swt.browser.XULRunnerInitialized"; //$NON-NLS-1$

	static {
		DisplayListener = new Listener () {
			public void handleEvent (Event event) {
				if (BrowserCount > 0) return; /* another display is still active */

				int /*long*/[] result = new int /*long*/[1];
				int rc = XPCOM.NS_GetServiceManager (result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
				result[0] = 0;		
				byte[] buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_OBSERVER_CONTRACTID, true);
				rc = serviceManager.GetServiceByContractID (buffer, nsIObserverService.NS_IOBSERVERSERVICE_IID, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsIObserverService observerService = new nsIObserverService (result[0]);
				result[0] = 0;
				buffer = MozillaDelegate.wcsToMbcs (null, PROFILE_BEFORE_CHANGE, true);
				int length = SHUTDOWN_PERSIST.length ();
				char[] chars = new char [length + 1];
				SHUTDOWN_PERSIST.getChars (0, length, chars, 0);
				rc = observerService.NotifyObservers (0, buffer, chars);
				if (rc != XPCOM.NS_OK) error (rc);
				observerService.Release ();

				if (LocationProvider != null) {
					String prefsLocation = LocationProvider.profilePath + AppFileLocProvider.PREFERENCES_FILE;
					nsEmbedString pathString = new nsEmbedString (prefsLocation);
					rc = XPCOM.NS_NewLocalFile (pathString.getAddress (), 1, result);
					if (rc != XPCOM.NS_OK) Mozilla.error (rc);
					if (result[0] == 0) Mozilla.error (XPCOM.NS_ERROR_NULL_POINTER);
					pathString.dispose ();

					nsILocalFile localFile = new nsILocalFile (result [0]);
					result[0] = 0;
					rc = localFile.QueryInterface (nsIFile.NS_IFILE_IID, result); 
					if (rc != XPCOM.NS_OK) Mozilla.error (rc);
					if (result[0] == 0) Mozilla.error (XPCOM.NS_ERROR_NO_INTERFACE);
					localFile.Release ();

					nsIFile prefFile = new nsIFile (result[0]);
					result[0] = 0;

					buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFSERVICE_CONTRACTID, true);
					rc = serviceManager.GetServiceByContractID (buffer, nsIPrefService.NS_IPREFSERVICE_IID, result);
					if (rc != XPCOM.NS_OK) error (rc);
					if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

					nsIPrefService prefService = new nsIPrefService (result[0]);
					result[0] = 0;
					revertProxySettings (prefService);
					rc = prefService.SavePrefFile (prefFile.getAddress ());
					prefService.Release ();

					prefFile.Release ();
				}
				serviceManager.Release ();

				if (XPCOMWasGlued) {
					/*
					* The following is intentionally commented because it causes subsequent
					* browser instantiations within the process to fail.  Mozilla does not
					* support being unloaded and then re-initialized in a process, see
					* http://www.mail-archive.com/dev-embedding@lists.mozilla.org/msg01732.html . 
					*/

//					int size = XPCOM.nsDynamicFunctionLoad_sizeof ();
//					/* alloc memory for two structs, the second is empty to signify the end of the list */
//					int /*long*/ ptr = C.malloc (size * 2);
//					C.memset (ptr, 0, size * 2);
//					nsDynamicFunctionLoad functionLoad = new nsDynamicFunctionLoad ();
//					byte[] bytes = MozillaDelegate.wcsToMbcs (null, "XRE_TermEmbedding", true); //$NON-NLS-1$
//					functionLoad.functionName = C.malloc (bytes.length);
//					C.memmove (functionLoad.functionName, bytes, bytes.length);
//					functionLoad.function = C.malloc (C.PTR_SIZEOF);
//					C.memmove (functionLoad.function, new int /*long*/[] {0} , C.PTR_SIZEOF);
//					XPCOM.memmove (ptr, functionLoad, XPCOM.nsDynamicFunctionLoad_sizeof ());
//					XPCOM.XPCOMGlueLoadXULFunctions (ptr);
//					C.memmove (result, functionLoad.function, C.PTR_SIZEOF);
//					int /*long*/ functionPtr = result[0];
//					result[0] = 0;
//					C.free (functionLoad.function);
//					C.free (functionLoad.functionName);
//					C.free (ptr);
//					XPCOM.Call (functionPtr);

//					XPCOM.XPCOMGlueShutdown ();
					XPCOMWasGlued = false;
				}
				if (XPCOMInitWasGlued) {
					XPCOMInit.XPCOMGlueShutdown ();
					XPCOMInitWasGlued = false;
				}
				Initialized = false;
			}

			void revertProxySettings (nsIPrefService prefService) {
				/* the proxy settings are typically not set, so check for this first */
				boolean hostSet = oldProxyHostFTP != null || oldProxyHostHTTP != null || oldProxyHostSSL != null;
				if (!hostSet && oldProxyPortFTP == -1 && oldProxyPortHTTP == -1 && oldProxyPortSSL == -1 && oldProxyType == -1) return;

				int /*long*/[] result = new int /*long*/[1];
				byte[] buffer = new byte[1];
				int rc = prefService.GetBranch (buffer, result);	/* empty buffer denotes root preference level */
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsIPrefBranch prefBranch = new nsIPrefBranch (result[0]);
				result[0] = 0;

				if (hostSet) {
					rc = XPCOM.NS_GetComponentManager (result);
					if (rc != XPCOM.NS_OK) error (rc);
					if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

					nsIComponentManager componentManager = new nsIComponentManager (result[0]);
					result[0] = 0;

					byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFLOCALIZEDSTRING_CONTRACTID, true);
					rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
					if (rc != XPCOM.NS_OK) error (rc);
					if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

					nsIPrefLocalizedString localizedString = new nsIPrefLocalizedString (result[0]);
					result[0] = 0;

					if (oldProxyHostFTP != null) {
						buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYHOST_FTP, true);
						if (oldProxyHostFTP.equals (DEFAULTVALUE_STRING)) {
							rc = prefBranch.ClearUserPref (buffer);
							if (rc != XPCOM.NS_OK) error (rc);
						} else {
							int length = oldProxyHostFTP.length ();
							char[] charBuffer = new char[length];
							oldProxyHostFTP.getChars (0, length, charBuffer, 0);
							rc = localizedString.SetDataWithLength (length, charBuffer);
							if (rc != XPCOM.NS_OK) error (rc);
							rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
							if (rc != XPCOM.NS_OK) error (rc);
						}
					}

					if (oldProxyHostHTTP != null) {
						buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYHOST_HTTP, true);
						if (oldProxyHostHTTP.equals (DEFAULTVALUE_STRING)) {
							rc = prefBranch.ClearUserPref (buffer);
							if (rc != XPCOM.NS_OK) error (rc);
						} else {
							int length = oldProxyHostHTTP.length ();
							char[] charBuffer = new char[length];
							oldProxyHostHTTP.getChars (0, length, charBuffer, 0);
							rc = localizedString.SetDataWithLength (length, charBuffer);
							if (rc != XPCOM.NS_OK) error (rc);
							rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
							if (rc != XPCOM.NS_OK) error (rc);
						}
					}

					if (oldProxyHostSSL != null) {
						buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYHOST_SSL, true);
						if (oldProxyHostSSL.equals (DEFAULTVALUE_STRING)) {
							rc = prefBranch.ClearUserPref (buffer);
							if (rc != XPCOM.NS_OK) error (rc);
						} else {
							int length = oldProxyHostSSL.length ();
							char[] charBuffer = new char[length];
							oldProxyHostSSL.getChars (0, length, charBuffer, 0);
							rc = localizedString.SetDataWithLength (length, charBuffer);
							if (rc != XPCOM.NS_OK) error (rc);
							rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
							if (rc != XPCOM.NS_OK) error (rc);
						}
					}

					localizedString.Release ();
					componentManager.Release ();
				}

				if (oldProxyPortFTP != -1) {
					buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYPORT_FTP, true);
					rc = prefBranch.SetIntPref (buffer, oldProxyPortFTP);
					if (rc != XPCOM.NS_OK) error (rc);
				}
				if (oldProxyPortHTTP != -1) {
					buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYPORT_HTTP, true);
					rc = prefBranch.SetIntPref (buffer, oldProxyPortHTTP);
					if (rc != XPCOM.NS_OK) error (rc);
				}
				if (oldProxyPortSSL != -1) {
					buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYPORT_SSL, true);
					rc = prefBranch.SetIntPref (buffer, oldProxyPortSSL);
					if (rc != XPCOM.NS_OK) error (rc);
				}
				if (oldProxyType != -1) {
					buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYTYPE, true);
					rc = prefBranch.SetIntPref (buffer, oldProxyType);
					if (rc != XPCOM.NS_OK) error (rc);
				}

				prefBranch.Release ();
			}
		};

		MozillaClearSessions = new Runnable () {
			public void run () {
				if (!Initialized) return;
				int /*long*/[] result = new int /*long*/[1];
				int rc = XPCOM.NS_GetServiceManager (result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
				nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
				result[0] = 0;
				byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_COOKIEMANAGER_CONTRACTID, true);
				rc = serviceManager.GetServiceByContractID (aContractID, nsICookieManager.NS_ICOOKIEMANAGER_IID, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
				serviceManager.Release ();

				nsICookieManager manager = new nsICookieManager (result[0]);
				result[0] = 0;
				rc = manager.GetEnumerator (result);
				if (rc != XPCOM.NS_OK) error (rc);

				nsISimpleEnumerator enumerator = new nsISimpleEnumerator (result[0]);
				int[] moreElements = new int[1]; /* PRBool */
				rc = enumerator.HasMoreElements (moreElements);
				if (rc != XPCOM.NS_OK) error (rc);
				while (moreElements[0] != 0) {
					result[0] = 0;
					rc = enumerator.GetNext (result);
					if (rc != XPCOM.NS_OK) error (rc);
					nsICookie cookie = new nsICookie (result[0]);
					long[] expires = new long[1];
					rc = cookie.GetExpires (expires);
					if (expires[0] == 0) {
						/* indicates a session cookie */
						int /*long*/ domain = XPCOM.nsEmbedCString_new ();
						int /*long*/ name = XPCOM.nsEmbedCString_new ();
						int /*long*/ path = XPCOM.nsEmbedCString_new ();
						cookie.GetHost (domain);
						cookie.GetName (name);
						cookie.GetPath (path);
						rc = manager.Remove (domain, name, path, 0);
						XPCOM.nsEmbedCString_delete (domain);
						XPCOM.nsEmbedCString_delete (name);
						XPCOM.nsEmbedCString_delete (path);
						if (rc != XPCOM.NS_OK) error (rc);
					}
					cookie.Release ();
					rc = enumerator.HasMoreElements (moreElements);
					if (rc != XPCOM.NS_OK) error (rc);
				}
				enumerator.Release ();
				manager.Release ();
			}
		};

		MozillaGetCookie = new Runnable() {
			public void run() {
				if (!Initialized) return;

				int /*long*/[] result = new int /*long*/[1];
				int rc = XPCOM.NS_GetServiceManager (result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
				result[0] = 0;
				rc = serviceManager.GetService (XPCOM.NS_IOSERVICE_CID, nsIIOService.NS_IIOSERVICE_IID, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsIIOService ioService = new nsIIOService (result[0]);
				result[0] = 0;
				byte[] bytes = MozillaDelegate.wcsToMbcs (null, CookieUrl, false);
				int /*long*/ aSpec = XPCOM.nsEmbedCString_new (bytes, bytes.length);
				rc = ioService.NewURI (aSpec, null, 0, result);
				XPCOM.nsEmbedCString_delete (aSpec);
				ioService.Release ();
				if (rc != XPCOM.NS_OK) {
					serviceManager.Release ();
					return;
				}
				if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);

				nsIURI aURI = new nsIURI (result[0]);
				result[0] = 0;
				byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_COOKIESERVICE_CONTRACTID, true);
				rc = serviceManager.GetServiceByContractID (aContractID, nsICookieService.NS_ICOOKIESERVICE_IID, result);
				int /*long*/ cookieString;
				if (rc == XPCOM.NS_OK && result[0] != 0) {
					nsICookieService cookieService = new nsICookieService (result[0]);
					result[0] = 0;
					rc = cookieService.GetCookieString (aURI.getAddress(), 0, result);
					cookieService.Release ();
					if (rc != XPCOM.NS_OK) error (rc);
					if (result[0] == 0) {
						aURI.Release ();
						serviceManager.Release ();
						return;
					}
					cookieString = result[0];
				} else {
					result[0] = 0;
					rc = serviceManager.GetServiceByContractID (aContractID, nsICookieService_1_9.NS_ICOOKIESERVICE_IID, result);
					if (rc != XPCOM.NS_OK) error (rc);
					if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
					nsICookieService_1_9 cookieService = new nsICookieService_1_9 (result[0]);
					result[0] = 0;
					rc = cookieService.GetCookieString(aURI.getAddress(), 0, result);
					cookieService.Release ();
					if (rc != XPCOM.NS_OK) error (rc);
					if (result[0] == 0) {
						aURI.Release ();
						serviceManager.Release ();
						return;
					}
					cookieString = result[0];
				}
				aURI.Release ();
				serviceManager.Release ();
				result[0] = 0;

				int length = C.strlen (cookieString);
				bytes = new byte[length];
				XPCOM.memmove (bytes, cookieString, length);

				/*
				 * NS_Free was introduced in mozilla 1.8, prior to this the standard free() call
				 * was to be used.  Try to free the cookie string with NS_Free first, and if it fails
				 * then assume that an older mozilla is being used, and use C's free() instead. 
				 */
				if (pathBytes_NSFree == null) {
					String mozillaPath = getMozillaPath () + MozillaDelegate.getLibraryName () + '\0';
					try {
						pathBytes_NSFree = mozillaPath.getBytes ("UTF-8"); //$NON-NLS-1$
					} catch (UnsupportedEncodingException e) {
						pathBytes_NSFree = mozillaPath.getBytes ();
					}
				}
				if (!XPCOM.NS_Free (pathBytes_NSFree, cookieString)) {
					C.free (cookieString);
				}

				String allCookies = new String (MozillaDelegate.mbcsToWcs (null, bytes));
				StringTokenizer tokenizer = new StringTokenizer (allCookies, ";"); //$NON-NLS-1$
				while (tokenizer.hasMoreTokens ()) {
					String cookie = tokenizer.nextToken ();
					int index = cookie.indexOf ('=');
					if (index != -1) {
						String name = cookie.substring (0, index).trim ();
						if (name.equals (CookieName)) {
							CookieValue = cookie.substring (index + 1).trim ();
							return;
						}
					}
				}
			}
		};

		MozillaSetCookie = new Runnable() {
			public void run() {
				if (!Initialized) return;

				int /*long*/[] result = new int /*long*/[1];
				int rc = XPCOM.NS_GetServiceManager (result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
				result[0] = 0;
				rc = serviceManager.GetService (XPCOM.NS_IOSERVICE_CID, nsIIOService.NS_IIOSERVICE_IID, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsIIOService ioService = new nsIIOService (result[0]);
				result[0] = 0;
				byte[] bytes = MozillaDelegate.wcsToMbcs (null, CookieUrl, false);
				int /*long*/ aSpec = XPCOM.nsEmbedCString_new (bytes, bytes.length);
				rc = ioService.NewURI (aSpec, null, 0, result);
				XPCOM.nsEmbedCString_delete (aSpec);
				ioService.Release ();
				if (rc != XPCOM.NS_OK) {
					serviceManager.Release ();
					return;
				}
				if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);

				nsIURI aURI = new nsIURI(result[0]);
				result[0] = 0;
				byte[] aCookie = MozillaDelegate.wcsToMbcs (null, CookieValue, true);
				byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_COOKIESERVICE_CONTRACTID, true);
				rc = serviceManager.GetServiceByContractID (aContractID, nsICookieService.NS_ICOOKIESERVICE_IID, result);
				if (rc == XPCOM.NS_OK && result[0] != 0) {
					nsICookieService cookieService = new nsICookieService (result[0]);
					rc = cookieService.SetCookieString (aURI.getAddress(), 0, aCookie, 0);
					cookieService.Release ();
				} else {
					result[0] = 0;
					rc = serviceManager.GetServiceByContractID (aContractID, nsICookieService_1_9.NS_ICOOKIESERVICE_IID, result);
					if (rc != XPCOM.NS_OK) error (rc);
					if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
					nsICookieService_1_9 cookieService = new nsICookieService_1_9 (result[0]);
					rc = cookieService.SetCookieString(aURI.getAddress(), 0, aCookie, 0);
					cookieService.Release ();
				}
				result[0] = 0;
				aURI.Release ();
				serviceManager.Release ();
				CookieResult = rc == 0;
			}
		};
	}

public boolean create (Composite parent, int style) {
	delegate = new MozillaDelegate (browser);
	final Display display = parent.getDisplay ();

	int /*long*/[] result = new int /*long*/[1];
	if (!Initialized) {
		boolean initLoaded = false;
		boolean isXULRunner = false;

		String greInitialized = System.getProperty (GRE_INITIALIZED); 
		if ("true".equals (greInitialized)) { //$NON-NLS-1$
			/* 
			 * Another browser has already initialized xulrunner in this process,
			 * so just bind to it instead of trying to initialize a new one.
			 */
			Initialized = true;
		}

		String mozillaPath = System.getProperty (XULRUNNER_PATH);
		/*
		* Browser clients that ship XULRunner in a plug-in must have an opportunity 
		* to set the org.eclipse.swt.browser.XULRunnerPath system property to point
		* at their XULRunner before the first Mozilla-based Browser is created.  To
		* facilitate this, reflection is used to reference non-existent class
		* org.eclipse.swt.browser.XULRunnerInitializer the first time a Mozilla-
		* based Browser is created.   A client wishing to use this hook can do so
		* by creating a fragment of org.eclipse.swt that implements this class and
		* sets the system property in its static initializer.
		*/
		if (mozillaPath == null) {
			try {
				Class.forName ("org.eclipse.swt.browser.XULRunnerInitializer"); //$NON-NLS-1$
				mozillaPath = System.getProperty (XULRUNNER_PATH);
			} catch (ClassNotFoundException e) {
				/* no fragment is providing this class, which is the typical case */
			}
		}

		if (mozillaPath == null) {
			try {
				String libName = delegate.getSWTInitLibraryName ();
				Library.loadLibrary (libName);
				initLoaded = true;
			} catch (UnsatisfiedLinkError e) {
				/* 
				* If this library failed to load then do not attempt to detect a
				* xulrunner to use.  The Browser may still be usable if MOZILLA_FIVE_HOME
				* points at a GRE. 
				*/
			}
		} else {
			mozillaPath += SEPARATOR_OS + MozillaDelegate.getLibraryName ();
			isXULRunner = true;
		}

		if (initLoaded) {
			/* attempt to discover a XULRunner to use as the GRE */
			mozillaPath = initDiscoverXULRunner ();
			isXULRunner = mozillaPath.length () > 0;

			/*
			 * Test whether the detected XULRunner can be used as the GRE before loading swt's
			 * XULRunner library.  If it cannot be used then fall back to attempting to use
			 * the GRE pointed to by MOZILLA_FIVE_HOME.
			 * 
			 * One case where this will fail is attempting to use a 64-bit xulrunner while swt
			 * is running in 32-bit mode, or vice versa.
			 */
			if (isXULRunner) {
				byte[] path = MozillaDelegate.wcsToMbcs (null, mozillaPath, true);
				int rc = XPCOMInit.XPCOMGlueStartup (path);
				if (rc != XPCOM.NS_OK) {
					mozillaPath = mozillaPath.substring (0, mozillaPath.lastIndexOf (SEPARATOR_OS));
					if (Device.DEBUG) System.out.println ("cannot use detected XULRunner: " + mozillaPath); //$NON-NLS-1$

					/* attempt to XPCOMGlueStartup the GRE pointed at by MOZILLA_FIVE_HOME */
					int /*long*/ ptr = C.getenv (MozillaDelegate.wcsToMbcs (null, XPCOM.MOZILLA_FIVE_HOME, true));
					if (ptr == 0) {
						isXULRunner = false;
					} else {
						int length = C.strlen (ptr);
						byte[] buffer = new byte[length];
						C.memmove (buffer, ptr, length);
						mozillaPath = new String (MozillaDelegate.mbcsToWcs (null, buffer));
						/*
						 * Attempting to XPCOMGlueStartup a mozilla-based GRE != xulrunner can
						 * crash, so don't attempt unless the GRE appears to be xulrunner.
						 */
						if (mozillaPath.indexOf("xulrunner") == -1) { //$NON-NLS-1$
							isXULRunner = false;	
						} else {
							mozillaPath += SEPARATOR_OS + MozillaDelegate.getLibraryName ();
							path = MozillaDelegate.wcsToMbcs (null, mozillaPath, true);
							rc = XPCOMInit.XPCOMGlueStartup (path);
							if (rc != XPCOM.NS_OK) {
								isXULRunner = false;
								mozillaPath = mozillaPath.substring (0, mozillaPath.lastIndexOf (SEPARATOR_OS));
								if (Device.DEBUG) System.out.println ("failed to start as XULRunner: " + mozillaPath); //$NON-NLS-1$
							}
						}
					} 
				}
				if (isXULRunner) {
					XPCOMInitWasGlued = true;
				}
			}
		}

		if (isXULRunner) {
			/* load swt's xulrunner library and invoke XPCOMGlueStartup to load xulrunner's dependencies */
			mozillaPath = initXULRunner (mozillaPath);
		} else {
			/*
			* If style SWT.MOZILLA was specified then this initialization has already
			* failed, because SWT.MOZILLA-style Browsers must utilize XULRunner.
			*/
			if ((style & SWT.MOZILLA) != 0) {
				browser.dispose ();
				String errorString = (mozillaPath != null && mozillaPath.length () > 0) ?
					" [Failed to use detected XULRunner: " + mozillaPath + "]" :
					" [Could not detect registered XULRunner to use]";	//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				SWT.error (SWT.ERROR_NO_HANDLES, null, errorString);
			}

			/* load swt's mozilla library */
			mozillaPath = initMozilla (mozillaPath);
		}

		if (!Initialized) {
			/* create LocationProvider, which tells mozilla where to find things on the file system */
			String profilePath = delegate.getProfilePath ();
			LocationProvider = new AppFileLocProvider (mozillaPath, profilePath, isXULRunner);
			LocationProvider.AddRef ();

			/* write external.xpt to the file system if needed */
			initExternal (profilePath);

			/* load swt's mozilla/xulrunner library and invoke appropriate Init function */
			initXPCOM (mozillaPath, isXULRunner);
		}

		/* attempt to initialize JavaXPCOM in the detected XULRunner */
		if (isXULRunner) initJavaXPCOM (mozillaPath);

		/* get the nsIComponentManager and nsIServiceManager, used throughout initialization */
		int rc = XPCOM.NS_GetComponentManager (result);
		if (rc != XPCOM.NS_OK) {
			browser.dispose ();
			error (rc);
		}
		if (result[0] == 0) {
			browser.dispose ();
			error (XPCOM.NS_NOINTERFACE);
		}
		nsIComponentManager componentManager = new nsIComponentManager (result[0]);
		result[0] = 0;

		rc = XPCOM.NS_GetServiceManager (result);
		if (rc != XPCOM.NS_OK) {
			browser.dispose ();
			error (rc);
		}
		if (result[0] == 0) {
			browser.dispose ();
			error (XPCOM.NS_NOINTERFACE);
		}
		nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
		result[0] = 0;	

		/* init the event handler if needed */
		initSpinup (componentManager);

		/* init our WindowCreator, which mozilla uses for the creation of child browsers in external Shells */
		initWindowCreator (serviceManager);

		/* notify mozilla that the profile directory has been changed from its default value */
		initProfile (serviceManager, isXULRunner);

		/* init preference values that give desired mozilla behaviours */ 
		initPreferences (serviceManager, componentManager);

		/* init our various factories that mozilla can invoke as needed */ 
		initFactories (serviceManager, componentManager, isXULRunner);

		serviceManager.Release ();
		componentManager.Release ();

		/* add cookies that were set by a client before the first Mozilla instance was created */
		if (MozillaPendingCookies != null) {
			SetPendingCookies (MozillaPendingCookies);
		}
		MozillaPendingCookies = null;

		Initialized = true;
	}

	BrowserCount++;

	if (display.getData (DISPOSE_LISTENER_HOOKED) == null) {
		display.setData (DISPOSE_LISTENER_HOOKED, DISPOSE_LISTENER_HOOKED);
		display.addListener (SWT.Dispose, DisplayListener);
	}

	/* get the nsIComponentManager, used throughout initialization */
	int rc = XPCOM.NS_GetComponentManager (result);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	if (result[0] == 0) {
		browser.dispose ();
		error (XPCOM.NS_NOINTERFACE);
	}
	nsIComponentManager componentManager = new nsIComponentManager (result[0]);
	result[0] = 0;

	/* create the nsIWebBrowser instance */
	nsID NS_IWEBBROWSER_CID = new nsID ("F1EAC761-87E9-11d3-AF80-00A024FFC08C"); //$NON-NLS-1$
	rc = componentManager.CreateInstance (NS_IWEBBROWSER_CID, 0, nsIWebBrowser.NS_IWEBBROWSER_IID, result);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	if (result[0] == 0) {
		browser.dispose ();
		error (XPCOM.NS_NOINTERFACE);	
	}
	webBrowser = new nsIWebBrowser (result[0]);
	result[0] = 0;

	/* create the instance-based callback interfaces */
	createCOMInterfaces ();
	AddRef ();

	/* init the nsIWebBrowser's container and base windows */
	initWebBrowserWindows ();

	if (!PerformedVersionCheck) {
		PerformedVersionCheck = true;

		rc = componentManager.QueryInterface (nsIComponentRegistrar.NS_ICOMPONENTREGISTRAR_IID, result);
		if (rc != XPCOM.NS_OK) {
			browser.dispose ();
			error (rc);
		}
		if (result[0] == 0) {
			browser.dispose ();
			error (XPCOM.NS_NOINTERFACE);
		}
		nsIComponentRegistrar componentRegistrar = new nsIComponentRegistrar (result[0]);
		result[0] = 0;

		HelperAppLauncherDialogFactory dialogFactory = new HelperAppLauncherDialogFactory ();
		dialogFactory.AddRef ();
		byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_HELPERAPPLAUNCHERDIALOG_CONTRACTID, true);
		byte[] aClassName = MozillaDelegate.wcsToMbcs (null, "swtHelperAppLauncherDialog", true); //$NON-NLS-1$
		rc = componentRegistrar.RegisterFactory (XPCOM.NS_HELPERAPPLAUNCHERDIALOG_CID, aClassName, aContractID, dialogFactory.getAddress ());
		if (rc != XPCOM.NS_OK) {
			browser.dispose ();
			error (rc);
		}
		dialogFactory.Release ();

		/*
		* Check for the availability of the pre-1.8 implementation of nsIDocShell
		* to determine if the GRE's version is < 1.8.
		*/
		rc = webBrowser.QueryInterface (nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID, result);
		if (rc != XPCOM.NS_OK) {
			browser.dispose ();
			error (XPCOM.NS_ERROR_FAILURE);
		}
		if (result[0] == 0) {
			browser.dispose ();
			error (XPCOM.NS_ERROR_NO_INTERFACE);
		}
		nsIInterfaceRequestor interfaceRequestor = new nsIInterfaceRequestor (result[0]);
		result[0] = 0;

		rc = interfaceRequestor.GetInterface (nsIDocShell.NS_IDOCSHELL_IID, result);
		if (rc == XPCOM.NS_OK && result[0] != 0) {
			IsPre_1_8 = true;
			new nsISupports (result[0]).Release ();
		}
		result[0] = 0;
		IsPre_1_9 = true;

		/*
		* A Download factory for contract "Transfer" must be registered iff the GRE's version is 1.8.x.
		*   Check for the availability of the 1.8 implementation of nsIDocShell to determine if the
		*   GRE's version is 1.8.x.
		* If the GRE version is < 1.8 then the previously-registered Download factory for contract
		*   "Download" will be used.
		* If the GRE version is >= 1.9 then no Download factory is registered because this
		*   functionality is provided by the GRE.
		*/
		if (!IsPre_1_8) {
			rc = interfaceRequestor.GetInterface (nsIDocShell_1_8.NS_IDOCSHELL_IID, result);
			if (rc == XPCOM.NS_OK && result[0] != 0) { /* 1.8 */
				new nsISupports (result[0]).Release ();
				result[0] = 0;

				DownloadFactory_1_8 downloadFactory_1_8 = new DownloadFactory_1_8 ();
				downloadFactory_1_8.AddRef ();
				aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_TRANSFER_CONTRACTID, true);
				aClassName = MozillaDelegate.wcsToMbcs (null, "swtTransfer", true); //$NON-NLS-1$
				rc = componentRegistrar.RegisterFactory (XPCOM.NS_DOWNLOAD_CID, aClassName, aContractID, downloadFactory_1_8.getAddress ());
				if (rc != XPCOM.NS_OK) {
					browser.dispose ();
					error (rc);
				}
				downloadFactory_1_8.Release ();
			} else { /* >= 1.9 */
				IsPre_1_9 = false;
			}
		}
		result[0] = 0;
		interfaceRequestor.Release ();
		componentRegistrar.Release ();
	}
	componentManager.Release ();

	/*
	 * Bug in XULRunner 1.9.  On win32, Mozilla does not clear its background before content has
	 * been set into it.  As a result, embedders appear broken if they do not immediately display
	 * a URL or text.  The Mozilla bug for this is https://bugzilla.mozilla.org/show_bug.cgi?id=453523.
	 * 
	 * The workaround is to subclass the Mozilla window and clear it whenever WM_ERASEBKGND is received.
	 * This subclass should be removed once content has been set into the browser.
	 */
	if (!IsPre_1_9) {
		delegate.addWindowSubclass ();
	}

	/* add listeners for progress and content */
	rc = webBrowser.AddWebBrowserListener (weakReference.getAddress (), nsIWebProgressListener.NS_IWEBPROGRESSLISTENER_IID);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	rc = webBrowser.SetParentURIContentListener (uriContentListener.getAddress ());
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}

	delegate.init ();

	listener = new Listener () {
		public void handleEvent (Event event) {
			switch (event.type) {
				case SWT.Dispose: {
					/* make this handler run after other dispose listeners */
					if (ignoreDispose) {
						ignoreDispose = false;
						break;
					}
					ignoreDispose = true;
					browser.notifyListeners (event.type, event);
					event.type = SWT.NONE;
					onDispose (event.display);
					break;
				}
				case SWT.Resize: onResize (); break;
				case SWT.FocusIn: Activate (); break;
				case SWT.Activate: Activate (); break;
				case SWT.Deactivate: {
					Display display = event.display;
					if (Mozilla.this.browser == display.getFocusControl ()) Deactivate ();
					break;
				}
				case SWT.Show: {
					/*
					* Feature in GTK Mozilla.  Mozilla does not show up when
					* its container (a GTK fixed handle) is made visible
					* after having been hidden.  The workaround is to reset
					* its size after the container has been made visible. 
					*/
					Display display = event.display;
					display.asyncExec(new Runnable () {
						public void run() {
							if (browser.isDisposed ()) return;
							onResize ();
						}
					});
					break;
				}
			}
		}
	};	
	int[] folderEvents = new int[] {
		SWT.Dispose,
		SWT.Resize,  
		SWT.FocusIn,
		SWT.Activate,
		SWT.Deactivate,
		SWT.Show,
		SWT.KeyDown,		/* needed to make browser traversable */
	};
	for (int i = 0; i < folderEvents.length; i++) {
		browser.addListener (folderEvents[i], listener);
	}

	return true;
}

public boolean back () {
	htmlBytes = null;

	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);		 	
	rc = webNavigation.GoBack ();	
	webNavigation.Release ();
	return rc == XPCOM.NS_OK;
}

public boolean close () {
	final boolean[] result = new boolean[] {false};
	LocationListener[] oldListeners = locationListeners;
	locationListeners = new LocationListener[] {
		new LocationAdapter () {
			public void changing (LocationEvent event) {
				/* implies that the user did not veto the page unload */
				result[0] = true;
			}
		} 
	};
	execute ("window.location.replace('about:blank');"); //$NON-NLS-1$
	locationListeners = oldListeners;
	return result[0];
}

void createCOMInterfaces () {
	// Create each of the interfaces that this object implements
	supports = new XPCOMObject (new int[] {2, 0, 0}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
	};
	
	weakReference = new XPCOMObject (new int[] {2, 0, 0, 2}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return QueryReferent (args[0], args[1]);}
	};

	webProgressListener = new XPCOMObject (new int[] {2, 0, 0, 4, 6, 3, 4, 3}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return OnStateChange (args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3]);}
		public int /*long*/ method4 (int /*long*/[] args) {return OnProgressChange (args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3], (int)/*64*/args[4], (int)/*64*/args[5]);}
		public int /*long*/ method5 (int /*long*/[] args) {return OnLocationChange (args[0], args[1], args[2]);}
		public int /*long*/ method6 (int /*long*/[] args) {return OnStatusChange (args[0], args[1], (int)/*64*/args[2], args[3]);}
		public int /*long*/ method7 (int /*long*/[] args) {return OnSecurityChange (args[0], args[1], (int)/*64*/args[2]);}
	};
	
	webBrowserChrome = new XPCOMObject (new int[] {2, 0, 0, 2, 1, 1, 1, 1, 0, 2, 0, 1, 1}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return SetStatus ((int)/*64*/args[0], args[1]);}
		public int /*long*/ method4 (int /*long*/[] args) {return GetWebBrowser (args[0]);}
		public int /*long*/ method5 (int /*long*/[] args) {return SetWebBrowser (args[0]);}
		public int /*long*/ method6 (int /*long*/[] args) {return GetChromeFlags (args[0]);}
		public int /*long*/ method7 (int /*long*/[] args) {return SetChromeFlags ((int)/*64*/args[0]);}
		public int /*long*/ method8 (int /*long*/[] args) {return DestroyBrowserWindow ();}
		public int /*long*/ method9 (int /*long*/[] args) {return SizeBrowserTo ((int)/*64*/args[0], (int)/*64*/args[1]);}
		public int /*long*/ method10 (int /*long*/[] args) {return ShowAsModal ();}
		public int /*long*/ method11 (int /*long*/[] args) {return IsWindowModal (args[0]);}
		public int /*long*/ method12 (int /*long*/[] args) {return ExitModalEventLoop ((int)/*64*/args[0]);}
	};
	
	webBrowserChromeFocus = new XPCOMObject (new int[] {2, 0, 0, 0, 0}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return FocusNextElement ();}
		public int /*long*/ method4 (int /*long*/[] args) {return FocusPrevElement ();}
	};
		
	embeddingSiteWindow = new XPCOMObject (new int[] {2, 0, 0, 5, 5, 0, 1, 1, 1, 1, 1}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return SetDimensions ((int)/*64*/args[0], (int)/*64*/args[1], (int)/*64*/args[2], (int)/*64*/args[3], (int)/*64*/args[4]);}
		public int /*long*/ method4 (int /*long*/[] args) {return GetDimensions ((int)/*64*/args[0], args[1], args[2], args[3], args[4]);}
		public int /*long*/ method5 (int /*long*/[] args) {return SetFocus ();}
		public int /*long*/ method6 (int /*long*/[] args) {return GetVisibility (args[0]);}
		public int /*long*/ method7 (int /*long*/[] args) {return SetVisibility ((int)/*64*/args[0]);}
		public int /*long*/ method8 (int /*long*/[] args) {return GetTitle (args[0]);}
		public int /*long*/ method9 (int /*long*/[] args) {return SetTitle (args[0]);}
		public int /*long*/ method10 (int /*long*/[] args) {return GetSiteWindow (args[0]);}
	};
	
	interfaceRequestor = new XPCOMObject (new int[] {2, 0, 0, 2} ){
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return GetInterface (args[0], args[1]);}
	};
		
	supportsWeakReference = new XPCOMObject (new int[] {2, 0, 0, 1}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return GetWeakReference (args[0]);}
	};
	
	contextMenuListener = new XPCOMObject (new int[] {2, 0, 0, 3}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return OnShowContextMenu ((int)/*64*/args[0], args[1], args[2]);}
	};
	
	uriContentListener = new XPCOMObject (new int[] {2, 0, 0, 2, 5, 3, 4, 1, 1, 1, 1}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return OnStartURIOpen (args[0], args[1]);}
		public int /*long*/ method4 (int /*long*/[] args) {return DoContent (args[0], (int)/*64*/args[1], args[2], args[3], args[4]);}
		public int /*long*/ method5 (int /*long*/[] args) {return IsPreferred (args[0], args[1], args[2]);}
		public int /*long*/ method6 (int /*long*/[] args) {return CanHandleContent (args[0], (int)/*64*/args[1], args[2], args[3]);}
		public int /*long*/ method7 (int /*long*/[] args) {return GetLoadCookie (args[0]);}
		public int /*long*/ method8 (int /*long*/[] args) {return SetLoadCookie (args[0]);}
		public int /*long*/ method9 (int /*long*/[] args) {return GetParentContentListener (args[0]);}
		public int /*long*/ method10 (int /*long*/[] args) {return SetParentContentListener (args[0]);}		
	};
	
	tooltipListener = new XPCOMObject (new int[] {2, 0, 0, 3, 0}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return OnShowTooltip ((int)/*64*/args[0], (int)/*64*/args[1], args[2]);}
		public int /*long*/ method4 (int /*long*/[] args) {return OnHideTooltip ();}		
	};

	domEventListener = new XPCOMObject (new int[] {2, 0, 0, 1}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return HandleEvent (args[0]);}
	};

	badCertListener = new XPCOMObject (new int[] {2, 0, 0, 4}) {
		public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
		public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
		public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
		public int /*long*/ method3 (int /*long*/[] args) {return NotifyCertProblem (args[0], args[1], args[2], args[3]);}
	};
}

void deregisterFunction (BrowserFunction function) {
	super.deregisterFunction (function);
	AllFunctions.remove (new Integer (function.index));
}

void disposeCOMInterfaces () {
	if (supports != null) {
		supports.dispose ();
		supports = null;
	}	
	if (weakReference != null) {
		weakReference.dispose ();
		weakReference = null;	
	}
	if (webProgressListener != null) {
		webProgressListener.dispose ();
		webProgressListener = null;
	}
	if (webBrowserChrome != null) {
		webBrowserChrome.dispose ();
		webBrowserChrome = null;
	}
	if (webBrowserChromeFocus != null) {
		webBrowserChromeFocus.dispose ();
		webBrowserChromeFocus = null;
	}
	if (embeddingSiteWindow != null) {
		embeddingSiteWindow.dispose ();
		embeddingSiteWindow = null;
	}
	if (interfaceRequestor != null) {
		interfaceRequestor.dispose ();
		interfaceRequestor = null;
	}		
	if (supportsWeakReference != null) {
		supportsWeakReference.dispose ();
		supportsWeakReference = null;
	}	
	if (contextMenuListener != null) {
		contextMenuListener.dispose ();
		contextMenuListener = null;
	}
	if (uriContentListener != null) {
		uriContentListener.dispose ();
		uriContentListener = null;
	}
	if (tooltipListener != null) {
		tooltipListener.dispose ();
		tooltipListener = null;
	}
	if (domEventListener != null) {
		domEventListener.dispose ();
		domEventListener = null;
	}
	if (badCertListener != null) {
		badCertListener.dispose ();
		badCertListener = null;
	}
}

public boolean execute (String script) {
	/*
	* This could be the first content that is set into the browser, so
	* ensure that the custom subclass that works around Mozilla bug
	* https://bugzilla.mozilla.org/show_bug.cgi?id=453523 is removed.
	*/
	delegate.removeWindowSubclass ();

	/*
	* As of mozilla 1.9 executing javascript via the javascript: protocol no
	* longer happens synchronously.  As a result, the result of executing JS
	* is not returned to the java side when expected by the client.  The
	* workaround is to invoke the javascript handler directly via C++, which is
	* exposed as of mozilla 1.9.
	*/
	int /*long*/[] result = new int /*long*/[1];
	if (!IsPre_1_9) {
		int rc = XPCOM.NS_GetServiceManager (result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

		nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
		result[0] = 0;
		nsIPrincipal principal = null;
		byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_SCRIPTSECURITYMANAGER_CONTRACTID, true);
		rc = serviceManager.GetServiceByContractID (aContractID, nsIScriptSecurityManager_1_9_1.NS_ISCRIPTSECURITYMANAGER_IID, result);
		if (rc == XPCOM.NS_OK && result[0] != 0) {
			nsIScriptSecurityManager_1_9_1 securityManager = new nsIScriptSecurityManager_1_9_1 (result[0]);
			result[0] = 0;
			rc = securityManager.GetSystemPrincipal (result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);
			principal = new nsIPrincipal (result[0]);
			result[0] = 0;
			securityManager.Release ();
		} else {
			rc = serviceManager.GetServiceByContractID (aContractID, nsIScriptSecurityManager_1_9.NS_ISCRIPTSECURITYMANAGER_IID, result);
			if (rc == XPCOM.NS_OK && result[0] != 0) {
				nsIScriptSecurityManager_1_9 securityManager = new nsIScriptSecurityManager_1_9 (result[0]);
				result[0] = 0;
				rc = securityManager.GetSystemPrincipal (result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);
				principal = new nsIPrincipal (result[0]);
				result[0] = 0;
				securityManager.Release ();
			}
		}

		if (principal != null) {
			rc = webBrowser.QueryInterface (nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID, result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

			nsIInterfaceRequestor interfaceRequestor = new nsIInterfaceRequestor (result[0]);
			result[0] = 0;
			nsID scriptGlobalObjectNSID_1_9 = new nsID ("6afecd40-0b9a-4cfd-8c42-0f645cd91829"); /* nsIScriptGlobalObject */ //$NON-NLS-1$
			rc = interfaceRequestor.GetInterface (scriptGlobalObjectNSID_1_9, result);
			if (!(rc == XPCOM.NS_OK && result[0] != 0)) {
				result[0] = 0;
				nsID scriptGlobalObjectNSID_1_9_2 = new nsID ("e9f3f2c1-2d94-4722-bbd4-2bf6fdf42f48"); /* nsIScriptGlobalObject */ //$NON-NLS-1$
				rc = interfaceRequestor.GetInterface (scriptGlobalObjectNSID_1_9_2, result);
			}
			interfaceRequestor.Release ();

			if (rc == XPCOM.NS_OK && result[0] != 0) {
				int /*long*/ scriptGlobalObject = result[0];
				result[0] = 0;
				rc = (int/*64*/)XPCOM.nsIScriptGlobalObject_EnsureScriptEnvironment (scriptGlobalObject, 2); /* nsIProgrammingLanguage.JAVASCRIPT */
				if (rc != XPCOM.NS_OK) error (rc);
				int /*long*/ scriptContext = XPCOM.nsIScriptGlobalObject_GetScriptContext (scriptGlobalObject, 2); /* nsIProgrammingLanguage.JAVASCRIPT */
				int /*long*/ globalJSObject = XPCOM.nsIScriptGlobalObject_GetScriptGlobal (scriptGlobalObject, 2); /* nsIProgrammingLanguage.JAVASCRIPT */
				new nsISupports (scriptGlobalObject).Release ();

				if (scriptContext != 0 && globalJSObject != 0) {
					/* ensure that the received nsIScriptContext implements the expected interface */
					nsID scriptContextNSID_1_9 = new nsID ("e7b9871d-3adc-4bf7-850d-7fb9554886bf"); /* nsIScriptContext */ //$NON-NLS-1$					
					rc = new nsISupports (scriptContext).QueryInterface (scriptContextNSID_1_9, result);
					if (!(rc == XPCOM.NS_OK && result[0] != 0)) {
						result[0] = 0;
						nsID scriptContextNSID_1_9_2 = new nsID ("87482b5e-e019-4df5-9bc2-b2a51b1f2d28"); /* nsIScriptContext */ //$NON-NLS-1$					
						rc = new nsISupports (scriptContext).QueryInterface (scriptContextNSID_1_9_2, result);
					}

					if (rc == XPCOM.NS_OK && result[0] != 0) {
						new nsISupports (result[0]).Release ();
						result[0] = 0;

						int /*long*/ nativeContext = XPCOM.nsIScriptContext_GetNativeContext (scriptContext);
						if (nativeContext != 0) {
							int length = script.length ();
							char[] scriptChars = new char[length];
							script.getChars(0, length, scriptChars, 0);
							byte[] urlbytes = MozillaDelegate.wcsToMbcs (null, getUrl (), true);
							rc = principal.GetJSPrincipals (nativeContext, result);
							if (rc == XPCOM.NS_OK && result[0] != 0) {
								int /*long*/ principals = result[0];
								result[0] = 0;
								principal.Release ();
								if (pathBytes_JSEvaluateUCScriptForPrincipals == null) {
									String mozillaPath = getMozillaPath () + delegate.getJSLibraryName () + '\0';
									try {
										pathBytes_JSEvaluateUCScriptForPrincipals = mozillaPath.getBytes ("UTF-8"); //$NON-NLS-1$
									} catch (UnsupportedEncodingException e) {
										pathBytes_JSEvaluateUCScriptForPrincipals = mozillaPath.getBytes ();
									}
								}

								aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_CONTEXTSTACK_CONTRACTID, true);
								rc = serviceManager.GetServiceByContractID (aContractID, nsIJSContextStack.NS_IJSCONTEXTSTACK_IID, result);
								if (rc != XPCOM.NS_OK) error (rc);
								if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
								serviceManager.Release ();

								nsIJSContextStack stack = new nsIJSContextStack (result[0]);
								result[0] = 0;
								rc = stack.Push (nativeContext);
								if (rc != XPCOM.NS_OK) error (rc);
								boolean success = XPCOM.JS_EvaluateUCScriptForPrincipals (pathBytes_JSEvaluateUCScriptForPrincipals, nativeContext, globalJSObject, principals, scriptChars, length, urlbytes, 0, result) != 0;
								result[0] = 0;
								rc = stack.Pop (result);
								if (rc != XPCOM.NS_OK) error (rc);
								stack.Release ();

								return success;
							}
						}
					}
				}
			}
			principal.Release ();
		}
		serviceManager.Release ();
	}

	/* fall back to the pre-1.9 approach */

	String url = PREFIX_JAVASCRIPT + script + ";void(0);";	//$NON-NLS-1$
	int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

	nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
	char[] arg = url.toCharArray (); 
	char[] c = new char[arg.length+1];
	System.arraycopy (arg, 0, c, 0, arg.length);
	rc = webNavigation.LoadURI (c, nsIWebNavigation.LOAD_FLAGS_NONE, 0, 0, 0);
	webNavigation.Release ();
	return rc == XPCOM.NS_OK;
}

static Browser findBrowser (int /*long*/ handle) {
	return MozillaDelegate.findBrowser (handle);
}

static Browser findBrowser (nsIDOMWindow aDOMWindow) {
	int /*long*/[] result = new int /*long*/[1];
	int rc = XPCOM.NS_GetServiceManager (result);
	if (rc != XPCOM.NS_OK) Mozilla.error (rc);
	if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);

	nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
	result[0] = 0;
	byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_WINDOWWATCHER_CONTRACTID, true);
	rc = serviceManager.GetServiceByContractID (aContractID, nsIWindowWatcher.NS_IWINDOWWATCHER_IID, result);
	if (rc != XPCOM.NS_OK) Mozilla.error(rc);
	if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);		
	serviceManager.Release ();

	nsIWindowWatcher windowWatcher = new nsIWindowWatcher (result[0]);
	result[0] = 0;
	/* the chrome will only be answered for the top-level nsIDOMWindow */
	rc = aDOMWindow.GetTop (result);
	if (rc != XPCOM.NS_OK) Mozilla.error (rc);
	if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);
	int /*long*/ topDOMWindow = result[0];
	result[0] = 0;
	rc = windowWatcher.GetChromeForWindow (topDOMWindow, result);
	if (rc != XPCOM.NS_OK) Mozilla.error (rc);
	new nsISupports (topDOMWindow).Release ();
	windowWatcher.Release ();
	if (result[0] == 0) return null;	/* the parent chrome is disconnected */

	nsIWebBrowserChrome webBrowserChrome = new nsIWebBrowserChrome (result[0]);
	result[0] = 0;
	rc = webBrowserChrome.QueryInterface (nsIEmbeddingSiteWindow.NS_IEMBEDDINGSITEWINDOW_IID, result);
	if (rc != XPCOM.NS_OK) Mozilla.error (rc);
	if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);		
	webBrowserChrome.Release ();

	nsIEmbeddingSiteWindow embeddingSiteWindow = new nsIEmbeddingSiteWindow (result[0]);
	result[0] = 0;
	rc = embeddingSiteWindow.GetSiteWindow (result);
	if (rc != XPCOM.NS_OK) Mozilla.error (rc);
	if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);		
	embeddingSiteWindow.Release ();

	return findBrowser (result[0]); 
}

public boolean forward () {
	htmlBytes = null;

	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
	rc = webNavigation.GoForward ();
	webNavigation.Release ();

	return rc == XPCOM.NS_OK;
}

public String getBrowserType () {
	return "mozilla"; //$NON-NLS-1$
}

static String getMozillaPath () {
	if (LocationProvider != null) return LocationProvider.mozillaPath;
	if (!Initialized) return "";

	int /*long*/[] result = new int /*long*/[1];
	int rc = XPCOM.NS_GetServiceManager (result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

	nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
	result[0] = 0;
	byte[] buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_DIRECTORYSERVICE_CONTRACTID, true);
	rc = serviceManager.GetServiceByContractID (buffer, nsIDirectoryService.NS_IDIRECTORYSERVICE_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
	serviceManager.Release();

	nsIDirectoryService directoryService = new nsIDirectoryService (result[0]);
	result[0] = 0;
	rc = directoryService.QueryInterface (nsIProperties.NS_IPROPERTIES_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
	directoryService.Release ();

	nsIProperties properties = new nsIProperties (result[0]);
	result[0] = 0;
	buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_GRE_DIR, true);
	rc = properties.Get (buffer, nsIFile.NS_IFILE_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
	properties.Release ();

	nsIFile mozillaDir = new nsIFile (result[0]);
	result[0] = 0;
	int /*long*/ path = XPCOM.nsEmbedCString_new ();
	rc = mozillaDir.GetNativePath (path);
	if (rc != XPCOM.NS_OK) error (rc);
	int length = XPCOM.nsEmbedCString_Length (path);
	int /*long*/ ptr = XPCOM.nsEmbedCString_get (path);
	buffer = new byte[length];
	XPCOM.memmove (buffer, ptr, length);
	XPCOM.nsEmbedCString_delete (path);
	mozillaDir.Release ();

	return new String (MozillaDelegate.mbcsToWcs (null, buffer)) + SEPARATOR_OS;
}

int getNextFunctionIndex () {
	return NextJSFunctionIndex++;
}

public String getText () {
	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.GetContentDOMWindow (result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

	nsIDOMWindow window = new nsIDOMWindow (result[0]);
	result[0] = 0;
	rc = window.GetDocument (result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
	window.Release ();

	int /*long*/ document = result[0];
	result[0] = 0;
	rc = XPCOM.NS_GetComponentManager (result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

	nsIComponentManager componentManager = new nsIComponentManager (result[0]);
	result[0] = 0;
	byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_DOMSERIALIZER_CONTRACTID, true);
	char[] chars = null;

	rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIDOMSerializer_1_7.NS_IDOMSERIALIZER_IID, result);
	if (rc == XPCOM.NS_OK) {	/* mozilla >= 1.7 */
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

		nsIDOMSerializer_1_7 serializer = new nsIDOMSerializer_1_7 (result[0]);
		result[0] = 0;
		int /*long*/ string = XPCOM.nsEmbedString_new ();
		rc = serializer.SerializeToString (document, string);
		serializer.Release ();

		int length = XPCOM.nsEmbedString_Length (string);
		int /*long*/ buffer = XPCOM.nsEmbedString_get (string);
		chars = new char[length];
		XPCOM.memmove (chars, buffer, length * 2);
		XPCOM.nsEmbedString_delete (string);
	} else {	/* mozilla < 1.7 */
		rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIDOMSerializer.NS_IDOMSERIALIZER_IID, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

		nsIDOMSerializer serializer = new nsIDOMSerializer (result[0]);
		result[0] = 0;
		rc = serializer.SerializeToString (document, result);
		serializer.Release ();

		int length = XPCOM.strlen_PRUnichar (result[0]);
		chars = new char[length];
		XPCOM.memmove (chars, result[0], length * 2);
	}

	componentManager.Release ();
	new nsISupports (document).Release ();
	return new String (chars);
}

public String getUrl () {
	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

	nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
	int /*long*/[] aCurrentURI = new int /*long*/[1];
	rc = webNavigation.GetCurrentURI (aCurrentURI);
	if (rc != XPCOM.NS_OK) error (rc);
	webNavigation.Release ();

	byte[] dest = null;
	if (aCurrentURI[0] != 0) {
		nsIURI uri = new nsIURI (aCurrentURI[0]);
		int /*long*/ aSpec = XPCOM.nsEmbedCString_new ();
		rc = uri.GetSpec (aSpec);
		if (rc != XPCOM.NS_OK) error (rc);
		int length = XPCOM.nsEmbedCString_Length (aSpec);
		int /*long*/ buffer = XPCOM.nsEmbedCString_get (aSpec);
		dest = new byte[length];
		XPCOM.memmove (dest, buffer, length);
		XPCOM.nsEmbedCString_delete (aSpec);
		uri.Release ();
	}
	if (dest == null) return ""; //$NON-NLS-1$

	String location = new String (dest);
	/*
	 * If the URI indicates that the page is being rendered from memory
	 * (via setText()) then set it to about:blank to be consistent with IE.
	 */
	if (location.equals (URI_FILEROOT)) {
		location = ABOUT_BLANK;
	} else {
		int length = URI_FILEROOT.length ();
		if (location.startsWith (URI_FILEROOT) && location.charAt (length) == '#') {
			location = ABOUT_BLANK + location.substring (length);
		}
	}
	return location;
}

public Object getWebBrowser () {
	if ((browser.getStyle () & SWT.MOZILLA) == 0) return null;
	if (webBrowserObject != null) return webBrowserObject;

	try {
		Class clazz = Class.forName ("org.mozilla.xpcom.Mozilla"); //$NON-NLS-1$
		Method method = clazz.getMethod ("getInstance", new Class[0]); //$NON-NLS-1$
		Object mozilla = method.invoke (null, new Object[0]);
		method = clazz.getMethod ("wrapXPCOMObject", new Class[] {Long.TYPE, String.class}); //$NON-NLS-1$
		webBrowserObject = method.invoke (mozilla, new Object[] {new Long (webBrowser.getAddress ()), nsIWebBrowser.NS_IWEBBROWSER_IID_STR});
		/*
		 * The following AddRef() is needed to offset the automatic Release() that
		 * will be performed by JavaXPCOM when webBrowserObject is finalized.
		 */
		webBrowser.AddRef ();
		return webBrowserObject;
	} catch (ClassNotFoundException e) {
	} catch (NoSuchMethodException e) {
	} catch (IllegalArgumentException e) {
	} catch (IllegalAccessException e) {
	} catch (InvocationTargetException e) {
	}
	return null;
}

String initDiscoverXULRunner () {
	GREVersionRange range = new GREVersionRange ();
	byte[] bytes = MozillaDelegate.wcsToMbcs (null, GRERANGE_LOWER, true);
	int /*long*/ lower = C.malloc (bytes.length);
	C.memmove (lower, bytes, bytes.length);
	range.lower = lower;
	range.lowerInclusive = LowerRangeInclusive;

	bytes = MozillaDelegate.wcsToMbcs (null, GRERANGE_UPPER, true);
	int /*long*/ upper = C.malloc (bytes.length);
	C.memmove (upper, bytes, bytes.length);
	range.upper = upper;
	range.upperInclusive = UpperRangeInclusive;

	int length = XPCOMInit.PATH_MAX;
	int /*long*/ greBuffer = C.malloc (length);
	int /*long*/ propertiesPtr = C.malloc (2 * C.PTR_SIZEOF);
	int rc = XPCOMInit.GRE_GetGREPathWithProperties (range, 1, propertiesPtr, 0, greBuffer, length);

	/*
	 * A XULRunner was not found that supports wrapping of XPCOM handles as JavaXPCOM objects.
	 * Drop the lower version bound and try to detect an earlier XULRunner installation.
	 */
	if (rc != XPCOM.NS_OK) {
		C.free (lower);
		bytes = MozillaDelegate.wcsToMbcs (null, GRERANGE_LOWER_FALLBACK, true);
		lower = C.malloc (bytes.length);
		C.memmove (lower, bytes, bytes.length);
		range.lower = lower;
		rc = XPCOMInit.GRE_GetGREPathWithProperties (range, 1, propertiesPtr, 0, greBuffer, length);
	}
	C.free (lower);
	C.free (upper);
	C.free (propertiesPtr);

	String result = null;
	if (rc == XPCOM.NS_OK) {
		/* indicates that a XULRunner was found */
		length = C.strlen (greBuffer);
		bytes = new byte[length];
		C.memmove (bytes, greBuffer, length);
		result = new String (MozillaDelegate.mbcsToWcs (null, bytes));
	} else {
		result = ""; //$NON-NLS-1$
	}
	C.free (greBuffer);
	return result;
}

void initExternal (String profilePath) {
	File componentsDir = new File (profilePath, AppFileLocProvider.COMPONENTS_DIR);
	java.io.InputStream is = Library.class.getResourceAsStream ("/external.xpt"); //$NON-NLS-1$
	if (is != null) {
		if (!componentsDir.exists ()) {
			componentsDir.mkdirs ();
		}
		int read;
		byte [] buffer = new byte [4096];
		File file = new File (componentsDir, "external.xpt"); //$NON-NLS-1$
		try {
			FileOutputStream os = new FileOutputStream (file);
			while ((read = is.read (buffer)) != -1) {
				os.write(buffer, 0, read);
			}
			os.close ();
			is.close ();
		} catch (FileNotFoundException e) {
		} catch (IOException e) {
		}
	}
}

void initFactories (nsIServiceManager serviceManager, nsIComponentManager componentManager, boolean isXULRunner) {
	int /*long*/[] result = new int /*long*/[1];

	PromptService2Factory factory = new PromptService2Factory ();
	factory.AddRef ();

	int rc = componentManager.QueryInterface (nsIComponentRegistrar.NS_ICOMPONENTREGISTRAR_IID, result);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	if (result[0] == 0) {
		browser.dispose ();
		error (XPCOM.NS_NOINTERFACE);
	}
	
	nsIComponentRegistrar componentRegistrar = new nsIComponentRegistrar (result[0]);
	result[0] = 0;
	componentRegistrar.AutoRegister (0);	 /* detect the External component */ 

	byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PROMPTSERVICE_CONTRACTID, true); 
	byte[] aClassName = MozillaDelegate.wcsToMbcs (null, "swtPromptService", true); //$NON-NLS-1$
	rc = componentRegistrar.RegisterFactory (XPCOM.NS_PROMPTSERVICE_CID, aClassName, aContractID, factory.getAddress ());
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	factory.Release ();

	ExternalFactory externalFactory = new ExternalFactory ();
	externalFactory.AddRef ();
	aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.EXTERNAL_CONTRACTID, true); 
	aClassName = MozillaDelegate.wcsToMbcs (null, "External", true); //$NON-NLS-1$
	rc = componentRegistrar.RegisterFactory (XPCOM.EXTERNAL_CID, aClassName, aContractID, externalFactory.getAddress ());
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	externalFactory.Release ();

	rc = serviceManager.GetService (XPCOM.NS_CATEGORYMANAGER_CID, nsICategoryManager.NS_ICATEGORYMANAGER_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

	nsICategoryManager categoryManager = new nsICategoryManager (result[0]);
	result[0] = 0;
	byte[] category = MozillaDelegate.wcsToMbcs (null, "JavaScript global property", true); //$NON-NLS-1$
	byte[] entry = MozillaDelegate.wcsToMbcs (null, "external", true); //$NON-NLS-1$
	rc = categoryManager.AddCategoryEntry(category, entry, aContractID, 1, 1, result);
	result[0] = 0;
	categoryManager.Release ();

	/*
	* This Download factory will be used if the GRE version is < 1.8.
	* If the GRE version is 1.8.x then the Download factory that is registered later for
	*   contract "Transfer" will be used.
	* If the GRE version is >= 1.9 then no Download factory is registered because this
	*   functionality is provided by the GRE.
	*/
	DownloadFactory downloadFactory = new DownloadFactory ();
	downloadFactory.AddRef ();
	aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_DOWNLOAD_CONTRACTID, true);
	aClassName = MozillaDelegate.wcsToMbcs (null, "swtDownload", true); //$NON-NLS-1$
	rc = componentRegistrar.RegisterFactory (XPCOM.NS_DOWNLOAD_CID, aClassName, aContractID, downloadFactory.getAddress ());
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	downloadFactory.Release ();

	FilePickerFactory pickerFactory = isXULRunner ? new FilePickerFactory_1_8 () : new FilePickerFactory ();
	pickerFactory.AddRef ();
	aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_FILEPICKER_CONTRACTID, true);
	aClassName = MozillaDelegate.wcsToMbcs (null, "swtFilePicker", true); //$NON-NLS-1$
	rc = componentRegistrar.RegisterFactory (XPCOM.NS_FILEPICKER_CID, aClassName, aContractID, pickerFactory.getAddress ());
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	pickerFactory.Release ();

	componentRegistrar.Release ();
}

void initJavaXPCOM (String mozillaPath) {
	try {
		Class clazz = Class.forName ("org.mozilla.xpcom.Mozilla"); //$NON-NLS-1$
		Method method = clazz.getMethod ("getInstance", new Class[0]); //$NON-NLS-1$
		Object mozilla = method.invoke (null, new Object[0]);
		method = clazz.getMethod ("getComponentManager", new Class[0]); //$NON-NLS-1$
		try {
			method.invoke (mozilla, new Object[0]);
		} catch (InvocationTargetException e) {
			/* indicates that JavaXPCOM has not been initialized yet */
			Class fileClass = Class.forName ("java.io.File"); //$NON-NLS-1$
			method = clazz.getMethod ("initialize", new Class[] {fileClass}); //$NON-NLS-1$
			Constructor constructor = fileClass.getDeclaredConstructor (new Class[] {String.class});
			Object argument = constructor.newInstance (new Object[] {mozillaPath});
			method.invoke (mozilla, new Object[] {argument});
		}
	} catch (ClassNotFoundException e) {
		/* JavaXPCOM is not on the classpath */
	} catch (NoSuchMethodException e) {
		/* the JavaXPCOM on the classpath does not implement initialize() */
	} catch (IllegalArgumentException e) {
	} catch (IllegalAccessException e) {
	} catch (InvocationTargetException e) {
	} catch (InstantiationException e) {
	}
}

String initMozilla (String mozillaPath) {
	/* attempt to use the GRE pointed at by MOZILLA_FIVE_HOME */
	int /*long*/ ptr = C.getenv (MozillaDelegate.wcsToMbcs (null, XPCOM.MOZILLA_FIVE_HOME, true));
	if (ptr != 0) {
		int length = C.strlen (ptr);
		byte[] buffer = new byte[length];
		C.memmove (buffer, ptr, length);
		mozillaPath = new String (MozillaDelegate.mbcsToWcs (null, buffer));
	} else {
		browser.dispose ();
		SWT.error (SWT.ERROR_NO_HANDLES, null, " [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)]"); //$NON-NLS-1$
	}
	if (Device.DEBUG) System.out.println ("Mozilla path: " + mozillaPath); //$NON-NLS-1$

	/*
	* Note.  Embedding a Mozilla GTK1.2 causes a crash.  The workaround
	* is to check the version of GTK used by Mozilla by looking for
	* the libwidget_gtk.so library used by Mozilla GTK1.2. Mozilla GTK2
	* uses the libwidget_gtk2.so library.   
	*/
	if (Compatibility.fileExists (mozillaPath, "components/libwidget_gtk.so")) { //$NON-NLS-1$
		browser.dispose ();
		SWT.error (SWT.ERROR_NO_HANDLES, null, " [Mozilla GTK2 required (GTK1.2 detected)]"); //$NON-NLS-1$							
	}

	try {
		Library.loadLibrary ("swt-mozilla"); //$NON-NLS-1$
	} catch (UnsatisfiedLinkError e) {
		try {
			/* 
			 * The initial loadLibrary attempt may have failed as a result of the user's
			 * system not having libstdc++.so.6 installed, so try to load the alternate
			 * swt mozilla library that depends on libswtc++.so.5 instead.
			 */
			Library.loadLibrary ("swt-mozilla-gcc3"); //$NON-NLS-1$
		} catch (UnsatisfiedLinkError ex) {
			browser.dispose ();
			/*
			 * Print the error from the first failed attempt since at this point it's
			 * known that the failure was not due to the libstdc++.so.6 dependency.
			 */
			SWT.error (SWT.ERROR_NO_HANDLES, e, " [MOZILLA_FIVE_HOME='" + mozillaPath + "']"); //$NON-NLS-1$ //$NON-NLS-2$
		}
	}

	return mozillaPath;
}

void initXPCOM (String mozillaPath, boolean isXULRunner) {
	int /*long*/[] result = new int /*long*/[1];

	nsEmbedString pathString = new nsEmbedString (mozillaPath);
	int rc = XPCOM.NS_NewLocalFile (pathString.getAddress (), 1, result);
	pathString.dispose ();
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	if (result[0] == 0) {
		browser.dispose ();
		error (XPCOM.NS_ERROR_NULL_POINTER);
	}

	nsILocalFile localFile = new nsILocalFile (result[0]);
	result[0] = 0;
	if (isXULRunner) {
		int size = XPCOM.nsDynamicFunctionLoad_sizeof ();
		/* alloc memory for two structs, the second is empty to signify the end of the list */
		int /*long*/ ptr = C.malloc (size * 2);
		C.memset (ptr, 0, size * 2);
		nsDynamicFunctionLoad functionLoad = new nsDynamicFunctionLoad ();
		byte[] bytes = MozillaDelegate.wcsToMbcs (null, "XRE_InitEmbedding", true); //$NON-NLS-1$
		functionLoad.functionName = C.malloc (bytes.length);
		C.memmove (functionLoad.functionName, bytes, bytes.length);
		functionLoad.function = C.malloc (C.PTR_SIZEOF);
		C.memmove (functionLoad.function, new int /*long*/[] {0} , C.PTR_SIZEOF);
		XPCOM.memmove (ptr, functionLoad, XPCOM.nsDynamicFunctionLoad_sizeof ());
		XPCOM.XPCOMGlueLoadXULFunctions (ptr);
		C.memmove (result, functionLoad.function, C.PTR_SIZEOF);
		int /*long*/ functionPtr = result[0];
		result[0] = 0;
		C.free (functionLoad.function);
		C.free (functionLoad.functionName);
		C.free (ptr);
		if (functionPtr == 0) {
			browser.dispose ();
			error (XPCOM.NS_ERROR_NULL_POINTER);
		}
		rc = XPCOM.Call (functionPtr, localFile.getAddress (), localFile.getAddress (), LocationProvider.getAddress (), 0, 0);
		if (rc == XPCOM.NS_OK) {
			System.setProperty (XULRUNNER_PATH, mozillaPath);
		}
	} else {
		rc = XPCOM.NS_InitXPCOM2 (0, localFile.getAddress(), LocationProvider.getAddress ());
	}
	localFile.Release ();
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		SWT.error (SWT.ERROR_NO_HANDLES, null, " [MOZILLA_FIVE_HOME may not point at an embeddable GRE] [NS_InitEmbedding " + mozillaPath + " error " + rc + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
	}
	System.setProperty (GRE_INITIALIZED, "true"); //$NON-NLS-1$
}

void initPreferences (nsIServiceManager serviceManager, nsIComponentManager componentManager) {
	int /*long*/[] result = new int /*long*/[1];

	/*
	 * As a result of using a common profile the user cannot change their locale
	 * and charset.  The fix for this is to set mozilla's locale and charset
	 * preference values according to the user's current locale and charset.
	 */
	byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFSERVICE_CONTRACTID, true);
	int rc = serviceManager.GetServiceByContractID (aContractID, nsIPrefService.NS_IPREFSERVICE_IID, result);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	if (result[0] == 0) {
		browser.dispose ();
		error (XPCOM.NS_NOINTERFACE);
	}

	nsIPrefService prefService = new nsIPrefService (result[0]);
	result[0] = 0;
	byte[] buffer = new byte[1];
	rc = prefService.GetBranch (buffer, result);	/* empty buffer denotes root preference level */
	prefService.Release ();
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	if (result[0] == 0) {
		browser.dispose ();
		error (XPCOM.NS_NOINTERFACE);
	}

	nsIPrefBranch prefBranch = new nsIPrefBranch (result[0]);
	result[0] = 0;

	/* get Mozilla's current locale preference value */
	String prefLocales = null;
	nsIPrefLocalizedString localizedString = null;
	buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_LANGUAGES, true);
	rc = prefBranch.GetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
	/* 
	 * Feature of Debian.  For some reason attempting to query for the current locale
	 * preference fails on Debian.  The workaround for this is to assume a value of
	 * "en-us,en" since this is typically the default value when mozilla is used without
	 * a profile.
	 */
	if (rc != XPCOM.NS_OK) {
		prefLocales = "en-us,en" + TOKENIZER_LOCALE;	//$NON-NLS-1$
	} else {
		if (result[0] == 0) {
			browser.dispose ();
			error (XPCOM.NS_NOINTERFACE);
		}
		localizedString = new nsIPrefLocalizedString (result[0]);
		result[0] = 0;
		rc = localizedString.ToString (result);
		if (rc != XPCOM.NS_OK) {
			browser.dispose ();
			error (rc);
		}
		if (result[0] == 0) {
			browser.dispose ();
			error (XPCOM.NS_NOINTERFACE);
		}
		int length = XPCOM.strlen_PRUnichar (result[0]);
		char[] dest = new char[length];
		XPCOM.memmove (dest, result[0], length * 2);
		prefLocales = new String (dest) + TOKENIZER_LOCALE;
	}
	result[0] = 0;

	/*
	 * construct the new locale preference value by prepending the
	 * user's current locale and language to the original value 
	 */
	Locale locale = Locale.getDefault ();
	String language = locale.getLanguage ();
	String country = locale.getCountry ();
	StringBuffer stringBuffer = new StringBuffer (language);
	stringBuffer.append (SEPARATOR_LOCALE);
	stringBuffer.append (country.toLowerCase ());
	stringBuffer.append (TOKENIZER_LOCALE);
	stringBuffer.append (language);
	stringBuffer.append (TOKENIZER_LOCALE);
	String newLocales = stringBuffer.toString ();

	int start, end = -1;
	do {
		start = end + 1;
		end = prefLocales.indexOf (TOKENIZER_LOCALE, start);
		String token;
		if (end == -1) {
			token = prefLocales.substring (start);
		} else {
			token = prefLocales.substring (start, end);
		}
		if (token.length () > 0) {
			token = (token + TOKENIZER_LOCALE).trim ();
			/* ensure that duplicate locale values are not added */
			if (newLocales.indexOf (token) == -1) {
				stringBuffer.append (token);
			}
		}
	} while (end != -1);
	newLocales = stringBuffer.toString ();
	if (!newLocales.equals (prefLocales)) {
		/* write the new locale value */
		newLocales = newLocales.substring (0, newLocales.length () - TOKENIZER_LOCALE.length ()); /* remove trailing tokenizer */
		int length = newLocales.length ();
		char[] charBuffer = new char[length + 1];
		newLocales.getChars (0, length, charBuffer, 0);
		if (localizedString == null) {
			byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFLOCALIZEDSTRING_CONTRACTID, true);
			rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
			if (rc != XPCOM.NS_OK) {
				browser.dispose ();
				error (rc);
			}
			if (result[0] == 0) {
				browser.dispose ();
				error (XPCOM.NS_NOINTERFACE);
			}
			localizedString = new nsIPrefLocalizedString (result[0]);
			result[0] = 0;
		}
		localizedString.SetDataWithLength (length, charBuffer);
		rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress());
	}
	if (localizedString != null) {
		localizedString.Release ();
		localizedString = null;
	}

	/* get Mozilla's current charset preference value */
	String prefCharset = null;
	buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_CHARSET, true);
	rc = prefBranch.GetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
	/* 
	 * Feature of Debian.  For some reason attempting to query for the current charset
	 * preference fails on Debian.  The workaround for this is to assume a value of
	 * "ISO-8859-1" since this is typically the default value when mozilla is used
	 * without a profile.
	 */
	if (rc != XPCOM.NS_OK) {
		prefCharset = "ISO-8859-1";	//$NON-NLS-1$
	} else {
		if (result[0] == 0) {
			browser.dispose ();
			error (XPCOM.NS_NOINTERFACE);
		}
		localizedString = new nsIPrefLocalizedString (result[0]);
		result[0] = 0;
		rc = localizedString.ToString (result);
		if (rc != XPCOM.NS_OK) {
			browser.dispose ();
			error (rc);
		}
		if (result[0] == 0) {
			browser.dispose ();
			error (XPCOM.NS_NOINTERFACE);
		}
		int length = XPCOM.strlen_PRUnichar (result[0]);
		char[] dest = new char[length];
		XPCOM.memmove (dest, result[0], length * 2);
		prefCharset = new String (dest);
	}
	result[0] = 0;

	String newCharset = System.getProperty ("file.encoding");	// $NON-NLS-1$
	if (!newCharset.equals (prefCharset)) {
		/* write the new charset value */
		int length = newCharset.length ();
		char[] charBuffer = new char[length + 1];
		newCharset.getChars (0, length, charBuffer, 0);
		if (localizedString == null) {
			byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFLOCALIZEDSTRING_CONTRACTID, true);
			rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
			if (rc != XPCOM.NS_OK) {
				browser.dispose ();
				error (rc);
			}
			if (result[0] == 0) {
				browser.dispose ();
				error (XPCOM.NS_NOINTERFACE);
			}
			localizedString = new nsIPrefLocalizedString (result[0]);
			result[0] = 0;
		}
		localizedString.SetDataWithLength (length, charBuffer);
		rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
	}
	if (localizedString != null) localizedString.Release ();

	/*
	* Check for proxy values set as documented java properties and update mozilla's
	* preferences with these values if needed.
	*/
	String proxyHost = System.getProperty (PROPERTY_PROXYHOST);
	String proxyPortString = System.getProperty (PROPERTY_PROXYPORT);

	int port = -1;
	if (proxyPortString != null) {
		try {
			int value = Integer.valueOf (proxyPortString).intValue ();
			if (0 <= value && value <= MAX_PORT) port = value;
		} catch (NumberFormatException e) {
			/* do nothing, java property has non-integer value */
		}
	}

	if (proxyHost != null) {
		byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFLOCALIZEDSTRING_CONTRACTID, true);
		rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

		localizedString = new nsIPrefLocalizedString (result[0]);
		result[0] = 0;
		
		int length = proxyHost.length ();
		char[] charBuffer = new char[length];
		proxyHost.getChars (0, length, charBuffer, 0);
		rc = localizedString.SetDataWithLength (length, charBuffer);
		if (rc != XPCOM.NS_OK) error (rc);

		buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYHOST_FTP, true);
		rc = prefBranch.GetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
		if (rc == XPCOM.NS_OK && result[0] != 0) {
			nsIPrefLocalizedString value = new nsIPrefLocalizedString (result[0]);
			result[0] = 0;
			rc = value.ToString (result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);
			length = XPCOM.strlen_PRUnichar (result[0]);
			char[] dest = new char[length];
			XPCOM.memmove (dest, result[0], length * 2);
			oldProxyHostFTP = new String (dest);
		} else {
			/* value is default */
			oldProxyHostFTP = DEFAULTVALUE_STRING;
		}
		result[0] = 0;
		rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
		if (rc != XPCOM.NS_OK) error (rc);

		buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYHOST_HTTP, true);
		rc = prefBranch.GetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
		if (rc == XPCOM.NS_OK && result[0] != 0) {
			nsIPrefLocalizedString value = new nsIPrefLocalizedString (result[0]);
			result[0] = 0;
			rc = value.ToString (result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);
			length = XPCOM.strlen_PRUnichar (result[0]);
			char[] dest = new char[length];
			XPCOM.memmove (dest, result[0], length * 2);
			oldProxyHostHTTP = new String (dest);
		} else {
			/* value is default */
			oldProxyHostHTTP = DEFAULTVALUE_STRING;
		}
		result[0] = 0;
		rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
		if (rc != XPCOM.NS_OK) error (rc);

		buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYHOST_SSL, true);
		rc = prefBranch.GetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
		if (rc == XPCOM.NS_OK && result[0] != 0) {
			nsIPrefLocalizedString value = new nsIPrefLocalizedString (result[0]);
			result[0] = 0;
			rc = value.ToString (result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);
			length = XPCOM.strlen_PRUnichar (result[0]);
			char[] dest = new char[length];
			XPCOM.memmove (dest, result[0], length * 2);
			oldProxyHostSSL = new String (dest);
		} else {
			/* value is default */
			oldProxyHostSSL = DEFAULTVALUE_STRING;
		}
		result[0] = 0;
		rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
		if (rc != XPCOM.NS_OK) error (rc);

		localizedString.Release ();
	}

	int[] intResult = new int[1]; /* C long*/
	if (port != -1) {
		buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYPORT_FTP, true);
		rc = prefBranch.GetIntPref (buffer, intResult);
		if (rc != XPCOM.NS_OK) error (rc);
		oldProxyPortFTP = intResult[0];
		intResult[0] = 0;
		rc = prefBranch.SetIntPref (buffer, port);
		if (rc != XPCOM.NS_OK) error (rc);

		buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYPORT_HTTP, true);
		rc = prefBranch.GetIntPref (buffer, intResult);
		if (rc != XPCOM.NS_OK) error (rc);
		oldProxyPortHTTP = intResult[0];
		intResult[0] = 0;
		rc = prefBranch.SetIntPref (buffer, port);
		if (rc != XPCOM.NS_OK) error (rc);

		buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYPORT_SSL, true);
		rc = prefBranch.GetIntPref (buffer, intResult);
		if (rc != XPCOM.NS_OK) error (rc);
		oldProxyPortSSL = intResult[0];
		intResult[0] = 0;
		rc = prefBranch.SetIntPref (buffer, port);
		if (rc != XPCOM.NS_OK) error (rc);
	}

	if (proxyHost != null || port != -1) {
		buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYTYPE, true);
		rc = prefBranch.GetIntPref (buffer, intResult);
		if (rc != XPCOM.NS_OK) error (rc);
		oldProxyType = intResult[0];
		intResult[0] = 0;
		rc = prefBranch.SetIntPref (buffer, 1);
		if (rc != XPCOM.NS_OK) error (rc);
	}

	/*
	* Ensure that windows that are shown during page loads are not blocked.  Firefox may
	* try to block these by default since such windows are often unwelcome, but this
	* assumption should not be made in the Browser's context.  Since the Browser client
	* is responsible for creating the new Browser and Shell in an OpenWindowListener,
	* they should decide whether the new window is unwelcome or not and act accordingly. 
	*/
	buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_DISABLEOPENDURINGLOAD, true);
	rc = prefBranch.SetBoolPref (buffer, 0);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}

	/* Ensure that the status text can be set through means like javascript */ 
	buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_DISABLEWINDOWSTATUSCHANGE, true);
	rc = prefBranch.SetBoolPref (buffer, 0);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}

	/* Ensure that the status line can be hidden when opening a window from javascript */ 
	buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_DISABLEOPENWINDOWSTATUSHIDE, true);
	rc = prefBranch.SetBoolPref (buffer, 0);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}

	/* Ensure that javascript execution is enabled since this is the Browser's default behaviour */ 
	buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_JAVASCRIPTENABLED, true);
	rc = prefBranch.SetBoolPref (buffer, 1);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}

	prefBranch.Release ();
}

void initProfile (nsIServiceManager serviceManager, boolean isXULRunner) {
	int /*long*/[] result = new int /*long*/[1];

	byte[] buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_OBSERVER_CONTRACTID, true);
	int rc = serviceManager.GetServiceByContractID (buffer, nsIObserverService.NS_IOBSERVERSERVICE_IID, result);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	if (result[0] == 0) {
		browser.dispose ();
		error (XPCOM.NS_NOINTERFACE);
	}

	nsIObserverService observerService = new nsIObserverService (result[0]);
	result[0] = 0;
	buffer = MozillaDelegate.wcsToMbcs (null, PROFILE_DO_CHANGE, true);
	int length = STARTUP.length ();
	char[] chars = new char [length + 1];
	STARTUP.getChars (0, length, chars, 0);
	rc = observerService.NotifyObservers (0, buffer, chars);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	buffer = MozillaDelegate.wcsToMbcs (null, PROFILE_AFTER_CHANGE, true);
	rc = observerService.NotifyObservers (0, buffer, chars);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	observerService.Release ();

	if (isXULRunner) {
		int size = XPCOM.nsDynamicFunctionLoad_sizeof ();
		/* alloc memory for two structs, the second is empty to signify the end of the list */
		int /*long*/ ptr = C.malloc (size * 2);
		C.memset (ptr, 0, size * 2);
		nsDynamicFunctionLoad functionLoad = new nsDynamicFunctionLoad ();
		byte[] bytes = MozillaDelegate.wcsToMbcs (null, "XRE_NotifyProfile", true); //$NON-NLS-1$
		functionLoad.functionName = C.malloc (bytes.length);
		C.memmove (functionLoad.functionName, bytes, bytes.length);
		functionLoad.function = C.malloc (C.PTR_SIZEOF);
		C.memmove (functionLoad.function, new int /*long*/[] {0} , C.PTR_SIZEOF);
		XPCOM.memmove (ptr, functionLoad, XPCOM.nsDynamicFunctionLoad_sizeof ());
		XPCOM.XPCOMGlueLoadXULFunctions (ptr);
		C.memmove (result, functionLoad.function, C.PTR_SIZEOF);
		int /*long*/ functionPtr = result[0];
		result[0] = 0;
		C.free (functionLoad.function);
		C.free (functionLoad.functionName);
		C.free (ptr);
		/* functionPtr == 0 for xulrunner < 1.9 */
		if (functionPtr != 0) {
			rc = XPCOM.Call (functionPtr);
			if (rc != XPCOM.NS_OK) {
				browser.dispose ();
				error (rc);
			}
		}
	}
}

void initSpinup (nsIComponentManager componentManager) {
	if (delegate.needsSpinup ()) {
		int /*long*/[] result = new int /*long*/[1];

		/* nsIAppShell is discontinued as of xulrunner 1.9, so do not fail if it is not found */
		int rc = componentManager.CreateInstance (XPCOM.NS_APPSHELL_CID, 0, nsIAppShell.NS_IAPPSHELL_IID, result);
		if (rc != XPCOM.NS_ERROR_NO_INTERFACE) {
			if (rc != XPCOM.NS_OK) {
				browser.dispose ();
				error (rc);
			}
			if (result[0] == 0) {
				browser.dispose ();
				error (XPCOM.NS_NOINTERFACE);
			}

			AppShell = new nsIAppShell (result[0]);
			result[0] = 0;
			rc = AppShell.Create (0, null);
			if (rc != XPCOM.NS_OK) {
				browser.dispose ();
				error (rc);
			}
			rc = AppShell.Spinup ();
			if (rc != XPCOM.NS_OK) {
				browser.dispose ();
				error (rc);
			}
		}
	}
}

void initWebBrowserWindows () {
	int rc = webBrowser.SetContainerWindow (webBrowserChrome.getAddress());
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}

	int /*long*/[] result = new int /*long*/[1];
	rc = webBrowser.QueryInterface (nsIBaseWindow.NS_IBASEWINDOW_IID, result);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	if (result[0] == 0) {
		browser.dispose ();
		error (XPCOM.NS_ERROR_NO_INTERFACE);
	}
	
	nsIBaseWindow baseWindow = new nsIBaseWindow (result[0]);
	result[0] = 0;
	Rectangle rect = browser.getClientArea ();
	if (rect.isEmpty ()) {
		rect.width = 1;
		rect.height = 1;
	}

	embedHandle = delegate.getHandle ();

	rc = baseWindow.InitWindow (embedHandle, 0, 0, 0, rect.width, rect.height);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (XPCOM.NS_ERROR_FAILURE);
	}
	rc = delegate.createBaseWindow (baseWindow);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (XPCOM.NS_ERROR_FAILURE);
	}
	rc = baseWindow.SetVisibility (1);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (XPCOM.NS_ERROR_FAILURE);
	}
	baseWindow.Release ();
}

void initWindowCreator (nsIServiceManager serviceManager) {
	WindowCreator = new WindowCreator2 ();
	WindowCreator.AddRef ();
	
	int /*long*/[] result = new int /*long*/[1];
	byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_WINDOWWATCHER_CONTRACTID, true);
	int rc = serviceManager.GetServiceByContractID (aContractID, nsIWindowWatcher.NS_IWINDOWWATCHER_IID, result);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	if (result[0] == 0) {
		browser.dispose ();
		error (XPCOM.NS_NOINTERFACE);		
	}

	nsIWindowWatcher windowWatcher = new nsIWindowWatcher (result[0]);
	result[0] = 0;
	rc = windowWatcher.SetWindowCreator (WindowCreator.getAddress());
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	windowWatcher.Release ();
}

String initXULRunner (String mozillaPath) {
	if (Device.DEBUG) System.out.println ("XULRunner path: " + mozillaPath); //$NON-NLS-1$
	try {
		Library.loadLibrary ("swt-xulrunner"); //$NON-NLS-1$
	} catch (UnsatisfiedLinkError e) {
		SWT.error (SWT.ERROR_NO_HANDLES, e);
	}
	byte[] path = MozillaDelegate.wcsToMbcs (null, mozillaPath, true);
	int rc = XPCOM.XPCOMGlueStartup (path);
	if (rc != XPCOM.NS_OK) {
		browser.dispose ();
		error (rc);
	}
	XPCOMWasGlued = true;

	/*
	 * Remove the trailing xpcom lib name from mozillaPath because the
	 * Mozilla.initialize and NS_InitXPCOM2 invocations require a directory name only.
	 */ 
	return mozillaPath.substring (0, mozillaPath.lastIndexOf (SEPARATOR_OS));
}
public boolean isBackEnabled () {
	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
	int[] aCanGoBack = new int[1]; /* PRBool */
	rc = webNavigation.GetCanGoBack (aCanGoBack);	
	webNavigation.Release ();
	return aCanGoBack[0] != 0;
}

public boolean isForwardEnabled () {
	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
	int[] aCanGoForward = new int[1]; /* PRBool */
	rc = webNavigation.GetCanGoForward (aCanGoForward);
	webNavigation.Release ();
	return aCanGoForward[0] != 0;
}

static String error (int code) {
	throw new SWTError ("XPCOM error " + code); //$NON-NLS-1$
}

void onDispose (Display display) {
	/* invoke onbeforeunload handlers */
	if (!browser.isClosing && !browser.isDisposed()) {
		LocationListener[] oldLocationListeners = locationListeners;
		locationListeners = new LocationListener[0];
		ignoreAllMessages = true;
		execute ("window.location.replace('about:blank');"); //$NON-NLS-1$
		ignoreAllMessages = false;
		locationListeners = oldLocationListeners;	
	}

	int rc = webBrowser.RemoveWebBrowserListener (weakReference.getAddress (), nsIWebProgressListener.NS_IWEBPROGRESSLISTENER_IID);
	if (rc != XPCOM.NS_OK) error (rc);

	rc = webBrowser.SetParentURIContentListener (0);
	if (rc != XPCOM.NS_OK) error (rc);
	
	rc = webBrowser.SetContainerWindow (0);
	if (rc != XPCOM.NS_OK) error (rc);

	unhookDOMListeners ();

	int /*long*/[] result = new int /*long*/[1];
	rc = webBrowser.QueryInterface (nsIBaseWindow.NS_IBASEWINDOW_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

	nsIBaseWindow baseWindow = new nsIBaseWindow (result[0]);
	rc = baseWindow.Destroy ();
	if (rc != XPCOM.NS_OK) error (rc);
	baseWindow.Release ();

	Release ();
	webBrowser.Release ();
	webBrowser = null;
	webBrowserObject = null;
	lastNavigateURL = null;
	htmlBytes = null;
	listener = null;

	if (tip != null && !tip.isDisposed ()) tip.dispose ();
	tip = null;
	location = size = null;

	Enumeration elements = unhookedDOMWindows.elements ();
	while (elements.hasMoreElements ()) {
		LONG ptrObject = (LONG)elements.nextElement ();
		new nsISupports (ptrObject.value).Release ();
	}
	unhookedDOMWindows = null;

	elements = functions.elements ();
	while (elements.hasMoreElements ()) {
		BrowserFunction function = ((BrowserFunction)elements.nextElement ());
		AllFunctions.remove (new Integer (function.index));
		function.dispose (false);
	}
	functions = null;

	delegate.onDispose (embedHandle);
	delegate = null;

	embedHandle = 0;
	BrowserCount--;
}

void Activate () {
	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebBrowserFocus.NS_IWEBBROWSERFOCUS_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIWebBrowserFocus webBrowserFocus = new nsIWebBrowserFocus (result[0]);
	rc = webBrowserFocus.Activate ();
	if (rc != XPCOM.NS_OK) error (rc);
	webBrowserFocus.Release ();
}

void Deactivate () {
	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebBrowserFocus.NS_IWEBBROWSERFOCUS_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIWebBrowserFocus webBrowserFocus = new nsIWebBrowserFocus (result[0]);
	rc = webBrowserFocus.Deactivate ();
	if (rc != XPCOM.NS_OK) error (rc);
	webBrowserFocus.Release ();
}

void onResize () {
	Rectangle rect = browser.getClientArea ();
	int width = Math.max (1, rect.width);
	int height = Math.max (1, rect.height);

	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIBaseWindow.NS_IBASEWINDOW_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

	delegate.setSize (embedHandle, width, height);
	nsIBaseWindow baseWindow = new nsIBaseWindow (result[0]);
	rc = baseWindow.SetPositionAndSize (0, 0, width, height, 1);
	if (rc != XPCOM.NS_OK) error (rc);
	baseWindow.Release ();
}

public void refresh () {
	htmlBytes = null;

	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
	if (rc != XPCOM.NS_OK) error(rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);		 	
	rc = webNavigation.Reload (nsIWebNavigation.LOAD_FLAGS_NONE);
	webNavigation.Release ();

	/*
	* The following error conditions do not indicate unrecoverable problems:
	* 
	* - NS_ERROR_INVALID_POINTER: happens when Reload is called immediately
	* after calling LoadURI.
	* - NS_ERROR_FILE_NOT_FOUND: happens when attempting to reload a file that
	* no longer exists.
	* - NS_BINDING_ABORTED: happens when the user aborts the load (eg.- chooses
	* to not resubmit a page with form data).
	*/
	switch (rc) {
		case XPCOM.NS_OK:
		case XPCOM.NS_ERROR_INVALID_POINTER:
		case XPCOM.NS_ERROR_FILE_NOT_FOUND:
		case XPCOM.NS_BINDING_ABORTED: {
			return;
		}
	}
	error (rc);
}

void registerFunction (BrowserFunction function) {
	super.registerFunction (function);
	AllFunctions.put (new Integer (function.index), function);
}

public boolean setText (String html, boolean trusted) {
	/*
	*  Feature in Mozilla.  The focus memory of Mozilla must be 
	*  properly managed through the nsIWebBrowserFocus interface.
	*  In particular, nsIWebBrowserFocus.deactivate must be called
	*  when the focus moves from the browser (or one of its children
	*  managed by Mozilla to another widget.  We currently do not
	*  get notified when a widget takes focus away from the Browser.
	*  As a result, deactivate is not properly called. This causes
	*  Mozilla to retake focus the next time a document is loaded.
	*  This breaks the case where the HTML loaded in the Browser 
	*  varies while the user enters characters in a text widget. The text
	*  widget loses focus every time new content is loaded.
	*  The current workaround is to call deactivate everytime if 
	*  the browser currently does not have focus. A better workaround
	*  would be to have a way to call deactivate when the Browser
	*  or one of its children loses focus.
	*/
	if (browser != browser.getDisplay ().getFocusControl ()) Deactivate ();
	
	/* convert the String containing HTML to an array of bytes with UTF-8 data */
	byte[] data = null;
	try {
		data = html.getBytes ("UTF-8"); //$NON-NLS-1$
	} catch (UnsupportedEncodingException e) {
		return false;
	}

	/*
	 * This could be the first content that is set into the browser, so
	 * ensure that the custom subclass that works around Mozilla bug
	 * https://bugzilla.mozilla.org/show_bug.cgi?id=453523 is removed.
	 */
	delegate.removeWindowSubclass ();

	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebBrowserStream.NS_IWEBBROWSERSTREAM_IID, result);
	if (rc == XPCOM.NS_OK && result[0] != 0) {
		/*
		* Setting mozilla's content through nsIWebBrowserStream does not cause a page
		* load to occur, so the events that usually accompany a page change are not
		* fired.  To make this behave as expected, navigate to about:blank first and
		* then set the html content once the page has loaded.
		*/
		new nsISupports (result[0]).Release ();
		result[0] = 0;

		/*
		* If htmlBytes is not null then the about:blank page is already being loaded,
		* so no Navigate is required.  Just set the html that is to be shown.
		*/
		boolean blankLoading = htmlBytes != null;
		htmlBytes = data;
		untrustedText = !trusted;
		if (blankLoading) return true;

		/* navigate to about:blank */
		rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
		nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
		result[0] = 0;
		char[] uri = new char[ABOUT_BLANK.length () + 1];
		ABOUT_BLANK.getChars (0, ABOUT_BLANK.length (), uri, 0);
		rc = webNavigation.LoadURI (uri, nsIWebNavigation.LOAD_FLAGS_NONE, 0, 0, 0);
		if (rc != XPCOM.NS_OK) error (rc);
		webNavigation.Release ();
	} else {
		byte[] contentCharsetBuffer = MozillaDelegate.wcsToMbcs (null, "UTF-8", false);	//$NON-NLS-1$
		int /*long*/ aContentCharset = XPCOM.nsEmbedCString_new (contentCharsetBuffer, contentCharsetBuffer.length);
		byte[] contentTypeBuffer = MozillaDelegate.wcsToMbcs (null, "text/html", false); // $NON-NLS-1$
		int /*long*/ aContentType = XPCOM.nsEmbedCString_new (contentTypeBuffer, contentTypeBuffer.length);

		rc = XPCOM.NS_GetServiceManager (result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

		nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
		result[0] = 0;
		rc = serviceManager.GetService (XPCOM.NS_IOSERVICE_CID, nsIIOService.NS_IIOSERVICE_IID, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

		nsIIOService ioService = new nsIIOService (result[0]);
		result[0] = 0;
		byte[] aString;
		if (trusted) {
			aString = MozillaDelegate.wcsToMbcs (null, URI_FILEROOT, false);
		} else {
			aString = MozillaDelegate.wcsToMbcs (null, ABOUT_BLANK, false);
		}
		int /*long*/ aSpec = XPCOM.nsEmbedCString_new (aString, aString.length);
		rc = ioService.NewURI (aSpec, null, 0, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
		XPCOM.nsEmbedCString_delete (aSpec);
		ioService.Release ();

		nsIURI uri = new nsIURI (result[0]);
		result[0] = 0;

		rc = webBrowser.QueryInterface (nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
		nsIInterfaceRequestor interfaceRequestor = new nsIInterfaceRequestor (result[0]);
		result[0] = 0;

		/*
		* Feature in Mozilla. LoadStream invokes the nsIInputStream argument
		* through a different thread.  The callback mechanism must attach 
		* a non java thread to the JVM otherwise the nsIInputStream Read and
		* Close methods never get called.
		*/
		InputStream inputStream = new InputStream (data);
		inputStream.AddRef ();

		rc = interfaceRequestor.GetInterface (nsIDocShell.NS_IDOCSHELL_IID, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
		nsIDocShell docShell = new nsIDocShell (result[0]);
		result[0] = 0;
		rc = docShell.LoadStream (inputStream.getAddress (), uri.getAddress (), aContentType,  aContentCharset, 0);
		docShell.Release ();

		inputStream.Release ();
		interfaceRequestor.Release ();
		uri.Release ();
		XPCOM.nsEmbedCString_delete (aContentType);
		XPCOM.nsEmbedCString_delete (aContentCharset);
	}
	return true;
}

public boolean setUrl (String url, String postData, String[] headers) {
	htmlBytes = null;

	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

	/*
	 * This could be the first content that is set into the browser, so
	 * ensure that the custom subclass that works around Mozilla bug
	 * https://bugzilla.mozilla.org/show_bug.cgi?id=453523 is removed.
	 */
	delegate.removeWindowSubclass ();

	nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
	result[0] = 0;
	char[] uri = new char[url.length () + 1];
	url.getChars (0, url.length (), uri, 0);

	nsIMIMEInputStream postDataStream = null;
	InputStream dataStream = null;
	if (postData != null) {
		rc = XPCOM.NS_GetComponentManager (result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
		nsIComponentManager componentManager = new nsIComponentManager (result[0]);
		result[0] = 0;
		byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_MIMEINPUTSTREAM_CONTRACTID, true);
		rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIMIMEInputStream.NS_IMIMEINPUTSTREAM_IID, result);
		componentManager.Release();

		if (rc == XPCOM.NS_OK && result[0] != 0) { /* nsIMIMEInputStream is not in mozilla 1.4 */
			byte[] bytes = MozillaDelegate.wcsToMbcs (null, postData, false);
			dataStream = new InputStream (bytes);
			dataStream.AddRef ();
			postDataStream = new nsIMIMEInputStream (result[0]);
			rc = postDataStream.SetData (dataStream.getAddress ());
			if (rc != XPCOM.NS_OK) error (rc);
			rc = postDataStream.SetAddContentLength (1);
			if (rc != XPCOM.NS_OK) error (rc);
			byte[] name = MozillaDelegate.wcsToMbcs (null, HEADER_CONTENTTYPE, true);
			byte[] value = MozillaDelegate.wcsToMbcs (null, MIMETYPE_FORMURLENCODED, true);
			rc = postDataStream.AddHeader (name, value);
			if (rc != XPCOM.NS_OK) error (rc);
		}
		result[0] = 0;
	}

	InputStream headersStream = null;
    if (headers != null) {
		StringBuffer buffer = new StringBuffer ();
		for (int i = 0; i < headers.length; i++) {
			String current = headers[i];
			if (current != null) {
				int sep = current.indexOf (':');
				if (sep != -1) {
					String key = current.substring (0, sep).trim ();
					String value = current.substring (sep + 1).trim ();
					if (key.length () > 0 && value.length () > 0) {
						buffer.append (key);
						buffer.append (':');
						buffer.append (value);
						buffer.append ("\r\n");
					}
				}
			}
		}
		byte[] bytes = MozillaDelegate.wcsToMbcs (null, buffer.toString (), true);
		headersStream = new InputStream (bytes);
		headersStream.AddRef ();
    }

	rc = webNavigation.LoadURI (
		uri,
		nsIWebNavigation.LOAD_FLAGS_NONE,
		0,
		postDataStream == null ? 0 : postDataStream.getAddress (),
		headersStream == null ? 0 : headersStream.getAddress ());
	if (dataStream != null) dataStream.Release ();
	if (headersStream != null) headersStream.Release ();
	webNavigation.Release ();
	return rc == XPCOM.NS_OK;
}

public void stop () {
	htmlBytes = null;

	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);	 	
	rc = webNavigation.Stop (nsIWebNavigation.STOP_ALL);
	if (rc != XPCOM.NS_OK) error (rc);
	webNavigation.Release ();
}

void hookDOMListeners (nsIDOMEventTarget target, boolean isTop) {
	nsEmbedString string = new nsEmbedString (XPCOM.DOMEVENT_FOCUS);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_UNLOAD);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEDOWN);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEUP);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEMOVE);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEWHEEL);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEDRAG);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();

	/*
	* Only hook mouseover and mouseout if the target is a top-level frame, so that mouse moves
	* between frames will not generate events.
	*/
	if (isTop && delegate.hookEnterExit ()) {
		string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEOVER);
		target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
		string.dispose ();
		string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEOUT);
		target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
		string.dispose ();
	}

	string = new nsEmbedString (XPCOM.DOMEVENT_KEYDOWN);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_KEYPRESS);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_KEYUP);
	target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
}

void unhookDOMListeners () {
	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.GetContentDOMWindow (result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

	nsIDOMWindow window = new nsIDOMWindow (result[0]);
	result[0] = 0;
	rc = window.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

	nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
	result[0] = 0;
	unhookDOMListeners (target);
	target.Release ();

	/* Listeners must be unhooked in pages contained in frames */
	rc = window.GetFrames (result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	nsIDOMWindowCollection frames = new nsIDOMWindowCollection (result[0]);
	result[0] = 0;
	int[] frameCount = new int[1];
	rc = frames.GetLength (frameCount); /* PRUint32 */
	if (rc != XPCOM.NS_OK) error (rc);
	int count = frameCount[0];

	if (count > 0) {
		for (int i = 0; i < count; i++) {
			rc = frames.Item (i, result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

			nsIDOMWindow frame = new nsIDOMWindow (result[0]);
			result[0] = 0;
			rc = frame.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

			target = new nsIDOMEventTarget (result[0]);
			result[0] = 0;
			unhookDOMListeners (target);
			target.Release ();
			frame.Release ();
		}
	}
	frames.Release ();
	window.Release ();
}

void unhookDOMListeners (nsIDOMEventTarget target) {
	nsEmbedString string = new nsEmbedString (XPCOM.DOMEVENT_FOCUS);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_UNLOAD);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEDOWN);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEUP);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEMOVE);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEWHEEL);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEDRAG);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEOVER);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEOUT);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_KEYDOWN);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_KEYPRESS);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
	string = new nsEmbedString (XPCOM.DOMEVENT_KEYUP);
	target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
	string.dispose ();
}

/* nsISupports */

int QueryInterface (int /*long*/ riid, int /*long*/ ppvObject) {
	if (riid == 0 || ppvObject == 0) return XPCOM.NS_ERROR_NO_INTERFACE;

	nsID guid = new nsID ();
	XPCOM.memmove (guid, riid, nsID.sizeof);

	if (guid.Equals (nsISupports.NS_ISUPPORTS_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {supports.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsIWeakReference.NS_IWEAKREFERENCE_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {weakReference.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsIWebProgressListener.NS_IWEBPROGRESSLISTENER_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {webProgressListener.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsIWebBrowserChrome.NS_IWEBBROWSERCHROME_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {webBrowserChrome.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsIWebBrowserChromeFocus.NS_IWEBBROWSERCHROMEFOCUS_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {webBrowserChromeFocus.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsIEmbeddingSiteWindow.NS_IEMBEDDINGSITEWINDOW_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {embeddingSiteWindow.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {interfaceRequestor.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsISupportsWeakReference.NS_ISUPPORTSWEAKREFERENCE_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {supportsWeakReference.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsIContextMenuListener.NS_ICONTEXTMENULISTENER_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {contextMenuListener.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsIURIContentListener.NS_IURICONTENTLISTENER_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {uriContentListener.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsITooltipListener.NS_ITOOLTIPLISTENER_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {tooltipListener.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	if (guid.Equals (nsIBadCertListener2.NS_IBADCERTLISTENER2_IID)) {
		XPCOM.memmove (ppvObject, new int /*long*/[] {badCertListener.getAddress ()}, C.PTR_SIZEOF);
		AddRef ();
		return XPCOM.NS_OK;
	}
	XPCOM.memmove (ppvObject, new int /*long*/[] {0}, C.PTR_SIZEOF);
	return XPCOM.NS_ERROR_NO_INTERFACE;
}

int AddRef () {
	refCount++;
	return refCount;
}

int Release () {
	refCount--;
	if (refCount == 0) disposeCOMInterfaces ();
	return refCount;
}

/* nsIWeakReference */	
	
int QueryReferent (int /*long*/ riid, int /*long*/ ppvObject) {
	return QueryInterface (riid, ppvObject);
}

/* nsIInterfaceRequestor */

int GetInterface (int /*long*/ riid, int /*long*/ ppvObject) {
	if (riid == 0 || ppvObject == 0) return XPCOM.NS_ERROR_NO_INTERFACE;
	nsID guid = new nsID ();
	XPCOM.memmove (guid, riid, nsID.sizeof);
	if (guid.Equals (nsIDOMWindow.NS_IDOMWINDOW_IID)) {
		int /*long*/[] aContentDOMWindow = new int /*long*/[1];
		int rc = webBrowser.GetContentDOMWindow (aContentDOMWindow);
		if (rc != XPCOM.NS_OK) error (rc);
		if (aContentDOMWindow[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
		XPCOM.memmove (ppvObject, aContentDOMWindow, C.PTR_SIZEOF);
		return rc;
	}
	return QueryInterface (riid, ppvObject);
}

int GetWeakReference (int /*long*/ ppvObject) {
	XPCOM.memmove (ppvObject, new int /*long*/[] {weakReference.getAddress ()}, C.PTR_SIZEOF);
	AddRef ();
	return XPCOM.NS_OK;
}

/* nsIWebProgressListener */

int OnStateChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int aStateFlags, int aStatus) {
	if (registerFunctionsOnState != 0 && ((aStateFlags & registerFunctionsOnState) == registerFunctionsOnState)) {
		registerFunctionsOnState = 0;
		Enumeration elements = functions.elements ();
		while (elements.hasMoreElements ()) {
			BrowserFunction function = (BrowserFunction)elements.nextElement ();
			execute (function.functionString);
		}
	}

	/*
	* Feature of Mozilla.  When a redirect occurs to a site with an invalid
	* certificate, no STATE_IS_DOCUMENT state transitions are received for the
	* new location, and an immediate attempt is made to show the invalid
	* certificate error.  However our invalid certificate handler must know
	* the site with the invalid certificate, not the site that redirected to
	* it.  The only opportunity to get this site before our invalid certificate
	* handler is invoked is in the subsequent STATE_START | STATE_IS_REQUEST
	* transition.  When this comes, if the request's name appears to be a
	* url then take this to be the new site, in case our invalid certificate
	* handler is about to be invoked.
	* 
	* Note that updateLastNavigateUrl is not reset to false here so that in
	* typical contexts where a redirect occurs without an accompanying invalid
	* certificate, the updated site will be retrieved from the channel (this
	* is more proper) on the next STATE_TRANSFERRING | STATE_IS_DOCUMENT transition.
	*/
	if (updateLastNavigateUrl && aStateFlags == (nsIWebProgressListener.STATE_IS_REQUEST | nsIWebProgressListener.STATE_START)) {
		nsIRequest request = new nsIRequest (aRequest);
		int /*long*/ name = XPCOM.nsEmbedCString_new ();
		int rc = request.GetName (name);
		if (rc == XPCOM.NS_OK) {
			int length = XPCOM.nsEmbedCString_Length (name);
			int /*long*/ buffer = XPCOM.nsEmbedCString_get (name);
			byte[] bytes = new byte[length];
			XPCOM.memmove (bytes, buffer, length);
			String value = new String (bytes);
			if (value.indexOf (":/") != -1) lastNavigateURL = value;	//$NON-NLS-1$
		}
		XPCOM.nsEmbedCString_delete (name);
	}

	if ((aStateFlags & nsIWebProgressListener.STATE_IS_DOCUMENT) == 0) return XPCOM.NS_OK;
	if ((aStateFlags & nsIWebProgressListener.STATE_START) != 0) {
		int /*long*/[] result = new int /*long*/[1];

		/*
		* When navigating to a site that is known to have a bad certificate, request notification
		* callbacks on the channel so that our nsIBadCertListener2 will be invoked.
		*/
		if (isRetrievingBadCert) {
			isRetrievingBadCert = false;
			nsIRequest request = new nsIRequest (aRequest);
			int rc = request.QueryInterface (nsIChannel.NS_ICHANNEL_IID, result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

			nsIChannel channel = new nsIChannel (result[0]);
			result[0] = 0;
			rc = channel.SetNotificationCallbacks (interfaceRequestor.getAddress ());
			if (rc != XPCOM.NS_OK) error (rc);
			channel.Release ();
			return XPCOM.NS_OK;
		}

		if (request == 0) request = aRequest;
		registerFunctionsOnState = nsIWebProgressListener.STATE_IS_REQUEST | nsIWebProgressListener.STATE_START;
		/*
		 * Add the page's nsIDOMWindow to the collection of windows that will
		 * have DOM listeners added to them later on in the page loading
		 * process.  These listeners cannot be added yet because the
		 * nsIDOMWindow is not ready to take them at this stage.
		 */
		nsIWebProgress progress = new nsIWebProgress (aWebProgress);
		int rc = progress.GetDOMWindow (result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
		unhookedDOMWindows.addElement (new LONG (result[0]));
	} else if ((aStateFlags & nsIWebProgressListener.STATE_REDIRECTING) != 0) {
		if (request == aRequest) request = 0;
		registerFunctionsOnState = nsIWebProgressListener.STATE_TRANSFERRING;
		updateLastNavigateUrl = true;
	} else if ((aStateFlags & nsIWebProgressListener.STATE_STOP) != 0) {
		/*
		* If this page's nsIDOMWindow handle is still in unhookedDOMWindows then
		* add its DOM listeners now.  It's possible for this to happen since
		* there is no guarantee that a STATE_TRANSFERRING state change will be
		* received for every window in a page, which is when these listeners
		* are typically added.
		*/
		int /*long*/[] result = new int /*long*/[1];
		nsIWebProgress progress = new nsIWebProgress (aWebProgress);
		int rc = progress.GetDOMWindow (result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
		nsIDOMWindow domWindow = new nsIDOMWindow (result[0]);

		LONG ptrObject = new LONG (result[0]);
		result[0] = 0;
		int index = unhookedDOMWindows.indexOf (ptrObject);
		if (index != -1) {
			rc = webBrowser.GetContentDOMWindow (result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
			boolean isTop = result[0] == domWindow.getAddress ();
			new nsISupports (result[0]).Release ();
			result[0] = 0;

			rc = domWindow.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

			nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
			result[0] = 0;
			hookDOMListeners (target, isTop);
			target.Release ();

			/*
			* Remove and unreference the nsIDOMWindow from the collection of windows
			* that are waiting to have DOM listeners hooked on them. 
			*/
			unhookedDOMWindows.remove (ptrObject);
			new nsISupports (ptrObject.value).Release ();
		}

		boolean deferCompleted = false;
		/*
		 * If htmlBytes is not null then there is html from a previous setText() call
		 * waiting to be set into the about:blank page once it has completed loading. 
		 */
		if (htmlBytes != null) {
			nsIRequest req = new nsIRequest (aRequest);
			int /*long*/ name = XPCOM.nsEmbedCString_new ();
			rc = req.GetName (name);
			if (rc != XPCOM.NS_OK) error (rc);
			int length = XPCOM.nsEmbedCString_Length (name);
			int /*long*/ buffer = XPCOM.nsEmbedCString_get (name);
			byte[] dest = new byte[length];
			XPCOM.memmove (dest, buffer, length);
			String url = new String (dest);
			XPCOM.nsEmbedCString_delete (name);

			if (url.startsWith (ABOUT_BLANK)) {
				/*
				 * Setting mozilla's content with nsIWebBrowserStream invalidates the 
				 * DOM listeners that were hooked on it (about:blank), so remove them and
				 * add new ones after the content has been set.
				 */
				unhookDOMListeners ();

				rc = XPCOM.NS_GetServiceManager (result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
				result[0] = 0;
				rc = serviceManager.GetService (XPCOM.NS_IOSERVICE_CID, nsIIOService.NS_IIOSERVICE_IID, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
				serviceManager.Release ();

				nsIIOService ioService = new nsIIOService (result[0]);
				result[0] = 0;
				byte[] aString;
				if (untrustedText) {
					aString = MozillaDelegate.wcsToMbcs (null, ABOUT_BLANK, false);
				} else {
					aString = MozillaDelegate.wcsToMbcs (null, URI_FILEROOT, false);
				}
				int /*long*/ aSpec = XPCOM.nsEmbedCString_new (aString, aString.length);
				rc = ioService.NewURI (aSpec, null, 0, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
				XPCOM.nsEmbedCString_delete (aSpec);
				ioService.Release ();

				nsIURI uri = new nsIURI (result[0]);
				result[0] = 0;

				rc = webBrowser.QueryInterface (nsIWebBrowserStream.NS_IWEBBROWSERSTREAM_IID, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsIWebBrowserStream stream = new nsIWebBrowserStream (result[0]);
				result[0] = 0;

				byte[] contentTypeBuffer = MozillaDelegate.wcsToMbcs (null, "text/html", false); // $NON-NLS-1$
				int /*long*/ aContentType = XPCOM.nsEmbedCString_new (contentTypeBuffer, contentTypeBuffer.length);

				rc = stream.OpenStream (uri.getAddress (), aContentType);
				if (rc != XPCOM.NS_OK) error (rc);

				/*
				* For Mozilla < 1.9.2, when content is being set via nsIWebBrowserStream, this
				* is the only place where registered functions can be re-installed such that
				* they will be invokable at load time by JS contained in the text.
				*/
				Enumeration elements = functions.elements ();
				while (elements.hasMoreElements ()) {
					BrowserFunction function = (BrowserFunction)elements.nextElement ();
					execute (function.functionString);
				}
				/* 
				* For Mozilla >= 1.9.2, when content is being set via nsIWebBrowserStream,
				* registered functions must be re-installed in the subsequent Start Request
				* in order to be invokable at load time by JS contained in the text.
				*/
				registerFunctionsOnState = nsIWebProgressListener.STATE_IS_REQUEST | nsIWebProgressListener.STATE_START;

				int /*long*/ ptr = C.malloc (htmlBytes.length);
				XPCOM.memmove (ptr, htmlBytes, htmlBytes.length);
				int pageSize = 8192;
				int pageCount = htmlBytes.length / pageSize + 1;
				int /*long*/ current = ptr;
				for (int i = 0; i < pageCount; i++) {
					length = i == pageCount - 1 ? htmlBytes.length % pageSize : pageSize;
					if (length > 0) {
						rc = stream.AppendToStream (current, length);
						if (rc != XPCOM.NS_OK) error (rc);
					}
					current += pageSize;
				}
				rc = stream.CloseStream ();
				if (rc != XPCOM.NS_OK) error (rc);
				C.free (ptr);
				XPCOM.nsEmbedCString_delete (aContentType);
				stream.Release ();
				uri.Release ();
				htmlBytes = null;
				/*
				* Browser content that is set via nsIWebBrowserStream is not parsed immediately.
				* Since clients depend on the Completed event to know when the browser's content
				* is available, delay the sending of this event so that the stream content will
				* be parsed first.
				*/
				deferCompleted = true;

				rc = webBrowser.GetContentDOMWindow (result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
				boolean isTop = result[0] == domWindow.getAddress ();
				new nsISupports (result[0]).Release ();
				result[0] = 0;

				rc = domWindow.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
				nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
				result[0] = 0;
				hookDOMListeners (target, isTop);
				target.Release ();
			} else {
				registerFunctionsOnState = 0;
			}
		}
		domWindow.Release ();

		/*
		* Feature in Mozilla.  When a request is redirected (STATE_REDIRECTING),
		* it never reaches the state STATE_STOP and it is replaced with a new request.
		* The new request is received when it is in the state STATE_STOP.
		* To handle this case,  the variable request is set to 0 when the corresponding
		* request is redirected. The following request received with the state STATE_STOP
		* - the new request resulting from the redirection - is used to send
		* the ProgressListener.completed event.
		*/
		if (request == aRequest || request == 0) {
			request = 0;
			StatusTextEvent event = new StatusTextEvent (browser);
			event.display = browser.getDisplay ();
			event.widget = browser;
			event.text = ""; //$NON-NLS-1$
			for (int i = 0; i < statusTextListeners.length; i++) {
				statusTextListeners[i].changed (event);
			}

			final Display display = browser.getDisplay ();
			final ProgressEvent event2 = new ProgressEvent (browser);
			event2.display = display;
			event2.widget = browser;
			Runnable runnable = new Runnable () {
				public void run () {
					if (browser.isDisposed ()) return;
					for (int i = 0; i < progressListeners.length; i++) {
						progressListeners[i].completed (event2);
					}
				}
			};
			if (deferCompleted) {
				display.asyncExec (runnable);
			} else {
				display.syncExec (runnable);
			}
		}
	} else if ((aStateFlags & nsIWebProgressListener.STATE_TRANSFERRING) != 0) {
		if (updateLastNavigateUrl) {
			updateLastNavigateUrl = false;
			int /*long*/[] result = new int /*long*/[1];
			nsIRequest request = new nsIRequest (aRequest);

			int rc = request.QueryInterface (nsIChannel.NS_ICHANNEL_IID, result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
			nsIChannel channel = new nsIChannel (result[0]);
			result[0] = 0;
			rc = channel.GetURI (result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);
			channel.Release ();

			nsIURI uri = new nsIURI (result[0]);
			result[0] = 0;
			int /*long*/ aSpec = XPCOM.nsEmbedCString_new ();
			rc = uri.GetSpec (aSpec);
			if (rc != XPCOM.NS_OK) error (rc);
			int length = XPCOM.nsEmbedCString_Length (aSpec);
			int /*long*/ buffer = XPCOM.nsEmbedCString_get (aSpec);
			byte[] bytes = new byte[length];
			XPCOM.memmove (bytes, buffer, length);
			lastNavigateURL = new String (bytes);
			XPCOM.nsEmbedCString_delete (aSpec);
			uri.Release ();
		}

		/*
		* Hook DOM listeners to the page's nsIDOMWindow here because this is
		* the earliest opportunity to do so.    
		*/
		int /*long*/[] result = new int /*long*/[1];
		nsIWebProgress progress = new nsIWebProgress (aWebProgress);
		int rc = progress.GetDOMWindow (result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
		nsIDOMWindow domWindow = new nsIDOMWindow (result[0]);

		LONG ptrObject = new LONG (result[0]);
		result[0] = 0;
		int index = unhookedDOMWindows.indexOf (ptrObject);
		if (index != -1) {
			rc = webBrowser.GetContentDOMWindow (result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
			boolean isTop = result[0] == domWindow.getAddress ();
			new nsISupports (result[0]).Release ();
			result[0] = 0;

			rc = domWindow.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

			nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
			result[0] = 0;
			hookDOMListeners (target, isTop);
			target.Release ();

			/*
			* Remove and unreference the nsIDOMWindow from the collection of windows
			* that are waiting to have DOM listeners hooked on them. 
			*/
			unhookedDOMWindows.remove (ptrObject);
			new nsISupports (ptrObject.value).Release ();
		}
		domWindow.Release ();
	}
	return XPCOM.NS_OK;
}

int OnProgressChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int aCurSelfProgress, int aMaxSelfProgress, int aCurTotalProgress, int aMaxTotalProgress) {
	if (progressListeners.length == 0) return XPCOM.NS_OK;
	ProgressEvent event = new ProgressEvent (browser);
	event.display = browser.getDisplay ();
	event.widget = browser;
	event.current = aCurTotalProgress;
	event.total = aMaxTotalProgress;
	for (int i = 0; i < progressListeners.length; i++) {
		progressListeners[i].changed (event);
	}
	return XPCOM.NS_OK;
}

int OnLocationChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int /*long*/ aLocation) {
	/*
	* Feature in Mozilla.  When a page is loaded via setText before a previous
	* setText page load has completed, the expected OnStateChange STATE_STOP for the
	* original setText never arrives because it gets replaced by the OnStateChange
	* STATE_STOP for the new request.  This results in the request field never being
	* cleared because the original request's OnStateChange STATE_STOP is still expected
	* (but never arrives).  To handle this case, the request field is updated to the new
	* overriding request since its OnStateChange STATE_STOP will be received next.
	*/
	if (request != 0 && request != aRequest) request = aRequest;

	if (locationListeners.length == 0) return XPCOM.NS_OK;

	nsIWebProgress webProgress = new nsIWebProgress (aWebProgress);
	int /*long*/[] aDOMWindow = new int /*long*/[1];
	int rc = webProgress.GetDOMWindow (aDOMWindow);
	if (rc != XPCOM.NS_OK) error (rc);
	if (aDOMWindow[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIDOMWindow domWindow = new nsIDOMWindow (aDOMWindow[0]);
	int /*long*/[] aTop = new int /*long*/[1];
	rc = domWindow.GetTop (aTop);
	if (rc != XPCOM.NS_OK) error (rc);
	if (aTop[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	domWindow.Release ();
	
	nsIDOMWindow topWindow = new nsIDOMWindow (aTop[0]);
	topWindow.Release ();
	
	nsIURI location = new nsIURI (aLocation);
	int /*long*/ aSpec = XPCOM.nsEmbedCString_new ();
	location.GetSpec (aSpec);
	int length = XPCOM.nsEmbedCString_Length (aSpec);
	int /*long*/ buffer = XPCOM.nsEmbedCString_get (aSpec);
	byte[] dest = new byte[length];
	XPCOM.memmove (dest, buffer, length);
	XPCOM.nsEmbedCString_delete (aSpec);
	String url = new String (dest);

	/*
	 * As of Mozilla 1.8, the first time that a page is displayed, regardless of
	 * whether it's via Browser.setURL() or Browser.setText(), the GRE navigates
	 * to about:blank and fires the corresponding navigation events.  Do not send
	 * this event on to the user since it is not expected.
	 */
	if (!IsPre_1_8 && aRequest == 0 && url.startsWith (ABOUT_BLANK)) return XPCOM.NS_OK;

	LocationEvent event = new LocationEvent (browser);
	event.display = browser.getDisplay ();
	event.widget = browser;
	event.location = url;
	/*
	 * If the URI indicates that the page is being rendered from memory
	 * (via setText()) then set it to about:blank to be consistent with IE.
	 */
	if (event.location.equals (URI_FILEROOT)) {
		event.location = ABOUT_BLANK;
	} else {
		length = URI_FILEROOT.length ();
		if (event.location.startsWith (URI_FILEROOT) && event.location.charAt (length) == '#') {
			event.location = ABOUT_BLANK + event.location.substring (length);
		}
	}
	event.top = aTop[0] == aDOMWindow[0];
	for (int i = 0; i < locationListeners.length; i++) {
		locationListeners[i].changed (event);
	}
	return XPCOM.NS_OK;
}

int OnStatusChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int aStatus, int /*long*/ aMessage) {
	if (statusTextListeners.length == 0) return XPCOM.NS_OK;
	StatusTextEvent event = new StatusTextEvent (browser);
	event.display = browser.getDisplay ();
	event.widget = browser;
	int length = XPCOM.strlen_PRUnichar (aMessage);
	char[] dest = new char[length];
	XPCOM.memmove (dest, aMessage, length * 2);
	event.text = new String (dest);
	for (int i = 0; i < statusTextListeners.length; i++) {
		statusTextListeners[i].changed (event);
	}
	return XPCOM.NS_OK;
}		

int OnSecurityChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int state) {
	return XPCOM.NS_OK;
}

/* nsIWebBrowserChrome */

int SetStatus (int statusType, int /*long*/ status) {
	if (statusTextListeners.length == 0) return XPCOM.NS_OK;
	StatusTextEvent event = new StatusTextEvent (browser);
	event.display = browser.getDisplay ();
	event.widget = browser;
	int length = XPCOM.strlen_PRUnichar (status);
	char[] dest = new char[length];
	XPCOM.memmove (dest, status, length * 2);
	String string = new String (dest);
	event.text = string;
	for (int i = 0; i < statusTextListeners.length; i++) {
		statusTextListeners[i].changed (event);
	}
	return XPCOM.NS_OK;
}

int GetWebBrowser (int /*long*/ aWebBrowser) {
	int /*long*/[] ret = new int /*long*/[1];	
	if (webBrowser != null) {
		webBrowser.AddRef ();
		ret[0] = webBrowser.getAddress ();	
	}
	XPCOM.memmove (aWebBrowser, ret, C.PTR_SIZEOF);
	return XPCOM.NS_OK;
}

int SetWebBrowser (int /*long*/ aWebBrowser) {
	if (webBrowser != null) webBrowser.Release ();
	webBrowser = aWebBrowser != 0 ? new nsIWebBrowser (aWebBrowser) : null;  				
	return XPCOM.NS_OK;
}
   
int GetChromeFlags (int /*long*/ aChromeFlags) {
	int[] ret = new int[1];
	ret[0] = chromeFlags;
	XPCOM.memmove (aChromeFlags, ret, 4); /* PRUint32 */
	return XPCOM.NS_OK;
}

int SetChromeFlags (int aChromeFlags) {
	chromeFlags = aChromeFlags;
	return XPCOM.NS_OK;
}

int DestroyBrowserWindow () {
	WindowEvent newEvent = new WindowEvent (browser);
	newEvent.display = browser.getDisplay ();
	newEvent.widget = browser;
	for (int i = 0; i < closeWindowListeners.length; i++) {
		closeWindowListeners[i].close (newEvent);
	}
	/*
	* Note on Mozilla.  The DestroyBrowserWindow notification cannot be cancelled.
	* The browser widget cannot be used after this notification has been received.
	* The application is advised to close the window hosting the browser widget.
	* The browser widget must be disposed in all cases.
	*/
	browser.dispose ();
	return XPCOM.NS_OK;
}
   	
int SizeBrowserTo (int aCX, int aCY) {
	size = new Point (aCX, aCY);
	boolean isChrome = (chromeFlags & nsIWebBrowserChrome.CHROME_OPENAS_CHROME) != 0;
	if (isChrome) {
		Shell shell = browser.getShell ();
		shell.setSize (shell.computeSize (size.x, size.y));
	}
	return XPCOM.NS_OK;
}

int ShowAsModal () {
	int /*long*/[] result = new int /*long*/[1];
	int rc = XPCOM.NS_GetServiceManager (result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

	nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
	result[0] = 0;
	byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_CONTEXTSTACK_CONTRACTID, true);
	rc = serviceManager.GetServiceByContractID (aContractID, nsIJSContextStack.NS_IJSCONTEXTSTACK_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
	serviceManager.Release ();

	nsIJSContextStack stack = new nsIJSContextStack (result[0]);
	result[0] = 0;
	rc = stack.Push (0);
	if (rc != XPCOM.NS_OK) error (rc);

	Shell shell = browser.getShell ();
	Display display = browser.getDisplay ();
	while (!shell.isDisposed ()) {
		if (!display.readAndDispatch ()) display.sleep ();
	}

	rc = stack.Pop (result);
	if (rc != XPCOM.NS_OK) error (rc);
	stack.Release ();
	return XPCOM.NS_OK;
}

int IsWindowModal (int /*long*/ retval) {
	int result = (chromeFlags & nsIWebBrowserChrome.CHROME_MODAL) != 0 ? 1 : 0;
	XPCOM.memmove (retval, new int[] {result}, 4); /* PRBool */
	return XPCOM.NS_OK;
}
   
int ExitModalEventLoop (int aStatus) {
	return XPCOM.NS_OK;
}

/* nsIEmbeddingSiteWindow */ 

int SetDimensions (int flags, int x, int y, int cx, int cy) {
	if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_POSITION) != 0) {
		location = new Point (x, y);
		browser.getShell ().setLocation (x, y);
	}
	if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_SIZE_INNER) != 0) {
		browser.setSize (cx, cy);
	}
	if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_SIZE_OUTER) != 0) {
		browser.getShell ().setSize (cx, cy);
	}
	return XPCOM.NS_OK;
}

int GetDimensions (int flags, int /*long*/ x, int /*long*/ y, int /*long*/ cx, int /*long*/ cy) {
	if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_POSITION) != 0) {
		Point location = browser.getShell ().getLocation ();
		if (x != 0) C.memmove (x, new int[] {location.x}, 4); /* PRInt32 */
		if (y != 0) C.memmove (y, new int[] {location.y}, 4); /* PRInt32 */
	}
	if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_SIZE_INNER) != 0) {
		Point size = browser.getSize ();
		if (cx != 0) C.memmove (cx, new int[] {size.x}, 4); /* PRInt32 */
		if (cy != 0) C.memmove (cy, new int[] {size.y}, 4); /* PRInt32 */
	}
	if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_SIZE_OUTER) != 0) {
		Point size = browser.getShell().getSize ();
		if (cx != 0) C.memmove (cx, new int[] {size.x}, 4); /* PRInt32 */
		if (cy != 0) C.memmove (cy, new int[] {size.y}, 4); /* PRInt32 */
	}
	return XPCOM.NS_OK;
}

int SetFocus () {
	int /*long*/[] result = new int /*long*/[1];
	int rc = webBrowser.QueryInterface (nsIBaseWindow.NS_IBASEWINDOW_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
	
	nsIBaseWindow baseWindow = new nsIBaseWindow (result[0]);
	rc = baseWindow.SetFocus ();
	if (rc != XPCOM.NS_OK) error (rc);
	baseWindow.Release ();

	/*
	* Note. Mozilla notifies here that one of the children took
	* focus. This could or should be used to fire an SWT.FOCUS_IN
	* event on Browser focus listeners.
	*/
	return XPCOM.NS_OK;     	
}	

int GetVisibility (int /*long*/ aVisibility) {
	boolean visible = browser.isVisible () && !browser.getShell ().getMinimized ();
	XPCOM.memmove (aVisibility, new int[] {visible ? 1 : 0}, 4); /* PRBool */
	return XPCOM.NS_OK;
}

int SetVisibility (int aVisibility) {
	if (isChild) {
		WindowEvent event = new WindowEvent (browser);
		event.display = browser.getDisplay ();
		event.widget = browser;
		if (aVisibility != 0) {
			/*
			* Bug in Mozilla.  When the JavaScript window.open is executed, Mozilla
			* fires multiple SetVisibility 1 notifications.  The workaround is
			* to ignore subsequent notifications. 
			*/
			if (!visible) {
				visible = true;
				event.location = location;
				event.size = size;
				event.addressBar = (chromeFlags & nsIWebBrowserChrome.CHROME_LOCATIONBAR) != 0;
				/* Feature of OSX.  The menu bar is always displayed. */
				boolean isOSX = Platform.PLATFORM.equals ("cocoa") || Platform.PLATFORM.equals ("carbon");
				event.menuBar = isOSX || (chromeFlags & nsIWebBrowserChrome.CHROME_MENUBAR) != 0;
				event.statusBar = (chromeFlags & nsIWebBrowserChrome.CHROME_STATUSBAR) != 0;
				event.toolBar = (chromeFlags & nsIWebBrowserChrome.CHROME_TOOLBAR) != 0;
				for (int i = 0; i < visibilityWindowListeners.length; i++) {
					visibilityWindowListeners[i].show (event);
				}
				location = null;
				size = null;
			}
		} else {
			visible = false;
			for (int i = 0; i < visibilityWindowListeners.length; i++) {
				visibilityWindowListeners[i].hide (event);
			}
		}
	}
	return XPCOM.NS_OK;     	
}

int GetTitle (int /*long*/ aTitle) {
	return XPCOM.NS_OK;     	
}
 
int SetTitle (int /*long*/ aTitle) {
	if (titleListeners.length == 0) return XPCOM.NS_OK;
	TitleEvent event = new TitleEvent (browser);
	event.display = browser.getDisplay ();
	event.widget = browser;
	/*
	* To be consistent with other platforms the title event should
	* contain the page's url if the page does not contain a <title>
	* tag. 
	*/
	int length = XPCOM.strlen_PRUnichar (aTitle);
	if (length > 0) {
		char[] dest = new char[length];
		XPCOM.memmove (dest, aTitle, length * 2);
		event.title = new String (dest);
	} else {
		event.title = getUrl ();
	}
	for (int i = 0; i < titleListeners.length; i++) {
		titleListeners[i].changed (event);
	}
	return XPCOM.NS_OK;     	
}

int GetSiteWindow (int /*long*/ aSiteWindow) {
	/*
	* Note.  The handle is expected to be an HWND on Windows and
	* a GtkWidget* on GTK.  This callback is invoked on Windows
	* when the javascript window.print is invoked and the print
	* dialog comes up. If no handle is returned, the print dialog
	* does not come up on this platform.  
	*/
	XPCOM.memmove (aSiteWindow, new int /*long*/[] {embedHandle}, C.PTR_SIZEOF);
	return XPCOM.NS_OK;     	
}  
 
/* nsIWebBrowserChromeFocus */

int FocusNextElement () {
	/*
	* Bug in Mozilla embedding API.  Mozilla takes back the focus after sending
	* this event.  This prevents tabbing out of Mozilla. This behaviour can be reproduced
	* with the Mozilla application TestGtkEmbed.  The workaround is to
	* send the traversal notification after this callback returns.
	*/
	browser.getDisplay ().asyncExec (new Runnable () {
		public void run () {
			if (browser.isDisposed ()) return;
			browser.traverse (SWT.TRAVERSE_TAB_NEXT);
		}
	});
	return XPCOM.NS_OK;  
}

int FocusPrevElement () {
	/*
	* Bug in Mozilla embedding API.  Mozilla takes back the focus after sending
	* this event.  This prevents tabbing out of Mozilla. This behaviour can be reproduced
	* with the Mozilla application TestGtkEmbed.  The workaround is to
	* send the traversal notification after this callback returns.
	*/
	browser.getDisplay ().asyncExec (new Runnable () {
		public void run () {
			if (browser.isDisposed ()) return;
			browser.traverse (SWT.TRAVERSE_TAB_PREVIOUS);
		}
	});
	return XPCOM.NS_OK;     	
}

/* nsIContextMenuListener */

int OnShowContextMenu (int aContextFlags, int /*long*/ aEvent, int /*long*/ aNode) {
	nsIDOMEvent domEvent = new nsIDOMEvent (aEvent);
	int /*long*/[] result = new int /*long*/[1];
	int rc = domEvent.QueryInterface (nsIDOMMouseEvent.NS_IDOMMOUSEEVENT_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

	nsIDOMMouseEvent domMouseEvent = new nsIDOMMouseEvent (result[0]);
	int[] aScreenX = new int[1], aScreenY = new int[1];
	rc = domMouseEvent.GetScreenX (aScreenX);
	if (rc != XPCOM.NS_OK) error (rc);
	rc = domMouseEvent.GetScreenY (aScreenY);
	if (rc != XPCOM.NS_OK) error (rc);
	domMouseEvent.Release ();
	
	Event event = new Event ();
	event.x = aScreenX[0];
	event.y = aScreenY[0];
	browser.notifyListeners (SWT.MenuDetect, event);
	if (!event.doit || browser.isDisposed ()) return XPCOM.NS_OK;
	Menu menu = browser.getMenu ();
	if (menu != null && !menu.isDisposed ()) {
		if (aScreenX[0] != event.x || aScreenY[0] != event.y) {
			menu.setLocation (event.x, event.y);
		}
		menu.setVisible (true);
	}
	return XPCOM.NS_OK;     	
}

/* nsIURIContentListener */

int OnStartURIOpen (int /*long*/ aURI, int /*long*/ retval) {
	if (isRetrievingBadCert) return XPCOM.NS_OK;
	authCount = 0;

	nsIURI location = new nsIURI (aURI);
	int /*long*/ aSpec = XPCOM.nsEmbedCString_new ();
	location.GetSpec (aSpec);
	int length = XPCOM.nsEmbedCString_Length (aSpec);
	int /*long*/ buffer = XPCOM.nsEmbedCString_get (aSpec);
	buffer = XPCOM.nsEmbedCString_get (aSpec);
	byte[] dest = new byte[length];
	XPCOM.memmove (dest, buffer, length);
	XPCOM.nsEmbedCString_delete (aSpec);
	String value = new String (dest);

	/*
	* Navigating to "...aboutCertError.xhtml", or to "javascript:showSecuritySection()" when
	* the page "netError.xhtml" is showing, indicates that the last attempted page view had
	* an invalid certificate.  When this happens, veto the current navigate and re-navigate
	* to the page with the bad certificate so that NotifyCertProblem will be invoked.
	*/
	if (value.indexOf ("aboutCertError.xhtml") != -1 || (isViewingErrorPage && value.indexOf ("javascript:showSecuritySection") != -1)) { //$NON-NLS-1$ //$NON-NLS-2$
		XPCOM.memmove (retval, new int[] {1}, 4); /* PRBool */
		isRetrievingBadCert = true;
		setUrl (lastNavigateURL, null, null);
		return XPCOM.NS_OK;
	}
	isViewingErrorPage = value.indexOf ("netError.xhtml") != -1; //$NON-NLS-1$

	boolean doit = true;
	if (request == 0) {
		/* 
		 * listeners should not be notified of internal transitions like "javascript:..."
		 * because this is an implementation side-effect, not a true navigate
		 */
		if (!value.startsWith (PREFIX_JAVASCRIPT)) {
			if (locationListeners.length > 0) {
				LocationEvent event = new LocationEvent (browser);
				event.display = browser.getDisplay();
				event.widget = browser;
				event.location = value;
				/*
				 * If the URI indicates that the page is being rendered from memory
				 * (via setText()) then set it to about:blank to be consistent with IE.
				 */
				if (event.location.equals (URI_FILEROOT)) {
					event.location = ABOUT_BLANK;
				} else {
					length = URI_FILEROOT.length ();
					if (event.location.startsWith (URI_FILEROOT) && event.location.charAt (length) == '#') {
						event.location = ABOUT_BLANK + event.location.substring (length);
					}
				}
				event.doit = doit;
				for (int i = 0; i < locationListeners.length; i++) {
					locationListeners[i].changing (event);
				}
				doit = event.doit && !browser.isDisposed();
			}

			if (doit) {
				if (jsEnabledChanged) {
					jsEnabledChanged = false;
	
					int /*long*/[] result = new int /*long*/[1];
					int rc = webBrowser.QueryInterface (nsIWebBrowserSetup.NS_IWEBBROWSERSETUP_IID, result);
					if (rc != XPCOM.NS_OK) error (rc);
					if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
	
					nsIWebBrowserSetup setup = new nsIWebBrowserSetup (result[0]);
					result[0] = 0;
					rc = setup.SetProperty (nsIWebBrowserSetup.SETUP_ALLOW_JAVASCRIPT, jsEnabled ? 1 : 0);
					if (rc != XPCOM.NS_OK) error (rc);
					setup.Release ();
				}
				if (!isViewingErrorPage) lastNavigateURL = value;
			}
		}
	}
	XPCOM.memmove (retval, new int[] {doit ? 0 : 1}, 4); /* PRBool */
	return XPCOM.NS_OK;
}

int DoContent (int /*long*/ aContentType, int aIsContentPreferred, int /*long*/ aRequest, int /*long*/ aContentHandler, int /*long*/ retval) {
	return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

int IsPreferred (int /*long*/ aContentType, int /*long*/ aDesiredContentType, int /*long*/ retval) {
	boolean preferred = false;
	int size = XPCOM.strlen (aContentType);
	if (size > 0) {
		byte[] typeBytes = new byte[size + 1];
		XPCOM.memmove (typeBytes, aContentType, size);
		String contentType = new String (typeBytes, 0, size);

		/* do not attempt to handle known problematic content types */
		if (!contentType.equals (XPCOM.CONTENT_MAYBETEXT) && !contentType.equals (XPCOM.CONTENT_MULTIPART)) {
			/* determine whether browser can handle the content type */
			int /*long*/[] result = new int /*long*/[1];
			int rc = XPCOM.NS_GetServiceManager (result);
			if (rc != XPCOM.NS_OK) error (rc);
			if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
			nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
			result[0] = 0;

			/* First try to use the nsIWebNavigationInfo if it's available (>= mozilla 1.8) */
			byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_WEBNAVIGATIONINFO_CONTRACTID, true);
			rc = serviceManager.GetServiceByContractID (aContractID, nsIWebNavigationInfo.NS_IWEBNAVIGATIONINFO_IID, result);
			if (rc == XPCOM.NS_OK) {
				byte[] bytes = MozillaDelegate.wcsToMbcs (null, contentType, false);
				int /*long*/ typePtr = XPCOM.nsEmbedCString_new (bytes, bytes.length);
				nsIWebNavigationInfo info = new nsIWebNavigationInfo (result[0]);
				result[0] = 0;
				int[] isSupportedResult = new int[1]; /* PRUint32 */
				rc = info.IsTypeSupported (typePtr, 0, isSupportedResult);
				if (rc != XPCOM.NS_OK) error (rc);
				info.Release ();
				XPCOM.nsEmbedCString_delete (typePtr);
				preferred = isSupportedResult[0] != 0;
			} else {
				/* nsIWebNavigationInfo is not available, so do the type lookup */
				result[0] = 0;
				rc = serviceManager.GetService (XPCOM.NS_CATEGORYMANAGER_CID, nsICategoryManager.NS_ICATEGORYMANAGER_IID, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

				nsICategoryManager categoryManager = new nsICategoryManager (result[0]);
				result[0] = 0;
				byte[] categoryBytes = MozillaDelegate.wcsToMbcs (null, "Gecko-Content-Viewers", true);	//$NON-NLS-1$
				rc = categoryManager.GetCategoryEntry (categoryBytes, typeBytes, result);
				categoryManager.Release ();
				/* if no viewer for the content type is registered then rc == XPCOM.NS_ERROR_NOT_AVAILABLE */
				preferred = rc == XPCOM.NS_OK;
			}
			serviceManager.Release ();
		}
	}

	XPCOM.memmove(retval, new int[] {preferred ? 1 : 0}, 4); /* PRBool */
	if (preferred) {
		XPCOM.memmove (aDesiredContentType, new int /*long*/[] {0}, C.PTR_SIZEOF);
	}
	return XPCOM.NS_OK;
}

int CanHandleContent (int /*long*/ aContentType, int aIsContentPreferred, int /*long*/ aDesiredContentType, int /*long*/ retval) {
	return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

int GetLoadCookie (int /*long*/ aLoadCookie) {
	return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

int SetLoadCookie (int /*long*/ aLoadCookie) {
	return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

int GetParentContentListener (int /*long*/ aParentContentListener) {
	return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}
	
int SetParentContentListener (int /*long*/ aParentContentListener) {
	return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

/* nsITooltipListener */

int OnShowTooltip (int aXCoords, int aYCoords, int /*long*/ aTipText) {
	int length = XPCOM.strlen_PRUnichar (aTipText);
	char[] dest = new char[length];
	XPCOM.memmove (dest, aTipText, length * 2);
	String text = new String (dest);
	if (tip != null && !tip.isDisposed ()) tip.dispose ();
	Display display = browser.getDisplay ();
	Shell parent = browser.getShell ();
	tip = new Shell (parent, SWT.ON_TOP);
	tip.setLayout (new FillLayout());
	Label label = new Label (tip, SWT.CENTER);
	label.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND));
	label.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND));
	label.setText (text);
	/*
	* Bug in Mozilla embedded API.  Tooltip coordinates are wrong for 
	* elements inside an inline frame (IFrame tag).  The workaround is 
	* to position the tooltip based on the mouse cursor location.
	*/
	Point point = display.getCursorLocation ();
	/* Assuming cursor is 21x21 because this is the size of
	 * the arrow cursor on Windows
	 */ 
	point.y += 21;
	tip.setLocation (point);
	tip.pack ();
	tip.setVisible (true);
	return XPCOM.NS_OK;
}

int OnHideTooltip () {
	if (tip != null && !tip.isDisposed ()) tip.dispose ();
	tip = null;
	return XPCOM.NS_OK;
}

/* nsIDOMEventListener */

int HandleEvent (int /*long*/ event) {
	nsIDOMEvent domEvent = new nsIDOMEvent (event);

	int /*long*/ type = XPCOM.nsEmbedString_new ();
	int rc = domEvent.GetType (type);
	if (rc != XPCOM.NS_OK) error (rc);
	int length = XPCOM.nsEmbedString_Length (type);
	int /*long*/ buffer = XPCOM.nsEmbedString_get (type);
	char[] chars = new char[length];
	XPCOM.memmove (chars, buffer, length * 2);
	String typeString = new String (chars);
	XPCOM.nsEmbedString_delete (type);

	if (XPCOM.DOMEVENT_UNLOAD.equals (typeString)) {
		int /*long*/[] result = new int /*long*/[1];
		rc = domEvent.GetCurrentTarget (result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

		nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
		unhookDOMListeners (target);
		target.Release ();
		return XPCOM.NS_OK;
	}

	if (XPCOM.DOMEVENT_FOCUS.equals (typeString)) {
		delegate.handleFocus ();
		return XPCOM.NS_OK;
	}

	if (XPCOM.DOMEVENT_KEYDOWN.equals (typeString)) {
		int /*long*/[] result = new int /*long*/[1];
		rc = domEvent.QueryInterface (nsIDOMKeyEvent.NS_IDOMKEYEVENT_IID, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
		nsIDOMKeyEvent domKeyEvent = new nsIDOMKeyEvent (result[0]);
		result[0] = 0;

		int[] aKeyCode = new int[1]; /* PRUint32 */
		rc = domKeyEvent.GetKeyCode (aKeyCode);
		if (rc != XPCOM.NS_OK) error (rc);
		int keyCode = translateKey (aKeyCode[0]);

		/*
		* if keyCode == lastKeyCode then either a repeating key like Shift
		* is being held or a key for which key events are not sent has been
		* pressed.  In both of these cases a KeyDown should not be sent.
		*/
		if (keyCode != lastKeyCode) {
			lastKeyCode = keyCode;
			switch (keyCode) {
				case SWT.SHIFT:
				case SWT.CONTROL:
				case SWT.ALT:
				case SWT.CAPS_LOCK:
				case SWT.NUM_LOCK:
				case SWT.SCROLL_LOCK:
				case SWT.COMMAND: {
					/* keypress events will not be received for these keys, so send KeyDowns for them now */
					int[] aAltKey = new int[1], aCtrlKey = new int[1], aShiftKey = new int[1], aMetaKey = new int[1]; /* PRBool */
					rc = domKeyEvent.GetAltKey (aAltKey);
					if (rc != XPCOM.NS_OK) error (rc);
					rc = domKeyEvent.GetCtrlKey (aCtrlKey);
					if (rc != XPCOM.NS_OK) error (rc);
					rc = domKeyEvent.GetShiftKey (aShiftKey);
					if (rc != XPCOM.NS_OK) error (rc);
					rc = domKeyEvent.GetMetaKey (aMetaKey);
					if (rc != XPCOM.NS_OK) error (rc);

					Event keyEvent = new Event ();
					keyEvent.widget = browser;
					keyEvent.type = SWT.KeyDown;
					keyEvent.keyCode = keyCode;
					keyEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);
					keyEvent.stateMask &= ~keyCode;		/* remove current keydown if it's a state key */
					browser.notifyListeners (keyEvent.type, keyEvent);
					if (!keyEvent.doit || browser.isDisposed ()) {
						domEvent.PreventDefault ();
					}
					break;
				}
				default: {
					/* 
					* If the keydown has Meta (but not Meta+Ctrl) as a modifier then send a KeyDown event for it here
					* because a corresponding keypress event will not be received for it from the DOM.  If the keydown
					* does not have Meta as a modifier, or has Meta+Ctrl as a modifier, then then do nothing here
					* because its KeyDown event will be sent from the keypress listener.
					*/
					int[] aMetaKey = new int[1]; /* PRBool */
					rc = domKeyEvent.GetMetaKey (aMetaKey);
					if (rc != XPCOM.NS_OK) error (rc);
					if (aMetaKey[0] != 0) {
						int[] aCtrlKey = new int[1]; /* PRBool */
						rc = domKeyEvent.GetCtrlKey (aCtrlKey);
						if (rc != XPCOM.NS_OK) error (rc);
						if (aCtrlKey[0] == 0) {
							int[] aAltKey = new int[1], aShiftKey = new int[1]; /* PRBool */
							rc = domKeyEvent.GetAltKey (aAltKey);
							if (rc != XPCOM.NS_OK) error (rc);
							rc = domKeyEvent.GetShiftKey (aShiftKey);
							if (rc != XPCOM.NS_OK) error (rc);

							Event keyEvent = new Event ();
							keyEvent.widget = browser;
							keyEvent.type = SWT.KeyDown;
							keyEvent.keyCode = lastKeyCode;
							keyEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0? SWT.CTRL : 0) | (aShiftKey[0] != 0? SWT.SHIFT : 0) | (aMetaKey[0] != 0? SWT.COMMAND : 0);
							browser.notifyListeners (keyEvent.type, keyEvent);
							if (!keyEvent.doit || browser.isDisposed ()) {
								domEvent.PreventDefault ();
							}
						}
					}
				}
			}
		}

		domKeyEvent.Release ();
		return XPCOM.NS_OK;
	}

	if (XPCOM.DOMEVENT_KEYPRESS.equals (typeString)) {
		/*
		* if keydown could not determine a keycode for this key then it's a
		* key for which key events are not sent (eg.- the Windows key)
		*/
		if (lastKeyCode == 0) return XPCOM.NS_OK;

		/*
		* On linux only, unexpected keypress events are received for some
		* modifier keys.  The workaround is to ignore these events since
		* KeyDown events are sent for these keys in the keydown listener.  
		*/
		switch (lastKeyCode) {
			case SWT.CAPS_LOCK:
			case SWT.NUM_LOCK:
			case SWT.SCROLL_LOCK: return XPCOM.NS_OK;
		}

		int /*long*/[] result = new int /*long*/[1];
		rc = domEvent.QueryInterface (nsIDOMKeyEvent.NS_IDOMKEYEVENT_IID, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
		nsIDOMKeyEvent domKeyEvent = new nsIDOMKeyEvent (result[0]);
		result[0] = 0;

		int[] aAltKey = new int[1], aCtrlKey = new int[1], aShiftKey = new int[1], aMetaKey = new int[1]; /* PRBool */
		rc = domKeyEvent.GetAltKey (aAltKey);
		if (rc != XPCOM.NS_OK) error (rc);
		rc = domKeyEvent.GetCtrlKey (aCtrlKey);
		if (rc != XPCOM.NS_OK) error (rc);
		rc = domKeyEvent.GetShiftKey (aShiftKey);
		if (rc != XPCOM.NS_OK) error (rc);
		rc = domKeyEvent.GetMetaKey (aMetaKey);
		if (rc != XPCOM.NS_OK) error (rc);
		domKeyEvent.Release ();

		int[] aCharCode = new int[1]; /* PRUint32 */
		rc = domKeyEvent.GetCharCode (aCharCode);
		if (rc != XPCOM.NS_OK) error (rc);
		lastCharCode = aCharCode[0];
		if (lastCharCode == 0) {
			switch (lastKeyCode) {
				case SWT.TAB: lastCharCode = SWT.TAB; break;
				case SWT.CR: lastCharCode = SWT.CR; break;
				case SWT.BS: lastCharCode = SWT.BS; break;
				case SWT.ESC: lastCharCode = SWT.ESC; break;
				case SWT.DEL: lastCharCode = SWT.DEL; break;
			}
		}
		if (aCtrlKey[0] != 0 && (0 <= lastCharCode && lastCharCode <= 0x7F)) {
			if ('a'  <= lastCharCode && lastCharCode <= 'z') lastCharCode -= 'a' - 'A';
			if (64 <= lastCharCode && lastCharCode <= 95) lastCharCode -= 64;
		}

		Event keyEvent = new Event ();
		keyEvent.widget = browser;
		keyEvent.type = SWT.KeyDown;
		keyEvent.keyCode = lastKeyCode;
		keyEvent.character = (char)lastCharCode;
		keyEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);
		boolean doit = true;
		if (delegate.sendTraverse ()) {
			doit = sendKeyEvent (keyEvent);
		} else {
			browser.notifyListeners (keyEvent.type, keyEvent);
			doit = keyEvent.doit; 
		}
		if (!doit || browser.isDisposed ()) {
			domEvent.PreventDefault ();
		}
		return XPCOM.NS_OK;
	}

	if (XPCOM.DOMEVENT_KEYUP.equals (typeString)) {
		int /*long*/[] result = new int /*long*/[1];
		rc = domEvent.QueryInterface (nsIDOMKeyEvent.NS_IDOMKEYEVENT_IID, result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
		nsIDOMKeyEvent domKeyEvent = new nsIDOMKeyEvent (result[0]);
		result[0] = 0;

		int[] aKeyCode = new int[1]; /* PRUint32 */
		rc = domKeyEvent.GetKeyCode (aKeyCode);
		if (rc != XPCOM.NS_OK) error (rc);
		int keyCode = translateKey (aKeyCode[0]);
		if (keyCode == 0) {
			/* indicates a key for which key events are not sent */
			domKeyEvent.Release ();
			return XPCOM.NS_OK;
		}
		if (keyCode != lastKeyCode) {
			/* keyup does not correspond to the last keydown */
			lastKeyCode = keyCode;
			lastCharCode = 0;
		}

		int[] aAltKey = new int[1], aCtrlKey = new int[1], aShiftKey = new int[1], aMetaKey = new int[1]; /* PRBool */
		rc = domKeyEvent.GetAltKey (aAltKey);
		if (rc != XPCOM.NS_OK) error (rc);
		rc = domKeyEvent.GetCtrlKey (aCtrlKey);
		if (rc != XPCOM.NS_OK) error (rc);
		rc = domKeyEvent.GetShiftKey (aShiftKey);
		if (rc != XPCOM.NS_OK) error (rc);
		rc = domKeyEvent.GetMetaKey (aMetaKey);
		if (rc != XPCOM.NS_OK) error (rc);
		domKeyEvent.Release ();

		Event keyEvent = new Event ();
		keyEvent.widget = browser;
		keyEvent.type = SWT.KeyUp;
		keyEvent.keyCode = lastKeyCode;
		keyEvent.character = (char)lastCharCode;
		keyEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);
		switch (lastKeyCode) {
			case SWT.SHIFT:
			case SWT.CONTROL:
			case SWT.ALT:
			case SWT.COMMAND: {
				keyEvent.stateMask |= lastKeyCode;
			}
		}
		browser.notifyListeners (keyEvent.type, keyEvent);
		if (!keyEvent.doit || browser.isDisposed ()) {
			domEvent.PreventDefault ();
		}
		lastKeyCode = lastCharCode = 0;
		return XPCOM.NS_OK;
	}

	/* mouse event */

	int /*long*/[] result = new int /*long*/[1];
	rc = domEvent.QueryInterface (nsIDOMMouseEvent.NS_IDOMMOUSEEVENT_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
	nsIDOMMouseEvent domMouseEvent = new nsIDOMMouseEvent (result[0]);
	result[0] = 0;

	/*
	 * MouseOver and MouseOut events are fired any time the mouse enters or exits
	 * any element within the Browser.  To ensure that SWT events are only
	 * fired for mouse movements into or out of the Browser, do not fire an
	 * event if the element being exited (on MouseOver) or entered (on MouseExit)
	 * is within the Browser.
	 */
	if (XPCOM.DOMEVENT_MOUSEOVER.equals (typeString) || XPCOM.DOMEVENT_MOUSEOUT.equals (typeString)) {
		rc = domMouseEvent.GetRelatedTarget (result);
		if (rc != XPCOM.NS_OK) error (rc);
		if (result[0] != 0) {
			domMouseEvent.Release ();
			return XPCOM.NS_OK;
		}
	}

	int[] aScreenX = new int[1], aScreenY = new int[1]; /* PRInt32 */

	/*
	 * The position of mouse events is received in screen-relative coordinates
	 * in order to handle pages with frames, since frames express their event
	 * coordinates relative to themselves rather than relative to their top-
	 * level page.  Convert screen-relative coordinates to be browser-relative.
	 */
	rc = domMouseEvent.GetScreenX (aScreenX);
	if (rc != XPCOM.NS_OK) error (rc);
	rc = domMouseEvent.GetScreenY (aScreenY);
	if (rc != XPCOM.NS_OK) error (rc);
	Point position = new Point (aScreenX[0], aScreenY[0]);
	position = browser.getDisplay ().map (null, browser, position);

	int[] aDetail = new int[1]; /* PRInt32 */
	rc = domMouseEvent.GetDetail (aDetail);
	if (rc != XPCOM.NS_OK) error (rc);
	short[] aButton = new short[1]; /* PRUint16 */
	rc = domMouseEvent.GetButton (aButton);
	if (rc != XPCOM.NS_OK) error (rc);
	int[] aAltKey = new int[1], aCtrlKey = new int[1], aShiftKey = new int[1], aMetaKey = new int[1]; /* PRBool */
	rc = domMouseEvent.GetAltKey (aAltKey);
	if (rc != XPCOM.NS_OK) error (rc);
	rc = domMouseEvent.GetCtrlKey (aCtrlKey);
	if (rc != XPCOM.NS_OK) error (rc);
	rc = domMouseEvent.GetShiftKey (aShiftKey);
	if (rc != XPCOM.NS_OK) error (rc);
	rc = domMouseEvent.GetMetaKey (aMetaKey);
	if (rc != XPCOM.NS_OK) error (rc);
	domMouseEvent.Release ();

	Event mouseEvent = new Event ();
	mouseEvent.widget = browser;
	mouseEvent.x = position.x; mouseEvent.y = position.y;
	mouseEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);

	if (XPCOM.DOMEVENT_MOUSEDOWN.equals (typeString)) {
		delegate.handleMouseDown ();
		mouseEvent.type = SWT.MouseDown;
		mouseEvent.button = aButton[0] + 1;
		mouseEvent.count = aDetail[0];
	} else if (XPCOM.DOMEVENT_MOUSEUP.equals (typeString)) {
		/*
		 * Bug on OSX.  For some reason multiple mouseup events come from the DOM
		 * when button 3 is released on OSX.  The first of these events has a count
		 * detail and the others do not.  The workaround is to not fire received
		 * button 3 mouseup events that do not have a count since mouse events
		 * without a click count are not valid.
		 */
		int button = aButton[0] + 1;
		int count = aDetail[0];
		if (count == 0 && button == 3) return XPCOM.NS_OK;
		mouseEvent.type = SWT.MouseUp;
		mouseEvent.button = button;
		mouseEvent.count = count;
	} else if (XPCOM.DOMEVENT_MOUSEMOVE.equals (typeString)) {
		mouseEvent.type = SWT.MouseMove;
	} else if (XPCOM.DOMEVENT_MOUSEWHEEL.equals (typeString)) {
		mouseEvent.type = SWT.MouseWheel;
		mouseEvent.count = -aDetail[0];
	} else if (XPCOM.DOMEVENT_MOUSEOVER.equals (typeString)) {
		mouseEvent.type = SWT.MouseEnter;
	} else if (XPCOM.DOMEVENT_MOUSEOUT.equals (typeString)) {
		mouseEvent.type = SWT.MouseExit;
	} else if (XPCOM.DOMEVENT_MOUSEDRAG.equals (typeString)) {
		mouseEvent.type = SWT.DragDetect;
		mouseEvent.button = aButton[0] + 1;
		switch (mouseEvent.button) {
			case 1: mouseEvent.stateMask |= SWT.BUTTON1; break;
			case 2: mouseEvent.stateMask |= SWT.BUTTON2; break;
			case 3: mouseEvent.stateMask |= SWT.BUTTON3; break;
			case 4: mouseEvent.stateMask |= SWT.BUTTON4; break;
			case 5: mouseEvent.stateMask |= SWT.BUTTON5; break;
		}
	}

	browser.notifyListeners (mouseEvent.type, mouseEvent);
	if (browser.isDisposed ()) return XPCOM.NS_OK;
	if (aDetail[0] == 2 && XPCOM.DOMEVENT_MOUSEDOWN.equals (typeString)) {
		mouseEvent = new Event ();
		mouseEvent.widget = browser;
		mouseEvent.x = position.x; mouseEvent.y = position.y;
		mouseEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);
		mouseEvent.type = SWT.MouseDoubleClick;
		mouseEvent.button = aButton[0] + 1;
		mouseEvent.count = aDetail[0];
		browser.notifyListeners (mouseEvent.type, mouseEvent);	
	}
	return XPCOM.NS_OK;
}

/* nsIBadCertListener2 */

int NotifyCertProblem (int /*long*/ socketInfo, int /*long*/ status, int /*long*/ targetSite, int /*long*/ _suppressError) {
	/* determine the host name and port */
	int length = XPCOM.nsEmbedCString_Length (targetSite);
	int /*long*/ buffer = XPCOM.nsEmbedCString_get (targetSite);
	byte[] dest = new byte[length];
	XPCOM.memmove (dest, buffer, length);
	final String urlPort = new String (dest);
	int index = urlPort.indexOf (':');
	final String host = urlPort.substring (0,index);
	final int port = Integer.valueOf (urlPort.substring (index + 1)).intValue ();

	/* create text descriptions of the certificate problem(s) */

	int /*long*/[] result = new int /*long*/[1];
	nsISupports supports = new nsISupports (status);
	int rc = supports.QueryInterface (nsISSLStatus.NS_ISSLSTATUS_IID, result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

	nsISSLStatus sslStatus = new nsISSLStatus (result[0]);
	result[0] = 0;
	rc = sslStatus.GetServerCert (result);
	if (rc != XPCOM.NS_OK) error (rc);
	if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);

	final nsIX509Cert cert = new nsIX509Cert (result[0]);
	result[0] = 0;
	String[] problems = new String[3];
	int problemCount = 0, flags = 0;
	int[] intResult = new int[1];

	rc = sslStatus.GetIsDomainMismatch (intResult);
	if (intResult[0] != 0) {
		int /*long*/ ptr = XPCOM.nsEmbedString_new ();
		rc = cert.GetCommonName (ptr);
		if (rc != XPCOM.NS_OK) SWT.error (rc);
		length = XPCOM.nsEmbedString_Length (ptr);
		buffer = XPCOM.nsEmbedString_get (ptr);
		char[] chars = new char[length];
		XPCOM.memmove (chars, buffer, length * 2);
		String name = new String (chars);
		problems[problemCount++] = Compatibility.getMessage ("SWT_InvalidCert_InvalidName", new String[] {name}); //$NON-NLS-1$
		flags |= nsICertOverrideService.ERROR_MISMATCH;
		XPCOM.nsEmbedString_delete (ptr);
	}
	intResult[0] = 0;

	rc = sslStatus.GetIsNotValidAtThisTime (intResult);
	if (intResult[0] != 0) {
		rc = cert.GetValidity (result);
		if (rc != XPCOM.NS_OK) SWT.error (rc);
		if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);

		nsIX509CertValidity validity = new nsIX509CertValidity(result[0]);
		result[0] = 0;

		int /*long*/ ptr = XPCOM.nsEmbedString_new ();
		rc = validity.GetNotBeforeGMT (ptr);
		if (rc != XPCOM.NS_OK) SWT.error (rc);
		length = XPCOM.nsEmbedString_Length (ptr);
		buffer = XPCOM.nsEmbedString_get (ptr);
		char[] chars = new char[length];
		XPCOM.memmove (chars, buffer, length * 2);
		String notBefore = new String (chars);
		XPCOM.nsEmbedString_delete (ptr);

		ptr = XPCOM.nsEmbedString_new ();
		rc = validity.GetNotAfterGMT (ptr);
		if (rc != XPCOM.NS_OK) SWT.error (rc);
		length = XPCOM.nsEmbedString_Length (ptr);
		buffer = XPCOM.nsEmbedString_get (ptr);
		chars = new char[length];
		XPCOM.memmove (chars, buffer, length * 2);
		String notAfter = new String (chars);
		XPCOM.nsEmbedString_delete (ptr);

		String range = notBefore + " - " + notAfter; //$NON-NLS-1$
		problems[problemCount++] = Compatibility.getMessage ("SWT_InvalidCert_NotValid", new String[] {range}); //$NON-NLS-1$
		flags |= nsICertOverrideService.ERROR_TIME;
		validity.Release ();
	}
	intResult[0] = 0;

	rc = sslStatus.GetIsUntrusted (intResult);
	if (intResult[0] != 0) {
		int /*long*/ ptr = XPCOM.nsEmbedString_new ();
		rc = cert.GetIssuerCommonName (ptr);
		if (rc != XPCOM.NS_OK) SWT.error (rc);
		length = XPCOM.nsEmbedString_Length (ptr);
		buffer = XPCOM.nsEmbedString_get (ptr);
		char[] chars = new char[length];
		XPCOM.memmove (chars, buffer, length * 2);
		String name = new String (chars);
		problems[problemCount++] = Compatibility.getMessage ("SWT_InvalidCert_NotTrusted", new String[] {name}); //$NON-NLS-1$
		flags |= nsICertOverrideService.ERROR_UNTRUSTED;
		XPCOM.nsEmbedString_delete (ptr);
	}
	intResult[0] = 0;
	sslStatus.Release ();

	/*
	* The invalid certificate dialog must be shown asynchronously because
	* NotifyCertProblem implementations cannot block.
	*/
	final int finalFlags = flags;
	final String[] finalProblems = new String[problemCount];
	System.arraycopy (problems, 0, finalProblems, 0, problemCount);
	final String url = lastNavigateURL;
	browser.getDisplay().asyncExec(new Runnable() {
		public void run() {
			if (browser.isDisposed ()) return;
			if (!url.equals (lastNavigateURL)) return;	/* user has navigated elsewhere */

			String message = Compatibility.getMessage ("SWT_InvalidCert_Message", new String[] {urlPort}); //$NON-NLS-1$
			if (new PromptDialog (browser.getShell ()).invalidCert (browser, message, finalProblems, cert)) {
				int /*long*/[] result = new int /*long*/[1];
				int rc = XPCOM.NS_GetServiceManager (result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
		
				nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
				result[0] = 0;
				byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_CERTOVERRIDE_CONTRACTID, true);
				rc = serviceManager.GetServiceByContractID (aContractID, nsICertOverrideService.NS_ICERTOVERRIDESERVICE_IID, result);
				if (rc != XPCOM.NS_OK) error (rc);
				if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
				serviceManager.Release ();
		
				nsICertOverrideService overrideService = new nsICertOverrideService (result[0]);
				result[0] = 0;
				byte[] hostBytes = MozillaDelegate.wcsToMbcs (null, host, false);
				int /*long*/ hostString = XPCOM.nsEmbedCString_new (hostBytes, hostBytes.length);
				rc = overrideService.RememberValidityOverride (hostString, port, cert.getAddress (), finalFlags, 1);
				browser.setUrl (url);
				XPCOM.nsEmbedCString_delete (hostString);
				overrideService.Release ();
			}
			cert.Release ();
		}
	});

	C.memmove (_suppressError, new int[] {1}, 4); /* PRInt32 */
	return XPCOM.NS_OK;
}

}
