/*******************************************************************************
 * Copyright (c) 2000, 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.util.Hashtable;

import org.eclipse.swt.*;
import org.eclipse.swt.internal.C;
import org.eclipse.swt.internal.ole.win32.*;
import org.eclipse.swt.ole.win32.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.internal.win32.*;

class WebSite extends OleControlSite {
	COMObject iDocHostUIHandler;
	COMObject iDocHostShowUI;
	COMObject iServiceProvider;
	COMObject iInternetSecurityManager;
	COMObject iOleCommandTarget;
	COMObject iAuthenticate;
	COMObject iDispatch;
	boolean ignoreNextMessage, ignoreAllMessages;
	Boolean canExecuteApplets;

	static final int OLECMDID_SHOWSCRIPTERROR = 40;
	static final short [] ACCENTS = new short [] {'~', '`', '\'', '^', '"'};
	static final String CONSUME_KEY = "org.eclipse.swt.OleFrame.ConsumeKey"; //$NON-NLS-1$

public WebSite(Composite parent, int style, String progId) {
	super(parent, style, progId);		
}

protected void createCOMInterfaces () {
	super.createCOMInterfaces();
	iDocHostUIHandler = new COMObject(new int[]{2, 0, 0, 4, 1, 5, 0, 0, 1, 1, 1, 3, 3, 2, 2, 1, 3, 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 ShowContextMenu((int)/*64*/args[0], args[1], args[2], args[3]);}
		public int /*long*/ method4(int /*long*/[] args) {return GetHostInfo(args[0]);}
		public int /*long*/ method5(int /*long*/[] args) {return ShowUI((int)/*64*/args[0], args[1], args[2], args[3], args[4]);}
		public int /*long*/ method6(int /*long*/[] args) {return HideUI();}
		public int /*long*/ method7(int /*long*/[] args) {return UpdateUI();}
		public int /*long*/ method8(int /*long*/[] args) {return EnableModeless((int)/*64*/args[0]);}
		public int /*long*/ method9(int /*long*/[] args) {return OnDocWindowActivate((int)/*64*/args[0]);}
		public int /*long*/ method10(int /*long*/[] args) {return OnFrameWindowActivate((int)/*64*/args[0]);}
		public int /*long*/ method11(int /*long*/[] args) {return ResizeBorder(args[0], args[1], (int)/*64*/args[2]);}
		public int /*long*/ method12(int /*long*/[] args) {return TranslateAccelerator(args[0], args[1], (int)/*64*/args[2]);}
		public int /*long*/ method13(int /*long*/[] args) {return GetOptionKeyPath(args[0], (int)/*64*/args[1]);}
		public int /*long*/ method14(int /*long*/[] args) {return GetDropTarget(args[0], args[1]);}
		public int /*long*/ method15(int /*long*/[] args) {return GetExternal(args[0]);}
		public int /*long*/ method16(int /*long*/[] args) {return TranslateUrl((int)/*64*/args[0], args[1], args[2]);}		
		public int /*long*/ method17(int /*long*/[] args) {return FilterDataObject(args[0], args[1]);}
	};
	iDocHostShowUI = new COMObject(new int[]{2, 0, 0, 7, C.PTR_SIZEOF == 4 ? 7 : 6}){
		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 ShowMessage(args[0], args[1], args[2], (int)/*64*/args[3], args[4], (int)/*64*/args[5], args[6]);}
		public int /*long*/ method4(int /*long*/[] args) {
			if (args.length == 7) {
				return ShowHelp(args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3], (int)/*64*/args[4], (int)/*64*/args[5], args[6]);
			} else {
				return ShowHelp_64(args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3], args[4], args[5]);
			}
		}
	};
	iServiceProvider = new COMObject(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 QueryService(args[0], args[1], args[2]);}
	};
	iInternetSecurityManager = new COMObject(new int[]{2, 0, 0, 1, 1, 3, 4, 8, 7, 3, 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 SetSecuritySite(args[0]);}
		public int /*long*/ method4(int /*long*/[] args) {return GetSecuritySite(args[0]);}
		public int /*long*/ method5(int /*long*/[] args) {return MapUrlToZone(args[0], args[1], (int)/*64*/args[2]);}
		public int /*long*/ method6(int /*long*/[] args) {return GetSecurityId(args[0], args[1], args[2], args[3]);}
		public int /*long*/ method7(int /*long*/[] args) {return ProcessUrlAction(args[0], (int)/*64*/args[1], args[2], (int)/*64*/args[3], args[4], (int)/*64*/args[5], (int)/*64*/args[6], (int)/*64*/args[7]);}
		public int /*long*/ method8(int /*long*/[] args) {return QueryCustomPolicy(args[0], args[1], args[2], args[3], args[4], (int)/*64*/args[5], (int)/*64*/args[6]);}
		public int /*long*/ method9(int /*long*/[] args) {return SetZoneMapping((int)/*64*/args[0], args[1], (int)/*64*/args[2]);}
		public int /*long*/ method10(int /*long*/[] args) {return GetZoneMappings((int)/*64*/args[0], args[1], (int)/*64*/args[2]);}
	};
	iOleCommandTarget = new COMObject(new int[]{2, 0, 0, 4, 5}) {
		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 QueryStatus(args[0], (int)/*64*/args[1], args[2], args[3]);}		
		public int /*long*/ method4(int /*long*/[] args) {return Exec(args[0], (int)/*64*/args[1], (int)/*64*/args[2], args[3], args[4]);}
	};
	iAuthenticate = new COMObject(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 Authenticate(args[0], args[1], args[2]);}
	};
	iDispatch = new COMObject (new int[] {2, 0, 0, 1, 3, 5, 8}) {
		public int /*long*/ method0 (int /*long*/[] args) {
			/* 
			 * IDispatch check must be done here instead of in the shared QueryInterface
			 * implementation, to avoid answering the superclass's IDispatch implementation
			 * instead of this one.
			 */
			GUID guid = new GUID ();
			COM.MoveMemory (guid, args[0], GUID.sizeof);
			if (COM.IsEqualGUID (guid, COM.IIDIDispatch)) {
				COM.MoveMemory (args[1], new int /*long*/[] {iDispatch.getAddress ()}, OS.PTR_SIZEOF);
				AddRef ();
				return COM.S_OK;
			}
			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 GetTypeInfoCount (args[0]);}
		public int /*long*/ method4 (int /*long*/[] args) {return GetTypeInfo ((int)/*64*/args[0], (int)/*64*/args[1], args[2]);}
		public int /*long*/ method5 (int /*long*/[] args) {return GetIDsOfNames ((int)/*64*/args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3], args[4]);}
		public int /*long*/ method6 (int /*long*/[] args) {return Invoke ((int)/*64*/args[0], (int)/*64*/args[1], (int)/*64*/args[2], (int)/*64*/args[3], args[4], args[5], args[6], args[7]);}
	};
}

