blob: 0e5c23e595a0bb7fd3388d3ac2141f72b8e5b502 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2012 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.browser;
import java.net.*;
import java.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.C;
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 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 = 9000;
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 = new Runnable() {
public void run() {
if (OS.IsPPC) return;
OS.InternetSetOption (0, OS.INTERNET_OPTION_END_BROWSER_SESSION, 0, 0);
}
};
NativeGetCookie = new Runnable () {
public void run () {
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 = new Runnable () {
public void run () {
if (OS.IsPPC) return;
TCHAR url = new TCHAR (0, CookieUrl, true);
TCHAR value = new TCHAR (0, CookieValue, true);
CookieResult = OS.InternetSetCookie (url, null, value);
}
};
/*
* Registry entry HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Version indicates
* which version of IE is installed. 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, "Version", true); //$NON-NLS-1$
int 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;
}
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) {
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);
if (OS.RegQueryValueEx(key[0], lpValueName, 0, null, (int[])null, null) == 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, new Listener() {
public void handleEvent(Event event) {
long /*int*/[] key = new long /*int*/[1];
if (OS.RegOpenKeyEx(OS.HKEY_CURRENT_USER, subkey, 0, OS.KEY_WRITE, key) == 0) {
OS.RegDeleteValue(key[0], lpValueName);
}
}
});
}
}
OS.RegCloseKey(key[0]);
}
}
}
site.doVerb(OLE.OLEIVERB_INPLACEACTIVATE);
auto = new OleAutomation(site);
domListener = new OleListener() {
public void handleEvent (OleEvent e) {
handleDOMEvent(e);
}
};
Listener listener = new Listener() {
public void handleEvent(Event 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;
Enumeration elements = functions.elements ();
while (elements.hasMoreElements ()) {
((BrowserFunction)elements.nextElement ()).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 = new OleListener() {
public void handleEvent(OleEvent 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 varResult = event.arguments[1];
String url = varResult.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(url) || (uncRedirect.startsWith(url) && uncRedirect.indexOf('\\', 2) == url.length())) {
Variant cancel = event.arguments[6];
if (cancel != null) {
long /*int*/ pCancel = cancel.getByRef();
COM.MoveMemory(pCancel, new short[] {COM.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 (url.indexOf(":/") == -1 && url.indexOf(":\\") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
TCHAR filePath = new TCHAR(0, url, true);
TCHAR urlResult = new TCHAR(0, OS.INTERNET_MAX_URL_LENGTH);
int[] size = new int[] {urlResult.length()};
if (!OS.IsWinCE && OS.UrlCreateFromPath(filePath, urlResult, size, 0) == COM.S_OK) {
url = urlResult.toString(0, size[0]);
} else {
url = PROTOCOL_FILE + url.replace('\\', '/');
}
}
/* Disallow local file system accesses if the browser content is untrusted */
if (url.startsWith(PROTOCOL_FILE) && _getUrl().startsWith(ABOUT_BLANK) && untrustedText) {
Variant cancel = event.arguments[6];
if (cancel != null) {
long /*int*/ pCancel = cancel.getByRef();
COM.MoveMemory(pCancel, new short[] {COM.VARIANT_TRUE}, 2);
}
break;
}
LocationEvent newEvent = new LocationEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.widget = browser;
newEvent.location = url;
newEvent.doit = true;
for (int i = 0; i < locationListeners.length; i++) {
locationListeners[i].changing(newEvent);
}
boolean doit = newEvent.doit && !browser.isDisposed();
Variant cancel = event.arguments[6];
if (cancel != null) {
long /*int*/ pCancel = cancel.getByRef();
COM.MoveMemory(pCancel, new short[] {doit ? COM.VARIANT_FALSE : COM.VARIANT_TRUE}, 2);
}
if (doit) {
varResult = event.arguments[0];
IDispatch dispatch = varResult.getDispatch();
Variant variant = new Variant(auto); /* does not need to be disposed */
IDispatch top = variant.getDispatch();
if (top.getAddress() == dispatch.getAddress()) {
isAboutBlank = url.startsWith(ABOUT_BLANK);
}
}
break;
}
case CommandStateChange: {
boolean enabled = false;
Variant varResult = event.arguments[0];
int command = varResult.getInt();
varResult = event.arguments[1];
enabled = varResult.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 varResult = event.arguments[0];
IDispatch dispatch = varResult.getDispatch();
varResult = event.arguments[1];
String url = varResult.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 (url.indexOf(":/") == -1 && url.indexOf(":\\") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
TCHAR filePath = new TCHAR(0, url, true);
TCHAR urlResult = new TCHAR(0, OS.INTERNET_MAX_URL_LENGTH);
int[] size = new int[] {urlResult.length()};
if (!OS.IsWinCE && OS.UrlCreateFromPath(filePath, urlResult, size, 0) == COM.S_OK) {
url = urlResult.toString(0, size[0]);
} else {
url = PROTOCOL_FILE + url.replace('\\', '/');
}
}
if (html != null && url.equals(ABOUT_BLANK)) {
if (delaySetText) {
delaySetText = false;
browser.getDisplay().asyncExec(new Runnable() {
public void run() {
if (browser.isDisposed() || html == null) return;
setHTML(html);
html = null;
}
});
} else {
setHTML(html);
html = null;
}
} else {
Variant variant = new Variant(auto); /* does not need to be disposed */
IDispatch top = variant.getDispatch();
LocationEvent locationEvent = new LocationEvent(browser);
locationEvent.display = browser.getDisplay();
locationEvent.widget = browser;
locationEvent.location = url;
locationEvent.top = top.getAddress() == dispatch.getAddress();
for (int i = 0; i < locationListeners.length; i++) {
locationListeners[i].changed(locationEvent);
}
if (browser.isDisposed()) return;
/*
* 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 && dispatch.getAddress() == globalDispatch) {
/* final document complete */
globalDispatch = 0;
/* re-install registered functions iff needed */
IE ie = (IE)browser.webBrowser;
if (ie.installFunctionsOnDocumentComplete) {
ie.installFunctionsOnDocumentComplete = false;
Enumeration elements = functions.elements ();
while (elements.hasMoreElements ()) {
BrowserFunction function = (BrowserFunction)elements.nextElement ();
execute (function.functionString);
}
}
ProgressEvent progressEvent = new ProgressEvent(browser);
progressEvent.display = browser.getDisplay();
progressEvent.widget = browser;
for (int i = 0; i < progressListeners.length; i++) {
progressListeners[i].completed(progressEvent);
}
}
}
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.
*/
Enumeration elements = functions.elements ();
while (elements.hasMoreElements ()) {
BrowserFunction function = (BrowserFunction)elements.nextElement ();
execute (function.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 progressEvent = new ProgressEvent(browser);
progressEvent.display = browser.getDisplay();
progressEvent.widget = browser;
for (int i = 0; i < progressListeners.length; i++) {
progressListeners[i].completed(progressEvent);
}
break;
}
case NavigateComplete2: {
jsEnabled = jsEnabledOnNextPage;
Variant varResult = event.arguments[1];
String url = varResult.getString();
if (!performingInitialNavigate) {
varResult = event.arguments[0];
IDispatch dispatch = varResult.getDispatch();
Variant variant = new Variant(auto); /* does not need to be disposed */
IDispatch top = variant.getDispatch();
if (top.getAddress() == dispatch.getAddress()) {
isAboutBlank = url.startsWith(ABOUT_BLANK);
lastNavigateURL = url;
}
}
/*
* 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(url).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(url)) {
/* full UNC path has been successfully navigated */
uncRedirect = null;
break;
}
if (uncRedirect.startsWith(url)) {
/*
* UNC first segment has been successfully navigated,
* now redirect to the full UNC path.
*/
navigate(uncRedirect, null, null, true);
break;
}
uncRedirect = null;
}
varResult = event.arguments[0];
IDispatch dispatch = varResult.getDispatch();
if (globalDispatch == 0) globalDispatch = dispatch.getAddress();
OleAutomation webBrowser = varResult.getAutomation();
Variant variant = new Variant(auto); /* does not need to be disposed */
IDispatch top = variant.getDispatch();
boolean isTop = top.getAddress() == dispatch.getAddress();
if (isTop) {
/* unhook DOM listeners and unref the last document(s) */
unhookDOMListeners(documents);
for (int i = 0; i < documents.length; i++) {
documents[i].dispose();
}
documents = new OleAutomation[0];
/* re-install registered functions */
Enumeration elements = functions.elements ();
while (elements.hasMoreElements ()) {
BrowserFunction function = (BrowserFunction)elements.nextElement ();
execute (function.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 varResult = event.arguments[1];
final String url = varResult.getString();
if (url.startsWith("\\\\")) { //$NON-NLS-1$
varResult = event.arguments[3];
int statusCode = varResult.getInt();
if (statusCode == INET_E_RESOURCE_NOT_FOUND) {
int index = url.indexOf('\\', 2);
if (index != -1) {
final String host = url.substring(0, index);
Variant cancel = event.arguments[4];
if (cancel != null) {
long /*int*/ pCancel = cancel.getByRef();
COM.MoveMemory(pCancel, new short[] {COM.VARIANT_TRUE}, 2);
}
browser.getDisplay().asyncExec(new Runnable() {
public void run() {
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 (url.endsWith("\\")) { //$NON-NLS-1$
uncRedirect = url.substring(0, url.length() - 1);
} else {
uncRedirect = url;
}
navigate(host, null, null, true);
}
});
}
}
}
break;
}
case NewWindow2: {
Variant cancel = event.arguments[1];
long /*int*/ pCancel = cancel.getByRef();
WindowEvent newEvent = new WindowEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.widget = browser;
newEvent.required = false;
for (int i = 0; i < openWindowListeners.length; i++) {
openWindowListeners[i].open(newEvent);
}
IE browser = null;
if (newEvent.browser != null && newEvent.browser.webBrowser instanceof IE) {
browser = (IE)newEvent.browser.webBrowser;
}
boolean doit = browser != null && !browser.browser.isDisposed();
if (doit) {
/*
* 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 variant = new Variant(browser.auto); /* does not need to be disposed */
IDispatch iDispatch = variant.getDispatch();
Variant ppDisp = event.arguments[0];
long /*int*/ byref = ppDisp.getByRef();
if (byref != 0) COM.MoveMemory(byref, new long /*int*/[] {iDispatch.getAddress()}, OS.PTR_SIZEOF);
}
if (newEvent.required) {
COM.MoveMemory(pCancel, new short[]{doit ? COM.VARIANT_FALSE : COM.VARIANT_TRUE}, 2);
}
break;
}
case OnMenuBar: {
Variant arg0 = event.arguments[0];
menuBar = arg0.getBoolean();
break;
}
case OnStatusBar: {
Variant arg0 = event.arguments[0];
statusBar = arg0.getBoolean();
break;
}
case OnToolBar: {
Variant arg0 = event.arguments[0];
toolBar = arg0.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 arg1 = event.arguments[0];
boolean visible = arg1.getBoolean();
WindowEvent newEvent = new WindowEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.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[] rgdispid = auto.getIDsOfNames(new String[] { "AddressBar" }); //$NON-NLS-1$
Variant pVarResult = auto.getProperty(rgdispid[0]);
if (pVarResult != null) {
if (pVarResult.getType () == OLE.VT_BOOL) {
addressBar = pVarResult.getBoolean ();
}
pVarResult.dispose ();
}
}
newEvent.addressBar = addressBar;
newEvent.menuBar = menuBar;
newEvent.statusBar = statusBar;
newEvent.toolBar = toolBar;
newEvent.location = location;
newEvent.size = size;
for (int i = 0; i < visibilityWindowListeners.length; i++) {
visibilityWindowListeners[i].show(newEvent);
}
location = null;
size = null;
} else {
for (int i = 0; i < visibilityWindowListeners.length; i++) {
visibilityWindowListeners[i].hide(newEvent);
}
}
break;
}
case ProgressChange: {
/* don't send client events if the initial navigate to about:blank has not completed */
if (performingInitialNavigate) break;
Variant arg1 = event.arguments[0];
int nProgress = arg1.getType() != OLE.VT_I4 ? 0 : arg1.getInt(); // may be -1
Variant arg2 = event.arguments[1];
int nProgressMax = arg2.getType() != OLE.VT_I4 ? 0 : arg2.getInt();
ProgressEvent newEvent = new ProgressEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.widget = browser;
newEvent.current = nProgress;
newEvent.total = nProgressMax;
if (nProgress != -1) {
for (int i = 0; i < progressListeners.length; i++) {
progressListeners[i].changed(newEvent);
}
}
break;
}
case StatusTextChange: {
/* don't send client events if the initial navigate to about:blank has not completed */
if (performingInitialNavigate) break;
Variant arg1 = event.arguments[0];
if (arg1.getType() == OLE.VT_BSTR) {
String text = arg1.getString();
StatusTextEvent newEvent = new StatusTextEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.widget = browser;
newEvent.text = text;
for (int i = 0; i < statusTextListeners.length; i++) {
statusTextListeners[i].changed(newEvent);
}
}
break;
}
case TitleChange: {
/* don't send client events if the initial navigate to about:blank has not completed */
if (performingInitialNavigate) break;
Variant arg1 = event.arguments[0];
if (arg1.getType() == OLE.VT_BSTR) {
String title = arg1.getString();
TitleEvent newEvent = new TitleEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.widget = browser;
newEvent.title = title;
for (int i = 0; i < titleListeners.length; i++) {
titleListeners[i].changed(newEvent);
}
}
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(new Runnable() {
public void run() {
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 cancel = event.arguments[1];
long /*int*/ pCancel = cancel.getByRef();
Variant arg1 = event.arguments[0];
boolean isChildWindow = arg1.getBoolean();
COM.MoveMemory(pCancel, new short[]{isChildWindow ? COM.VARIANT_FALSE : COM.VARIANT_TRUE}, 2);
break;
}
case WindowSetHeight: {
if (size == null) size = new Point(0, 0);
Variant arg1 = event.arguments[0];
size.y = arg1.getInt();
break;
}
case WindowSetLeft: {
if (location == null) location = new Point(0, 0);
Variant arg1 = event.arguments[0];
location.x = arg1.getInt();
break;
}
case WindowSetTop: {
if (location == null) location = new Point(0, 0);
Variant arg1 = event.arguments[0];
location.y = arg1.getInt();
break;
}
case WindowSetWidth: {
if (size == null) size = new Point(0, 0);
Variant arg1 = event.arguments[0];
size.x = arg1.getInt();
break;
}
}
}
/*
* Dispose all arguments passed in the OleEvent. This must be
* done to properly release any IDispatch reference that was
* automatically addRef'ed when constructing the OleEvent.
*/
Variant[] arguments = event.arguments;
for (int i = 0; i < arguments.length; i++) arguments[i].dispose();
}
};
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();
}
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;
}
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));
}
public boolean execute(String script) {
/* 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);
OleAutomation ihtmlWindow2 = pVarResult.getAutomation();
pVarResult.dispose();
document.dispose();
rgdispid = ihtmlWindow2.getIDsOfNames(new String[] { "execScript", "code" }); //$NON-NLS-1$ //$NON-NLS-2$
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;
}
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;
}
public String getBrowserType () {
return "ie"; //$NON-NLS-1$
}
String getDeleteFunctionString (String functionName) {
return "window." + functionName + "=undefined"; //$NON-NLS-1$ //$NON-NLS-2$
}
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) {
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;
}
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;
}
public boolean isBackEnabled() {
return back;
}
public boolean isForwardEnabled() {
return forward;
}
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;
}
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);
}
}
}
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;
}
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);
}
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]);
}
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 = new Point(screenX, screenY);
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) 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);
}
}
}
}