/*******************************************************************************
 * Copyright (c) 2003, 2012 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;

/* keywords */
	abstract, //$NON-NLS-0$
	boolean, byte, //$NON-NLS-1$ //$NON-NLS-0$
	char, class, //$NON-NLS-1$ //$NON-NLS-0$
	double, //$NON-NLS-0$
	extends, //$NON-NLS-0$
	final, float, //$NON-NLS-1$ //$NON-NLS-0$
	implements, import, instanceof, int, interface, //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
	long, //$NON-NLS-0$
	native, new, //$NON-NLS-1$ //$NON-NLS-0$
	package, private, protected, public, //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
	short, static, synchronized, //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
	throws, transient, //$NON-NLS-1$ //$NON-NLS-0$
	void, volatile //$NON-NLS-1$ //$NON-NLS-0$

/* controlKeywords */
	break, //$NON-NLS-0$
	case, catch, continue, //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
	default, do, //$NON-NLS-1$ //$NON-NLS-0$
	else, //$NON-NLS-0$
	finally, for, //$NON-NLS-1$ //$NON-NLS-0$
	if, //$NON-NLS-0$
	return, //$NON-NLS-0$
	switch, //$NON-NLS-0$
	throw, try, //$NON-NLS-1$ //$NON-NLS-0$
	while //$NON-NLS-0$

/* constants */
	false, null, true //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$

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

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

	/* Interfaces for this Mozilla embedding notification */
	XPCOMObject supports;

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

	/* XULRunner detect constants */
	static final String GCC3 = "-gcc3"; //$NON-NLS-1$

	static final int MAX_PORT = 65535;
	static final String DEFAULTVALUE_STRING = "default"; //$NON-NLS-1$
	static final char SEPARATOR_OS = System.getProperty ("file.separator").charAt (0); //$NON-NLS-1$

	// TEMPORARY CODE
	static final String FACTORIES_REGISTERED = "org.eclipse.swt.browser.MozillaFactoriesRegistered"; //$NON-NLS-1$
	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 */

				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 . 
					*/
					
					XPCOMWasGlued = false;
				}
				if (XPCOMInitWasGlued) {
					XPCOMInit.XPCOMGlueShutdown ();
					XPCOMInitWasGlued = false;
				}
				Initialized = PerformedVersionCheck = false;
			}
		};
	}