protected void disposeCOMInterfaces() {
	super.disposeCOMInterfaces();
	if (iDocHostUIHandler != null) {
		iDocHostUIHandler.dispose();
		iDocHostUIHandler = null;
	}
	if (iDocHostShowUI != null) {
		iDocHostShowUI.dispose();
		iDocHostShowUI = null;
	}
	if (iServiceProvider != null) {
		iServiceProvider.dispose();
		iServiceProvider = null;
	}
	if (iInternetSecurityManager != null) {
		iInternetSecurityManager.dispose();
		iInternetSecurityManager = null;
	}
	if (iOleCommandTarget != null) {
		iOleCommandTarget.dispose();
		iOleCommandTarget = null;
	}
	if (iAuthenticate != null) {
		iAuthenticate.dispose();
		iAuthenticate = null;
	}
	if (iDispatch != null) {
		iDispatch.dispose ();
		iDispatch = null;
	}
}

protected int AddRef() {
	/* Workaround for javac 1.1.8 bug */
	return super.AddRef();
}

protected int QueryInterface(int /*long*/ riid, int /*long*/ ppvObject) {
	int result = super.QueryInterface(riid, ppvObject);
	if (result == COM.S_OK) return result;
	if (riid == 0 || ppvObject == 0) return COM.E_INVALIDARG;
	GUID guid = new GUID();
	COM.MoveMemory(guid, riid, GUID.sizeof);
	if (COM.IsEqualGUID(guid, COM.IIDIDocHostUIHandler)) {
		COM.MoveMemory(ppvObject, new int /*long*/[] {iDocHostUIHandler.getAddress()}, OS.PTR_SIZEOF);
		AddRef();
		return COM.S_OK;
	}
	if (COM.IsEqualGUID(guid, COM.IIDIDocHostShowUI)) {
		COM.MoveMemory(ppvObject, new int /*long*/[] {iDocHostShowUI.getAddress()}, OS.PTR_SIZEOF);
		AddRef();
		return COM.S_OK;
	}
	if (COM.IsEqualGUID(guid, COM.IIDIServiceProvider)) {
		COM.MoveMemory(ppvObject, new int /*long*/[] {iServiceProvider.getAddress()}, OS.PTR_SIZEOF);
		AddRef();
		return COM.S_OK;
	}
    if (COM.IsEqualGUID(guid, COM.IIDIInternetSecurityManager)) {
        COM.MoveMemory(ppvObject, new int /*long*/[] {iInternetSecurityManager.getAddress()}, OS.PTR_SIZEOF);
        AddRef();
        return COM.S_OK;
    }
	if (COM.IsEqualGUID(guid, COM.IIDIOleCommandTarget)) {
		COM.MoveMemory(ppvObject, new int /*long*/[] {iOleCommandTarget.getAddress()}, OS.PTR_SIZEOF);
		AddRef();
		return COM.S_OK;
	}
	COM.MoveMemory(ppvObject, new int /*long*/[] {0}, OS.PTR_SIZEOF);
	return COM.E_NOINTERFACE;
}

/* IDocHostUIHandler */

int EnableModeless(int EnableModeless) {
	return COM.E_NOTIMPL;
}

int FilterDataObject(int /*long*/ pDO, int /*long*/ ppDORet) {
	return COM.E_NOTIMPL;
}

int GetDropTarget(int /*long*/ pDropTarget, int /*long*/ ppDropTarget) {
	return COM.E_NOTIMPL;
}

