1 /*******************************************************************************
2  * Copyright (c) 2003, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.browser;
15 
16 import java.io.*;
17 import java.net.*;
18 import java.util.*;
19 
20 import org.eclipse.swt.*;
21 import org.eclipse.swt.graphics.*;
22 import org.eclipse.swt.internal.*;
23 import org.eclipse.swt.internal.ole.win32.*;
24 import org.eclipse.swt.internal.win32.*;
25 import org.eclipse.swt.ole.win32.*;
26 import org.eclipse.swt.widgets.*;
27 
28 class IE extends WebBrowser {
29 
30 	OleFrame frame;
31 	WebSite site;
32 	OleAutomation auto;
33 	OleListener domListener;
34 	OleAutomation[] documents = new OleAutomation[0];
35 
36 	boolean back, forward, delaySetText, ignoreDispose, ignoreTraverse, performingInitialNavigate;
37 	boolean installFunctionsOnDocumentComplete, untrustedText, isRefresh, isAboutBlank;
38 	Point location;
39 	Point size;
40 	boolean addressBar = true, menuBar = true, statusBar = true, toolBar = true;
41 	long globalDispatch;
42 	String html, lastNavigateURL, uncRedirect;
43 	Object[] pendingText, pendingUrl;
44 	int style, lastKeyCode, lastCharCode;
45 	int lastMouseMoveX, lastMouseMoveY;
46 
47 	static boolean Initialized;
48 	static int IEVersion, PDFCount;
49 	static String ProgId = "Shell.Explorer";	//$NON-NLS-1$
50 
51 	static final int BeforeNavigate2 = 0xfa;
52 	static final int CommandStateChange = 0x69;
53 	static final int DocumentComplete = 0x103;
54 	static final int DownloadComplete = 0x68;
55 	static final int NavigateComplete2 = 0xfc;
56 	static final int NewWindow2 = 0xfb;
57 	static final int OnMenuBar = 0x100;
58 	static final int OnStatusBar = 0x101;
59 	static final int OnToolBar = 0xff;
60 	static final int OnVisible = 0xfe;
61 	static final int ProgressChange = 0x6c;
62 	static final int RegisterAsBrowser = 0x228;
63 	static final int StatusTextChange = 0x66;
64 	static final int TitleChange = 0x71;
65 	static final int WindowClosing = 0x107;
66 	static final int WindowSetHeight = 0x10b;
67 	static final int WindowSetLeft = 0x108;
68 	static final int WindowSetResizable = 0x106;
69 	static final int WindowSetTop = 0x109;
70 	static final int WindowSetWidth = 0x10a;
71 	static final int NavigateError = 0x10f;
72 
73 	static final short CSC_NAVIGATEFORWARD = 1;
74 	static final short CSC_NAVIGATEBACK = 2;
75 	static final int INET_E_DEFAULT_ACTION = 0x800C0011;
76 	static final int INET_E_RESOURCE_NOT_FOUND = 0x800C0005;
77 	static final int READYSTATE_COMPLETE = 4;
78 	static final int URLPOLICY_ALLOW = 0x00;
79 	static final int URLPOLICY_DISALLOW = 0x03;
80 	static final int URLPOLICY_JAVA_PROHIBIT = 0x0;
81 	static final int URLPOLICY_JAVA_LOW = 0x00030000;
82 	static final int URLZONE_LOCAL_MACHINE = 0;
83 	static final int URLZONE_INTRANET = 1;
84 	static final int URLACTION_ACTIVEX_MIN = 0x00001200;
85 	static final int URLACTION_ACTIVEX_MAX = 0x000013ff;
86 	static final int URLACTION_ACTIVEX_RUN = 0x00001200;
87 	static final int URLACTION_FEATURE_ZONE_ELEVATION = 0x00002101;
88 	static final int URLACTION_JAVA_MIN = 0x00001C00;
89 	static final int URLACTION_JAVA_MAX = 0x00001Cff;
90 	static final int URLACTION_SCRIPT_RUN = 0x00001400;
91 
92 	static final int DISPID_AMBIENT_DLCONTROL = -5512;
93 	static final int DLCTL_DLIMAGES = 0x00000010;
94 	static final int DLCTL_VIDEOS = 0x00000020;
95 	static final int DLCTL_BGSOUNDS = 0x00000040;
96 	static final int DLCTL_NO_SCRIPTS = 0x00000080;
97 	static final int DLCTL_NO_JAVA = 0x00000100;
98 	static final int DLCTL_NO_RUNACTIVEXCTLS = 0x00000200;
99 	static final int DLCTL_NO_DLACTIVEXCTLS = 0x00000400;
100 	static final int DLCTL_DOWNLOADONLY = 0x00000800;
101 	static final int DLCTL_NO_FRAMEDOWNLOAD = 0x00001000;
102 	static final int DLCTL_RESYNCHRONIZE = 0x00002000;
103 	static final int DLCTL_PRAGMA_NO_CACHE = 0x00004000;
104 	static final int DLCTL_FORCEOFFLINE = 0x10000000;
105 	static final int DLCTL_NO_CLIENTPULL = 0x20000000;
106 	static final int DLCTL_SILENT = 0x40000000;
107 	static final int DOCHOSTUIFLAG_THEME = 0x00040000;
108 	static final int DOCHOSTUIFLAG_NO3DBORDER  = 0x0000004;
109 	static final int DOCHOSTUIFLAG_NO3DOUTERBORDER = 0x00200000;
110 	static final int DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION = 0x04000000;
111 	static final int DOCHOSTUIFLAG_DPI_AWARE = 0x40000000;
112 
113 	static final String ABOUT_BLANK = "about:blank"; //$NON-NLS-1$
114 	static final String CLSID_SHELLEXPLORER1 = "{EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B}"; //$NON-NLS-1$
115 	static final int DEFAULT_IE_VERSION = 9999;
116 	static final String EXTENSION_PDF = ".pdf";	//$NON-NLS-1$
117 	static final String HTML_DOCUMENT = "HTML Document";	//$NON-NLS-1$
118 	static final int MAX_PDF = 20;
119 	static final char SEPARATOR_OS = File.separatorChar;
120 	static final String PROPERTY_IEVERSION = "org.eclipse.swt.browser.IEVersion"; //$NON-NLS-1$
121 	static final String VALUE_DEFAULT = "default"; //$NON-NLS-1$
122 
123 	static final String EVENT_DOUBLECLICK = "dblclick"; //$NON-NLS-1$
124 	static final String EVENT_DRAGEND = "dragend";	//$NON-NLS-1$
125 	static final String EVENT_DRAGSTART = "dragstart";	//$NON-NLS-1$
126 	static final String EVENT_KEYDOWN = "keydown";	//$NON-NLS-1$
127 	static final String EVENT_KEYPRESS = "keypress";	//$NON-NLS-1$
128 	static final String EVENT_KEYUP = "keyup";	//$NON-NLS-1$
129 	static final String EVENT_MOUSEMOVE = "mousemove";	//$NON-NLS-1$
130 	static final String EVENT_MOUSEWHEEL = "mousewheel";	//$NON-NLS-1$
131 	static final String EVENT_MOUSEUP = "mouseup";	//$NON-NLS-1$
132 	static final String EVENT_MOUSEDOWN = "mousedown";	//$NON-NLS-1$
133 	static final String EVENT_MOUSEOUT = "mouseout";	//$NON-NLS-1$
134 	static final String EVENT_MOUSEOVER = "mouseover";	//$NON-NLS-1$
135 	static final String PROTOCOL_FILE = "file://"; //$NON-NLS-1$
136 	static final String PROPERTY_ALTKEY = "altKey"; //$NON-NLS-1$
137 	static final String PROPERTY_BUTTON = "button"; //$NON-NLS-1$
138 	static final String PROPERTY_CTRLKEY = "ctrlKey"; //$NON-NLS-1$
139 	static final String PROPERTY_DOCUMENT = "Document"; //$NON-NLS-1$
140 	static final String PROPERTY_FROMELEMENT = "fromElement"; //$NON-NLS-1$
141 	static final String PROPERTY_KEYCODE = "keyCode"; //$NON-NLS-1$
142 	static final String PROPERTY_REPEAT = "repeat"; //$NON-NLS-1$
143 	static final String PROPERTY_RETURNVALUE = "returnValue"; //$NON-NLS-1$
144 	static final String PROPERTY_SCREENX = "screenX"; //$NON-NLS-1$
145 	static final String PROPERTY_SCREENY = "screenY"; //$NON-NLS-1$
146 	static final String PROPERTY_SHIFTKEY = "shiftKey"; //$NON-NLS-1$
147 	static final String PROPERTY_TOELEMENT = "toElement"; //$NON-NLS-1$
148 	static final String PROPERTY_TYPE = "type"; //$NON-NLS-1$
149 	static final String PROPERTY_WHEELDELTA = "wheelDelta"; //$NON-NLS-1$
150 
151 	static {
152 		NativeClearSessions = () -> {
153 			OS.InternetSetOption (0, OS.INTERNET_OPTION_END_BROWSER_SESSION, 0, 0);
154 		};
155 
156 		NativeGetCookie = () -> {
157 			TCHAR url = new TCHAR (0, CookieUrl, true);
158 			TCHAR cookieData = new TCHAR (0, 8192);
159 			int[] size = new int[] {cookieData.length ()};
160 			if (!OS.InternetGetCookie (url, null, cookieData, size)) {
161 				/* original cookieData size was not large enough */
162 				size[0] /= TCHAR.sizeof;
163 				cookieData = new TCHAR (0, size[0]);
164 				if (!OS.InternetGetCookie (url, null, cookieData, size)) return;
165 			}
166 			String allCookies = cookieData.toString (0, size[0]);
167 			StringTokenizer tokenizer = new StringTokenizer (allCookies, ";"); //$NON-NLS-1$
168 			while (tokenizer.hasMoreTokens ()) {
169 				String cookie = tokenizer.nextToken ();
170 				int index = cookie.indexOf ('=');
171 				if (index != -1) {
172 					String name = cookie.substring (0, index).trim ();
173 					if (name.equals (CookieName)) {
174 						CookieValue = cookie.substring (index + 1).trim ();
175 						return;
176 					}
177 				}
178 			}
179 		};
180 
181 		NativeSetCookie = () -> {
182 			TCHAR url = new TCHAR (0, CookieUrl, true);
183 			TCHAR value = new TCHAR (0, CookieValue, true);
184 			CookieResult = OS.InternetSetCookie (url, null, value);
185 		};
186 
187 		/*
188 		* The installed version of IE can be determined by looking at registry entry
189 		* HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\svcVersion, or
190 		* HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Version (for
191 		* IE releases prior to IE10).  Check this value in order to determine
192 		* version-specific features that can be enabled.
193 		*/
194 		TCHAR key = new TCHAR (0, "Software\\Microsoft\\Internet Explorer", true);	//$NON-NLS-1$
195 		long [] phkResult = new long [1];
196 		if (OS.RegOpenKeyEx (OS.HKEY_LOCAL_MACHINE, key, 0, OS.KEY_READ, phkResult) == 0) {
197 			int [] lpcbData = new int [1];
198 			TCHAR buffer = new TCHAR (0, "svcVersion", true); //$NON-NLS-1$
199 			int result = OS.RegQueryValueEx (phkResult [0], buffer, 0, null, (TCHAR) null, lpcbData);
200 			if (result != 0) {
201 				buffer = new TCHAR (0, "Version", true); //$NON-NLS-1$
202 				result = OS.RegQueryValueEx (phkResult [0], buffer, 0, null, (TCHAR) null, lpcbData);
203 			}
204 			if (result == 0) {
205 				TCHAR lpData = new TCHAR (0, lpcbData [0] / TCHAR.sizeof);
206 				result = OS.RegQueryValueEx (phkResult [0], buffer, 0, null, lpData, lpcbData);
207 				if (result == 0) {
208 					String versionString = lpData.toString (0, lpData.strlen ());
209 					int index = versionString.indexOf ("."); //$NON-NLS-1$
210 					if (index != -1) {
211 						String majorString = versionString.substring (0, index);
212 						try {
213 							IEVersion = Integer.valueOf (majorString).intValue ();
214 						} catch (NumberFormatException e) {
215 							/* just continue, version-specific features will not be enabled */
216 						}
217 					}
218 				}
219 			}
220 			OS.RegCloseKey (phkResult [0]);
221 		}
222 
223 		/*
224 		* Registry entry HKEY_CLASSES_ROOT\Shell.Explorer\CLSID indicates which version of
225 		* Shell.Explorer to use by default.  We usually want to use this value because it
226 		* typically points at the newest one that is available.  However it is possible for
227 		* this registry entry to be changed by another application to point at some other
228 		* Shell.Explorer version.
229 		*
230 		* The Browser depends on the Shell.Explorer version being at least Shell.Explorer.2.
231 		* If it is detected in the registry to be Shell.Explorer.1 then change the progId that
232 		* will be embedded to explicitly specify Shell.Explorer.2.
233 		*/
234 		key = new TCHAR (0, "Shell.Explorer\\CLSID", true);	//$NON-NLS-1$
235 		phkResult = new long [1];
236 		if (OS.RegOpenKeyEx (OS.HKEY_CLASSES_ROOT, key, 0, OS.KEY_READ, phkResult) == 0) {
237 			int [] lpcbData = new int [1];
238 			int result = OS.RegQueryValueEx (phkResult [0], null, 0, null, (TCHAR) null, lpcbData);
239 			if (result == 0) {
240 				TCHAR lpData = new TCHAR (0, lpcbData [0] / TCHAR.sizeof);
241 				result = OS.RegQueryValueEx (phkResult [0], null, 0, null, lpData, lpcbData);
242 				if (result == 0) {
243 					String clsid = lpData.toString (0, lpData.strlen ());
244 					if (clsid.equals (CLSID_SHELLEXPLORER1)) {
245 						/* Shell.Explorer.1 is the default, ensure that Shell.Explorer.2 is available */
246 						key = new TCHAR (0, "Shell.Explorer.2", true);	//$NON-NLS-1$
247 						long [] phkResult2 = new long [1];
248 						if (OS.RegOpenKeyEx (OS.HKEY_CLASSES_ROOT, key, 0, OS.KEY_READ, phkResult2) == 0) {
249 							/* specify that Shell.Explorer.2 is to be used */
250 							OS.RegCloseKey (phkResult2 [0]);
251 							ProgId = "Shell.Explorer.2";	//$NON-NLS-1$
252 						}
253 					}
254 				}
255 			}
256 			OS.RegCloseKey (phkResult [0]);
257 		}
258 
259 		if (NativePendingCookies != null) {
260 			SetPendingCookies (NativePendingCookies);
261 		}
262 		NativePendingCookies = null;
263 	}
264 
265 @Override
create(Composite parent, int style)266 public void create(Composite parent, int style) {
267 	this.style = style;
268 	frame = new OleFrame(browser, SWT.NONE);
269 
270 	try {
271 		site = new WebSite(frame, SWT.NONE, ProgId);
272 	} catch (SWTException e) {
273 		browser.dispose();
274 		SWT.error(SWT.ERROR_NO_HANDLES);
275 	}
276 
277 	if (!Initialized) {
278 		Initialized = true;
279 		int version = 0;
280 		String versionProperty = System.getProperty(PROPERTY_IEVERSION);
281 		if (versionProperty != null) {
282 			if (versionProperty.equalsIgnoreCase(VALUE_DEFAULT)) {
283 				version = -1;
284 			} else {
285 				try {
286 					version = Integer.valueOf(versionProperty).intValue();
287 				} catch (NumberFormatException e) {
288 					/*
289 					 * An invalid value was specified for the IEVersion java property.  Ignore it
290 					 * and continue with the usual steps for determining the version to specify.
291 					 */
292 				}
293 			}
294 		}
295 		if (version == 0) {
296 			if (IEVersion != 0) {
297 				/*
298 				 * By default in Embedded IE the docuemntMode is Quirks(5)
299 				 * mode unless !DOCTYPE directives is defined in the HTML.
300 				 * As per MSDN IE8 and onwards, there is a way we could hint
301 				 * embedded IE to use current documentMode via appropriate
302 				 * version value in the registry. Refer bug 342145.
303 				 *
304 				 * Complete list of IE emulation modes is listed on MSDN:
305 				 * http://msdn.microsoft
306 				 * .com/en-us/library/ie/ee330730%28v=vs
307 				 * .85%29.aspx#browser_emulation
308 				 */
309 				if (IEVersion >= 10) {
310 					version = IEVersion * 1000 + 1;
311 				}
312 				else if (IEVersion >= 8) {
313 					version = IEVersion * 1111;
314 				}
315 				else {
316 					version = IEVersion * 1000;
317 				}
318 			} else {
319 				version = DEFAULT_IE_VERSION;
320 			}
321 		}
322 
323 		if (version != -1) {
324 			long[] key = new long[1];
325 			final TCHAR subkey = new TCHAR(0, "Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", true);	//$NON-NLS-1$
326 			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) {
327 				TCHAR lpszFile = new TCHAR(0, OS.MAX_PATH);
328 				OS.GetModuleFileName(0, lpszFile, lpszFile.length());
329 				String path = lpszFile.toString(0, lpszFile.strlen());
330 				int index = path.lastIndexOf(SEPARATOR_OS);
331 				String executable = index != -1 ? path.substring(index + 1) : path;
332 				final TCHAR lpValueName = new TCHAR(0, executable, true);
333 				/*
334 				 * Program name & IE version entry is added to the Windows
335 				 * registry and same gets deleted during the dispose cycle.
336 				 * There is a possibility if the SWT application crashes or
337 				 * is exited forcefully, which leaves the registry entry
338 				 * as-is and hence if entry exists, updating it next time
339 				 * the SWT application creates embedded IE, refer bug 440300
340 				 */
341 				int result = OS.RegQueryValueEx(key[0], lpValueName, 0, null, (int[])null, null);
342 				if (result == 0 || result == OS.ERROR_FILE_NOT_FOUND) {
343 					if (OS.RegSetValueEx(key[0], lpValueName, 0, OS.REG_DWORD, new int[] {version}, 4) == 0) {
344 						parent.getDisplay().addListener(SWT.Dispose, event -> {
345 							long[] key1 = new long[1];
346 							if (OS.RegOpenKeyEx(OS.HKEY_CURRENT_USER, subkey, 0, OS.KEY_WRITE, key1) == 0) {
347 								OS.RegDeleteValue(key1[0], lpValueName);
348 							}
349 						});
350 					}
351 				}
352 				OS.RegCloseKey(key[0]);
353 			}
354 		}
355 	}
356 
357 	site.doVerb(OLE.OLEIVERB_INPLACEACTIVATE);
358 	auto = new OleAutomation(site);
359 
360 	domListener = e -> handleDOMEvent(e);
361 
362 	Listener listener = e -> {
363 		switch (e.type) {
364 			case SWT.Dispose: {
365 				/* make this handler run after other dispose listeners */
366 				if (ignoreDispose) {
367 					ignoreDispose = false;
368 					break;
369 				}
370 				ignoreDispose = true;
371 				browser.notifyListeners (e.type, e);
372 				e.type = SWT.NONE;
373 
374 				/* invoke onbeforeunload handlers */
375 				if (!browser.isClosing) {
376 					LocationListener[] oldLocationListeners = locationListeners;
377 					locationListeners = new LocationListener[0];
378 					site.ignoreAllMessages = true;
379 					execute ("window.location.href='about:blank'"); //$NON-NLS-1$
380 					site.ignoreAllMessages = false;
381 					locationListeners = oldLocationListeners;
382 				}
383 
384 				/*
385 				* It is possible for the Browser's OLE frame to have been disposed
386 				* by a Dispose listener that was invoked by notifyListeners above,
387 				* so check for this before unhooking its DOM listeners.
388 				*/
389 				if (!frame.isDisposed ()) unhookDOMListeners(documents);
390 
391 				for (OleAutomation document : documents) {
392 					document.dispose();
393 				}
394 				documents = null;
395 
396 				Iterator<BrowserFunction> elements = functions.values().iterator ();
397 				while (elements.hasNext ()) {
398 					elements.next ().dispose (false);
399 				}
400 				functions = null;
401 
402 				lastNavigateURL = uncRedirect = null;
403 				domListener = null;
404 				if (auto != null) auto.dispose();
405 				auto = null;
406 				break;
407 			}
408 			case SWT.Resize: {
409 				frame.setBounds(browser.getClientArea());
410 				break;
411 			}
412 			case SWT.MouseWheel: {
413 				/* MouseWheel events come from the DOM */
414 				e.doit = false;
415 				break;
416 			}
417 			case SWT.FocusIn: {
418 				site.setFocus();
419 				break;
420 			}
421 			case SWT.Traverse: {
422 				/*
423 				 * Tabbing out of the browser can fail as a result of the WebSite
424 				 * control embedded within the Browser.  The workaround is to
425 				 * listen for traversals and re-perform the traversal on the
426 				 * appropriate control.
427 				 */
428 				if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS && e.widget instanceof WebSite) {
429 					/* otherwise will traverse to the Browser control */
430 					browser.traverse(SWT.TRAVERSE_TAB_PREVIOUS, e);
431 					e.doit = false;
432 				}
433 				/*
434 				 * Return traversals can sometimes come through TranslateAccelerator,
435 				 * depending on where focus is within the Browser.  Traversal
436 				 * events should always be triggered by a key event from the DOM,
437 				 * so if a Traversal from TranslateAccelerator is detected
438 				 * (e.doit == true) then stop its propagation.
439 				 */
440 				if (e.detail == SWT.TRAVERSE_RETURN && e.doit && e.widget instanceof Browser) {
441 					e.type = SWT.None;
442 					e.doit = false;
443 				}
444 				break;
445 			}
446 		}
447 	};
448 	browser.addListener(SWT.Dispose, listener);
449 	browser.addListener(SWT.FocusIn, listener);
450 	browser.addListener(SWT.Resize, listener);
451 	browser.addListener(SWT.Traverse, listener);
452 	site.addListener(SWT.MouseWheel, listener);
453 	site.addListener(SWT.Traverse, listener);
454 
455 	OleListener oleListener = event -> {
456 		/* callbacks are asynchronous, auto could be disposed */
457 		if (auto != null) {
458 			switch (event.type) {
459 				case BeforeNavigate2: {
460 
461 					/* don't send client events if the initial navigate to about:blank has not completed */
462 					if (performingInitialNavigate) break;
463 
464 					Variant varResult1 = event.arguments[1];
465 					String url1 = varResult1.getString();
466 
467 					if (uncRedirect != null) {
468 						/*
469 						* Silently allow the navigate to proceed if the url is the first segment of a
470 						* UNC path being navigated to (initiated by the NavigateError listener to show
471 						* a name/password prompter), or if the url is the full UNC path (initiated by
472 						* the NavigateComplete listener to redirect from the UNC's first segment to its
473 						* full path).
474 						*/
475 						if (uncRedirect.equals(url1) || (uncRedirect.startsWith(url1) && uncRedirect.indexOf('\\', 2) == url1.length())) {
476 							Variant cancel1 = event.arguments[6];
477 							if (cancel1 != null) {
478 								long pCancel1 = cancel1.getByRef();
479 								OS.MoveMemory(pCancel1, new short[] {OS.VARIANT_FALSE}, 2);
480 							}
481 							setAboutBlank(false);
482 							break;
483 						} else {
484 							/*
485 							* This navigate does not correspond to the previously-initiated
486 							* UNC navigation so clear this state since it's no longer valid.
487 							*/
488 							uncRedirect = null;
489 						}
490 					}
491 
492 					/*
493 					* Feature in IE.  For navigations on the local machine, BeforeNavigate2's url
494 					* field contains a string representation of the file path in a non-URL format.
495 					* In order to be consistent with the other Browser implementations, this
496 					* case is detected and the string is changed to be a proper url string.
497 					*/
498 					if (url1.indexOf(":/") == -1 && url1.indexOf(":\\") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
499 						TCHAR filePath1 = new TCHAR(0, url1, true);
500 						TCHAR urlResult1 = new TCHAR(0, OS.INTERNET_MAX_URL_LENGTH);
501 						int[] size1 = new int[] {urlResult1.length()};
502 						if (OS.UrlCreateFromPath(filePath1, urlResult1, size1, 0) == COM.S_OK) {
503 							url1 = urlResult1.toString(0, size1[0]);
504 						} else {
505 							url1 = PROTOCOL_FILE + url1.replace('\\', '/');
506 						}
507 					}
508 
509 					/* Disallow local file system accesses if the browser content is untrusted */
510 					if (url1.startsWith(PROTOCOL_FILE) && _getUrl().startsWith(ABOUT_BLANK) && untrustedText) {
511 						Variant cancel2 = event.arguments[6];
512 						if (cancel2 != null) {
513 							long pCancel2 = cancel2.getByRef();
514 							OS.MoveMemory(pCancel2, new short[] {OS.VARIANT_TRUE}, 2);
515 						}
516 						break;
517 					}
518 
519 					LocationEvent newEvent1 = new LocationEvent(browser);
520 					newEvent1.display = browser.getDisplay();
521 					newEvent1.widget = browser;
522 					newEvent1.location = url1;
523 					newEvent1.doit = true;
524 					for (LocationListener locationListener : locationListeners) {
525 						locationListener.changing(newEvent1);
526 					}
527 					boolean doit1 = newEvent1.doit && !browser.isDisposed();
528 					Variant cancel3 = event.arguments[6];
529 					if (cancel3 != null) {
530 						long pCancel3 = cancel3.getByRef();
531 						OS.MoveMemory(pCancel3, new short[] {doit1 ? OS.VARIANT_FALSE : OS.VARIANT_TRUE}, 2);
532 					}
533 					if (doit1) {
534 						varResult1 = event.arguments[0];
535 						IDispatch dispatch1 = varResult1.getDispatch();
536 						Variant variant1 = new Variant(auto); /* does not need to be disposed */
537 						IDispatch top1 = variant1.getDispatch();
538 						if (top1.getAddress() == dispatch1.getAddress()) {
539 							setAboutBlank(url1.startsWith(ABOUT_BLANK));
540 						}
541 					}
542 					break;
543 				}
544 				case CommandStateChange: {
545 					boolean enabled = false;
546 					Variant varResult2 = event.arguments[0];
547 					int command = varResult2.getInt();
548 					varResult2 = event.arguments[1];
549 					enabled = varResult2.getBoolean();
550 					switch (command) {
551 						case CSC_NAVIGATEBACK : back = enabled; break;
552 						case CSC_NAVIGATEFORWARD : forward = enabled; break;
553 					}
554 					break;
555 				}
556 				case DocumentComplete: {
557 					if (performingInitialNavigate) {
558 						/* this event marks the completion of the initial navigate to about:blank */
559 						performingInitialNavigate = false;
560 
561 						/* if browser content has been provided by the client then set it now */
562 						if (pendingText != null) {
563 							setText((String)pendingText[0], ((Boolean)pendingText[1]).booleanValue());
564 						} else if (pendingUrl != null) {
565 							setUrl((String)pendingUrl[0], (String)pendingUrl[1], (String[])pendingUrl[2]);
566 						}
567 						pendingText = pendingUrl = null;
568 						break;
569 					}
570 
571 					Variant varResult3 = event.arguments[0];
572 					IDispatch dispatch2 = varResult3.getDispatch();
573 
574 					varResult3 = event.arguments[1];
575 					String url2 = varResult3.getString();
576 					/*
577 					* Feature in IE.  For navigations on the local machine, DocumentComplete's url
578 					* field contains a string representation of the file path in a non-URL format.
579 					* In order to be consistent with the other Browser implementations, this
580 					* case is detected and the string is changed to be a proper url string.
581 					*/
582 					if (url2.indexOf(":/") == -1 && url2.indexOf(":\\") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
583 						TCHAR filePath2 = new TCHAR(0, url2, true);
584 						TCHAR urlResult2 = new TCHAR(0, OS.INTERNET_MAX_URL_LENGTH);
585 						int[] size2 = new int[] {urlResult2.length()};
586 						if (OS.UrlCreateFromPath(filePath2, urlResult2, size2, 0) == COM.S_OK) {
587 							url2 = urlResult2.toString(0, size2[0]);
588 						} else {
589 							url2 = PROTOCOL_FILE + url2.replace('\\', '/');
590 						}
591 					}
592 					if (html != null && url2.equals(ABOUT_BLANK)) {
593 						if (delaySetText) {
594 							delaySetText = false;
595 							browser.getDisplay().asyncExec(() -> {
596 								if (browser.isDisposed() || html == null) return;
597 								setHTML(html);
598 								html = null;
599 							});
600 						} else {
601 							setHTML(html);
602 							html = null;
603 						}
604 					} else {
605 						Variant variant2 = new Variant(auto); /* does not need to be disposed */
606 						IDispatch top2 = variant2.getDispatch();
607 						LocationEvent locationEvent = new LocationEvent(browser);
608 						locationEvent.display = browser.getDisplay();
609 						locationEvent.widget = browser;
610 						locationEvent.location = url2;
611 						locationEvent.top = top2.getAddress() == dispatch2.getAddress();
612 						for (LocationListener locationListener : locationListeners) {
613 							locationListener.changed(locationEvent);
614 						}
615 						if (browser.isDisposed()) return;
616 
617 						/*
618 						* With the IBM 64-bit JVM an unexpected document complete event occurs before
619 						* the native browser's DOM has been built. Filter this premature event based
620 						* on the browser's ready state.
621 						*/
622 						int[] rgdispid1 = auto.getIDsOfNames(new String[] { "ReadyState" }); //$NON-NLS-1$
623 						Variant pVarResult1 = auto.getProperty(rgdispid1[0]);
624 						if (pVarResult1 != null) {
625 							int readyState = pVarResult1.getInt();
626 							pVarResult1.dispose ();
627 							if (readyState != READYSTATE_COMPLETE) {
628 								break;
629 							}
630 						}
631 
632 						/*
633 						 * Note.  The completion of the page loading is detected as
634 						 * described in the MSDN article "Determine when a page is
635 						 * done loading in WebBrowser Control".
636 						 */
637 						if (globalDispatch != 0 && dispatch2.getAddress() == globalDispatch) {
638 							/* final document complete */
639 							globalDispatch = 0;
640 
641 							/* re-install registered functions iff needed */
642 							IE ie = (IE)browser.webBrowser;
643 							if (ie.installFunctionsOnDocumentComplete) {
644 								ie.installFunctionsOnDocumentComplete = false;
645 								Iterator<BrowserFunction> elements1 = functions.values().iterator ();
646 								while (elements1.hasNext ()) {
647 									BrowserFunction function1 = elements1.next ();
648 									execute (function1.functionString);
649 								}
650 							}
651 
652 							ProgressEvent progressEvent1 = new ProgressEvent(browser);
653 							progressEvent1.display = browser.getDisplay();
654 							progressEvent1.widget = browser;
655 							for (ProgressListener progressListener : progressListeners) {
656 								progressListener.completed(progressEvent1);
657 							}
658 						}
659 					}
660 					break;
661 				}
662 				case DownloadComplete: {
663 					/*
664 					* IE feature.  Some events that swt relies on are not sent when
665 					* a page is refreshed (as opposed to being navigated to).  The
666 					* workaround is to use DownloadComplete as an opportunity to
667 					* do this work.
668 					*/
669 
670 					Iterator<BrowserFunction> elements2 = functions.values().iterator ();
671 					while (elements2.hasNext ()) {
672 						BrowserFunction function2 = elements2.next ();
673 						execute (function2.functionString);
674 					}
675 
676 					if (!isRefresh) break;
677 					isRefresh = false;
678 
679 					/*
680 					* DocumentComplete is not received for refreshes, but clients may rely
681 					* on this event for tasks like hooking javascript listeners, so send the
682 					* event here.
683 					*/
684 					ProgressEvent progressEvent2 = new ProgressEvent(browser);
685 					progressEvent2.display = browser.getDisplay();
686 					progressEvent2.widget = browser;
687 					for (ProgressListener progressListener : progressListeners) {
688 						progressListener.completed(progressEvent2);
689 					}
690 
691 					break;
692 				}
693 				case NavigateComplete2: {
694 					jsEnabled = jsEnabledOnNextPage;
695 
696 					Variant varResult4 = event.arguments[1];
697 					String url3 = varResult4.getString();
698 					if (!performingInitialNavigate) {
699 						varResult4 = event.arguments[0];
700 						IDispatch dispatch3 = varResult4.getDispatch();
701 						Variant variant3 = new Variant(auto); /* does not need to be disposed */
702 						IDispatch top3 = variant3.getDispatch();
703 						if (top3.getAddress() == dispatch3.getAddress()) {
704 							setAboutBlank(url3.startsWith(ABOUT_BLANK));
705 							lastNavigateURL = url3;
706 						}
707 					}
708 
709 					/*
710 					* Bug in Acrobat Reader.  Opening > MAX_PDF PDF files causes Acrobat to not
711 					* clean up its shells properly when the container Browser is disposed.
712 					* This results in Eclipse crashing at shutdown time because the leftover
713 					* shells have invalid references to unloaded Acrobat libraries.  The
714 					* workaround is to not unload the Acrobat libraries if > MAX_PDF PDF
715 					* files have been opened.
716 					*/
717 					boolean isPDF = false;
718 					String path = null;
719 					try {
720 						path = new URL(url3).getPath();
721 					} catch (MalformedURLException e) {
722 					}
723 					if (path != null) {
724 						int extensionIndex = path.lastIndexOf('.');
725 						if (extensionIndex != -1) {
726 							String extension = path.substring(extensionIndex);
727 							if (extension.equalsIgnoreCase(EXTENSION_PDF)) {
728 								isPDF = true;
729 								PDFCount++;
730 								if (PDFCount > MAX_PDF) {
731 									COM.FreeUnusedLibraries = false;
732 								}
733 							}
734 						}
735 					}
736 
737 					if (uncRedirect != null) {
738 						if (uncRedirect.equals(url3)) {
739 							/* full UNC path has been successfully navigated */
740 							uncRedirect = null;
741 							break;
742 						}
743 						if (uncRedirect.startsWith(url3)) {
744 							/*
745 							* UNC first segment has been successfully navigated,
746 							* now redirect to the full UNC path.
747 							*/
748 							navigate(uncRedirect, null, null, true);
749 							break;
750 						}
751 						uncRedirect = null;
752 					}
753 
754 					varResult4 = event.arguments[0];
755 					IDispatch dispatch4 = varResult4.getDispatch();
756 					if (globalDispatch == 0) globalDispatch = dispatch4.getAddress();
757 
758 					OleAutomation webBrowser = varResult4.getAutomation();
759 					Variant variant4 = new Variant(auto); /* does not need to be disposed */
760 					IDispatch top4 = variant4.getDispatch();
761 					boolean isTop = top4.getAddress() == dispatch4.getAddress();
762 					if (isTop) {
763 						/* unhook DOM listeners and unref the last document(s) */
764 						unhookDOMListeners(documents);
765 						for (OleAutomation document : documents) {
766 							document.dispose();
767 						}
768 						documents = new OleAutomation[0];
769 
770 						/* re-install registered functions */
771 						Iterator<BrowserFunction> elements3 = functions.values().iterator ();
772 						while (elements3.hasNext ()) {
773 							BrowserFunction function3 = elements3.next ();
774 							execute (function3.functionString);
775 						}
776 					}
777 					if (!isPDF) {
778 						hookDOMListeners(webBrowser, isTop);
779 					}
780 					webBrowser.dispose();
781 					break;
782 				}
783 				case NavigateError: {
784 					if (uncRedirect != null) {
785 						/*
786 						* This is the second error attempting to reach this UNC path, so
787 						* it does not exist.  Don't override the default error handling.
788 						*/
789 						uncRedirect = null;
790 						break;
791 					}
792 					Variant varResult5 = event.arguments[1];
793 					final String url4 = varResult5.getString();
794 					if (url4.startsWith("\\\\")) { //$NON-NLS-1$
795 						varResult5 = event.arguments[3];
796 						int statusCode = varResult5.getInt();
797 						if (statusCode == INET_E_RESOURCE_NOT_FOUND) {
798 							int index = url4.indexOf('\\', 2);
799 							if (index != -1) {
800 								final String host = url4.substring(0, index);
801 								Variant cancel4 = event.arguments[4];
802 								if (cancel4 != null) {
803 									long pCancel4 = cancel4.getByRef();
804 									OS.MoveMemory(pCancel4, new short[] {OS.VARIANT_TRUE}, 2);
805 								}
806 								browser.getDisplay().asyncExec(() -> {
807 									if (browser.isDisposed()) return;
808 									/*
809 									* Feature of IE.  When a UNC path ends with a '\' character IE
810 									* drops this character when providing the path as an argument
811 									* to some IE listeners.  Remove this character here too in
812 									* order to match these other listener argument values.
813 									*/
814 									if (url4.endsWith("\\")) { //$NON-NLS-1$
815 										uncRedirect = url4.substring(0, url4.length() - 1);
816 									} else {
817 										uncRedirect = url4;
818 									}
819 									navigate(host, null, null, true);
820 								});
821 							}
822 						}
823 					}
824 					break;
825 				}
826 				case NewWindow2: {
827 					Variant cancel5 = event.arguments[1];
828 					long pCancel5 = cancel5.getByRef();
829 					WindowEvent newEvent2 = new WindowEvent(browser);
830 					newEvent2.display = browser.getDisplay();
831 					newEvent2.widget = browser;
832 					newEvent2.required = false;
833 					for (OpenWindowListener openWindowListener : openWindowListeners) {
834 						openWindowListener.open(newEvent2);
835 					}
836 					IE browser = null;
837 					if (newEvent2.browser != null && newEvent2.browser.webBrowser instanceof IE) {
838 						browser = (IE)newEvent2.browser.webBrowser;
839 					}
840 					boolean doit2 = browser != null && !browser.browser.isDisposed();
841 					if (doit2) {
842 						/*
843 						* When a Browser is opened in a new window, BrowserFunctions that are
844 						* installed in it in the NavigateComplete2 callback are not retained
845 						* through the loading of the page.  The workaround is to re-install
846 						* the functions when DocumentComplete is received.
847 						*/
848 						browser.installFunctionsOnDocumentComplete = true;
849 
850 						Variant variant5 = new Variant(browser.auto); /* does not need to be disposed */
851 						IDispatch iDispatch = variant5.getDispatch();
852 						Variant ppDisp = event.arguments[0];
853 						long byref = ppDisp.getByRef();
854 						if (byref != 0) OS.MoveMemory(byref, new long[] {iDispatch.getAddress()}, C.PTR_SIZEOF);
855 					}
856 					if (newEvent2.required) {
857 						OS.MoveMemory(pCancel5, new short[]{doit2 ? OS.VARIANT_FALSE : OS.VARIANT_TRUE}, 2);
858 					}
859 					break;
860 				}
861 				case OnMenuBar: {
862 					Variant arg01 = event.arguments[0];
863 					menuBar = arg01.getBoolean();
864 					break;
865 				}
866 				case OnStatusBar: {
867 					Variant arg02 = event.arguments[0];
868 					statusBar = arg02.getBoolean();
869 					break;
870 				}
871 				case OnToolBar: {
872 					Variant arg03 = event.arguments[0];
873 					toolBar = arg03.getBoolean();
874 					/*
875 					* Feature in Internet Explorer.  OnToolBar FALSE is emitted
876 					* when both tool bar, address bar and menu bar must not be visible.
877 					* OnToolBar TRUE is emitted when either of tool bar, address bar
878 					* or menu bar is visible.
879 					*/
880 					if (!toolBar) {
881 						addressBar = false;
882 						menuBar = false;
883 					}
884 					break;
885 				}
886 				case OnVisible: {
887 					Variant arg11 = event.arguments[0];
888 					boolean visible = arg11.getBoolean();
889 					WindowEvent newEvent3 = new WindowEvent(browser);
890 					newEvent3.display = browser.getDisplay();
891 					newEvent3.widget = browser;
892 					if (visible) {
893 						if (addressBar) {
894 							/*
895 							* Bug in Internet Explorer.  There is no distinct notification for
896 							* the address bar.  If neither address, menu or tool bars are visible,
897 							* OnToolBar FALSE is emitted. For some reason, querying the value of
898 							* AddressBar in this case returns true even though it should not be
899 							* set visible.  The workaround is to only query the value of AddressBar
900 							* when OnToolBar FALSE has not been emitted.
901 							*/
902 							int[] rgdispid2 = auto.getIDsOfNames(new String[] { "AddressBar" }); //$NON-NLS-1$
903 							Variant pVarResult2 = auto.getProperty(rgdispid2[0]);
904 							if (pVarResult2 != null) {
905 								if (pVarResult2.getType () == OLE.VT_BOOL) {
906 									addressBar = pVarResult2.getBoolean ();
907 								}
908 								pVarResult2.dispose ();
909 							}
910 						}
911 						newEvent3.addressBar = addressBar;
912 						newEvent3.menuBar = menuBar;
913 						newEvent3.statusBar = statusBar;
914 						newEvent3.toolBar = toolBar;
915 						newEvent3.location = location;
916 						newEvent3.size = size;
917 						for (VisibilityWindowListener visibilityWindowListener : visibilityWindowListeners) {
918 							visibilityWindowListener.show(newEvent3);
919 						}
920 						location = null;
921 						size = null;
922 					} else {
923 						for (VisibilityWindowListener visibilityWindowListener : visibilityWindowListeners) {
924 							visibilityWindowListener.hide(newEvent3);
925 						}
926 					}
927 					break;
928 				}
929 				case ProgressChange: {
930 					/* don't send client events if the initial navigate to about:blank has not completed */
931 					if (performingInitialNavigate) break;
932 
933 					Variant arg12 = event.arguments[0];
934 					int nProgress = arg12.getType() != OLE.VT_I4 ? 0 : arg12.getInt(); // may be -1
935 					Variant arg2 = event.arguments[1];
936 					int nProgressMax = arg2.getType() != OLE.VT_I4 ? 0 : arg2.getInt();
937 					ProgressEvent newEvent4 = new ProgressEvent(browser);
938 					newEvent4.display = browser.getDisplay();
939 					newEvent4.widget = browser;
940 					newEvent4.current = nProgress;
941 					newEvent4.total = nProgressMax;
942 					if (nProgress != -1) {
943 						for (ProgressListener progressListener : progressListeners) {
944 							progressListener.changed(newEvent4);
945 						}
946 					}
947 					break;
948 				}
949 				case StatusTextChange: {
950 					/* don't send client events if the initial navigate to about:blank has not completed */
951 					if (performingInitialNavigate) break;
952 
953 					Variant arg13 = event.arguments[0];
954 					if (arg13.getType() == OLE.VT_BSTR) {
955 						String text = arg13.getString();
956 						StatusTextEvent newEvent5 = new StatusTextEvent(browser);
957 						newEvent5.display = browser.getDisplay();
958 						newEvent5.widget = browser;
959 						newEvent5.text = text;
960 						for (StatusTextListener statusTextListener : statusTextListeners) {
961 							statusTextListener.changed(newEvent5);
962 						}
963 					}
964 					break;
965 				}
966 				case TitleChange: {
967 					/* don't send client events if the initial navigate to about:blank has not completed */
968 					if (performingInitialNavigate) break;
969 
970 					Variant arg14 = event.arguments[0];
971 					if (arg14.getType() == OLE.VT_BSTR) {
972 						String title = arg14.getString();
973 						TitleEvent newEvent6 = new TitleEvent(browser);
974 						newEvent6.display = browser.getDisplay();
975 						newEvent6.widget = browser;
976 						newEvent6.title = title;
977 						for (TitleListener titleListener : titleListeners) {
978 							titleListener.changed(newEvent6);
979 						}
980 					}
981 					break;
982 				}
983 				case WindowClosing: {
984 					/*
985 					* Disposing the Browser directly from this callback will crash if the
986 					* Browser has a text field with an active caret.  As a workaround fire
987 					* the Close event and dispose the Browser in an async block.
988 					*/
989 					browser.getDisplay().asyncExec(() -> {
990 						if (browser.isDisposed()) return;
991 						WindowEvent newEvent = new WindowEvent(browser);
992 						newEvent.display = browser.getDisplay();
993 						newEvent.widget = browser;
994 						for (CloseWindowListener closeWindowListener : closeWindowListeners) {
995 							closeWindowListener.close(newEvent);
996 						}
997 						browser.dispose();
998 					});
999 					Variant cancel6 = event.arguments[1];
1000 					long pCancel6 = cancel6.getByRef();
1001 					Variant arg15 = event.arguments[0];
1002 					boolean isChildWindow = arg15.getBoolean();
1003 					OS.MoveMemory(pCancel6, new short[]{isChildWindow ? OS.VARIANT_FALSE : OS.VARIANT_TRUE}, 2);
1004 					break;
1005 				}
1006 				case WindowSetHeight: {
1007 					if (size == null) size = new Point(0, 0);
1008 					Variant arg16 = event.arguments[0];
1009 					size.y = arg16.getInt();
1010 					break;
1011 				}
1012 				case WindowSetLeft: {
1013 					if (location == null) location = new Point(0, 0);
1014 					Variant arg17 = event.arguments[0];
1015 					location.x = arg17.getInt();
1016 					break;
1017 				}
1018 				case WindowSetTop: {
1019 					if (location == null) location = new Point(0, 0);
1020 					Variant arg18 = event.arguments[0];
1021 					location.y = arg18.getInt();
1022 					break;
1023 				}
1024 				case WindowSetWidth: {
1025 					if (size == null) size = new Point(0, 0);
1026 					Variant arg19 = event.arguments[0];
1027 					size.x = arg19.getInt();
1028 					break;
1029 				}
1030 			}
1031 		}
1032 	};
1033 	site.addEventListener(BeforeNavigate2, oleListener);
1034 	site.addEventListener(CommandStateChange, oleListener);
1035 	site.addEventListener(DocumentComplete, oleListener);
1036 	site.addEventListener(DownloadComplete, oleListener);
1037 	site.addEventListener(NavigateComplete2, oleListener);
1038 	site.addEventListener(NavigateError, oleListener);
1039 	site.addEventListener(NewWindow2, oleListener);
1040 	site.addEventListener(OnMenuBar, oleListener);
1041 	site.addEventListener(OnStatusBar, oleListener);
1042 	site.addEventListener(OnToolBar, oleListener);
1043 	site.addEventListener(OnVisible, oleListener);
1044 	site.addEventListener(ProgressChange, oleListener);
1045 	site.addEventListener(StatusTextChange, oleListener);
1046 	site.addEventListener(TitleChange, oleListener);
1047 	site.addEventListener(WindowClosing, oleListener);
1048 	site.addEventListener(WindowSetHeight, oleListener);
1049 	site.addEventListener(WindowSetLeft, oleListener);
1050 	site.addEventListener(WindowSetTop, oleListener);
1051 	site.addEventListener(WindowSetWidth, oleListener);
1052 
1053 	Variant variant = new Variant(true);
1054 	auto.setProperty(RegisterAsBrowser, variant);
1055 	variant.dispose();
1056 
1057 	variant = new Variant(false);
1058 	int[] rgdispid = auto.getIDsOfNames(new String[] {"RegisterAsDropTarget"}); //$NON-NLS-1$
1059 	if (rgdispid != null) auto.setProperty(rgdispid[0], variant);
1060 	variant.dispose();
1061 }
1062 
1063 @Override
back()1064 public boolean back() {
1065 	if (!back) return false;
1066 	int[] rgdispid = auto.getIDsOfNames(new String[] { "GoBack" }); //$NON-NLS-1$
1067 	Variant pVarResult = auto.invoke(rgdispid[0]);
1068 	return pVarResult != null && pVarResult.getType() == OLE.VT_EMPTY;
1069 }
1070 
1071 @Override
close()1072 public boolean close() {
1073 	boolean result = true;
1074 	int[] rgdispid = auto.getIDsOfNames(new String[] {PROPERTY_DOCUMENT});
1075 	int dispIdMember = rgdispid[0];
1076 	Variant pVarResult = auto.getProperty(dispIdMember);
1077 	if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) {
1078 		if (pVarResult != null) pVarResult.dispose();
1079 	} else {
1080 		OleAutomation document = pVarResult.getAutomation();
1081 		pVarResult.dispose();
1082 		rgdispid = document.getIDsOfNames(new String[]{"parentWindow"}); //$NON-NLS-1$
1083 		/* rgdispid != null implies HTML content */
1084 		if (rgdispid != null) {
1085 			dispIdMember = rgdispid[0];
1086 			pVarResult = document.getProperty(dispIdMember);
1087 			if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) {
1088 				if (pVarResult != null) pVarResult.dispose();
1089 			} else {
1090 				OleAutomation window = pVarResult.getAutomation();
1091 				pVarResult.dispose();
1092 				rgdispid = window.getIDsOfNames(new String[]{"location"}); //$NON-NLS-1$
1093 				dispIdMember = rgdispid[0];
1094 				pVarResult = window.getProperty(dispIdMember);
1095 				if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) {
1096 					if (pVarResult != null) pVarResult.dispose();
1097 				} else {
1098 					OleAutomation location = pVarResult.getAutomation();
1099 					pVarResult.dispose();
1100 					LocationListener[] oldListeners = locationListeners;
1101 					locationListeners = new LocationListener[0];
1102 					rgdispid = location.getIDsOfNames(new String[]{"replace"}); //$NON-NLS-1$
1103 					dispIdMember = rgdispid[0];
1104 					Variant[] args = new Variant[] {new Variant("about:blank")}; //$NON-NLS-1$
1105 					pVarResult = location.invoke(dispIdMember, args);
1106 					if (pVarResult == null) {
1107 						/* cancelled by user */
1108 						result = false;
1109 					} else {
1110 						pVarResult.dispose();
1111 					}
1112 					args[0].dispose();
1113 					locationListeners = oldListeners;
1114 					location.dispose();
1115 				}
1116 				window.dispose();
1117 			}
1118 		}
1119 		document.dispose();
1120 	}
1121 	return result;
1122 }
1123 
createSafeArray(String string)1124 static Variant createSafeArray(String string) {
1125 	/* Create a pointer and copy the data into it */
1126 	byte[] bytes = string.getBytes();
1127 	int length = bytes.length;
1128 	long pvData = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, length);
1129 	C.memmove(pvData, bytes, length);
1130 	int cElements1 = length;
1131 
1132 	/* Create a SAFEARRAY in memory */
1133 	long pSafeArray = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, SAFEARRAY.sizeof);
1134 	SAFEARRAY safeArray = new SAFEARRAY();
1135 	safeArray.cDims = 1;
1136 	safeArray.fFeatures = OS.FADF_FIXEDSIZE;
1137 	safeArray.cbElements = 1;
1138 	safeArray.pvData = pvData;
1139 	SAFEARRAYBOUND safeArrayBound = new SAFEARRAYBOUND();
1140 	safeArray.rgsabound = safeArrayBound;
1141 	safeArrayBound.cElements = cElements1;
1142 	OS.MoveMemory (pSafeArray, safeArray, SAFEARRAY.sizeof);
1143 
1144 	/* Return a Variant that holds the SAFEARRAY */
1145 	long pVariant = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, Variant.sizeof);
1146 	short vt = (short)(OLE.VT_ARRAY | OLE.VT_UI1);
1147 	OS.MoveMemory(pVariant, new short[] {vt}, 2);
1148 	OS.MoveMemory(pVariant + 8, new long[] {pSafeArray}, C.PTR_SIZEOF);
1149 	return new Variant(pVariant, (short)(OLE.VT_BYREF | OLE.VT_VARIANT));
1150 }
1151 
1152 @Override
execute(String script)1153 public boolean execute(String script) {
1154 	/*
1155 	 * Issue with IE: If the browser has not shown any content yet then
1156 	 * first navigate to about:blank to work around bug 465822, then execute
1157 	 * the requested script.
1158 	 */
1159 	if (!performingInitialNavigate && _getUrl().length() == 0) {
1160 		performingInitialNavigate = true;
1161 		navigate (ABOUT_BLANK, null, null, true);
1162 	}
1163 
1164 	/* get IHTMLDocument2 */
1165 	int[] rgdispid = auto.getIDsOfNames(new String[] {PROPERTY_DOCUMENT});
1166 	int dispIdMember = rgdispid[0];
1167 	Variant pVarResult = auto.getProperty(dispIdMember);
1168 	if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) {
1169 		if (pVarResult != null) pVarResult.dispose ();
1170 		return false;
1171 	}
1172 	OleAutomation document = pVarResult.getAutomation();
1173 	pVarResult.dispose();
1174 
1175 	/* get IHTMLWindow2 */
1176 	rgdispid = document.getIDsOfNames(new String[]{"parentWindow"}); //$NON-NLS-1$
1177 	if (rgdispid == null) {
1178 		/* implies that browser's content is not a IHTMLDocument2 (eg.- acrobat reader) */
1179 		document.dispose();
1180 		return false;
1181 	}
1182 	dispIdMember = rgdispid[0];
1183 	pVarResult = document.getProperty(dispIdMember);
1184 	if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) {
1185 		if (pVarResult != null) pVarResult.dispose ();
1186 		document.dispose();
1187 		return false;
1188 	}
1189 	OleAutomation ihtmlWindow2 = pVarResult.getAutomation();
1190 	pVarResult.dispose();
1191 	document.dispose();
1192 
1193 	rgdispid = ihtmlWindow2.getIDsOfNames(new String[] { "execScript", "code" }); //$NON-NLS-1$  //$NON-NLS-2$
1194 	if (rgdispid == null) {
1195 		ihtmlWindow2.dispose();
1196 		return false;
1197 	}
1198 	Variant[] rgvarg = new Variant[1];
1199 	rgvarg[0] = new Variant(script);
1200 	int[] rgdispidNamedArgs = new int[1];
1201 	rgdispidNamedArgs[0] = rgdispid[1];
1202 	pVarResult = ihtmlWindow2.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs);
1203 	rgvarg[0].dispose();
1204 	ihtmlWindow2.dispose();
1205 	if (pVarResult == null) return false;
1206 	pVarResult.dispose();
1207 	return true;
1208 }
1209 
1210 @Override
forward()1211 public boolean forward() {
1212 	if (!forward) return false;
1213 	int[] rgdispid = auto.getIDsOfNames(new String[] { "GoForward" }); //$NON-NLS-1$
1214 	Variant pVarResult = auto.invoke(rgdispid[0]);
1215 	return pVarResult != null && pVarResult.getType() == OLE.VT_EMPTY;
1216 }
1217 
1218 @Override
getBrowserType()1219 public String getBrowserType () {
1220 	return "ie"; //$NON-NLS-1$
1221 }
1222 
1223 @Override
getDeleteFunctionString(String functionName)1224 String getDeleteFunctionString (String functionName) {
1225 	return "window." + functionName + "=undefined"; //$NON-NLS-1$ //$NON-NLS-2$
1226 }
1227 
1228 @Override
getText()1229 public String getText() {
1230 	/* get the document object */
1231 	int[] rgdispid = auto.getIDsOfNames(new String[] {PROPERTY_DOCUMENT});
1232 	Variant pVarResult = auto.getProperty(rgdispid[0]);
1233 	if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) {
1234 		if (pVarResult != null) pVarResult.dispose ();
1235 		return ""; //$NON-NLS-1$
1236 	}
1237 	OleAutomation document = pVarResult.getAutomation();
1238 	pVarResult.dispose();
1239 
1240 	/* get the html object */
1241 	rgdispid = document.getIDsOfNames(new String[] {"documentElement"}); //$NON-NLS-1$
1242 	if (rgdispid == null) {
1243 		/* implies that the browser is displaying non-HTML content */
1244 		document.dispose();
1245 		return ""; //$NON-NLS-1$
1246 	}
1247 	pVarResult = document.getProperty(rgdispid[0]);
1248 	document.dispose();
1249 	if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY || pVarResult.getType() == COM.VT_NULL) {
1250 		if (pVarResult != null) pVarResult.dispose ();
1251 		return ""; //$NON-NLS-1$
1252 	}
1253 	OleAutomation element = pVarResult.getAutomation();
1254 	pVarResult.dispose();
1255 
1256 	/* get its outerHTML property */
1257 	rgdispid = element.getIDsOfNames(new String[] {"outerHTML"}); //$NON-NLS-1$
1258 	pVarResult = element.getProperty(rgdispid[0]);
1259 	element.dispose();
1260 	if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) {
1261 		if (pVarResult != null) pVarResult.dispose ();
1262 		return ""; //$NON-NLS-1$
1263 	}
1264 	String result = pVarResult.getString();
1265 	pVarResult.dispose();
1266 
1267 	return result;
1268 }
1269 
1270 @Override
getUrl()1271 public String getUrl() {
1272 	/*
1273 	 * If the url is "" then return ABOUT_BLANK in order to be consistent
1274 	 * with the other Browser implementations which auto-navigate to ABOUT_BLANK
1275 	 * when opened.
1276 	 */
1277 	String result = _getUrl();
1278 	return result.length() != 0 ? result : ABOUT_BLANK;
1279 }
1280 
_getUrl()1281 String _getUrl() {
1282 	int[] rgdispid = auto.getIDsOfNames(new String[] { "LocationURL" }); //$NON-NLS-1$
1283 	Variant pVarResult = auto.getProperty(rgdispid[0]);
1284 	if (pVarResult == null || pVarResult.getType() != OLE.VT_BSTR) return ""; //$NON-NLS-1$
1285 	String result = pVarResult.getString();
1286 	pVarResult.dispose();
1287 	return result;
1288 }
1289 
1290 @Override
isBackEnabled()1291 public boolean isBackEnabled() {
1292 	return back;
1293 }
1294 
1295 @Override
isForwardEnabled()1296 public boolean isForwardEnabled() {
1297 	return forward;
1298 }
1299 
1300 @Override
isFocusControl()1301 public boolean isFocusControl () {
1302 	return site.isFocusControl() || frame.isFocusControl();
1303 }
1304 
navigate(String url, String postData, String headers[], boolean silent)1305 boolean navigate(String url, String postData, String headers[], boolean silent) {
1306 	int count = 1;
1307 	if (postData != null) count++;
1308 	if (headers != null) count++;
1309 	Variant[] rgvarg = new Variant[count];
1310 	int[] rgdispidNamedArgs = new int[count];
1311 	int[] rgdispid = auto.getIDsOfNames(new String[] { "Navigate", "URL", "PostData", "Headers" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
1312 	int index = 0;
1313 	rgvarg[index] = new Variant(url);
1314 	rgdispidNamedArgs[index++] = rgdispid[1];
1315 	if (postData != null) {
1316 		rgvarg[index] = createSafeArray(postData);
1317 		rgdispidNamedArgs[index++] = rgdispid[2];
1318 	}
1319 	if (headers != null) {
1320 		StringBuilder buffer = new StringBuilder();
1321 		for (String current : headers) {
1322 			if (current != null) {
1323 				int sep = current.indexOf(':');
1324 				if (sep != -1) {
1325 					String key = current.substring(0, sep).trim();
1326 					String value = current.substring(sep + 1).trim();
1327 					if (key.length() > 0 && value.length() > 0) {
1328 						buffer.append(key);
1329 						buffer.append(':');
1330 						buffer.append(value);
1331 						buffer.append("\r\n");
1332 					}
1333 				}
1334 			}
1335 		}
1336 		rgvarg[index] = new Variant(buffer.toString());
1337 		rgdispidNamedArgs[index++] = rgdispid[3];
1338 	}
1339 	boolean oldValue = false;
1340 	if (silent && IEVersion >= 7) {
1341 		int hResult = OS.CoInternetIsFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.GET_FEATURE_FROM_PROCESS);
1342 		oldValue = hResult == COM.S_OK;
1343 		OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, true);
1344 	}
1345 	Variant pVarResult = auto.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs);
1346 	if (silent && IEVersion >= 7) {
1347 		OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, oldValue);
1348 	}
1349 	for (int i = 0; i < count; i++) {
1350 		rgvarg[i].dispose();
1351 	}
1352 	if (pVarResult == null) return false;
1353 	boolean result = pVarResult.getType() == OLE.VT_EMPTY;
1354 	pVarResult.dispose();
1355 	return result;
1356 }
1357 
1358 @Override
refresh()1359 public void refresh() {
1360 	uncRedirect = null;
1361 
1362 	/*
1363 	* Bug in Acrobat Reader.  Opening > MAX_PDF PDF files causes Acrobat to not
1364 	* clean up its shells properly when the container Browser is disposed.
1365 	* This results in Eclipse crashing at shutdown time because the leftover
1366 	* shells have invalid references to unloaded Acrobat libraries.  The
1367 	* workaround is to not unload the Acrobat libraries if > MAX_PDF PDF
1368 	* files have been opened.
1369 	*/
1370 	String url = _getUrl();
1371 	int extensionIndex = url.lastIndexOf('.');
1372 	if (extensionIndex != -1) {
1373 		String extension = url.substring(extensionIndex);
1374 		if (extension.equalsIgnoreCase (EXTENSION_PDF)) {
1375 			PDFCount++;
1376 			if (PDFCount > MAX_PDF) {
1377 				COM.FreeUnusedLibraries = false;
1378 			}
1379 		}
1380 	}
1381 
1382 	isRefresh = true;
1383 	int[] rgdispid = auto.getIDsOfNames(new String[] { "Refresh" }); //$NON-NLS-1$
1384 	auto.invoke(rgdispid[0]);
1385 }
1386 
setHTML(String string)1387 void setHTML (String string) {
1388 	int charCount = string.length();
1389 	char[] chars = new char[charCount];
1390 	string.getChars(0, charCount, chars, 0);
1391 	int byteCount = OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, charCount, null, 0, null, null);
1392 	/*
1393 	* Internet Explorer appears to treat the data loaded with
1394 	* nsIPersistStreamInit.Load as if it were encoded using the default
1395 	* local charset.  There does not seem to be an API to set the
1396 	* desired charset explicitly in this case.  The fix is to
1397 	* prepend the UTF-8 Byte Order Mark signature to the data.
1398 	*/
1399 	byte[] UTF8BOM = {(byte)0xEF, (byte)0xBB, (byte)0xBF};
1400 	long hGlobal = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, UTF8BOM.length + byteCount);
1401 	if (hGlobal != 0) {
1402 		OS.MoveMemory(hGlobal, UTF8BOM, UTF8BOM.length);
1403 		OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, charCount, hGlobal + UTF8BOM.length, byteCount, null, null);
1404 		long [] ppstm = new long [1];
1405 		/*
1406 		* CreateStreamOnHGlobal is called with the flag fDeleteOnRelease.
1407 		* If the call succeeds the buffer hGlobal is freed automatically
1408 		* when the IStream object is released. If the call fails, free the
1409 		* buffer hGlobal.
1410 		*/
1411 		if (OS.CreateStreamOnHGlobal(hGlobal, true, ppstm) == OS.S_OK) {
1412 			int[] rgdispid = auto.getIDsOfNames(new String[] {PROPERTY_DOCUMENT});
1413 			Variant pVarResult = auto.getProperty(rgdispid[0]);
1414 			IDispatch dispatchDocument = pVarResult.getDispatch();
1415 			long [] ppvObject = new long [1];
1416 			int result = dispatchDocument.QueryInterface(COM.IIDIPersistStreamInit, ppvObject);
1417 			if (result == OS.S_OK) {
1418 				IPersistStreamInit persistStreamInit = new IPersistStreamInit(ppvObject[0]);
1419 				if (persistStreamInit.InitNew() == OS.S_OK) {
1420 					persistStreamInit.Load(ppstm[0]);
1421 				}
1422 				persistStreamInit.Release();
1423 			}
1424 			pVarResult.dispose();
1425 			IUnknown stream = new IUnknown(ppstm[0]);
1426 			stream.Release();
1427 		} else {
1428 			OS.GlobalFree(hGlobal);
1429 		}
1430 	}
1431 }
1432 
setAboutBlank(boolean value)1433 private void setAboutBlank(boolean value) {
1434 	isAboutBlank = value;
1435 	updateForceTrusted();
1436 }
1437 
setUntrustedText(boolean value)1438 private void setUntrustedText(boolean value) {
1439 	untrustedText = value;
1440 	updateForceTrusted();
1441 }
1442 
updateForceTrusted()1443 private void updateForceTrusted() {
1444 	site.isForceTrusted = isAboutBlank && !untrustedText;
1445 }
1446 
1447 @Override
setText(final String html, boolean trusted)1448 public boolean setText(final String html, boolean trusted) {
1449 	/*
1450 	* If the browser is navigating to about:blank in response to its first
1451 	* setUrl() invocation then delay setting this text content until the
1452 	* navigate has completed.  about:blank will be re-navigated to in order
1453 	* to ensure that all expected client events are sent.
1454 	*/
1455 	if (performingInitialNavigate) {
1456 		pendingText = new Object[] {html, trusted};
1457 		pendingUrl = null;
1458 		return true;
1459 	}
1460 
1461 	/*
1462 	* If the html field is non-null then the about:blank page is already being
1463 	* loaded from a previous setText() invocation, so no Stop or Navigate is
1464 	* required.  Just set the html that is to be shown.
1465 	*/
1466 	boolean blankLoading = this.html != null;
1467 	this.html = html;
1468 	setUntrustedText(!trusted);
1469 	if (blankLoading) return true;
1470 
1471 	/*
1472 	* Navigate to the blank page and insert the given html when
1473 	* receiving the next DocumentComplete notification.  See the
1474 	* MSDN article "Loading HTML content from a Stream".
1475 	*
1476 	* Note.  Stop any pending request.  This is required to avoid displaying a
1477 	* blank page as a result of consecutive calls to setUrl and/or setText.
1478 	* The previous request would otherwise render the new html content and
1479 	* reset the html field before the browser actually navigates to the blank
1480 	* page as requested below.
1481 	*
1482 	* Feature in Internet Explorer.  Stopping pending requests when no request
1483 	* is pending causes a default page 'Action cancelled' to be displayed.  The
1484 	* workaround is to not invoke 'stop' when no request has been set since
1485 	* that instance was created.
1486 	*/
1487 
1488 	/*
1489 	* Stopping the loading of a page causes DocumentComplete events from previous
1490 	* requests to be received before the DocumentComplete for this page.  In such
1491 	* cases we must be sure to not set the html into the browser too soon, since
1492 	* doing so could result in its page being cleared out by a subsequent
1493 	* DocumentComplete.  The Browser's ReadyState can be used to determine whether
1494 	* these extra events will be received or not.
1495 	*/
1496 	if (_getUrl().length() != 0) {
1497 		int[] rgdispid = auto.getIDsOfNames(new String[] { "ReadyState" }); //$NON-NLS-1$
1498 		Variant pVarResult = auto.getProperty(rgdispid[0]);
1499 		if (pVarResult == null) return false;
1500 		delaySetText = pVarResult.getInt() != READYSTATE_COMPLETE;
1501 		pVarResult.dispose();
1502 		rgdispid = auto.getIDsOfNames(new String[] { "Stop" }); //$NON-NLS-1$
1503 		auto.invoke(rgdispid[0]);
1504 	}
1505 
1506 	int[] rgdispid = auto.getIDsOfNames(new String[] { "Navigate", "URL" }); //$NON-NLS-1$ //$NON-NLS-2$
1507 	Variant[] rgvarg = new Variant[1];
1508 	rgvarg[0] = new Variant(ABOUT_BLANK);
1509 	int[] rgdispidNamedArgs = new int[1];
1510 	rgdispidNamedArgs[0] = rgdispid[1];
1511 	boolean oldValue = false;
1512 	if (IEVersion >= 7) {
1513 		int hResult = OS.CoInternetIsFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.GET_FEATURE_FROM_PROCESS);
1514 		oldValue = hResult == COM.S_OK;
1515 		OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, true);
1516 	}
1517 	Variant pVarResult = auto.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs);
1518 	if (IEVersion >= 7) {
1519 		OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, oldValue);
1520 	}
1521 	rgvarg[0].dispose();
1522 	if (pVarResult == null) return false;
1523 	boolean result = pVarResult.getType() == OLE.VT_EMPTY;
1524 	pVarResult.dispose();
1525 	return result;
1526 }
1527 
1528 @Override
setUrl(String url, String postData, String headers[])1529 public boolean setUrl(String url, String postData, String headers[]) {
1530 	html = uncRedirect = null;
1531 
1532 	/*
1533 	* If the browser has not shown any content yet then first navigate to
1534 	* about:blank to work around IE bug http://support.microsoft.com/kb/320153,
1535 	* then navigate to the requested url once about:blank has loaded.
1536 	*/
1537 	if (_getUrl().length() == 0 && !ABOUT_BLANK.equalsIgnoreCase(url)) {
1538 		pendingText = null;
1539 		pendingUrl = new Object[] {url, postData, headers};
1540 		performingInitialNavigate = true;
1541 		navigate (ABOUT_BLANK, null, null, true);
1542 		return true;
1543 	}
1544 
1545 	/*
1546 	* Bug in Internet Explorer.  For some reason, Navigating to an xml document before
1547 	* a previous Navigate has completed will leave the Browser in a bad state if the
1548 	* Navigate to the xml document does not complete.  This bad state causes a GP when
1549 	* the parent window is eventually disposed.  The workaround is to issue a Stop before
1550 	* navigating to any xml document.
1551 	*/
1552 	if (url.endsWith(".xml")) {	//$NON-NLS-1$
1553 		int[] rgdispid = auto.getIDsOfNames(new String[] { "Stop" }); //$NON-NLS-1$
1554 		auto.invoke(rgdispid[0]);
1555 	}
1556 	return navigate(url, postData, headers, false);
1557 }
1558 
1559 @Override
stop()1560 public void stop() {
1561 	/*
1562 	* If the browser has not completed its initial navigate to about:blank
1563 	* then do not issue Stop here, as this will display the IE error page.
1564 	* Just clear the pending url and text fields so that any pending content
1565 	* will not be set into the browser when the about:blank navigate completes.
1566 	*/
1567 	if (performingInitialNavigate) {
1568 		pendingText = pendingUrl = null;
1569 		return;
1570 	}
1571 
1572 	/*
1573 	* Feature of IE.  Invoking Stop in IE before any content has been shown
1574 	* displays a Navigation Cancelled error page.  The workaround is to not
1575 	* invoke Stop if no content has been shown yet.
1576 	*/
1577 	if (_getUrl().length() == 0) return;
1578 
1579 	/*
1580 	* Ensure that isAboutBlank is set accurately since Stop can be issued at
1581 	* any stage in the page load cycle.
1582 	*/
1583 	setAboutBlank(getUrl().startsWith(ABOUT_BLANK));
1584 
1585 	uncRedirect = null;
1586 	int[] rgdispid = auto.getIDsOfNames(new String[] { "Stop" }); //$NON-NLS-1$
1587 	auto.invoke(rgdispid[0]);
1588 }
1589 
1590 @Override
translateMnemonics()1591 boolean translateMnemonics () {
1592 	return false;
1593 }
1594 
handleDOMEvent(OleEvent e)1595 void handleDOMEvent (OleEvent e) {
1596 	if (e.arguments == null || e.arguments.length == 0) return; /* for IE5 */
1597 
1598 	Variant arg = e.arguments[0];
1599 	OleAutomation event = arg.getAutomation();
1600 	int[] rgdispid = event.getIDsOfNames(new String[]{ PROPERTY_TYPE });
1601 	int dispIdMember = rgdispid[0];
1602 	Variant pVarResult = event.getProperty(dispIdMember);
1603 	String eventType = pVarResult.getString();
1604 	pVarResult.dispose();
1605 
1606 	if (eventType.equals(EVENT_KEYDOWN)) {
1607 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_KEYCODE });
1608 		dispIdMember = rgdispid[0];
1609 		pVarResult = event.getProperty(dispIdMember);
1610 		lastKeyCode = translateKey (pVarResult.getInt());
1611 		pVarResult.dispose();
1612 
1613 		rgdispid = event.getIDsOfNames (new String[] {PROPERTY_RETURNVALUE});
1614 		pVarResult = event.getProperty (rgdispid[0]);
1615 		boolean consume = pVarResult != null && pVarResult.getType () == OLE.VT_BOOL && !pVarResult.getBoolean ();
1616 		pVarResult.dispose ();
1617 
1618 		MSG msg = new MSG ();
1619 		int flags = OS.PM_NOYIELD | (consume ? OS.PM_REMOVE : OS.PM_NOREMOVE);
1620 		if (OS.PeekMessage (msg, frame.handle, OS.WM_CHAR, OS.WM_CHAR, flags)) {
1621 			/* a keypress will be received for this key so don't send KeyDown here */
1622 			event.dispose();
1623 			return;
1624 		}
1625 
1626 		if (consume) {
1627 			/*
1628 			 * an event should not be sent if another listener has vetoed the
1629 			 * KeyDown (this is for non-character cases like arrow keys, etc.)
1630 			 */
1631 			event.dispose();
1632 			return;
1633 		}
1634 
1635 		/* if this is a repeating key then an event should not be fired for it */
1636 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_REPEAT });
1637 		dispIdMember = rgdispid[0];
1638 		pVarResult = event.getProperty(dispIdMember);
1639 		boolean repeating = pVarResult.getBoolean();
1640 		pVarResult.dispose();
1641 		if (repeating) {
1642 			event.dispose();
1643 			return;
1644 		}
1645 
1646 		int mask = 0;
1647 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_ALTKEY });
1648 		dispIdMember = rgdispid[0];
1649 		pVarResult = event.getProperty(dispIdMember);
1650 		if (pVarResult.getBoolean()) mask |= SWT.ALT;
1651 		pVarResult.dispose();
1652 
1653 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CTRLKEY });
1654 		dispIdMember = rgdispid[0];
1655 		pVarResult = event.getProperty(dispIdMember);
1656 		if (pVarResult.getBoolean()) mask |= SWT.CTRL;
1657 		pVarResult.dispose();
1658 
1659 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SHIFTKEY });
1660 		dispIdMember = rgdispid[0];
1661 		pVarResult = event.getProperty(dispIdMember);
1662 		if (pVarResult.getBoolean()) mask |= SWT.SHIFT;
1663 		pVarResult.dispose();
1664 
1665 		Event keyEvent = new Event ();
1666 		keyEvent.widget = browser;
1667 		keyEvent.type = SWT.KeyDown;
1668 		keyEvent.keyCode = lastKeyCode;
1669 		keyEvent.stateMask = mask;
1670 		keyEvent.stateMask &= ~lastKeyCode;		/* remove current keydown if it's a state key */
1671 		/*
1672 		* keypress events are not received for Backspace, Enter, Delete and
1673 		* Tab, so KeyDown events are sent for them here.  Set the KeyDown
1674 		* event's character field and IE's lastCharCode field for these keys
1675 		* so that the Browser's key events are consistent with other controls.
1676 		*/
1677 		switch (lastKeyCode) {
1678 			case SWT.BS: lastCharCode = keyEvent.character = SWT.BS; break;
1679 			case SWT.CR: lastCharCode = keyEvent.character = SWT.CR; break;
1680 			case SWT.DEL: lastCharCode = keyEvent.character = SWT.DEL; break;
1681 			case SWT.TAB: lastCharCode = keyEvent.character = SWT.TAB; break;
1682 		}
1683 
1684 		if (!sendKeyEvent(keyEvent)) {
1685 			rgdispid = event.getIDsOfNames(new String[] { PROPERTY_RETURNVALUE });
1686 			dispIdMember = rgdispid[0];
1687 			Variant pVarFalse = new Variant(false);
1688 			event.setProperty(dispIdMember, pVarFalse);
1689 			pVarFalse.dispose();
1690 		}
1691 
1692 		/*
1693 		* Pressing F5 refreshes the current page.  If this is about to happen
1694 		* then set isRefresh to true so that received IE events will be treated
1695 		* accordingly.
1696 		*/
1697 		if (lastKeyCode == SWT.F5) isRefresh = true;
1698 
1699 		event.dispose();
1700 		return;
1701 	}
1702 
1703 	if (eventType.equals(EVENT_KEYPRESS)) {
1704 		int mask = 0;
1705 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CTRLKEY });
1706 		dispIdMember = rgdispid[0];
1707 		pVarResult = event.getProperty(dispIdMember);
1708 		if (pVarResult.getBoolean()) mask |= SWT.CTRL;
1709 		pVarResult.dispose();
1710 
1711 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SHIFTKEY });
1712 		dispIdMember = rgdispid[0];
1713 		pVarResult = event.getProperty(dispIdMember);
1714 		if (pVarResult.getBoolean()) mask |= SWT.SHIFT;
1715 		pVarResult.dispose();
1716 
1717 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_ALTKEY });
1718 		dispIdMember = rgdispid[0];
1719 		pVarResult = event.getProperty(dispIdMember);
1720 		if (pVarResult.getBoolean()) mask |= SWT.ALT;
1721 		pVarResult.dispose();
1722 
1723 		/* in the keypress event the keyCode actually corresponds to the character code */
1724 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_KEYCODE });
1725 		dispIdMember = rgdispid[0];
1726 		pVarResult = event.getProperty(dispIdMember);
1727 		lastCharCode = pVarResult.getInt();
1728 		pVarResult.dispose();
1729 
1730 		/*
1731 		* WebSite.TranslateAccelerator() explicitly does not translate OS.VK_RETURN
1732 		* keys, so the PeekMessage check in the keydown handler always allows a
1733 		* KeyDown to be sent for this key.  However, keydown and keypress events are
1734 		* both sometimes received for OS.VK_RETURN, depending on the page's focus
1735 		* control.  To handle this, do not send a KeyDown for CR or LF here since
1736 		* one is always sent for it from the keydown handler.
1737 		*/
1738 		if (lastCharCode == SWT.CR || lastCharCode == SWT.LF) {
1739 			event.dispose();
1740 			return;
1741 		}
1742 
1743 		Event keyEvent = new Event ();
1744 		keyEvent.widget = browser;
1745 		keyEvent.type = SWT.KeyDown;
1746 		keyEvent.keyCode = lastKeyCode;
1747 		keyEvent.character = (char)lastCharCode;
1748 		keyEvent.stateMask = mask;
1749 		if (!sendKeyEvent(keyEvent)) {
1750 			rgdispid = event.getIDsOfNames(new String[] { PROPERTY_RETURNVALUE });
1751 			dispIdMember = rgdispid[0];
1752 			Variant pVarFalse = new Variant(false);
1753 			event.setProperty(dispIdMember, pVarFalse);
1754 			pVarFalse.dispose();
1755 		}
1756 
1757 		event.dispose();
1758 		return;
1759 	}
1760 
1761 	if (eventType.equals(EVENT_KEYUP)) {
1762 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_KEYCODE });
1763 		dispIdMember = rgdispid[0];
1764 		pVarResult = event.getProperty(dispIdMember);
1765 		int keyCode = translateKey (pVarResult.getInt());
1766 		pVarResult.dispose();
1767 
1768 		/*
1769 		* if a key code could not be determined for this key then it's a
1770 		* key for which key events are not sent (eg.- the Windows key)
1771 		*/
1772 		if (keyCode == 0) {
1773 			lastKeyCode = lastCharCode = 0;
1774 			event.dispose();
1775 			return;
1776 		}
1777 
1778 		if (keyCode != lastKeyCode) {
1779 			/* keyup does not correspond to the last keydown */
1780 			lastKeyCode = keyCode;
1781 			lastCharCode = 0;
1782 		}
1783 
1784 		int mask = 0;
1785 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CTRLKEY });
1786 		dispIdMember = rgdispid[0];
1787 		pVarResult = event.getProperty(dispIdMember);
1788 		if (pVarResult.getBoolean()) mask |= SWT.CTRL;
1789 		pVarResult.dispose();
1790 
1791 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_ALTKEY });
1792 		dispIdMember = rgdispid[0];
1793 		pVarResult = event.getProperty(dispIdMember);
1794 		if (pVarResult.getBoolean()) mask |= SWT.ALT;
1795 		pVarResult.dispose();
1796 
1797 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SHIFTKEY });
1798 		dispIdMember = rgdispid[0];
1799 		pVarResult = event.getProperty(dispIdMember);
1800 		if (pVarResult.getBoolean()) mask |= SWT.SHIFT;
1801 		pVarResult.dispose();
1802 
1803 		Event keyEvent = new Event ();
1804 		keyEvent.widget = browser;
1805 		keyEvent.type = SWT.KeyUp;
1806 		keyEvent.keyCode = lastKeyCode;
1807 		keyEvent.character = (char)lastCharCode;
1808 		keyEvent.stateMask = mask;
1809 		switch (lastKeyCode) {
1810 			case SWT.SHIFT:
1811 			case SWT.CONTROL:
1812 			case SWT.ALT:
1813 			case SWT.COMMAND: {
1814 				keyEvent.stateMask |= lastKeyCode;
1815 			}
1816 		}
1817 		browser.notifyListeners (keyEvent.type, keyEvent);
1818 		if (!keyEvent.doit) {
1819 			rgdispid = event.getIDsOfNames(new String[] { PROPERTY_RETURNVALUE });
1820 			dispIdMember = rgdispid[0];
1821 			Variant pVarFalse = new Variant(false);
1822 			event.setProperty(dispIdMember, pVarFalse);
1823 			pVarFalse.dispose();
1824 		}
1825 
1826 		lastKeyCode = lastCharCode = 0;
1827 		event.dispose();
1828 		return;
1829 	}
1830 
1831 	/*
1832 	 * Feature in IE. MouseOver/MouseOut events are fired any time the mouse enters
1833 	 * or exits any element within the Browser.  To ensure that SWT events are only
1834 	 * fired for mouse movements into or out of the Browser, do not fire an event if
1835 	 * the element being exited (on MouseOver) or entered (on MouseExit) is within
1836 	 * the Browser.
1837 	 */
1838 	if (eventType.equals(EVENT_MOUSEOVER)) {
1839 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_FROMELEMENT });
1840 		dispIdMember = rgdispid[0];
1841 		pVarResult = event.getProperty(dispIdMember);
1842 		boolean isInternal = pVarResult.getType() != COM.VT_EMPTY;
1843 		pVarResult.dispose();
1844 		if (isInternal) {
1845 			event.dispose();
1846 			return;
1847 		}
1848 	}
1849 	if (eventType.equals(EVENT_MOUSEOUT)) {
1850 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_TOELEMENT });
1851 		dispIdMember = rgdispid[0];
1852 		pVarResult = event.getProperty(dispIdMember);
1853 		boolean isInternal = pVarResult.getType() != COM.VT_EMPTY;
1854 		pVarResult.dispose();
1855 		if (isInternal) {
1856 			event.dispose();
1857 			return;
1858 		}
1859 	}
1860 
1861 	int mask = 0;
1862 	Event newEvent = new Event();
1863 	newEvent.widget = browser;
1864 
1865 	/*
1866 	 * The position of mouse events is received in screen-relative coordinates
1867 	 * in order to handle pages with frames, since frames express their event
1868 	 * coordinates relative to themselves rather than relative to their top-
1869 	 * level page.  Convert screen-relative coordinates to be browser-relative.
1870 	 */
1871 	rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SCREENX });
1872 	dispIdMember = rgdispid[0];
1873 	pVarResult = event.getProperty(dispIdMember);
1874 	int screenX = pVarResult.getInt();
1875 	pVarResult.dispose();
1876 
1877 	rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SCREENY });
1878 	dispIdMember = rgdispid[0];
1879 	pVarResult = event.getProperty(dispIdMember);
1880 	int screenY = pVarResult.getInt();
1881 	pVarResult.dispose();
1882 
1883 	Point position = DPIUtil.autoScaleDown(new Point(screenX, screenY)); // To Points
1884 	position = browser.getDisplay().map(null, browser, position);
1885 	newEvent.x = position.x; newEvent.y = position.y;
1886 
1887 	rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CTRLKEY });
1888 	dispIdMember = rgdispid[0];
1889 	pVarResult = event.getProperty(dispIdMember);
1890 	if (pVarResult.getBoolean()) mask |= SWT.CTRL;
1891 	pVarResult.dispose();
1892 
1893 	rgdispid = event.getIDsOfNames(new String[] { PROPERTY_ALTKEY });
1894 	dispIdMember = rgdispid[0];
1895 	pVarResult = event.getProperty(dispIdMember);
1896 	if (pVarResult.getBoolean()) mask |= SWT.ALT;
1897 	pVarResult.dispose();
1898 
1899 	rgdispid = event.getIDsOfNames(new String[] { PROPERTY_SHIFTKEY });
1900 	dispIdMember = rgdispid[0];
1901 	pVarResult = event.getProperty(dispIdMember);
1902 	if (pVarResult.getBoolean()) mask |= SWT.SHIFT;
1903 	pVarResult.dispose();
1904 
1905 	newEvent.stateMask = mask;
1906 
1907 	rgdispid = event.getIDsOfNames(new String[] { PROPERTY_BUTTON });
1908 	dispIdMember = rgdispid[0];
1909 	pVarResult = event.getProperty(dispIdMember);
1910 	int button = pVarResult.getInt();
1911 	pVarResult.dispose();
1912 	switch (button) {
1913 		case 1: button = 1; break;
1914 		case 2: button = 3; break;
1915 		case 4: button = 2; break;
1916 	}
1917 
1918 	if (eventType.equals(EVENT_MOUSEDOWN)) {
1919 		newEvent.type = SWT.MouseDown;
1920 		newEvent.button = button;
1921 		newEvent.count = 1;
1922 	} else if (eventType.equals(EVENT_MOUSEUP) || eventType.equals(EVENT_DRAGEND)) {
1923 		newEvent.type = SWT.MouseUp;
1924 		newEvent.button = button != 0 ? button : 1;	/* button assumed to be 1 for dragends */
1925 		newEvent.count = 1;
1926 		switch (newEvent.button) {
1927 			case 1: newEvent.stateMask |= SWT.BUTTON1; break;
1928 			case 2: newEvent.stateMask |= SWT.BUTTON2; break;
1929 			case 3: newEvent.stateMask |= SWT.BUTTON3; break;
1930 			case 4: newEvent.stateMask |= SWT.BUTTON4; break;
1931 			case 5: newEvent.stateMask |= SWT.BUTTON5; break;
1932 		}
1933 	} else if (eventType.equals(EVENT_MOUSEWHEEL)) {
1934 		newEvent.type = SWT.MouseWheel;
1935 		rgdispid = event.getIDsOfNames(new String[] { PROPERTY_WHEELDELTA });
1936 		dispIdMember = rgdispid[0];
1937 		pVarResult = event.getProperty(dispIdMember);
1938 		newEvent.count = pVarResult.getInt () / 120 * 3;
1939 		pVarResult.dispose();
1940 	} else if (eventType.equals(EVENT_MOUSEMOVE)) {
1941 		/*
1942 		* Feature in IE.  Spurious and redundant mousemove events are often received.  The workaround
1943 		* is to not fire MouseMove events whose x and y values match the last MouseMove.
1944 		*/
1945 		if (newEvent.x == lastMouseMoveX && newEvent.y == lastMouseMoveY) {
1946 			event.dispose();
1947 			return;
1948 		}
1949 		newEvent.type = SWT.MouseMove;
1950 		lastMouseMoveX = newEvent.x; lastMouseMoveY = newEvent.y;
1951 	} else if (eventType.equals(EVENT_MOUSEOVER)) {
1952 		newEvent.type = SWT.MouseEnter;
1953 	} else if (eventType.equals(EVENT_MOUSEOUT)) {
1954 		newEvent.type = SWT.MouseExit;
1955 	} else if (eventType.equals(EVENT_DRAGSTART)) {
1956 		newEvent.type = SWT.DragDetect;
1957 		newEvent.button = 1;	/* button assumed to be 1 for dragstarts */
1958 		newEvent.stateMask |= SWT.BUTTON1;
1959 	}
1960 
1961 	event.dispose();
1962 	browser.notifyListeners(newEvent.type, newEvent);
1963 
1964 	if (eventType.equals(EVENT_DOUBLECLICK)) {
1965 		newEvent = new Event ();
1966 		newEvent.widget = browser;
1967 		newEvent.type = SWT.MouseDoubleClick;
1968 		newEvent.x = position.x; newEvent.y = position.y;
1969 		newEvent.stateMask = mask;
1970 		newEvent.type = SWT.MouseDoubleClick;
1971 		newEvent.button = 1; /* dblclick only comes for button 1 and does not set the button property */
1972 		newEvent.count = 2;
1973 		browser.notifyListeners (newEvent.type, newEvent);
1974 	}
1975 }
1976 
hookDOMListeners(OleAutomation webBrowser, final boolean isTop)1977 void hookDOMListeners(OleAutomation webBrowser, final boolean isTop) {
1978 	int[] rgdispid = webBrowser.getIDsOfNames(new String[] { PROPERTY_DOCUMENT });
1979 	int dispIdMember = rgdispid[0];
1980 	Variant pVarResult = webBrowser.getProperty(dispIdMember);
1981 	if (pVarResult == null) return;
1982 	if (pVarResult.getType() == COM.VT_EMPTY) {
1983 		pVarResult.dispose();
1984 		return;
1985 	}
1986 	final OleAutomation document = pVarResult.getAutomation();
1987 	pVarResult.dispose();
1988 
1989 	/*
1990 	 * In some cases, such as setting the Browser's content from a string,
1991 	 * NavigateComplete2 is received multiple times for a top-level document.
1992 	 * For cases like this, any previously-hooked DOM listeners must be
1993 	 * removed from the document before hooking the new set of listeners,
1994 	 * otherwise multiple sets of events will be received.
1995 	 */
1996 	unhookDOMListeners (new OleAutomation[] {document});
1997 
1998 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYDOWN, domListener);
1999 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYPRESS, domListener);
2000 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYUP, domListener);
2001 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEDOWN, domListener);
2002 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEUP, domListener);
2003 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEWHEEL, domListener);
2004 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONDBLCLICK, domListener);
2005 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEMOVE, domListener);
2006 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONDRAGSTART, domListener);
2007 	site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONDRAGEND, domListener);
2008 	/* ensure that enter/exit are only fired once, by the top-level document */
2009 	if (isTop) {
2010 		site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOVER, domListener);
2011 		site.addEventListener(document, COM.IIDIHTMLDocumentEvents2, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOUT, domListener);
2012 	}
2013 
2014 	OleAutomation[] newDocuments = new OleAutomation[documents.length + 1];
2015 	System.arraycopy(documents, 0, newDocuments, 0, documents.length);
2016 	newDocuments[documents.length] = document;
2017 	documents = newDocuments;
2018 }
2019 
unhookDOMListeners(OleAutomation[] documents)2020 void unhookDOMListeners(OleAutomation[] documents) {
2021 	char[] buffer = (COM.IIDIHTMLDocumentEvents2 + '\0').toCharArray();
2022 	GUID guid = new GUID();
2023 	if (COM.IIDFromString(buffer, guid) == COM.S_OK) {
2024 		for (OleAutomation document : documents) {
2025 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYDOWN, domListener);
2026 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYPRESS, domListener);
2027 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONKEYUP, domListener);
2028 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEDOWN, domListener);
2029 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEUP, domListener);
2030 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEWHEEL, domListener);
2031 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONDBLCLICK, domListener);
2032 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEMOVE, domListener);
2033 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONDRAGSTART, domListener);
2034 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONDRAGEND, domListener);
2035 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOVER, domListener);
2036 			site.removeEventListener(document, guid, COM.DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOUT, domListener);
2037 		}
2038 	}
2039 }
2040 }
2041