| /******************************************************************************* |
| * Copyright (c) 2003, 2017 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.net.*; |
| import java.util.*; |
| |
| import org.eclipse.swt.*; |
| import org.eclipse.swt.graphics.*; |
| import org.eclipse.swt.internal.*; |
| import org.eclipse.swt.internal.ole.win32.*; |
| import org.eclipse.swt.internal.win32.*; |
| import org.eclipse.swt.ole.win32.*; |
| import org.eclipse.swt.widgets.*; |
| |
| class IE extends WebBrowser { |
| |
| OleFrame frame; |
| WebSite site; |
| OleAutomation auto; |
| OleListener domListener; |
| OleAutomation[] documents = new OleAutomation[0]; |
| |
| boolean back, forward, delaySetText, ignoreDispose, ignoreTraverse, performingInitialNavigate; |
| boolean installFunctionsOnDocumentComplete, untrustedText, isRefresh, isAboutBlank; |
| Point location; |
| Point size; |
| boolean addressBar = true, menuBar = true, statusBar = true, toolBar = true; |
| long /*int*/ globalDispatch; |
| String html, lastNavigateURL, uncRedirect; |
| Object[] pendingText, pendingUrl; |
| int style, lastKeyCode, lastCharCode; |
| int lastMouseMoveX, lastMouseMoveY; |
| |
| static boolean Initialized; |
| static int IEVersion, PDFCount; |
| static String ProgId = "Shell.Explorer"; //$NON-NLS-1$ |
| |
| static final int BeforeNavigate2 = 0xfa; |
| static final int CommandStateChange = 0x69; |
| static final int DocumentComplete = 0x103; |
| static final int DownloadComplete = 0x68; |
| static final int NavigateComplete2 = 0xfc; |
| static final int NewWindow2 = 0xfb; |
| static final int OnMenuBar = 0x100; |
| static final int OnStatusBar = 0x101; |
| static final int OnToolBar = 0xff; |
| static final int OnVisible = 0xfe; |
| static final int ProgressChange = 0x6c; |
| static final int RegisterAsBrowser = 0x228; |
| static final int StatusTextChange = 0x66; |
| static final int TitleChange = 0x71; |
| static final int WindowClosing = 0x107; |
| static final int WindowSetHeight = 0x10b; |
| static final int WindowSetLeft = 0x108; |
| static final int WindowSetResizable = 0x106; |
| static final int WindowSetTop = 0x109; |
| static final int WindowSetWidth = 0x10a; |
| static final int NavigateError = 0x10f; |
| |
| static final short CSC_NAVIGATEFORWARD = 1; |
| static final short CSC_NAVIGATEBACK = 2; |
| static final int INET_E_DEFAULT_ACTION = 0x800C0011; |
| static final int INET_E_RESOURCE_NOT_FOUND = 0x800C0005; |
| static final int READYSTATE_COMPLETE = 4; |
| static final int URLPOLICY_ALLOW = 0x00; |
| static final int URLPOLICY_DISALLOW = 0x03; |
| static final int URLPOLICY_JAVA_PROHIBIT = 0x0; |
| static final int URLPOLICY_JAVA_LOW = 0x00030000; |
| static final int URLZONE_LOCAL_MACHINE = 0; |
| static final int URLZONE_INTRANET = 1; |
| static final int URLACTION_ACTIVEX_MIN = 0x00001200; |
| static final int URLACTION_ACTIVEX_MAX = 0x000013ff; |
| static final int URLACTION_ACTIVEX_RUN = 0x00001200; |
| static final int URLACTION_FEATURE_ZONE_ELEVATION = 0x00002101; |
| static final int URLACTION_JAVA_MIN = 0x00001C00; |
| static final int URLACTION_JAVA_MAX = 0x00001Cff; |
| static final int URLACTION_SCRIPT_RUN = 0x00001400; |
| |
| static final int DISPID_AMBIENT_DLCONTROL = -5512; |
| static final int DLCTL_DLIMAGES = 0x00000010; |
| static final int DLCTL_VIDEOS = 0x00000020; |
| static final int DLCTL_BGSOUNDS = 0x00000040; |
| static final int DLCTL_NO_SCRIPTS = 0x00000080; |
| static final int DLCTL_NO_JAVA = 0x00000100; |
| static final int DLCTL_NO_RUNACTIVEXCTLS = 0x00000200; |
| static final int DLCTL_NO_DLACTIVEXCTLS = 0x00000400; |
| static final int DLCTL_DOWNLOADONLY = 0x00000800; |
| static final int DLCTL_NO_FRAMEDOWNLOAD = 0x00001000; |
| static final int DLCTL_RESYNCHRONIZE = 0x00002000; |
| static final int DLCTL_PRAGMA_NO_CACHE = 0x00004000; |
| static final int DLCTL_FORCEOFFLINE = 0x10000000; |
| static final int DLCTL_NO_CLIENTPULL = 0x20000000; |
| static final int DLCTL_SILENT = 0x40000000; |
| static final int DOCHOSTUIFLAG_THEME = 0x00040000; |
| static final int DOCHOSTUIFLAG_NO3DBORDER = 0x0000004; |
| static final int DOCHOSTUIFLAG_NO3DOUTERBORDER = 0x00200000; |
| static final int DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION = 0x04000000; |
| static final int DOCHOSTUIFLAG_DPI_AWARE = 0x40000000; |
| |
| static final String ABOUT_BLANK = "about:blank"; //$NON-NLS-1$ |
| static final String CLSID_SHELLEXPLORER1 = "{EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B}"; //$NON-NLS-1$ |
| static final int DEFAULT_IE_VERSION = 9999; |
| static final String EXTENSION_PDF = ".pdf"; //$NON-NLS-1$ |
| static final String HTML_DOCUMENT = "HTML Document"; //$NON-NLS-1$ |
| static final int MAX_PDF = 20; |
| static final char SEPARATOR_OS = System.getProperty ("file.separator").charAt (0); //$NON-NLS-1$ |
| static final String PROPERTY_IEVERSION = "org.eclipse.swt.browser.IEVersion"; //$NON-NLS-1$ |
| static final String VALUE_DEFAULT = "default"; //$NON-NLS-1$ |
| |
| static final String EVENT_DOUBLECLICK = "dblclick"; //$NON-NLS-1$ |
| static final String EVENT_DRAGEND = "dragend"; //$NON-NLS-1$ |
| static final String EVENT_DRAGSTART = "dragstart"; //$NON-NLS-1$ |
| static final String EVENT_KEYDOWN = "keydown"; //$NON-NLS-1$ |
| static final String EVENT_KEYPRESS = "keypress"; //$NON-NLS-1$ |
| static final String EVENT_KEYUP = "keyup"; //$NON-NLS-1$ |
| static final String EVENT_MOUSEMOVE = "mousemove"; //$NON-NLS-1$ |
| static final String EVENT_MOUSEWHEEL = "mousewheel"; //$NON-NLS-1$ |
| static final String EVENT_MOUSEUP = "mouseup"; //$NON-NLS-1$ |
| static final String EVENT_MOUSEDOWN = "mousedown"; //$NON-NLS-1$ |
| static final String EVENT_MOUSEOUT = "mouseout"; //$NON-NLS-1$ |
| static final String EVENT_MOUSEOVER = "mouseover"; //$NON-NLS-1$ |
| static final String PROTOCOL_FILE = "file://"; //$NON-NLS-1$ |
| static final String PROPERTY_ALTKEY = "altKey"; //$NON-NLS-1$ |
| static final String PROPERTY_BUTTON = "button"; //$NON-NLS-1$ |
| static final String PROPERTY_CTRLKEY = "ctrlKey"; //$NON-NLS-1$ |
| static final String PROPERTY_DOCUMENT = "Document"; //$NON-NLS-1$ |
| static final String PROPERTY_FROMELEMENT = "fromElement"; //$NON-NLS-1$ |
| static final String PROPERTY_KEYCODE = "keyCode"; //$NON-NLS-1$ |
| static final String PROPERTY_REPEAT = "repeat"; //$NON-NLS-1$ |
| static final String PROPERTY_RETURNVALUE = "returnValue"; //$NON-NLS-1$ |
| static final String PROPERTY_SCREENX = "screenX"; //$NON-NLS-1$ |
| static final String PROPERTY_SCREENY = "screenY"; //$NON-NLS-1$ |
| static final String PROPERTY_SHIFTKEY = "shiftKey"; //$NON-NLS-1$ |
| static final String PROPERTY_TOELEMENT = "toElement"; //$NON-NLS-1$ |
| static final String PROPERTY_TYPE = "type"; //$NON-NLS-1$ |
| static final String PROPERTY_WHEELDELTA = "wheelDelta"; //$NON-NLS-1$ |
| |
| static { |
| NativeClearSessions = () -> { |
| if (OS.IsPPC) return; |
| OS.InternetSetOption (0, OS.INTERNET_OPTION_END_BROWSER_SESSION, 0, 0); |
| }; |
| |
| NativeGetCookie = () -> { |
| if (OS.IsPPC) return; |
| TCHAR url = new TCHAR (0, CookieUrl, true); |
| TCHAR cookieData = new TCHAR (0, 8192); |
| int[] size = new int[] {cookieData.length ()}; |
| if (!OS.InternetGetCookie (url, null, cookieData, size)) { |
| /* original cookieData size was not large enough */ |
| size[0] /= TCHAR.sizeof; |
| cookieData = new TCHAR (0, size[0]); |
| if (!OS.InternetGetCookie (url, null, cookieData, size)) return; |
| } |
| String allCookies = cookieData.toString (0, size[0]); |
| 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; |
| } |
| } |
| } |
| }; |
| |
| NativeSetCookie = () -> { |
| if (OS.IsPPC) return; |
| TCHAR url = new TCHAR (0, CookieUrl, true); |
| TCHAR value = new TCHAR (0, CookieValue, true); |
| CookieResult = OS.InternetSetCookie (url, null, value); |
| }; |
| |
| /* |
| * The installed version of IE can be determined by looking at registry entry |
| * HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\svcVersion, or |
| * HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Version (for |
| * IE releases prior to IE10). Check this value in order to determine |
| * version-specific features that can be enabled. |
| */ |
| TCHAR key = new TCHAR (0, "Software\\Microsoft\\Internet Explorer", true); //$NON-NLS-1$ |
| long /*int*/ [] phkResult = new long /*int*/ [1]; |
| if (OS.RegOpenKeyEx (OS.HKEY_LOCAL_MACHINE, key, 0, OS.KEY_READ, phkResult) == 0) { |
| int [] lpcbData = new int [1]; |
| TCHAR buffer = new TCHAR (0, "svcVersion", true); //$NON-NLS-1$ |
| int result = OS.RegQueryValueEx (phkResult [0], buffer, 0, null, (TCHAR) null, lpcbData); |
| if (result != 0) { |
| buffer = new TCHAR (0, "Version", true); //$NON-NLS-1$ |
| result = OS.RegQueryValueEx (phkResult [0], buffer, 0, null, (TCHAR) null, lpcbData); |
| } |
| if (result == 0) { |
| TCHAR lpData = new TCHAR (0, lpcbData [0] / TCHAR.sizeof); |
| result = OS.RegQueryValueEx (phkResult [0], buffer, 0, null, lpData, lpcbData); |
| if (result == 0) { |
| String versionString = lpData.toString (0, lpData.strlen ()); |
| int index = versionString.indexOf ("."); //$NON-NLS-1$ |
| if (index != -1) { |
| String majorString = versionString.substring (0, index); |
| try { |
| IEVersion = Integer.valueOf (majorString).intValue (); |
| } catch (NumberFormatException e) { |
| /* just continue, version-specific features will not be enabled */ |
| } |
| } |
| } |
| } |
| OS.RegCloseKey (phkResult [0]); |
| } |
| |
| /* |
| * Registry entry HKEY_CLASSES_ROOT\Shell.Explorer\CLSID indicates which version of |
| * Shell.Explorer to use by default. We usually want to use this value because it |
| * typically points at the newest one that is available. However it is possible for |
| * this registry entry to be changed by another application to point at some other |
| * Shell.Explorer version. |
| * |
| * The Browser depends on the Shell.Explorer version being at least Shell.Explorer.2. |
| * If it is detected in the registry to be Shell.Explorer.1 then change the progId that |
| * will be embedded to explicitly specify Shell.Explorer.2. |
| */ |
| key = new TCHAR (0, "Shell.Explorer\\CLSID", true); //$NON-NLS-1$ |
| phkResult = new long /*int*/ [1]; |
| if (OS.RegOpenKeyEx (OS.HKEY_CLASSES_ROOT, key, 0, OS.KEY_READ, phkResult) == 0) { |
| int [] lpcbData = new int [1]; |
| int result = OS.RegQueryValueEx (phkResult [0], null, 0, null, (TCHAR) null, lpcbData); |
| if (result == 0) { |
| TCHAR lpData = new TCHAR (0, lpcbData [0] / TCHAR.sizeof); |
| result = OS.RegQueryValueEx (phkResult [0], null, 0, null, lpData, lpcbData); |
| if (result == 0) { |
| String clsid = lpData.toString (0, lpData.strlen ()); |
| if (clsid.equals (CLSID_SHELLEXPLORER1)) { |
| /* Shell.Explorer.1 is the default, ensure that Shell.Explorer.2 is available */ |
| key = new TCHAR (0, "Shell.Explorer.2", true); //$NON-NLS-1$ |
| long /*int*/ [] phkResult2 = new long /*int*/ [1]; |
| if (OS.RegOpenKeyEx (OS.HKEY_CLASSES_ROOT, key, 0, OS.KEY_READ, phkResult2) == 0) { |
| /* specify that Shell.Explorer.2 is to be used */ |
| OS.RegCloseKey (phkResult2 [0]); |
| ProgId = "Shell.Explorer.2"; //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| OS.RegCloseKey (phkResult [0]); |
| } |
| |
| if (NativePendingCookies != null) { |
| SetPendingCookies (NativePendingCookies); |
| } |
| NativePendingCookies = null; |
| } |
| |
| @Override |
| public void create(Composite parent, int style) { |
| this.style = style; |
| frame = new OleFrame(browser, SWT.NONE); |
| |
| try { |
| site = new WebSite(frame, SWT.NONE, ProgId); |
| } catch (SWTException e) { |
| browser.dispose(); |
| SWT.error(SWT.ERROR_NO_HANDLES); |
| } |
| |
| if (!Initialized) { |
| Initialized = true; |
| int version = 0; |
| String versionProperty = System.getProperty(PROPERTY_IEVERSION); |
| if (versionProperty != null) { |
| if (versionProperty.equalsIgnoreCase(VALUE_DEFAULT)) { |
| version = -1; |
| } else { |
| try { |
| version = Integer.valueOf(versionProperty).intValue(); |
| } catch (NumberFormatException e) { |
| /* |
| * An invalid value was specified for the IEVersion java property. Ignore it |
| * and continue with the usual steps for determining the version to specify. |
| */ |
| } |
| } |
| } |
| if (version == 0) { |
| if (IEVersion != 0) { |
| /* |
| * By default in Embedded IE the docuemntMode is Quirks(5) |
| * mode unless !DOCTYPE directives is defined in the HTML. |
| * As per MSDN IE8 and onwards, there is a way we could hint |
| * embedded IE to use current documentMode via appropriate |
| * version value in the registry. Refer bug 342145. |
| * |
| * Complete list of IE emulation modes is listed on MSDN: |
| * http://msdn.microsoft |
| * .com/en-us/library/ie/ee330730%28v=vs |
| * .85%29.aspx#browser_emulation |
| */ |
| if (IEVersion >= 10) { |
| version = IEVersion * 1000 + 1; |
| } |
| else if (IEVersion >= 8) { |
| version = IEVersion * 1111; |
| } |
| else { |
| version = IEVersion * 1000; |
| } |
| } else { |
| version = DEFAULT_IE_VERSION; |
| } |
| } |
| |
| if (version != -1) { |
| long /*int*/[] key = new long /*int*/[1]; |
| final TCHAR subkey = new TCHAR(0, "Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", true); //$NON-NLS-1$ |
| if (OS.RegCreateKeyEx(OS.HKEY_CURRENT_USER, subkey, 0, null, OS.REG_OPTION_VOLATILE, OS.KEY_WRITE | OS.KEY_QUERY_VALUE, 0, key, null) == 0) { |
| TCHAR lpszFile = new TCHAR(0, OS.MAX_PATH); |
| OS.GetModuleFileName(0, lpszFile, lpszFile.length()); |
| String path = lpszFile.toString(0, lpszFile.strlen()); |
| int index = path.lastIndexOf(SEPARATOR_OS); |
| String executable = index != -1 ? path.substring(index + 1) : path; |
| final TCHAR lpValueName = new TCHAR(0, executable, true); |
| /* |
| * Program name & IE version entry is added to the Windows |
| * registry and same gets deleted during the dispose cycle. |
| * There is a possibility if the SWT application crashes or |
| * is exited forcefully, which leaves the registry entry |
| * as-is and hence if entry exists, updating it next time |
| * the SWT application creates embedded IE, refer bug 440300 |
| */ |
| int result = OS.RegQueryValueEx(key[0], lpValueName, 0, null, (int[])null, null); |
| if (result == 0 || result == OS.ERROR_FILE_NOT_FOUND) { |
| if (OS.RegSetValueEx(key[0], lpValueName, 0, OS.REG_DWORD, new int[] {version}, 4) == 0) { |
| parent.getDisplay().addListener(SWT.Dispose, event -> { |
| long /*int*/[] key1 = new long /*int*/[1]; |
| if (OS.RegOpenKeyEx(OS.HKEY_CURRENT_USER, subkey, 0, OS.KEY_WRITE, key1) == 0) { |
| OS.RegDeleteValue(key1[0], lpValueName); |
| } |
| }); |
| } |
| } |
| OS.RegCloseKey(key[0]); |
| } |
| } |
| } |
| |
| site.doVerb(OLE.OLEIVERB_INPLACEACTIVATE); |
| auto = new OleAutomation(site); |
| |
| domListener = e -> handleDOMEvent(e); |
| |
| Listener listener = e -> { |
| switch (e.type) { |
| case SWT.Dispose: { |
| /* make this handler run after other dispose listeners */ |
| if (ignoreDispose) { |
| ignoreDispose = false; |
| break; |
| } |
| ignoreDispose = true; |
| browser.notifyListeners (e.type, e); |
| e.type = SWT.NONE; |
| |
| /* invoke onbeforeunload handlers */ |
| if (!browser.isClosing) { |
| LocationListener[] oldLocationListeners = locationListeners; |
| locationListeners = new LocationListener[0]; |
| site.ignoreAllMessages = true; |
| execute ("window.location.href='about:blank'"); //$NON-NLS-1$ |
| site.ignoreAllMessages = false; |
| locationListeners = oldLocationListeners; |
| } |
| |
| /* |
| * It is possible for the Browser's OLE frame to have been disposed |
| * by a Dispose listener that was invoked by notifyListeners above, |
| * so check for this before unhooking its DOM listeners. |
| */ |
| if (!frame.isDisposed ()) unhookDOMListeners(documents); |
| |
| for (int i = 0; i < documents.length; i++) { |
| documents[i].dispose(); |
| } |
| documents = null; |
| |
| Iterator<BrowserFunction> elements = functions.values().iterator (); |
| while (elements.hasNext ()) { |
| elements.next ().dispose (false); |
| } |
| functions = null; |
| |
| lastNavigateURL = uncRedirect = null; |
| domListener = null; |
| if (auto != null) auto.dispose(); |
| auto = null; |
| break; |
| } |
| case SWT.Resize: { |
| frame.setBounds(browser.getClientArea()); |
| break; |
| } |
| case SWT.MouseWheel: { |
| /* MouseWheel events come from the DOM */ |
| e.doit = false; |
| break; |
| } |
| case SWT.FocusIn: { |
| site.setFocus(); |
| break; |
| } |
| case SWT.Traverse: { |
| /* |
| * Tabbing out of the browser can fail as a result of the WebSite |
| * control embedded within the Browser. The workaround is to |
| * listen for traversals and re-perform the traversal on the |
| * appropriate control. |
| */ |
| if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS && e.widget instanceof WebSite) { |
| /* otherwise will traverse to the Browser control */ |
| browser.traverse(SWT.TRAVERSE_TAB_PREVIOUS, e); |
| e.doit = false; |
| } |
| /* |
| * Return traversals can sometimes come through TranslateAccelerator, |
| * depending on where focus is within the Browser. Traversal |
| * events should always be triggered by a key event from the DOM, |
| * so if a Traversal from TranslateAccelerator is detected |
| * (e.doit == true) then stop its propagation. |
| */ |
| if (e.detail == SWT.TRAVERSE_RETURN && e.doit && e.widget instanceof Browser) { |
| e.type = SWT.None; |
| e.doit = false; |
| } |
| break; |
| } |
| } |
| }; |
| browser.addListener(SWT.Dispose, listener); |
| browser.addListener(SWT.FocusIn, listener); |
| browser.addListener(SWT.Resize, listener); |
| browser.addListener(SWT.Traverse, listener); |
| site.addListener(SWT.MouseWheel, listener); |
| site.addListener(SWT.Traverse, listener); |
| |
| OleListener oleListener = event -> { |
| /* callbacks are asynchronous, auto could be disposed */ |
| if (auto != null) { |
| switch (event.type) { |
| case BeforeNavigate2: { |
| isRefresh = false; /* refreshes do not come through here */ |
| |
| /* don't send client events if the initial navigate to about:blank has not completed */ |
| if (performingInitialNavigate) break; |
| |
| Variant varResult1 = event.arguments[1]; |
| String url1 = varResult1.getString(); |
| |
| if (uncRedirect != null) { |
| /* |
| * Silently allow the navigate to proceed if the url is the first segment of a |
| * UNC path being navigated to (initiated by the NavigateError listener to show |
| * a name/password prompter), or if the url is the full UNC path (initiated by |
| * the NavigateComplete listener to redirect from the UNC's first segment to its |
| * full path). |
| */ |
| if (uncRedirect.equals(url1) || (uncRedirect.startsWith(url1) && uncRedirect.indexOf('\\', 2) == url1.length())) { |
| Variant cancel1 = event.arguments[6]; |
| if (cancel1 != null) { |
| long /*int*/ pCancel1 = cancel1.getByRef(); |
| OS.MoveMemory(pCancel1, new short[] {OS.VARIANT_FALSE}, 2); |
| } |
| isAboutBlank = false; |
| break; |
| } else { |
| /* |
| * This navigate does not correspond to the previously-initiated |
| * UNC navigation so clear this state since it's no longer valid. |
| */ |
| uncRedirect = null; |
| } |
| } |
| |
| /* |
| * Feature in IE. For navigations on the local machine, BeforeNavigate2's url |
| * field contains a string representation of the file path in a non-URL format. |
| * In order to be consistent with the other Browser implementations, this |
| * case is detected and the string is changed to be a proper url string. |
| */ |
| if (url1.indexOf(":/") == -1 && url1.indexOf(":\\") != -1) { //$NON-NLS-1$ //$NON-NLS-2$ |
| TCHAR filePath1 = new TCHAR(0, url1, true); |
| TCHAR urlResult1 = new TCHAR(0, OS.INTERNET_MAX_URL_LENGTH); |
| int[] size1 = new int[] {urlResult1.length()}; |
| if (!OS.IsWinCE && OS.UrlCreateFromPath(filePath1, urlResult1, size1, 0) == COM.S_OK) { |
| url1 = urlResult1.toString(0, size1[0]); |
| } else { |
| url1 = PROTOCOL_FILE + url1.replace('\\', '/'); |
| } |
| } |
| |
| /* Disallow local file system accesses if the browser content is untrusted */ |
| if (url1.startsWith(PROTOCOL_FILE) && _getUrl().startsWith(ABOUT_BLANK) && untrustedText) { |
| Variant cancel2 = event.arguments[6]; |
| if (cancel2 != null) { |
| long /*int*/ pCancel2 = cancel2.getByRef(); |
| OS.MoveMemory(pCancel2, new short[] {OS.VARIANT_TRUE}, 2); |
| } |
| break; |
| } |
| |
| LocationEvent newEvent1 = new LocationEvent(browser); |
| newEvent1.display = browser.getDisplay(); |
| newEvent1.widget = browser; |
| newEvent1.location = url1; |
| newEvent1.doit = true; |
| for (int i1 = 0; i1 < locationListeners.length; i1++) { |
| locationListeners[i1].changing(newEvent1); |
| } |
| boolean doit1 = newEvent1.doit && !browser.isDisposed(); |
| Variant cancel3 = event.arguments[6]; |
| if (cancel3 != null) { |
| long /*int*/ pCancel3 = cancel3.getByRef(); |
| OS.MoveMemory(pCancel3, new short[] {doit1 ? OS.VARIANT_FALSE : OS.VARIANT_TRUE}, 2); |
| } |
| if (doit1) { |
| varResult1 = event.arguments[0]; |
| IDispatch dispatch1 = varResult1.getDispatch(); |
| Variant variant1 = new Variant(auto); /* does not need to be disposed */ |
| IDispatch top1 = variant1.getDispatch(); |
| if (top1.getAddress() == dispatch1.getAddress()) { |
| isAboutBlank = url1.startsWith(ABOUT_BLANK); |
| } |
| } |
| break; |
| } |
| case CommandStateChange: { |
| boolean enabled = false; |
| Variant varResult2 = event.arguments[0]; |
| int command = varResult2.getInt(); |
| varResult2 = event.arguments[1]; |
| enabled = varResult2.getBoolean(); |
| switch (command) { |
| case CSC_NAVIGATEBACK : back = enabled; break; |
| case CSC_NAVIGATEFORWARD : forward = enabled; break; |
| } |
| break; |
| } |
| case DocumentComplete: { |
| if (performingInitialNavigate) { |
| /* this event marks the completion of the initial navigate to about:blank */ |
| performingInitialNavigate = false; |
| |
| /* if browser content has been provided by the client then set it now */ |
| if (pendingText != null) { |
| setText((String)pendingText[0], ((Boolean)pendingText[1]).booleanValue()); |
| } else if (pendingUrl != null) { |
| setUrl((String)pendingUrl[0], (String)pendingUrl[1], (String[])pendingUrl[2]); |
| } |
| pendingText = pendingUrl = null; |
| break; |
| } |
| |
| Variant varResult3 = event.arguments[0]; |
| IDispatch dispatch2 = varResult3.getDispatch(); |
| |
| varResult3 = event.arguments[1]; |
| String url2 = varResult3.getString(); |
| /* |
| * Feature in IE. For navigations on the local machine, DocumentComplete's url |
| * field contains a string representation of the file path in a non-URL format. |
| * In order to be consistent with the other Browser implementations, this |
| * case is detected and the string is changed to be a proper url string. |
| */ |
| if (url2.indexOf(":/") == -1 && url2.indexOf(":\\") != -1) { //$NON-NLS-1$ //$NON-NLS-2$ |
| TCHAR filePath2 = new TCHAR(0, url2, true); |
| TCHAR urlResult2 = new TCHAR(0, OS.INTERNET_MAX_URL_LENGTH); |
| int[] size2 = new int[] {urlResult2.length()}; |
| if (!OS.IsWinCE && OS.UrlCreateFromPath(filePath2, urlResult2, size2, 0) == COM.S_OK) { |
| url2 = urlResult2.toString(0, size2[0]); |
| } else { |
| url2 = PROTOCOL_FILE + url2.replace('\\', '/'); |
| } |
| } |
| if (html != null && url2.equals(ABOUT_BLANK)) { |
| if (delaySetText) { |
| delaySetText = false; |
| browser.getDisplay().asyncExec(() -> { |
| if (browser.isDisposed() || html == null) return; |
| setHTML(html); |
| html = null; |
| }); |
| } else { |
| setHTML(html); |
| html = null; |
| } |
| } else { |
| Variant variant2 = new Variant(auto); /* does not need to be disposed */ |
| IDispatch top2 = variant2.getDispatch(); |
| LocationEvent locationEvent = new LocationEvent(browser); |
| locationEvent.display = browser.getDisplay(); |
| locationEvent.widget = browser; |
| locationEvent.location = url2; |
| locationEvent.top = top2.getAddress() == dispatch2.getAddress(); |
| for (int i2 = 0; i2 < locationListeners.length; i2++) { |
| locationListeners[i2].changed(locationEvent); |
| } |
| if (browser.isDisposed()) return; |
| |
| /* |
| * With the IBM 64-bit JVM an unexpected document complete event occurs before |
| * the native browser's DOM has been built. Filter this premature event based |
| * on the browser's ready state. |
| */ |
| int[] rgdispid1 = auto.getIDsOfNames(new String[] { "ReadyState" }); //$NON-NLS-1$ |
| Variant pVarResult1 = auto.getProperty(rgdispid1[0]); |
| if (pVarResult1 != null) { |
| int readyState = pVarResult1.getInt(); |
| pVarResult1.dispose (); |
| if (readyState != READYSTATE_COMPLETE) { |
| break; |
| } |
| } |
| |
| /* |
| * Note. The completion of the page loading is detected as |
| * described in the MSDN article "Determine when a page is |
| * done loading in WebBrowser Control". |
| */ |
| if (globalDispatch != 0 && dispatch2.getAddress() == globalDispatch) { |
| /* final document complete */ |
| globalDispatch = 0; |
| |
| /* re-install registered functions iff needed */ |
| IE ie = (IE)browser.webBrowser; |
| if (ie.installFunctionsOnDocumentComplete) { |
| ie.installFunctionsOnDocumentComplete = false; |
| Iterator<BrowserFunction> elements1 = functions.values().iterator (); |
| while (elements1.hasNext ()) { |
| BrowserFunction function1 = elements1.next (); |
| execute (function1.functionString); |
| } |
| } |
| |
| ProgressEvent progressEvent1 = new ProgressEvent(browser); |
| progressEvent1.display = browser.getDisplay(); |
| progressEvent1.widget = browser; |
| for (int i3 = 0; i3 < progressListeners.length; i3++) { |
| progressListeners[i3].completed(progressEvent1); |
| } |
| } |
| } |
| break; |
| } |
| case DownloadComplete: { |
| /* |
| * IE feature. Some events that swt relies on are not sent when |
| * a page is refreshed (as opposed to being navigated to). The |
| * workaround is to use DownloadComplete as an opportunity to |
| * do this work. |
| */ |
| |
| Iterator<BrowserFunction> elements2 = functions.values().iterator (); |
| while (elements2.hasNext ()) { |
| BrowserFunction function2 = elements2.next (); |
| execute (function2.functionString); |
| } |
| |
| if (!isRefresh) break; |
| isRefresh = false; |
| |
| /* |
| * DocumentComplete is not received for refreshes, but clients may rely |
| * on this event for tasks like hooking javascript listeners, so send the |
| * event here. |
| */ |
| ProgressEvent progressEvent2 = new ProgressEvent(browser); |
| progressEvent2.display = browser.getDisplay(); |
| progressEvent2.widget = browser; |
| for (int i4 = 0; i4 < progressListeners.length; i4++) { |
| progressListeners[i4].completed(progressEvent2); |
| } |
| |
| break; |
| } |
| case NavigateComplete2: { |
| jsEnabled = jsEnabledOnNextPage; |
| |
| Variant varResult4 = event.arguments[1]; |
| String url3 = varResult4.getString(); |
| if (!performingInitialNavigate) { |
| varResult4 = event.arguments[0]; |
| IDispatch dispatch3 = varResult4.getDispatch(); |
| Variant variant3 = new Variant(auto); /* does not need to be disposed */ |
| IDispatch top3 = variant3.getDispatch(); |
| if (top3.getAddress() == dispatch3.getAddress()) { |
| isAboutBlank = url3.startsWith(ABOUT_BLANK); |
| lastNavigateURL = url3; |
| } |
| } |
| |
| /* |
| * Bug in Acrobat Reader. Opening > MAX_PDF PDF files causes Acrobat to not |
| * clean up its shells properly when the container Browser is disposed. |
| * This results in Eclipse crashing at shutdown time because the leftover |
| * shells have invalid references to unloaded Acrobat libraries. The |
| * workaround is to not unload the Acrobat libraries if > MAX_PDF PDF |
| * files have been opened. |
| */ |
| boolean isPDF = false; |
| String path = null; |
| try { |
| path = new URL(url3).getPath(); |
| } catch (MalformedURLException e) { |
| } |
| if (path != null) { |
| int extensionIndex = path.lastIndexOf('.'); |
| if (extensionIndex != -1) { |
| String extension = path.substring(extensionIndex); |
| if (extension.equalsIgnoreCase(EXTENSION_PDF)) { |
| isPDF = true; |
| PDFCount++; |
| if (PDFCount > MAX_PDF) { |
| COM.FreeUnusedLibraries = false; |
| } |
| } |
| } |
| } |
| |
| if (uncRedirect != null) { |
| if (uncRedirect.equals(url3)) { |
| /* full UNC path has been successfully navigated */ |
| uncRedirect = null; |
| break; |
| } |
| if (uncRedirect.startsWith(url3)) { |
| /* |
| * UNC first segment has been successfully navigated, |
| * now redirect to the full UNC path. |
| */ |
| navigate(uncRedirect, null, null, true); |
| break; |
| } |
| uncRedirect = null; |
| } |
| |
| varResult4 = event.arguments[0]; |
| IDispatch dispatch4 = varResult4.getDispatch(); |
| if (globalDispatch == 0) globalDispatch = dispatch4.getAddress(); |
| |
| OleAutomation webBrowser = varResult4.getAutomation(); |
| Variant variant4 = new Variant(auto); /* does not need to be disposed */ |
| IDispatch top4 = variant4.getDispatch(); |
| boolean isTop = top4.getAddress() == dispatch4.getAddress(); |
| if (isTop) { |
| /* unhook DOM listeners and unref the last document(s) */ |
| unhookDOMListeners(documents); |
| for (int i5 = 0; i5 < documents.length; i5++) { |
| documents[i5].dispose(); |
| } |
| documents = new OleAutomation[0]; |
| |
| /* re-install registered functions */ |
| Iterator<BrowserFunction> elements3 = functions.values().iterator (); |
| while (elements3.hasNext ()) { |
| BrowserFunction function3 = elements3.next (); |
| execute (function3.functionString); |
| } |
| } |
| if (!isPDF) { |
| hookDOMListeners(webBrowser, isTop); |
| } |
| webBrowser.dispose(); |
| break; |
| } |
| case NavigateError: { |
| if (uncRedirect != null) { |
| /* |
| * This is the second error attempting to reach this UNC path, so |
| * it does not exist. Don't override the default error handling. |
| */ |
| uncRedirect = null; |
| break; |
| } |
| Variant varResult5 = event.arguments[1]; |
| final String url4 = varResult5.getString(); |
| if (url4.startsWith("\\\\")) { //$NON-NLS-1$ |
| varResult5 = event.arguments[3]; |
| int statusCode = varResult5.getInt(); |
| if (statusCode == INET_E_RESOURCE_NOT_FOUND) { |
| int index = url4.indexOf('\\', 2); |
| if (index != -1) { |
| final String host = url4.substring(0, index); |
| Variant cancel4 = event.arguments[4]; |
| if (cancel4 != null) { |
| long /*int*/ pCancel4 = cancel4.getByRef(); |
| OS.MoveMemory(pCancel4, new short[] {OS.VARIANT_TRUE}, 2); |
| } |
| browser.getDisplay().asyncExec(() -> { |
| if (browser.isDisposed()) return; |
| /* |
| * Feature of IE. When a UNC path ends with a '\' character IE |
| * drops this character when providing the path as an argument |
| * to some IE listeners. Remove this character here too in |
| * order to match these other listener argument values. |
| */ |
| if (url4.endsWith("\\")) { //$NON-NLS-1$ |
| uncRedirect = url4.substring(0, url4.length() - 1); |
| } else { |
| uncRedirect = url4; |
| } |
| navigate(host, null, null, true); |
| }); |
| } |
| } |
| } |
| break; |
| } |
| case NewWindow2: { |
| Variant cancel5 = event.arguments[1]; |
| long /*int*/ pCancel5 = cancel5.getByRef(); |
| WindowEvent newEvent2 = new WindowEvent(browser); |
| newEvent2.display = browser.getDisplay(); |
| newEvent2.widget = browser; |
| newEvent2.required = false; |
| for (int i6 = 0; i6 < openWindowListeners.length; i6++) { |
| openWindowListeners[i6].open(newEvent2); |
| } |
| IE browser = null; |
| if (newEvent2.browser != null && newEvent2.browser.webBrowser instanceof IE) { |
| browser = (IE)newEvent2.browser.webBrowser; |
| } |
| boolean doit2 = browser != null && !browser.browser.isDisposed(); |
| if (doit2) { |
| /* |
| * When a Browser is opened in a new window, BrowserFunctions that are |
| * installed in it in the NavigateComplete2 callback are not retained |
| * through the loading of the page. The workaround is to re-install |
| * the functions when DocumentComplete is received. |
| */ |
| browser.installFunctionsOnDocumentComplete = true; |
| |
| Variant variant5 = new Variant(browser.auto); /* does not need to be disposed */ |
| IDispatch iDispatch = variant5.getDispatch(); |
| Variant ppDisp = event.arguments[0]; |
| long /*int*/ byref = ppDisp.getByRef(); |
| if (byref != 0) OS.MoveMemory(byref, new long /*int*/[] {iDispatch.getAddress()}, C.PTR_SIZEOF); |
| } |
| if (newEvent2.required) { |
| OS.MoveMemory(pCancel5, new short[]{doit2 ? OS.VARIANT_FALSE : OS.VARIANT_TRUE}, 2); |
| } |
| break; |
| } |
| case OnMenuBar: { |
| Variant arg01 = event.arguments[0]; |
| menuBar = arg01.getBoolean(); |
| break; |
| } |
| case OnStatusBar: { |
| Variant arg02 = event.arguments[0]; |
| statusBar = arg02.getBoolean(); |
| break; |
| } |
| case OnToolBar: { |
| Variant arg03 = event.arguments[0]; |
| toolBar = arg03.getBoolean(); |
| /* |
| * Feature in Internet Explorer. OnToolBar FALSE is emitted |
| * when both tool bar, address bar and menu bar must not be visible. |
| * OnToolBar TRUE is emitted when either of tool bar, address bar |
| * or menu bar is visible. |
| */ |
| if (!toolBar) { |
| addressBar = false; |
| menuBar = false; |
| } |
| break; |
| } |
| case OnVisible: { |
| Variant arg11 = event.arguments[0]; |
| boolean visible = arg11.getBoolean(); |
| WindowEvent newEvent3 = new WindowEvent(browser); |
| newEvent3.display = browser.getDisplay(); |
| newEvent3.widget = browser; |
| if (visible) { |
| if (addressBar) { |
| /* |
| * Bug in Internet Explorer. There is no distinct notification for |
| * the address bar. If neither address, menu or tool bars are visible, |
| * OnToolBar FALSE is emitted. For some reason, querying the value of |
| * AddressBar in this case returns true even though it should not be |
| * set visible. The workaround is to only query the value of AddressBar |
| * when OnToolBar FALSE has not been emitted. |
| */ |
| int[] rgdispid2 = auto.getIDsOfNames(new String[] { "AddressBar" }); //$NON-NLS-1$ |
| Variant pVarResult2 = auto.getProperty(rgdispid2[0]); |
| if (pVarResult2 != null) { |
| if (pVarResult2.getType () == OLE.VT_BOOL) { |
| addressBar = pVarResult2.getBoolean (); |
| } |
| pVarResult2.dispose (); |
| } |
| } |
| newEvent3.addressBar = addressBar; |
| newEvent3.menuBar = menuBar; |
| newEvent3.statusBar = statusBar; |
| newEvent3.toolBar = toolBar; |
| newEvent3.location = location; |
| newEvent3.size = size; |
| for (int i7 = 0; i7 < visibilityWindowListeners.length; i7++) { |
| visibilityWindowListeners[i7].show(newEvent3); |
| } |
| location = null; |
| size = null; |
| } else { |
| for (int i8 = 0; i8 < visibilityWindowListeners.length; i8++) { |
| visibilityWindowListeners[i8].hide(newEvent3); |
| } |
| } |
| break; |
| } |
| case ProgressChange: { |
| /* don't send client events if the initial navigate to about:blank has not completed */ |
| if (performingInitialNavigate) break; |
| |
| Variant arg12 = event.arguments[0]; |
| int nProgress = arg12.getType() != OLE.VT_I4 ? 0 : arg12.getInt(); // may be -1 |
| Variant arg2 = event.arguments[1]; |
| int nProgressMax = arg2.getType() != OLE.VT_I4 ? 0 : arg2.getInt(); |
| ProgressEvent newEvent4 = new ProgressEvent(browser); |
| newEvent4.display = browser.getDisplay(); |
| newEvent4.widget = browser; |
| newEvent4.current = nProgress; |
| newEvent4.total = nProgressMax; |
| if (nProgress != -1) { |
| for (int i9 = 0; i9 < progressListeners.length; i9++) { |
| progressListeners[i9].changed(newEvent4); |
| } |
| } |
| break; |
| } |
| case StatusTextChange: { |
| /* don't send client events if the initial navigate to about:blank has not completed */ |
| if (performingInitialNavigate) break; |
| |
| Variant arg13 = event.arguments[0]; |
| if (arg13.getType() == OLE.VT_BSTR) { |
| String text = arg13.getString(); |
| StatusTextEvent newEvent5 = new StatusTextEvent(browser); |
| newEvent5.display = browser.getDisplay(); |
| newEvent5.widget = browser; |
| newEvent5.text = text; |
| for (int i10 = 0; i10 < statusTextListeners.length; i10++) { |
| statusTextListeners[i10].changed(newEvent5); |
| } |
| } |
| break; |
| } |
| case TitleChange: { |
| /* don't send client events if the initial navigate to about:blank has not completed */ |
| if (performingInitialNavigate) break; |
| |
| Variant arg14 = event.arguments[0]; |
| if (arg14.getType() == OLE.VT_BSTR) { |
| String title = arg14.getString(); |
| TitleEvent newEvent6 = new TitleEvent(browser); |
| newEvent6.display = browser.getDisplay(); |
| newEvent6.widget = browser; |
| newEvent6.title = title; |
| for (int i11 = 0; i11 < titleListeners.length; i11++) { |
| titleListeners[i11].changed(newEvent6); |
| } |
| } |
| break; |
| } |
| case WindowClosing: { |
| /* |
| * Disposing the Browser directly from this callback will crash if the |
| * Browser has a text field with an active caret. As a workaround fire |
| * the Close event and dispose the Browser in an async block. |
| */ |
| browser.getDisplay().asyncExec(() -> { |
| if (browser.isDisposed()) return; |
| WindowEvent newEvent = new WindowEvent(browser); |
| newEvent.display = browser.getDisplay(); |
| newEvent.widget = browser; |
| for (int i = 0; i < closeWindowListeners.length; i++) { |
| closeWindowListeners[i].close(newEvent); |
| } |
| browser.dispose(); |
| }); |
| Variant cancel6 = event.arguments[1]; |
| long /*int*/ pCancel6 = cancel6.getByRef(); |
| Variant arg15 = event.arguments[0]; |
| boolean isChildWindow = arg15.getBoolean(); |
| OS.MoveMemory(pCancel6, new short[]{isChildWindow ? OS.VARIANT_FALSE : OS.VARIANT_TRUE}, 2); |
| break; |
| } |
| case WindowSetHeight: { |
| if (size == null) size = new Point(0, 0); |
| Variant arg16 = event.arguments[0]; |
| size.y = arg16.getInt(); |
| break; |
| } |
| case WindowSetLeft: { |
| if (location == null) location = new Point(0, 0); |
| Variant arg17 = event.arguments[0]; |
| location.x = arg17.getInt(); |
| break; |
| } |
| case WindowSetTop: { |
| if (location == null) location = new Point(0, 0); |
| Variant arg18 = event.arguments[0]; |
| location.y = arg18.getInt(); |
| break; |
| } |
| case WindowSetWidth: { |
| if (size == null) size = new Point(0, 0); |
| Variant arg19 = event.arguments[0]; |
| size.x = arg19.getInt(); |
| break; |
| } |
| } |
| } |
| }; |
| site.addEventListener(BeforeNavigate2, oleListener); |
| site.addEventListener(CommandStateChange, oleListener); |
| site.addEventListener(DocumentComplete, oleListener); |
| site.addEventListener(DownloadComplete, oleListener); |
| site.addEventListener(NavigateComplete2, oleListener); |
| site.addEventListener(NavigateError, oleListener); |
| site.addEventListener(NewWindow2, oleListener); |
| site.addEventListener(OnMenuBar, oleListener); |
| site.addEventListener(OnStatusBar, oleListener); |
| site.addEventListener(OnToolBar, oleListener); |
| site.addEventListener(OnVisible, oleListener); |
| site.addEventListener(ProgressChange, oleListener); |
| site.addEventListener(StatusTextChange, oleListener); |
| site.addEventListener(TitleChange, oleListener); |
| site.addEventListener(WindowClosing, oleListener); |
| site.addEventListener(WindowSetHeight, oleListener); |
| site.addEventListener(WindowSetLeft, oleListener); |
| site.addEventListener(WindowSetTop, oleListener); |
| site.addEventListener(WindowSetWidth, oleListener); |
| |
| Variant variant = new Variant(true); |
| auto.setProperty(RegisterAsBrowser, variant); |
| variant.dispose(); |
| |
| variant = new Variant(false); |
| int[] rgdispid = auto.getIDsOfNames(new String[] {"RegisterAsDropTarget"}); //$NON-NLS-1$ |
| if (rgdispid != null) auto.setProperty(rgdispid[0], variant); |
| variant.dispose(); |
| } |
| |
| @Override |
| public boolean back() { |
| if (!back) return false; |
| int[] rgdispid = auto.getIDsOfNames(new String[] { "GoBack" }); //$NON-NLS-1$ |
| Variant pVarResult = auto.invoke(rgdispid[0]); |
| return pVarResult != null && pVarResult.getType() == OLE.VT_EMPTY; |
| } |
| |
| @Override |
| public boolean close() { |
| boolean result = true; |
| int[] rgdispid = auto.getIDsOfNames(new String[] {PROPERTY_DOCUMENT}); |
| int dispIdMember = rgdispid[0]; |
| Variant pVarResult = auto.getProperty(dispIdMember); |
| if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) { |
| if (pVarResult != null) pVarResult.dispose(); |
| } else { |
| OleAutomation document = pVarResult.getAutomation(); |
| pVarResult.dispose(); |
| rgdispid = document.getIDsOfNames(new String[]{"parentWindow"}); //$NON-NLS-1$ |
| /* rgdispid != null implies HTML content */ |
| if (rgdispid != null) { |
| dispIdMember = rgdispid[0]; |
| pVarResult = document.getProperty(dispIdMember); |
| if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) { |
| if (pVarResult != null) pVarResult.dispose(); |
| } else { |
| OleAutomation window = pVarResult.getAutomation(); |
| pVarResult.dispose(); |
| rgdispid = window.getIDsOfNames(new String[]{"location"}); //$NON-NLS-1$ |
| dispIdMember = rgdispid[0]; |
| pVarResult = window.getProperty(dispIdMember); |
| if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) { |
| if (pVarResult != null) pVarResult.dispose(); |
| } else { |
| OleAutomation location = pVarResult.getAutomation(); |
| pVarResult.dispose(); |
| LocationListener[] oldListeners = locationListeners; |
| locationListeners = new LocationListener[0]; |
| rgdispid = location.getIDsOfNames(new String[]{"replace"}); //$NON-NLS-1$ |
| dispIdMember = rgdispid[0]; |
| Variant[] args = new Variant[] {new Variant("about:blank")}; //$NON-NLS-1$ |
| pVarResult = location.invoke(dispIdMember, args); |
| if (pVarResult == null) { |
| /* cancelled by user */ |
| result = false; |
| } else { |
| pVarResult.dispose(); |
| } |
| args[0].dispose(); |
| locationListeners = oldListeners; |
| location.dispose(); |
| } |
| window.dispose(); |
| } |
| } |
| document.dispose(); |
| } |
| return result; |
| } |
| |
| static Variant createSafeArray(String string) { |
| /* Create a pointer and copy the data into it */ |
| byte[] bytes = string.getBytes(); |
| int length = bytes.length; |
| long /*int*/ pvData = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, length); |
| C.memmove(pvData, bytes, length); |
| int cElements1 = length; |
| |
| /* Create a SAFEARRAY in memory */ |
| long /*int*/ pSafeArray = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, SAFEARRAY.sizeof); |
| SAFEARRAY safeArray = new SAFEARRAY(); |
| safeArray.cDims = 1; |
| safeArray.fFeatures = OS.FADF_FIXEDSIZE; |
| safeArray.cbElements = 1; |
| safeArray.pvData = pvData; |
| SAFEARRAYBOUND safeArrayBound = new SAFEARRAYBOUND(); |
| safeArray.rgsabound = safeArrayBound; |
| safeArrayBound.cElements = cElements1; |
| OS.MoveMemory (pSafeArray, safeArray, SAFEARRAY.sizeof); |
| |
| /* Return a Variant that holds the SAFEARRAY */ |
| long /*int*/ pVariant = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, Variant.sizeof); |
| short vt = (short)(OLE.VT_ARRAY | OLE.VT_UI1); |
| OS.MoveMemory(pVariant, new short[] {vt}, 2); |
| OS.MoveMemory(pVariant + 8, new long /*int*/[] {pSafeArray}, C.PTR_SIZEOF); |
| return new Variant(pVariant, (short)(OLE.VT_BYREF | OLE.VT_VARIANT)); |
| } |
| |
| @Override |
| public boolean execute(String script) { |
| /* |
| * Issue with IE: If the browser has not shown any content yet then |
| * first navigate to about:blank to work around bug 465822, then execute |
| * the requested script. |
| */ |
| if (!performingInitialNavigate && _getUrl().length() == 0) { |
| performingInitialNavigate = true; |
| navigate (ABOUT_BLANK, null, null, true); |
| } |
| |
| /* get IHTMLDocument2 */ |
| int[] rgdispid = auto.getIDsOfNames(new String[] {PROPERTY_DOCUMENT}); |
| int dispIdMember = rgdispid[0]; |
| Variant pVarResult = auto.getProperty(dispIdMember); |
| if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) { |
| if (pVarResult != null) pVarResult.dispose (); |
| return false; |
| } |
| OleAutomation document = pVarResult.getAutomation(); |
| pVarResult.dispose(); |
| |
| /* get IHTMLWindow2 */ |
| rgdispid = document.getIDsOfNames(new String[]{"parentWindow"}); //$NON-NLS-1$ |
| if (rgdispid == null) { |
| /* implies that browser's content is not a IHTMLDocument2 (eg.- acrobat reader) */ |
| document.dispose(); |
| return false; |
| } |
| dispIdMember = rgdispid[0]; |
| pVarResult = document.getProperty(dispIdMember); |
| if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) { |
| if (pVarResult != null) pVarResult.dispose (); |
| document.dispose(); |
| return false; |
| } |
| OleAutomation ihtmlWindow2 = pVarResult.getAutomation(); |
| pVarResult.dispose(); |
| document.dispose(); |
| |
| rgdispid = ihtmlWindow2.getIDsOfNames(new String[] { "execScript", "code" }); //$NON-NLS-1$ //$NON-NLS-2$ |
| if (rgdispid == null) { |
| ihtmlWindow2.dispose(); |
| return false; |
| } |
| Variant[] rgvarg = new Variant[1]; |
| rgvarg[0] = new Variant(script); |
| int[] rgdispidNamedArgs = new int[1]; |
| rgdispidNamedArgs[0] = rgdispid[1]; |
| pVarResult = ihtmlWindow2.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs); |
| rgvarg[0].dispose(); |
| ihtmlWindow2.dispose(); |
| if (pVarResult == null) return false; |
| pVarResult.dispose(); |
| return true; |
| } |
| |
| @Override |
| public boolean forward() { |
| if (!forward) return false; |
| int[] rgdispid = auto.getIDsOfNames(new String[] { "GoForward" }); //$NON-NLS-1$ |
| Variant pVarResult = auto.invoke(rgdispid[0]); |
| return pVarResult != null && pVarResult.getType() == OLE.VT_EMPTY; |
| } |
| |
| @Override |
| public String getBrowserType () { |
| return "ie"; //$NON-NLS-1$ |
| } |
| |
| @Override |
| String getDeleteFunctionString (String functionName) { |
| return "window." + functionName + "=undefined"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| @Override |
| public String getText() { |
| /* get the document object */ |
| int[] rgdispid = auto.getIDsOfNames(new String[] {PROPERTY_DOCUMENT}); |
| Variant pVarResult = auto.getProperty(rgdispid[0]); |
| if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) { |
| if (pVarResult != null) pVarResult.dispose (); |
| return ""; //$NON-NLS-1$ |
| } |
| OleAutomation document = pVarResult.getAutomation(); |
| pVarResult.dispose(); |
| |
| /* get the html object */ |
| rgdispid = document.getIDsOfNames(new String[] {"documentElement"}); //$NON-NLS-1$ |
| if (rgdispid == null) { |
| /* implies that the browser is displaying non-HTML content */ |
| document.dispose(); |
| return ""; //$NON-NLS-1$ |
| } |
| pVarResult = document.getProperty(rgdispid[0]); |
| document.dispose(); |
| if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY || pVarResult.getType() == COM.VT_NULL) { |
| if (pVarResult != null) pVarResult.dispose (); |
| return ""; //$NON-NLS-1$ |
| } |
| OleAutomation element = pVarResult.getAutomation(); |
| pVarResult.dispose(); |
| |
| /* get its outerHTML property */ |
| rgdispid = element.getIDsOfNames(new String[] {"outerHTML"}); //$NON-NLS-1$ |
| pVarResult = element.getProperty(rgdispid[0]); |
| element.dispose(); |
| if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) { |
| if (pVarResult != null) pVarResult.dispose (); |
| return ""; //$NON-NLS-1$ |
| } |
| String result = pVarResult.getString(); |
| pVarResult.dispose(); |
| |
| return result; |
| } |
| |
| @Override |
| public String getUrl() { |
| /* |
| * If the url is "" then return ABOUT_BLANK in order to be consistent |
| * with the other Browser implementations which auto-navigate to ABOUT_BLANK |
| * when opened. |
| */ |
| String result = _getUrl(); |
| return result.length() != 0 ? result : ABOUT_BLANK; |
| } |
| |
| String _getUrl() { |
| int[] rgdispid = auto.getIDsOfNames(new String[] { "LocationURL" }); //$NON-NLS-1$ |
| Variant pVarResult = auto.getProperty(rgdispid[0]); |
| if (pVarResult == null || pVarResult.getType() != OLE.VT_BSTR) return ""; //$NON-NLS-1$ |
| String result = pVarResult.getString(); |
| pVarResult.dispose(); |
| return result; |
| } |
| |
| @Override |
| public boolean isBackEnabled() { |
| return back; |
| } |
| |
| @Override |
| public boolean isForwardEnabled() { |
| return forward; |
| } |
| |
| @Override |
| public boolean isFocusControl () { |
| return site.isFocusControl() || frame.isFocusControl(); |
| } |
| |
| boolean navigate(String url, String postData, String headers[], boolean silent) { |
| int count = 1; |
| if (postData != null) count++; |
| if (headers != null) count++; |
| Variant[] rgvarg = new Variant[count]; |
| int[] rgdispidNamedArgs = new int[count]; |
| int[] rgdispid = auto.getIDsOfNames(new String[] { "Navigate", "URL", "PostData", "Headers" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| int index = 0; |
| rgvarg[index] = new Variant(url); |
| rgdispidNamedArgs[index++] = rgdispid[1]; |
| if (postData != null) { |
| rgvarg[index] = createSafeArray(postData); |
| rgdispidNamedArgs[index++] = rgdispid[2]; |
| } |
| 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"); |
| } |
| } |
| } |
| } |
| rgvarg[index] = new Variant(buffer.toString()); |
| rgdispidNamedArgs[index++] = rgdispid[3]; |
| } |
| boolean oldValue = false; |
| if (silent && !OS.IsWinCE && IEVersion >= 7) { |
| int hResult = OS.CoInternetIsFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.GET_FEATURE_FROM_PROCESS); |
| oldValue = hResult == COM.S_OK; |
| OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, true); |
| } |
| Variant pVarResult = auto.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs); |
| if (silent && !OS.IsWinCE && IEVersion >= 7) { |
| OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, oldValue); |
| } |
| for (int i = 0; i < count; i++) { |
| rgvarg[i].dispose(); |
| } |
| if (pVarResult == null) return false; |
| boolean result = pVarResult.getType() == OLE.VT_EMPTY; |
| pVarResult.dispose(); |
| return result; |
| } |
| |
| @Override |
| public void refresh() { |
| uncRedirect = null; |
| |
| /* |
| * Bug in Acrobat Reader. Opening > MAX_PDF PDF files causes Acrobat to not |
| * clean up its shells properly when the container Browser is disposed. |
| * This results in Eclipse crashing at shutdown time because the leftover |
| * shells have invalid references to unloaded Acrobat libraries. The |
| * workaround is to not unload the Acrobat libraries if > MAX_PDF PDF |
| * files have been opened. |
| */ |
| String url = _getUrl(); |
| int extensionIndex = url.lastIndexOf('.'); |
| if (extensionIndex != -1) { |
| String extension = url.substring(extensionIndex); |
| if (extension.equalsIgnoreCase (EXTENSION_PDF)) { |
| PDFCount++; |
| if (PDFCount > MAX_PDF) { |
| COM.FreeUnusedLibraries = false; |
| } |
| } |
| } |
| |
| isRefresh = true; |
| int[] rgdispid = auto.getIDsOfNames(new String[] { "Refresh" }); //$NON-NLS-1$ |
| auto.invoke(rgdispid[0]); |
| } |
| |
| void setHTML (String string) { |
| int charCount = string.length(); |
| char[] chars = new char[charCount]; |
| string.getChars(0, charCount, chars, 0); |
| int byteCount = OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, charCount, null, 0, null, null); |
| /* |
| * Internet Explorer appears to treat the data loaded with |
| * nsIPersistStreamInit.Load as if it were encoded using the default |
| * local charset. There does not seem to be an API to set the |
| * desired charset explicitly in this case. The fix is to |
| * prepend the UTF-8 Byte Order Mark signature to the data. |
| */ |
| byte[] UTF8BOM = {(byte)0xEF, (byte)0xBB, (byte)0xBF}; |
| long /*int*/ hGlobal = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, UTF8BOM.length + byteCount); |
| if (hGlobal != 0) { |
| OS.MoveMemory(hGlobal, UTF8BOM, UTF8BOM.length); |
| OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, charCount, hGlobal + UTF8BOM.length, byteCount, null, null); |
| long /*int*/ [] ppstm = new long /*int*/ [1]; |
| /* |
| * CreateStreamOnHGlobal is called with the flag fDeleteOnRelease. |
| * If the call succeeds the buffer hGlobal is freed automatically |
| * when the IStream object is released. If the call fails, free the |
| * buffer hGlobal. |
| */ |
| if (OS.CreateStreamOnHGlobal(hGlobal, true, ppstm) == OS.S_OK) { |
| int[] rgdispid = auto.getIDsOfNames(new String[] {PROPERTY_DOCUMENT}); |
| Variant pVarResult = auto.getProperty(rgdispid[0]); |
| IDispatch dispatchDocument = pVarResult.getDispatch(); |
| long /*int*/ [] ppvObject = new long /*int*/ [1]; |
| int result = dispatchDocument.QueryInterface(COM.IIDIPersistStreamInit, ppvObject); |
| if (result == OS.S_OK) { |
| IPersistStreamInit persistStreamInit = new IPersistStreamInit(ppvObject[0]); |
| if (persistStreamInit.InitNew() == OS.S_OK) { |
| persistStreamInit.Load(ppstm[0]); |
| } |
| persistStreamInit.Release(); |
| } |
| pVarResult.dispose(); |
| IUnknown stream = new IUnknown(ppstm[0]); |
| stream.Release(); |
| } else { |
| OS.GlobalFree(hGlobal); |
| } |
| } |
| } |
| |
| @Override |
| public boolean setText(final String html, boolean trusted) { |
| /* |
| * If the browser is navigating to about:blank in response to its first |
| * setUrl() invocation then delay setting this text content until the |
| * navigate has completed. about:blank will be re-navigated to in order |
| * to ensure that all expected client events are sent. |
| */ |
| if (performingInitialNavigate) { |
| pendingText = new Object[] {html, new Boolean (trusted)}; |
| pendingUrl = null; |
| return true; |
| } |
| |
| /* |
| * If the html field is non-null then the about:blank page is already being |
| * loaded from a previous setText() invocation, so no Stop or Navigate is |
| * required. Just set the html that is to be shown. |
| */ |
| boolean blankLoading = this.html != null; |
| this.html = html; |
| untrustedText = !trusted; |
| if (blankLoading) return true; |
| |
| /* |
| * Navigate to the blank page and insert the given html when |
| * receiving the next DocumentComplete notification. See the |
| * MSDN article "Loading HTML content from a Stream". |
| * |
| * Note. Stop any pending request. This is required to avoid displaying a |
| * blank page as a result of consecutive calls to setUrl and/or setText. |
| * The previous request would otherwise render the new html content and |
| * reset the html field before the browser actually navigates to the blank |
| * page as requested below. |
| * |
| * Feature in Internet Explorer. Stopping pending requests when no request |
| * is pending causes a default page 'Action cancelled' to be displayed. The |
| * workaround is to not invoke 'stop' when no request has been set since |
| * that instance was created. |
| */ |
| |
| /* |
| * Stopping the loading of a page causes DocumentComplete events from previous |
| * requests to be received before the DocumentComplete for this page. In such |
| * cases we must be sure to not set the html into the browser too soon, since |
| * doing so could result in its page being cleared out by a subsequent |
| * DocumentComplete. The Browser's ReadyState can be used to determine whether |
| * these extra events will be received or not. |
| */ |
| if (_getUrl().length() != 0) { |
| int[] rgdispid = auto.getIDsOfNames(new String[] { "ReadyState" }); //$NON-NLS-1$ |
| Variant pVarResult = auto.getProperty(rgdispid[0]); |
| if (pVarResult == null) return false; |
| delaySetText = pVarResult.getInt() != READYSTATE_COMPLETE; |
| pVarResult.dispose(); |
| rgdispid = auto.getIDsOfNames(new String[] { "Stop" }); //$NON-NLS-1$ |
| auto.invoke(rgdispid[0]); |
| } |
| |
| int[] rgdispid = auto.getIDsOfNames(new String[] { "Navigate", "URL" }); //$NON-NLS-1$ //$NON-NLS-2$ |
| Variant[] rgvarg = new Variant[1]; |
| rgvarg[0] = new Variant(ABOUT_BLANK); |
| int[] rgdispidNamedArgs = new int[1]; |
| rgdispidNamedArgs[0] = rgdispid[1]; |
| boolean oldValue = false; |
| if (!OS.IsWinCE && IEVersion >= 7) { |
| int hResult = OS.CoInternetIsFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.GET_FEATURE_FROM_PROCESS); |
| oldValue = hResult == COM.S_OK; |
| OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, true); |
| } |
| Variant pVarResult = auto.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs); |
| if (!OS.IsWinCE && IEVersion >= 7) { |
| OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, oldValue); |
| } |
| rgvarg[0].dispose(); |
| if (pVarResult == null) return false; |
| boolean result = pVarResult.getType() == OLE.VT_EMPTY; |
| pVarResult.dispose(); |
| return result; |
| } |
| |
| @Override |
| public boolean setUrl(String url, String postData, String headers[]) { |
| html = uncRedirect = null; |
| |
| /* |
| * If the browser has not shown any content yet then first navigate to |
| * about:blank to work around IE bug http://support.microsoft.com/kb/320153, |
| * then navigate to the requested url once about:blank has loaded. |
| */ |
| if (_getUrl().length() == 0 && !ABOUT_BLANK.equalsIgnoreCase(url)) { |
| pendingText = null; |
| pendingUrl = new Object[] {url, postData, headers}; |
| performingInitialNavigate = true; |
| navigate (ABOUT_BLANK, null, null, true); |
| return true; |
| } |
| |
| /* |
| * Bug in Internet Explorer. For some reason, Navigating to an xml document before |
| * a previous Navigate has completed will leave the Browser in a bad state if the |
| * Navigate to the xml document does not complete. This bad state causes a GP when |
| * the parent window is eventually disposed. The workaround is to issue a Stop before |
| * navigating to any xml document. |
| */ |
| if (url.endsWith(".xml")) { //$NON-NLS-1$ |
| int[] rgdispid = auto.getIDsOfNames(new String[] { "Stop" }); //$NON-NLS-1$ |
| auto.invoke(rgdispid[0]); |
| } |
| return navigate(url, postData, headers, false); |
| } |
| |
| @Override |
| public void stop() { |
| /* |
| * If the browser has not completed its initial navigate to about:blank |
| * then do not issue Stop here, as this will display the IE error page. |
| * Just clear the pending url and text fields so that any pending content |
| * will not be set into the browser when the about:blank navigate completes. |
| */ |
| if (performingInitialNavigate) { |
| pendingText = pendingUrl = null; |
| return; |
| } |
| |
| /* |
| * Feature of IE. Invoking Stop in IE before any content has been shown |
| * displays a Navigation Cancelled error page. The workaround is to not |
| * invoke Stop if no content has been shown yet. |
| */ |
| if (_getUrl().length() == 0) return; |
| |
| /* |
| * Ensure that isAboutBlank is set accurately since Stop can be issued at |
| * any stage in the page load cycle. |
| */ |
| isAboutBlank = getUrl().startsWith(ABOUT_BLANK); |
| |
| uncRedirect = null; |
| int[] rgdispid = auto.getIDsOfNames(new String[] { "Stop" }); //$NON-NLS-1$ |
| auto.invoke(rgdispid[0]); |
| } |
| |
| @Override |
| boolean translateMnemonics () { |
| return false; |
| } |
| |
| void handleDOMEvent (OleEvent e) { |
| if (e.arguments == null || e.arguments.length == 0) return; /* for IE5 */ |
| |
| Variant arg = e.arguments[0]; |
| OleAutomation event = arg.getAutomation(); |
| int[] rgdispid = event.getIDsOfNames(new String[]{ PROPERTY_TYPE }); |
| int dispIdMember = rgdispid[0]; |
| Variant pVarResult = event.getProperty(dispIdMember); |
| String eventType = pVarResult.getString(); |
| pVarResult.dispose(); |
| |
| if (eventType.equals(EVENT_KEYDOWN)) { |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_KEYCODE }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| lastKeyCode = translateKey (pVarResult.getInt()); |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames (new String[] {PROPERTY_RETURNVALUE}); |
| pVarResult = event.getProperty (rgdispid[0]); |
| boolean consume = pVarResult != null && pVarResult.getType () == OLE.VT_BOOL && !pVarResult.getBoolean (); |
| pVarResult.dispose (); |
| |
| MSG msg = new MSG (); |
| int flags = OS.PM_NOYIELD | (consume ? OS.PM_REMOVE : OS.PM_NOREMOVE); |
| if (OS.PeekMessage (msg, frame.handle, OS.WM_CHAR, OS.WM_CHAR, flags)) { |
| /* a keypress will be received for this key so don't send KeyDown here */ |
| event.dispose(); |
| return; |
| } |
| |
| if (consume) { |
| /* |
| * an event should not be sent if another listener has vetoed the |
| * KeyDown (this is for non-character cases like arrow keys, etc.) |
| */ |
| event.dispose(); |
| return; |
| } |
| |
| /* if this is a repeating key then an event should not be fired for it */ |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_REPEAT }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| boolean repeating = pVarResult.getBoolean(); |
| pVarResult.dispose(); |
| if (repeating) { |
| event.dispose(); |
| return; |
| } |
| |
| int mask = 0; |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_ALTKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.ALT; |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CTRLKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.CTRL; |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SHIFTKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.SHIFT; |
| pVarResult.dispose(); |
| |
| Event keyEvent = new Event (); |
| keyEvent.widget = browser; |
| keyEvent.type = SWT.KeyDown; |
| keyEvent.keyCode = lastKeyCode; |
| keyEvent.stateMask = mask; |
| keyEvent.stateMask &= ~lastKeyCode; /* remove current keydown if it's a state key */ |
| /* |
| * keypress events are not received for Backspace, Enter, Delete and |
| * Tab, so KeyDown events are sent for them here. Set the KeyDown |
| * event's character field and IE's lastCharCode field for these keys |
| * so that the Browser's key events are consistent with other controls. |
| */ |
| switch (lastKeyCode) { |
| case SWT.BS: lastCharCode = keyEvent.character = SWT.BS; break; |
| case SWT.CR: lastCharCode = keyEvent.character = SWT.CR; break; |
| case SWT.DEL: lastCharCode = keyEvent.character = SWT.DEL; break; |
| case SWT.TAB: lastCharCode = keyEvent.character = SWT.TAB; break; |
| } |
| |
| if (!sendKeyEvent(keyEvent)) { |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_RETURNVALUE }); |
| dispIdMember = rgdispid[0]; |
| Variant pVarFalse = new Variant(false); |
| event.setProperty(dispIdMember, pVarFalse); |
| pVarFalse.dispose(); |
| } |
| |
| /* |
| * Pressing F5 refreshes the current page. If this is about to happen |
| * then set isRefresh to true so that received IE events will be treated |
| * accordingly. |
| */ |
| if (lastKeyCode == SWT.F5) isRefresh = true; |
| |
| event.dispose(); |
| return; |
| } |
| |
| if (eventType.equals(EVENT_KEYPRESS)) { |
| int mask = 0; |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CTRLKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.CTRL; |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SHIFTKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.SHIFT; |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_ALTKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.ALT; |
| pVarResult.dispose(); |
| |
| /* in the keypress event the keyCode actually corresponds to the character code */ |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_KEYCODE }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| lastCharCode = pVarResult.getInt(); |
| pVarResult.dispose(); |
| |
| /* |
| * WebSite.TranslateAccelerator() explicitly does not translate OS.VK_RETURN |
| * keys, so the PeekMessage check in the keydown handler always allows a |
| * KeyDown to be sent for this key. However, keydown and keypress events are |
| * both sometimes received for OS.VK_RETURN, depending on the page's focus |
| * control. To handle this, do not send a KeyDown for CR or LF here since |
| * one is always sent for it from the keydown handler. |
| */ |
| if (lastCharCode == SWT.CR || lastCharCode == SWT.LF) { |
| event.dispose(); |
| return; |
| } |
| |
| Event keyEvent = new Event (); |
| keyEvent.widget = browser; |
| keyEvent.type = SWT.KeyDown; |
| keyEvent.keyCode = lastKeyCode; |
| keyEvent.character = (char)lastCharCode; |
| keyEvent.stateMask = mask; |
| if (!sendKeyEvent(keyEvent)) { |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_RETURNVALUE }); |
| dispIdMember = rgdispid[0]; |
| Variant pVarFalse = new Variant(false); |
| event.setProperty(dispIdMember, pVarFalse); |
| pVarFalse.dispose(); |
| } |
| |
| event.dispose(); |
| return; |
| } |
| |
| if (eventType.equals(EVENT_KEYUP)) { |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_KEYCODE }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| int keyCode = translateKey (pVarResult.getInt()); |
| pVarResult.dispose(); |
| |
| /* |
| * if a key code could not be determined for this key then it's a |
| * key for which key events are not sent (eg.- the Windows key) |
| */ |
| if (keyCode == 0) { |
| lastKeyCode = lastCharCode = 0; |
| event.dispose(); |
| return; |
| } |
| |
| if (keyCode != lastKeyCode) { |
| /* keyup does not correspond to the last keydown */ |
| lastKeyCode = keyCode; |
| lastCharCode = 0; |
| } |
| |
| int mask = 0; |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CTRLKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.CTRL; |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_ALTKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.ALT; |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SHIFTKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.SHIFT; |
| pVarResult.dispose(); |
| |
| Event keyEvent = new Event (); |
| keyEvent.widget = browser; |
| keyEvent.type = SWT.KeyUp; |
| keyEvent.keyCode = lastKeyCode; |
| keyEvent.character = (char)lastCharCode; |
| keyEvent.stateMask = mask; |
| 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) { |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_RETURNVALUE }); |
| dispIdMember = rgdispid[0]; |
| Variant pVarFalse = new Variant(false); |
| event.setProperty(dispIdMember, pVarFalse); |
| pVarFalse.dispose(); |
| } |
| |
| lastKeyCode = lastCharCode = 0; |
| event.dispose(); |
| return; |
| } |
| |
| /* |
| * Feature in IE. MouseOver/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 (eventType.equals(EVENT_MOUSEOVER)) { |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_FROMELEMENT }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| boolean isInternal = pVarResult.getType() != COM.VT_EMPTY; |
| pVarResult.dispose(); |
| if (isInternal) { |
| event.dispose(); |
| return; |
| } |
| } |
| if (eventType.equals(EVENT_MOUSEOUT)) { |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_TOELEMENT }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| boolean isInternal = pVarResult.getType() != COM.VT_EMPTY; |
| pVarResult.dispose(); |
| if (isInternal) { |
| event.dispose(); |
| return; |
| } |
| } |
| |
| int mask = 0; |
| Event newEvent = new Event(); |
| newEvent.widget = browser; |
| |
| /* |
| * 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. |
| */ |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SCREENX }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| int screenX = pVarResult.getInt(); |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SCREENY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| int screenY = pVarResult.getInt(); |
| pVarResult.dispose(); |
| |
| Point position = DPIUtil.autoScaleDown(new Point(screenX, screenY)); // To Points |
| position = browser.getDisplay().map(null, browser, position); |
| newEvent.x = position.x; newEvent.y = position.y; |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CTRLKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.CTRL; |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_ALTKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.ALT; |
| pVarResult.dispose(); |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SHIFTKEY }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| if (pVarResult.getBoolean()) mask |= SWT.SHIFT; |
| pVarResult.dispose(); |
| |
| newEvent.stateMask = mask; |
| |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_BUTTON }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| int button = pVarResult.getInt(); |
| pVarResult.dispose(); |
| switch (button) { |
| case 1: button = 1; break; |
| case 2: button = 3; break; |
| case 4: button = 2; break; |
| } |
| |
| if (eventType.equals(EVENT_MOUSEDOWN)) { |
| newEvent.type = SWT.MouseDown; |
| newEvent.button = button; |
| newEvent.count = 1; |
| } else if (eventType.equals(EVENT_MOUSEUP) || eventType.equals(EVENT_DRAGEND)) { |
| newEvent.type = SWT.MouseUp; |
| newEvent.button = button != 0 ? button : 1; /* button assumed to be 1 for dragends */ |
| newEvent.count = 1; |
| switch (newEvent.button) { |
| case 1: newEvent.stateMask |= SWT.BUTTON1; break; |
| case 2: newEvent.stateMask |= SWT.BUTTON2; break; |
| case 3: newEvent.stateMask |= SWT.BUTTON3; break; |
| case 4: newEvent.stateMask |= SWT.BUTTON4; break; |
| case 5: newEvent.stateMask |= SWT.BUTTON5; break; |
| } |
| } else if (eventType.equals(EVENT_MOUSEWHEEL)) { |
| newEvent.type = SWT.MouseWheel; |
| rgdispid = event.getIDsOfNames(new String[] { PROPERTY_WHEELDELTA }); |
| dispIdMember = rgdispid[0]; |
| pVarResult = event.getProperty(dispIdMember); |
| newEvent.count = pVarResult.getInt () / 120 * 3; |
| pVarResult.dispose(); |
| } else if (eventType.equals(EVENT_MOUSEMOVE)) { |
| /* |
| * Feature in IE. Spurious and redundant mousemove events are often received. The workaround |
| * is to not fire MouseMove events whose x and y values match the last MouseMove. |
| */ |
| if (newEvent.x == lastMouseMoveX && newEvent.y == lastMouseMoveY) { |
| event.dispose(); |
| return; |
| } |
| newEvent.type = SWT.MouseMove; |
| lastMouseMoveX = newEvent.x; lastMouseMoveY = newEvent.y; |
| } else if (eventType.equals(EVENT_MOUSEOVER)) { |
| newEvent.type = SWT.MouseEnter; |
| } else if (eventType.equals(EVENT_MOUSEOUT)) { |
| newEvent.type = SWT.MouseExit; |
| } else if (eventType.equals(EVENT_DRAGSTART)) { |
| newEvent.type = SWT.DragDetect; |
| newEvent.button = 1; /* button assumed to be 1 for dragstarts */ |
| newEvent.stateMask |= SWT.BUTTON1; |
| } |
| |
| event.dispose(); |
| browser.notifyListeners(newEvent.type, newEvent); |
| |
| if (eventType.equals(EVENT_DOUBLECLICK)) { |
| newEvent = new Event (); |
| newEvent.widget = browser; |
| newEvent.type = SWT.MouseDoubleClick; |
| newEvent.x = position.x; newEvent.y = position.y; |
| newEvent.stateMask = mask; |
| newEvent.type = SWT.MouseDoubleClick; |
| newEvent.button = 1; /* dblclick only comes for button 1 and does not set the button property */ |
| newEvent.count = 2; |
| browser.notifyListeners (newEvent.type, newEvent); |
| } |
| } |
| |
| void hookDOMListeners(OleAutomation webBrowser, final boolean isTop) { |
| int[] rgdispid = webBrowser.getIDsOfNames(new String[] { PROPERTY_DOCUMENT }); |
| int dispIdMember = rgdispid[0]; |
| Variant pVarResult = webBrowser.getProperty(dispIdMember); |
| if (pVarResult == null) return; |
| if (pVarResult.getType() == COM.VT_EMPTY) { |
| pVarResult.dispose(); |
| return; |
| } |
| final OleAutomation document = pVarResult.getAutomation(); |
| pVarResult.dispose(); |
| |
| /* |
| * In some cases, such as setting the Browser's content from a string, |
| * NavigateComplete2 is received multiple times for a top-level document. |
| * For cases like this, any previously-hooked DOM listeners must be |
| * removed from the document before hooking the new set of listeners, |
| * otherwise multiple sets of events will be received. |
| */ |
| unhookDOMListeners (new OleAutomation[] {document}); |
| |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYDOWN, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYPRESS, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYUP, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEDOWN, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEUP, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEWHEEL, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONDBLCLICK, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEMOVE, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONDRAGSTART, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONDRAGEND, domListener); |
| /* ensure that enter/exit are only fired once, by the top-level document */ |
| if (isTop) { |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOVER, domListener); |
| site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOUT, domListener); |
| } |
| |
| OleAutomation[] newDocuments = new OleAutomation[documents.length + 1]; |
| System.arraycopy(documents, 0, newDocuments, 0, documents.length); |
| newDocuments[documents.length] = document; |
| documents = newDocuments; |
| } |
| |
| void unhookDOMListeners(OleAutomation[] documents) { |
| char[] buffer = (COM.IIDIHTMLDocumentEvents2 + '\0').toCharArray(); |
| GUID guid = new GUID(); |
| if (COM.IIDFromString(buffer, guid) == COM.S_OK) { |
| for (int i = 0; i < documents.length; i++) { |
| OleAutomation document = documents[i]; |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYDOWN, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYPRESS, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYUP, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEDOWN, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEUP, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEWHEEL, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONDBLCLICK, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEMOVE, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONDRAGSTART, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONDRAGEND, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOVER, domListener); |
| site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOUT, domListener); |
| } |
| } |
| } |
| } |