int GetExternal(int /*long*/ ppDispatch) {
	OS.MoveMemory (ppDispatch, new int /*long*/[] {iDispatch.getAddress()}, C.PTR_SIZEOF);
	AddRef ();
	return COM.S_OK;
}

int GetHostInfo(int /*long*/ pInfo) {
	int info = IE.DOCHOSTUIFLAG_THEME;
	IE browser = (IE)((Browser)getParent().getParent()).webBrowser;
	if ((browser.style & SWT.BORDER) == 0) info |= IE.DOCHOSTUIFLAG_NO3DOUTERBORDER;
	DOCHOSTUIINFO uiInfo = new DOCHOSTUIINFO ();
	OS.MoveMemory(uiInfo, pInfo, DOCHOSTUIINFO.sizeof);
	uiInfo.dwFlags = info;
	OS.MoveMemory(pInfo, uiInfo, DOCHOSTUIINFO.sizeof);
	return COM.S_OK;
}

int GetOptionKeyPath(int /*long*/ pchKey, int dw) {
	return COM.E_NOTIMPL;
}

int HideUI() {
	return COM.E_NOTIMPL;
}

int OnDocWindowActivate(int fActivate) {
	return COM.E_NOTIMPL;
}

int OnFrameWindowActivate(int fActivate) {
	return COM.E_NOTIMPL;
}

protected int Release() {
	/* Workaround for javac 1.1.8 bug */
	return super.Release();
}

int ResizeBorder(int /*long*/ prcBorder, int /*long*/ pUIWindow, int fFrameWindow) {
	return COM.E_NOTIMPL;
}

int ShowContextMenu(int dwID, int /*long*/ ppt, int /*long*/ pcmdtReserved, int /*long*/ pdispReserved) {
	Browser browser = (Browser)getParent().getParent();
	Event event = new Event();
	POINT pt = new POINT();
	OS.MoveMemory(pt, ppt, POINT.sizeof);
	event.x = pt.x;
	event.y = pt.y;
	browser.notifyListeners(SWT.MenuDetect, event);
	if (!event.doit) return COM.S_OK;
	Menu menu = browser.getMenu();
	if (menu != null && !menu.isDisposed ()) {
		if (pt.x != event.x || pt.y != event.y) {
			menu.setLocation (event.x, event.y);
		}
		menu.setVisible (true);
		return COM.S_OK;
	}
	/* Show default IE popup menu */
	return COM.S_FALSE;
}

int ShowUI(int dwID, int /*long*/ pActiveObject, int /*long*/ pCommandTarget, int /*long*/ pFrame, int /*long*/ pDoc) {
	return COM.S_FALSE;
}