static void LoadLibraries () {
	boolean initLoaded = false;

	if (Boolean.getBoolean (GRE_INITIALIZED)) {
		/* 
		 * Another browser has already initialized xulrunner in this process,
		 * so just bind to it instead of trying to initialize a new one.
		 */
		Initialized = true;
	}

	MozillaPath = System.getProperty (XULRUNNER_PATH);
	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 = MozillaDelegate.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 {
		/* ensure that client-supplied path is using correct separators */
		if (SEPARATOR_OS == '/') {
			MozillaPath = MozillaPath.replace ('\\', SEPARATOR_OS);
		} else {
			MozillaPath = MozillaPath.replace ('/', SEPARATOR_OS);
		}

		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[] bytes = MozillaDelegate.wcsToMbcs (null, MozillaPath, true);
			int rc = XPCOMInit.XPCOMGlueStartup (bytes);
			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);
					bytes = new byte[length];
					C.memmove (bytes, ptr, length);
					MozillaPath = new String (MozillaDelegate.mbcsToWcs (null, bytes));
					/*
					 * 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 ();
						bytes = MozillaDelegate.wcsToMbcs (null, MozillaPath, true);
						rc = XPCOMInit.XPCOMGlueStartup (bytes);
						if (rc == XPCOM.NS_OK) {
							/* ensure that client-supplied path is using correct separators */
							if (SEPARATOR_OS == '/') {
								MozillaPath = MozillaPath.replace ('\\', SEPARATOR_OS);
							} else {
								MozillaPath = MozillaPath.replace ('/', SEPARATOR_OS);
							}
						} else {
							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;
			}
		}
	}
}

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

	int /*long*/[] result = new int /*long*/[1];
	if (!Initialized) {
		LoadLibraries ();

		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);
		}

		/* 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);

		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++;
	
	/* 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 */
	rc = componentManager.CreateInstance (XPCOM.NS_IWEBBROWSER_CID, 0, nsIWebBrowser.NS_IWEBBROWSER_10_IID, result);
	if (rc != XPCOM.NS_OK) {
		rc = componentManager.CreateInstance (XPCOM.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) {
		if (!IsPre_1_8) {
			rc = interfaceRequestor.GetInterface (nsIDocShell.NS_IDOCSHELL_1_8_IID, result);
			if (rc == XPCOM.NS_OK && result[0] != 0) { /* 1.8 */
				new nsISupports (result[0]).Release ();
				result[0] = 0;

				if (!factoriesRegistered) {
					DownloadFactory_1_8 downloadFactory_1_8 = new DownloadFactory_1_8 ();
					downloadFactory_1_8.AddRef ();
					byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_TRANSFER_CONTRACTID, true);
					byte[] 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;
				rc = interfaceRequestor.GetInterface(nsIDocShell.NS_IDOCSHELL_10_IID, result);
				if (rc == XPCOM.NS_OK && result[0] != 0) { /* >= 4.0 */
					IsPre_4 = false;
					new nsISupports (result[0]).Release();
				}
			}
		}
		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 ();
}

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

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] == 0x0) error (XPCOM.NS_NOINTERFACE);

		boolean isXULRunner190x = false;
		nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
		result[0] = 0;
		byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_SCRIPTSECURITYMANAGER_CONTRACTID, true);
		rc = serviceManager.GetServiceByContractID (aContractID, nsIScriptSecurityManager.NS_ISCRIPTSECURITYMANAGER_10_IID, result);
		if (!(rc == XPCOM.NS_OK && result[0] != 0)) {
			result[0] = 0;
			rc = serviceManager.GetServiceByContractID (aContractID, nsIScriptSecurityManager.NS_ISCRIPTSECURITYMANAGER_191_IID, result);
			if (!(rc == XPCOM.NS_OK && result[0] != 0)) {
				result[0] = 0;
				rc = serviceManager.GetServiceByContractID (aContractID, nsIScriptSecurityManager.NS_ISCRIPTSECURITYMANAGER_IID, result);
				if (rc == XPCOM.NS_OK && result[0] != 0) {
					isXULRunner190x = true;
				}
			}
		}

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

			if (rc == XPCOM.NS_OK && result[0] != 0) {
				nsIPrincipal principal = new nsIPrincipal (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_NOINTERFACE);

				nsIInterfaceRequestor interfaceRequestor = new nsIInterfaceRequestor (result[0]);
				result[0] = 0;
				nsID scriptGlobalObjectNSID_10 = new nsID ("08f73284-26e3-4fa6-bf89-8326f92a94b3"); /* nsIScriptGlobalObject */ //$NON-NLS-1$
				rc = interfaceRequestor.GetInterface (scriptGlobalObjectNSID_10, 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);
					if (!(rc == XPCOM.NS_OK && result[0] != 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);
					}
				}
				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) {
						new nsISupports (scriptGlobalObject).Release ();
					} else {
						int /*long*/ scriptContext = XPCOM.nsIScriptGlobalObject_GetScriptContext (scriptGlobalObject, 2); /* nsIProgrammingLanguage.JAVASCRIPT */
						new nsISupports (scriptGlobalObject).Release ();

						if (scriptContext != 0) {
							/* ensure that the received nsIScriptContext implements the expected interface */
							nsISupports supports = new nsISupports (scriptContext);
							nsID scriptContextNSID_10 = new nsID ("2e583bf4-3c1f-432d-8283-8dee7eccc88b"); /* nsIScriptContext */ //$NON-NLS-1$					
							rc = supports.QueryInterface (scriptContextNSID_10, 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 = supports.QueryInterface (scriptContextNSID_1_9_2, result);
								if (!(rc == XPCOM.NS_OK && result[0] != 0)) {
									result[0] = 0;
									nsID scriptContextNSID_1_9 = new nsID ("e7b9871d-3adc-4bf7-850d-7fb9554886bf"); /* nsIScriptContext */ //$NON-NLS-1$					
									rc = supports.QueryInterface (scriptContextNSID_1_9, 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;

										byte[] jsLibPath = getJSLibPathBytes ();
										int /*long*/ globalJSObject = XPCOM.JS_GetGlobalObject (jsLibPath, nativeContext);
										if (globalJSObject != 0) {
											aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_CONTEXTSTACK_CONTRACTID, true);
											rc = serviceManager.GetServiceByContractID (aContractID, nsIJSContextStack.NS_IJSCONTEXTSTACK_IID, result);
											if (rc == XPCOM.NS_OK && result[0] != 0) {
												nsIJSContextStack stack = new nsIJSContextStack (result[0]);
												result[0] = 0;
												rc = stack.Push (nativeContext);
												if (rc != XPCOM.NS_OK) {
													stack.Release ();
												} else {
													boolean success = XPCOM.JS_EvaluateUCScriptForPrincipals (jsLibPath, nativeContext, globalJSObject, principals, scriptChars, length, urlbytes, 0, isXULRunner190x ? result : null) != 0;
													result[0] = 0;
													rc = stack.Pop (result);
													stack.Release ();
													// should principals be Release()d too?
													principal.Release ();
													serviceManager.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);
}

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 ();

		/* 
		 * Attempt to load the XRE_InitEmbedding2 function first, which is present in
		 * mozilla versions > 3.x.
		 */
		byte[] bytes = MozillaDelegate.wcsToMbcs (null, "XRE_InitEmbedding2", 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 ());
		rc = XPCOM.XPCOMGlueLoadXULFunctions (ptr);
		if (rc == XPCOM.NS_OK) {
			IsPre_4 = false;
			nsISupports.IsXULRunner10 = true;
		} else {
			/*
			 * XRE_InitEmbedding2 was not found, so fall back to XRE_InitEmbedding, which is
			 * present in older mozilla versions.
			 */
			C.free (functionLoad.functionName);
			bytes = MozillaDelegate.wcsToMbcs (null, "XRE_InitEmbedding", true); //$NON-NLS-1$
			functionLoad.functionName = C.malloc (bytes.length);
			C.memmove (functionLoad.functionName, bytes, bytes.length);
			rc = XPCOM.XPCOMGlueLoadXULFunctions (ptr);
			if (rc == XPCOM.NS_OK) {
				IsPre_4 = true;
				nsISupports.IsXULRunner10 = false;
			}
		}

		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);
		}
		if (IsPre_4) {
			rc = XPCOM.Call (functionPtr, localFile.getAddress (), localFile.getAddress (), LocationProvider.getAddress (), 0, 0);
		} else {
			rc = XPCOM.Call (functionPtr, localFile.getAddress (), localFile.getAddress (), LocationProvider.getAddress ());
		}
		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);
}

}