int TranslateAccelerator(int /*long*/ lpMsg, int /*long*/ pguidCmdGroup, int nCmdID) {
	/*
	* Feature in Internet Explorer.  By default the embedded Internet Explorer control runs
	* the Internet Explorer shortcuts (e.g. Ctrl+F for Find).  This overrides the shortcuts
	* defined by SWT.  The workaround is to forward the accelerator keys to the parent window
	* and have Internet Explorer ignore the ones handled by the parent window.
	*/
	Menu menubar = getShell().getMenuBar();
	if (menubar != null && !menubar.isDisposed() && menubar.isEnabled()) {
		Shell shell = menubar.getShell();
		int /*long*/ hwnd = shell.handle;
		int /*long*/ hAccel = OS.SendMessage(hwnd, OS.WM_APP+1, 0, 0);
		if (hAccel != 0) {
			MSG msg = new MSG();
			OS.MoveMemory(msg, lpMsg, MSG.sizeof);
			if (OS.TranslateAccelerator(hwnd, hAccel, msg) != 0) return COM.S_OK;
		}
	}
	/*
	* By default the IE shortcuts are run.  However, the shortcuts below should not run
	* in this context.  The workaround is to block IE from handling these shortcuts by
	* answering COM.S_OK.
	* 
	* - F5 causes a refresh, which is not appropriate when rendering HTML from memory
	* - CTRL+L opens an Open Location dialog in IE8, which is undesirable and can
	* crash in some contexts
	* - CTRL+N opens a standalone IE, which is undesirable and can crash in some contexts
	*/
	int result = COM.S_FALSE;
	MSG msg = new MSG();
	OS.MoveMemory(msg, lpMsg, MSG.sizeof);
	if (msg.message == OS.WM_KEYDOWN) {
		switch ((int)/*64*/msg.wParam) {
			case OS.VK_F5:
				OleAutomation auto = new OleAutomation(this);
				int[] rgdispid = auto.getIDsOfNames(new String[] { "LocationURL" }); //$NON-NLS-1$
				Variant pVarResult = auto.getProperty(rgdispid[0]);
				auto.dispose();
				if (pVarResult != null) {
					if (pVarResult.getType() == OLE.VT_BSTR) {
						String url = pVarResult.getString();
						if (url.equals(IE.ABOUT_BLANK)) result = COM.S_OK;
					}
					pVarResult.dispose();
				}
				break;
			case OS.VK_TAB:
				/* 
				 * Do not interfere with tab traversal since it's not known
				 * if it will be within IE or out to another Control.
				 */
				break;
			case OS.VK_BACK:
			case OS.VK_RETURN:
				/*
				* Translating OS.VK_BACK or OS.VK_RETURN results in the native control
				* handling them twice (eg.- inserting two lines instead of one).  So
				* these keys are not translated here, and instead are explicitly handled
				* in the keypress handler.
				*/
				break;
			case OS.VK_L:
			case OS.VK_N:
				if (OS.GetKeyState (OS.VK_CONTROL) < 0 && OS.GetKeyState (OS.VK_MENU) >= 0 && OS.GetKeyState (OS.VK_SHIFT) >= 0) {
					if (msg.wParam == OS.VK_N || IE.IEVersion >= 8) {
						frame.setData(CONSUME_KEY, "false"); //$NON-NLS-1$
						result = COM.S_OK;
						break;
					}
				}
				// FALL THROUGH
			default:
				OS.TranslateMessage(msg);
				frame.setData(CONSUME_KEY, "true"); //$NON-NLS-1$
				break;
		}
	}

	switch (msg.message) {
		case OS.WM_KEYDOWN:
		case OS.WM_KEYUP: {
			if (!OS.IsWinCE) {
				boolean isAccent = false;
				switch ((int)/*64*/msg.wParam) {
					case OS.VK_SHIFT:
					case OS.VK_MENU:
					case OS.VK_CONTROL:
					case OS.VK_CAPITAL:
					case OS.VK_NUMLOCK:
					case OS.VK_SCROLL:
						break;
					default: {
						/*
						* Bug in Windows. The high bit in the result of MapVirtualKey() on
						* Windows NT is bit 32 while the high bit on Windows 95 is bit 16.
						* They should both be bit 32.  The fix is to test the right bit.
						*/
						int mapKey = OS.MapVirtualKey ((int)/*64*/msg.wParam, 2);
						if (mapKey != 0) {
							isAccent = (mapKey & (OS.IsWinNT ? 0x80000000 : 0x8000)) != 0;
							if (!isAccent) {
								for (int i=0; i<ACCENTS.length; i++) {
									int value = OS.VkKeyScan (ACCENTS [i]);
									if (value != -1 && (value & 0xFF) == msg.wParam) {
										int state = value >> 8;
										if ((OS.GetKeyState (OS.VK_SHIFT) < 0) == ((state & 0x1) != 0) &&
											(OS.GetKeyState (OS.VK_CONTROL) < 0) == ((state & 0x2) != 0) &&
											(OS.GetKeyState (OS.VK_MENU) < 0) == ((state & 0x4) != 0)) {
												if ((state & 0x7) != 0) isAccent = true;
												break;
										}
									}
								}
							}
						}
						break;
					}
				}
				if (isAccent) result = COM.S_OK;
			}
		}
	}
	return result;
}

int TranslateUrl(int dwTranslate, int /*long*/ pchURLIn, int /*long*/ ppchURLOut) {
	return COM.E_NOTIMPL;
}

int UpdateUI() {
	return COM.E_NOTIMPL;
}

/* IDocHostShowUI */

int ShowMessage(int /*long*/ hwnd, int /*long*/ lpstrText, int /*long*/ lpstrCaption, int dwType, int /*long*/ lpstrHelpFile, int dwHelpContext, int /*long*/ plResult) {
	boolean ignore = ignoreNextMessage || ignoreAllMessages;
	ignoreNextMessage = false;
	return ignore ? COM.S_OK : COM.S_FALSE;
}

int ShowHelp_64(int /*long*/ hwnd, int /*long*/ pszHelpFile, int uCommand, int dwData, long pt, int /*long*/ pDispatchObjectHit) {
	POINT point = new POINT();
	OS.MoveMemory(point, new long[]{pt}, 8);
	return ShowHelp(hwnd, pszHelpFile, uCommand, dwData, point.x, point.y, pDispatchObjectHit);
}

/* Note.  One of the arguments of ShowHelp is a POINT struct and not a pointer to a POINT struct. Because
 * of the way Callback gets int parameters from a va_list of C arguments 2 integer arguments must be declared,
 * ptMouse_x and ptMouse_y. Otherwise the Browser crashes when the user presses F1 to invoke
 * the help.
 */
int ShowHelp(int /*long*/ hwnd, int /*long*/ pszHelpFile, int uCommand, int dwData, int ptMouse_x, int ptMouse_y, int /*long*/ pDispatchObjectHit) {
	Browser browser = (Browser)getParent().getParent();
	Event event = new Event();
	event.type = SWT.Help;
	event.display = getDisplay();
	event.widget = browser;
	Shell shell = browser.getShell();
	Control control = browser;
	do {
		if (control.isListening(SWT.Help)) {
			control.notifyListeners(SWT.Help, event);
			break;
		}
		if (control == shell) break;
		control = control.getParent();
	} while (true);
	return COM.S_OK;
}

/* IServiceProvider */

int QueryService(int /*long*/ guidService, int /*long*/ riid, int /*long*/ ppvObject) {
	if (riid == 0 || ppvObject == 0) return COM.E_INVALIDARG;
	GUID guid = new GUID();
	COM.MoveMemory(guid, riid, GUID.sizeof);
	if (COM.IsEqualGUID(guid, COM.IIDIInternetSecurityManager)) {
		COM.MoveMemory(ppvObject, new int /*long*/[] {iInternetSecurityManager.getAddress()}, OS.PTR_SIZEOF);
		AddRef();
		return COM.S_OK;
	}
	if (COM.IsEqualGUID(guid, COM.IIDIAuthenticate)) {
		COM.MoveMemory(ppvObject, new int /*long*/[] {iAuthenticate.getAddress()}, OS.PTR_SIZEOF);
		AddRef();
		return COM.S_OK;
	}
	COM.MoveMemory(ppvObject, new int /*long*/[] {0}, OS.PTR_SIZEOF);
	return COM.E_NOINTERFACE;
}

/* IInternetSecurityManager */

int SetSecuritySite(int /*long*/ pSite) {
	return IE.INET_E_DEFAULT_ACTION;
}

int GetSecuritySite(int /*long*/ ppSite) {
	return IE.INET_E_DEFAULT_ACTION;
}

int MapUrlToZone(int /*long*/ pwszUrl, int /*long*/ pdwZone, int dwFlags) {
	/*
	* Feature in IE.  HTML rendered in memory does not enable local links
	* but the same HTML document loaded through a local file is permitted
	* to follow local links.  The workaround is to return URLZONE_INTRANET
	* instead of the default value URLZONE_LOCAL_MACHINE.
	*/
	IE ie = (IE)((Browser)getParent().getParent()).webBrowser;
	/*
	* For some reason IE8 invokes this function after the Browser has
	* been disposed.  To detect this case check for ie.auto != null. 
	*/
	if (ie.auto != null && ie._getUrl().startsWith(IE.ABOUT_BLANK) && !ie.untrustedText) {
		COM.MoveMemory(pdwZone, new int[] {IE.URLZONE_INTRANET}, 4);
		return COM.S_OK;
	}
	return IE.INET_E_DEFAULT_ACTION;
}

int GetSecurityId(int /*long*/ pwszUrl, int /*long*/ pbSecurityId, int /*long*/ pcbSecurityId, int /*long*/ dwReserved) {
	return IE.INET_E_DEFAULT_ACTION;
}

int ProcessUrlAction(int /*long*/ pwszUrl, int dwAction, int /*long*/ pPolicy, int cbPolicy, int /*long*/ pContext, int cbContext, int dwFlags, int dwReserved) {
	ignoreNextMessage = false;

	/*
	* If the current page is about:blank and is trusted then
	* override default zone elevation settings to allow the action.  
	*/
	if (dwAction == IE.URLACTION_FEATURE_ZONE_ELEVATION) {
	    IE ie = (IE)((Browser)getParent().getParent()).webBrowser;
	    if (ie.auto != null && ie._getUrl().startsWith(IE.ABOUT_BLANK) && !ie.untrustedText) {
			if (cbPolicy >= 4) COM.MoveMemory(pPolicy, new int[] {IE.URLPOLICY_ALLOW}, 4);
			return COM.S_OK;
	    }
	}

	int policy = IE.INET_E_DEFAULT_ACTION;

	if (dwAction >= IE.URLACTION_JAVA_MIN && dwAction <= IE.URLACTION_JAVA_MAX) {
		if (canExecuteApplets ()) {
			policy = IE.URLPOLICY_JAVA_LOW;
		} else {
			policy = IE.URLPOLICY_JAVA_PROHIBIT;
			ignoreNextMessage = true;
		}
	}
	if (dwAction == IE.URLACTION_ACTIVEX_RUN) {
		GUID guid = new GUID();
		COM.MoveMemory(guid, pContext, GUID.sizeof);
		if (COM.IsEqualGUID(guid, COM.IIDJavaBeansBridge) && !canExecuteApplets ()) {
			policy = IE.URLPOLICY_DISALLOW;
			ignoreNextMessage = true;
		}
		if (COM.IsEqualGUID(guid, COM.IIDShockwaveActiveXControl)) {
			policy = IE.URLPOLICY_DISALLOW;
			ignoreNextMessage = true;
		}
	}
	if (dwAction == IE.URLACTION_SCRIPT_RUN) {
		IE browser = (IE)((Browser)getParent ().getParent ()).webBrowser;
		policy = browser.jsEnabled ? IE.URLPOLICY_ALLOW : IE.URLPOLICY_DISALLOW;
	}

	if (policy == IE.INET_E_DEFAULT_ACTION) return IE.INET_E_DEFAULT_ACTION;
	if (cbPolicy >= 4) COM.MoveMemory(pPolicy, new int[] {policy}, 4);
	return policy == IE.URLPOLICY_ALLOW ? COM.S_OK : COM.S_FALSE;
}

boolean canExecuteApplets () {
	/*
	* Executing an applet in embedded IE will crash if IE's Java plug-in
	* launches its jre in IE's process, because this new jre conflicts
	* with the one running eclipse.  These cases need to be avoided by
	* vetoing the running of applets.
	* 
	* However as of Sun jre 1.6u10, applets can be launched in a separate
	* process, which avoids the conflict with the jre running eclipse.
	* Therefore if this condition is detected, and if the required jar 
	* libraries are available, then applets can be executed. 
	*/

	/* 
	* executing applets with IE6 embedded can crash, so do not
	* attempt this if the version is less than IE7
	*/
	if (IE.IEVersion < 7) return false;

	if (canExecuteApplets == null) {
		WebBrowser webBrowser = ((Browser)getParent ().getParent ()).webBrowser;
		String script = "try {var element = document.createElement('object');element.classid='clsid:CAFEEFAC-DEC7-0000-0000-ABCDEFFEDCBA';return element.object.isPlugin2();} catch (err) {};return false;"; //$NON-NLS-1$
		canExecuteApplets = ((Boolean)webBrowser.evaluate (script)); 
		if (canExecuteApplets.booleanValue ()) {
			try {
				Class.forName ("sun.plugin2.main.server.IExplorerPlugin"); /* plugin.jar */	//$NON-NLS-1$
				Class.forName ("com.sun.deploy.services.Service"); /* deploy.jar */	//$NON-NLS-1$
				Class.forName ("com.sun.javaws.Globals"); /* javaws.jar */	//$NON-NLS-1$
			} catch (ClassNotFoundException e) {
				/* one or more of the required jar libraries are not available */
				canExecuteApplets = Boolean.FALSE;
			}
		}
	}
	return canExecuteApplets.booleanValue ();
}

int QueryCustomPolicy(int /*long*/ pwszUrl, int /*long*/ guidKey, int /*long*/ ppPolicy, int /*long*/ pcbPolicy, int /*long*/ pContext, int cbContext, int dwReserved) {
	return IE.INET_E_DEFAULT_ACTION;
}

int SetZoneMapping(int dwZone, int /*long*/ lpszPattern, int dwFlags) {
	return IE.INET_E_DEFAULT_ACTION;
}

int GetZoneMappings(int dwZone, int /*long*/ ppenumString, int dwFlags) {
	return COM.E_NOTIMPL;
}

/* IOleCommandTarget */
int QueryStatus(int /*long*/ pguidCmdGroup, int cCmds, int /*long*/ prgCmds, int /*long*/ pCmdText) {
	return COM.E_NOTSUPPORTED;
}

int Exec(int /*long*/ pguidCmdGroup, int nCmdID, int nCmdExecOpt, int /*long*/ pvaIn, int /*long*/ pvaOut) {
	if (pguidCmdGroup != 0) {
		GUID guid = new GUID();
		COM.MoveMemory(guid, pguidCmdGroup, GUID.sizeof);

		/*
		* If a javascript error occurred then suppress IE's default script error dialog.
		*/
		if (COM.IsEqualGUID(guid, COM.CGID_DocHostCommandHandler)) {
			if (nCmdID == OLECMDID_SHOWSCRIPTERROR) return COM.S_OK;
		}

		/*
		* Bug in Internet Explorer.  OnToolBar TRUE is also fired when any of the 
		* address bar or menu bar are requested but not the tool bar.  A workaround
		* has been posted by a Microsoft developer on the public webbrowser_ctl
		* newsgroup. The workaround is to implement the IOleCommandTarget interface
		* to test the argument of an undocumented command.
		*/
		if (nCmdID == 1 && COM.IsEqualGUID(guid, COM.CGID_Explorer) && ((nCmdExecOpt & 0xFFFF) == 0xA)) {
			IE browser = (IE)((Browser)getParent().getParent()).webBrowser;
			browser.toolBar = (nCmdExecOpt & 0xFFFF0000) != 0;
		}
	}
	return COM.E_NOTSUPPORTED;
}

/* IAuthenticate */

int Authenticate (int /*long*/ hwnd, int /*long*/ szUsername, int /*long*/ szPassword) {
	IE browser = (IE)((Browser)getParent ().getParent ()).webBrowser;
	for (int i = 0; i < browser.authenticationListeners.length; i++) {
		AuthenticationEvent event = new AuthenticationEvent (browser.browser);
		event.location = browser.lastNavigateURL;
		browser.authenticationListeners[i].authenticate (event);
		if (!event.doit) return COM.E_ACCESSDENIED;
		if (event.user != null && event.password != null) {
			TCHAR user = new TCHAR (0, event.user, true);
			int size = user.length () * TCHAR.sizeof;
			int /*long*/ userPtr = COM.CoTaskMemAlloc (size);
			OS.MoveMemory (userPtr, user, size);
			TCHAR password = new TCHAR (0, event.password, true);
			size = password.length () * TCHAR.sizeof;
			int /*long*/ passwordPtr = COM.CoTaskMemAlloc (size);
			OS.MoveMemory (passwordPtr, password, size);
			C.memmove (hwnd, new int /*long*/[] {0}, C.PTR_SIZEOF);
			C.memmove (szUsername, new int /*long*/[] {userPtr}, C.PTR_SIZEOF);
			C.memmove (szPassword, new int /*long*/[] {passwordPtr}, C.PTR_SIZEOF);
			return COM.S_OK;
		}
	}

	/* no listener handled the challenge, so defer to the native dialog */
	C.memmove (hwnd, new int /*long*/[] {getShell().handle}, C.PTR_SIZEOF);
	return COM.S_OK;
}

/* IDispatch */

int GetTypeInfoCount (int /*long*/ pctinfo) {
	C.memmove (pctinfo, new int[] {0}, 4);
	return COM.S_OK;
}

int GetTypeInfo (int iTInfo, int lcid, int /*long*/ ppTInfo) {
	return COM.S_OK;
}

int GetIDsOfNames (int riid, int /*long*/ rgszNames, int cNames, int lcid, int /*long*/ rgDispId) {
    int /*long*/[] ptr = new int /*long*/[1];
    OS.MoveMemory (ptr, rgszNames, C.PTR_SIZEOF);
    int length = OS.wcslen (ptr[0]);
    char[] buffer = new char[length];
    OS.MoveMemory (buffer, ptr[0], length * 2);
    String functionName = String.valueOf (buffer);
    int result = COM.S_OK; 
    int[] ids = new int[cNames];	/* DISPIDs */
    if (functionName.equals ("callJava")) { //$NON-NLS-1$
	    for (int i = 0; i < cNames; i++) {
	        ids[i] = i + 1;
	    }
    } else {
    	result = COM.DISP_E_UNKNOWNNAME;
	    for (int i = 0; i < cNames; i++) {
	        ids[i] = COM.DISPID_UNKNOWN;
	    }
    }
    OS.MoveMemory (rgDispId, ids, cNames * 4);
	return result;
}

int Invoke (int dispIdMember, int /*long*/ riid, int lcid, int dwFlags, int /*long*/ pDispParams, int /*long*/ pVarResult, int /*long*/ pExcepInfo, int /*long*/ pArgErr) {
	IE ie = (IE)((Browser)getParent ().getParent ()).webBrowser;
	Hashtable functions = ie.functions;
	if (functions == null) {
		if (pVarResult != 0) {
			COM.MoveMemory (pVarResult, new int /*long*/[] {0}, C.PTR_SIZEOF);
		}
		return COM.S_OK;
	}

	DISPPARAMS dispParams = new DISPPARAMS ();
	COM.MoveMemory (dispParams, pDispParams, DISPPARAMS.sizeof);
	if (dispParams.cArgs != 2) {
		if (pVarResult != 0) {
			COM.MoveMemory (pVarResult, new int /*long*/[] {0}, C.PTR_SIZEOF);
		}
		return COM.S_OK;
	}

	int /*long*/ ptr = dispParams.rgvarg + Variant.sizeof;
	Variant variant = Variant.win32_new (ptr);
	if (variant.getType () != COM.VT_I4) {
		variant.dispose ();
		if (pVarResult != 0) {
			COM.MoveMemory (pVarResult, new int /*long*/[] {0}, C.PTR_SIZEOF);
		}
		return COM.S_OK;
	}
	int index = variant.getInt ();
	variant.dispose ();
	if (index <= 0) {
		if (pVarResult != 0) {
			COM.MoveMemory (pVarResult, new int /*long*/[] {0}, C.PTR_SIZEOF);
		}
		return COM.S_OK;
	}

	variant = Variant.win32_new (dispParams.rgvarg);
	Object key = new Integer (index);
	BrowserFunction function = (BrowserFunction)functions.get (key);
	Object returnValue = null;
	if (function != null) {
		try {
			Object temp = convertToJava (variant);
			if (temp instanceof Object[]) {
				Object[] args = (Object[])temp;
				try {
					returnValue = function.function (args);
				} catch (Exception e) {
					/* exception during function invocation */
					returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ());
				}
			}
		} catch (IllegalArgumentException e) {
			/* invalid argument value type */
			if (function.isEvaluate) {
				/* notify the function so that a java exception can be thrown */
				function.function (new String[] {WebBrowser.CreateErrorString (new SWTException (SWT.ERROR_INVALID_RETURN_VALUE).getLocalizedMessage ())});
			}
			returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ());
		}
	}
	variant.dispose ();

	if (pVarResult != 0) {
		if (returnValue == null) {
			COM.MoveMemory (pVarResult, new int /*long*/[] {0}, C.PTR_SIZEOF);
		} else {
			try {
				variant = convertToJS (returnValue);
			} catch (SWTException e) {
				/* invalid return value type */
				variant = convertToJS (WebBrowser.CreateErrorString (e.getLocalizedMessage ()));
			}
			Variant.win32_copy (pVarResult, variant);
			variant.dispose ();
		}
	}
	return COM.S_OK;
}

Object convertToJava (Variant variant) {
	switch (variant.getType ()) {
		case OLE.VT_NULL: return null;
		case OLE.VT_BSTR: return variant.getString ();
		case OLE.VT_BOOL: return new Boolean (variant.getBoolean ());
		case OLE.VT_I2:
		case OLE.VT_I4:
		case OLE.VT_I8:
		case OLE.VT_R4:
		case OLE.VT_R8:
			return new Double (variant.getDouble ());
		case OLE.VT_DISPATCH: {
			Object[] args = null;
			OleAutomation auto = variant.getAutomation ();
			TYPEATTR typeattr = auto.getTypeInfoAttributes ();
			if (typeattr != null) {
				GUID guid = new GUID ();
				guid.Data1 = typeattr.guid_Data1;
				guid.Data2 = typeattr.guid_Data2;
				guid.Data3 = typeattr.guid_Data3;
				guid.Data4 = typeattr.guid_Data4;
				if (COM.IsEqualGUID (guid, COM.IIDIJScriptTypeInfo)) {
					int[] rgdispid = auto.getIDsOfNames (new String[] {"length"}); //$NON-NLS-1$
					if (rgdispid != null) {
						Variant varLength = auto.getProperty (rgdispid[0]);
						int length = varLength.getInt ();
						varLength.dispose ();
						args = new Object[length];
						for (int i = 0; i < length; i++) {
							rgdispid = auto.getIDsOfNames (new String[] {String.valueOf (i)});
							if (rgdispid != null) {
								Variant current = auto.getProperty (rgdispid[0]);
								try {
									args[i] = convertToJava (current);
									current.dispose ();
								} catch (IllegalArgumentException e) {
									/* invalid argument value type */
									current.dispose ();
									auto.dispose ();
									throw e;
								}
							}
						}
					}
				} else {
					auto.dispose ();
					SWT.error (SWT.ERROR_INVALID_ARGUMENT);
				}
			}
			auto.dispose ();
			return args;
		}
	}
	SWT.error (SWT.ERROR_INVALID_ARGUMENT);
	return null;
}

Variant convertToJS (Object value) {
	if (value == null) {
		return new Variant ();
	}
	if (value instanceof String) {
		return new Variant ((String)value);
	}
	if (value instanceof Boolean) {
		return new Variant (((Boolean)value).booleanValue ());
	}
	if (value instanceof Number) {
		return new Variant (((Number)value).doubleValue ());
	}
	if (value instanceof Object[]) {
		/* get IHTMLDocument2 */
		IE browser = (IE)((Browser)getParent ().getParent ()).webBrowser;
		OleAutomation auto = browser.auto;
		int[] rgdispid = auto.getIDsOfNames (new String[] {"Document"}); //$NON-NLS-1$
		if (rgdispid == null) return new Variant ();
		Variant pVarResult = auto.getProperty (rgdispid[0]);
		if (pVarResult == null) return new Variant ();
		if (pVarResult.getType () == COM.VT_EMPTY) {
			pVarResult.dispose ();
			return new Variant ();
		}
		OleAutomation document = pVarResult.getAutomation ();
		pVarResult.dispose ();

		/* get IHTMLWindow2 */
		rgdispid = document.getIDsOfNames (new String[] {"parentWindow"}); //$NON-NLS-1$
		if (rgdispid == null) {
			document.dispose ();
			return new Variant ();
		}
		pVarResult = document.getProperty (rgdispid[0]);
		if (pVarResult == null || pVarResult.getType () == COM.VT_EMPTY) {
			if (pVarResult != null) pVarResult.dispose ();
			document.dispose ();
			return new Variant ();	
		}
		OleAutomation ihtmlWindow2 = pVarResult.getAutomation ();
		pVarResult.dispose ();
		document.dispose ();

		/* create a new JS array to be returned */
		rgdispid = ihtmlWindow2.getIDsOfNames (new String[] {"Array"}); //$NON-NLS-1$
		if (rgdispid == null) {
			ihtmlWindow2.dispose ();
			return new Variant ();
		}
		Variant arrayType = ihtmlWindow2.getProperty (rgdispid[0]);
		ihtmlWindow2.dispose ();
		IDispatch arrayTypeDispatch = arrayType.getDispatch ();
		arrayType.dispose ();

		int /*long*/[] result = new int /*long*/[1];
		int rc = arrayTypeDispatch.QueryInterface (COM.IIDIDispatchEx, result);
		if (rc != COM.S_OK) return new Variant ();
		IDispatchEx arrayTypeDispatchEx = new IDispatchEx (result[0]);
		result[0] = 0;
		int /*long*/ resultPtr = OS.GlobalAlloc (OS.GMEM_FIXED | OS.GMEM_ZEROINIT, VARIANT.sizeof);
		DISPPARAMS params = new DISPPARAMS ();
		rc = arrayTypeDispatchEx.InvokeEx (COM.DISPID_VALUE, COM.LOCALE_USER_DEFAULT, COM.DISPATCH_CONSTRUCT, params, resultPtr, null, 0);
		if (rc != COM.S_OK) {
			OS.GlobalFree (resultPtr);
			return new Variant ();	
		}
		Variant array = Variant.win32_new (resultPtr);
		OS.GlobalFree (resultPtr);

		/* populate the array */
		Object[] arrayValue = (Object[])value;
		int length = arrayValue.length;
		auto = array.getAutomation ();
		int[] rgdispids = auto.getIDsOfNames (new String[] {"push"}); //$NON-NLS-1$
		if (rgdispids != null) {
			for (int i = 0; i < length; i++) {
				Object currentObject = arrayValue[i];
				try {
					Variant variant = convertToJS (currentObject);
					auto.invoke (rgdispids[0], new Variant[] {variant});
					variant.dispose ();
				} catch (SWTException e) {
					/* invalid return value type */
					auto.dispose ();
					array.dispose ();
					throw e;
				}
			}
		}
		auto.dispose ();
		return array;
	}
	SWT.error (SWT.ERROR_INVALID_RETURN_VALUE);
	return null;
}

}
