diff options
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT Browser')
28 files changed, 9879 insertions, 0 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/carbon/org/eclipse/swt/browser/Safari.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/carbon/org/eclipse/swt/browser/Safari.java new file mode 100755 index 0000000000..ec720be249 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/carbon/org/eclipse/swt/browser/Safari.java @@ -0,0 +1,2082 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.browser; + +import java.util.Enumeration; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.carbon.*; +import org.eclipse.swt.internal.cocoa.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +class Safari extends WebBrowser { + + /* Objective-C WebView delegate */ + int delegate; + + /* Carbon HIView handle */ + int webViewHandle, webView; + int windowBoundsHandler; + int preferences; + + boolean changingLocation, hasNewFocusElement; + String lastHoveredLinkURL, lastNavigateURL; + String html; + int identifier; + int resourceCount; + int lastMouseMoveX, lastMouseMoveY; + String url = ""; //$NON-NLS-1$ + Point location; + Point size; + boolean statusBar = true, toolBar = true, ignoreDispose; + //TEMPORARY CODE +// boolean doit; + + static boolean Initialized; + static Callback Callback3, Callback7; + + static final int MIN_SIZE = 16; + static final int MAX_PROGRESS = 100; + static final String WebElementLinkURLKey = "WebElementLinkURL"; //$NON-NLS-1$ + static final String AGENT_STRING = "Safari/412.0"; /* Safari version on OSX 10.4 initial release */ //$NON-NLS-1$ + static final String URI_FROMMEMORY = "file:///"; //$NON-NLS-1$ + static final String PROTOCOL_FILE = "file://"; //$NON-NLS-1$ + static final String PROTOCOL_HTTP = "http://"; //$NON-NLS-1$ + static final String URI_APPLEWEBDATA = "applewebdata://"; //$NON-NLS-1$ + static final String ABOUT_BLANK = "about:blank"; //$NON-NLS-1$ + static final String HEADER_SETCOOKIE = "Set-Cookie"; //$NON-NLS-1$ + static final String ADD_WIDGET_KEY = "org.eclipse.swt.internal.addWidget"; //$NON-NLS-1$ + static final String BROWSER_WINDOW = "org.eclipse.swt.browser.Browser.Window"; //$NON-NLS-1$ + static final String SAFARI_EVENTS_FIX_KEY = "org.eclipse.swt.internal.safariEventsFix"; //$NON-NLS-1$ + + /* event strings */ + static final String DOMEVENT_KEYUP = "keyup"; //$NON-NLS-1$ + static final String DOMEVENT_KEYDOWN = "keydown"; //$NON-NLS-1$ + static final String DOMEVENT_MOUSEDOWN = "mousedown"; //$NON-NLS-1$ + static final String DOMEVENT_MOUSEUP = "mouseup"; //$NON-NLS-1$ + static final String DOMEVENT_MOUSEMOVE = "mousemove"; //$NON-NLS-1$ + static final String DOMEVENT_MOUSEWHEEL = "mousewheel"; //$NON-NLS-1$ + static final String DOMEVENT_FOCUSIN = "DOMFocusIn"; //$NON-NLS-1$ + static final String DOMEVENT_FOCUSOUT = "DOMFocusOut"; //$NON-NLS-1$ + + static { + NativeClearSessions = new Runnable() { + public void run() { + int storage = Cocoa.objc_msgSend (Cocoa.C_NSHTTPCookieStorage, Cocoa.S_sharedHTTPCookieStorage); + int cookies = Cocoa.objc_msgSend (storage, Cocoa.S_cookies); + int count = Cocoa.objc_msgSend (cookies, Cocoa.S_count); + for (int i = 0; i < count; i++) { + int cookie = Cocoa.objc_msgSend (cookies, Cocoa.S_objectAtIndex, i); + boolean isSession = Cocoa.objc_msgSend (cookie, Cocoa.S_isSessionOnly) != 0; + if (isSession) { + Cocoa.objc_msgSend (storage, Cocoa.S_deleteCookie, cookie); + } + } + } + }; + + NativeGetCookie = new Runnable () { + public void run () { + int storage = Cocoa.objc_msgSend (Cocoa.C_NSHTTPCookieStorage, Cocoa.S_sharedHTTPCookieStorage); + int length = CookieUrl.length (); + char[] buffer = new char[length]; + CookieUrl.getChars (0, length, buffer, 0); + int urlString = OS.CFStringCreateWithCharacters (0, buffer, length); + int url = Cocoa.objc_msgSend (Cocoa.C_NSURL, Cocoa.S_URLWithString, urlString); + OS.CFRelease (urlString); + int cookies = Cocoa.objc_msgSend (storage, Cocoa.S_cookiesForURL, url); + int count = Cocoa.objc_msgSend (cookies, Cocoa.S_count); + if (count == 0) return; + + length = CookieName.length (); + buffer = new char[length]; + CookieName.getChars (0, length, buffer, 0); + int name = OS.CFStringCreateWithCharacters (0, buffer, length); + for (int i = 0; i < count; i++) { + int current = Cocoa.objc_msgSend (cookies, Cocoa.S_objectAtIndex, i); + int currentName = Cocoa.objc_msgSend (current, Cocoa.S_name); + if (Cocoa.objc_msgSend (currentName, Cocoa.S_compare, name) == Cocoa.NSOrderedSame) { + int value = Cocoa.objc_msgSend (current, Cocoa.S_value); + length = OS.CFStringGetLength (value); + buffer = new char[length]; + CFRange range = new CFRange (); + range.length = length; + OS.CFStringGetCharacters (value, range, buffer); + CookieValue = new String (buffer); + OS.CFRelease (name); + return; + } + } + OS.CFRelease (name); + } + }; + + NativeSetCookie = new Runnable () { + public void run () { + int length = CookieUrl.length (); + char[] buffer = new char[length]; + CookieUrl.getChars (0, length, buffer, 0); + int urlString = OS.CFStringCreateWithCharacters (0, buffer, length); + int url = Cocoa.objc_msgSend (Cocoa.C_NSURL, Cocoa.S_URLWithString, urlString); + OS.CFRelease (urlString); + + length = CookieValue.length (); + buffer = new char[length]; + CookieValue.getChars (0, length, buffer, 0); + int value = OS.CFStringCreateWithCharacters (0, buffer, length); + length = HEADER_SETCOOKIE.length (); + buffer = new char[length]; + HEADER_SETCOOKIE.getChars (0, length, buffer, 0); + int key = OS.CFStringCreateWithCharacters (0, buffer, length); + int headers = Cocoa.objc_msgSend (Cocoa.C_NSMutableDictionary, Cocoa.S_dictionaryWithCapacity, 1); + Cocoa.objc_msgSend (headers, Cocoa.S_setValue, value, key); + OS.CFRelease (key); + OS.CFRelease (value); + + int cookies = Cocoa.objc_msgSend (Cocoa.C_NSHTTPCookie, Cocoa.S_cookiesWithResponseHeaderFields, headers, url); + if (Cocoa.objc_msgSend (cookies, Cocoa.S_count) == 0) return; + int cookie = Cocoa.objc_msgSend (cookies, Cocoa.S_objectAtIndex, 0); + int storage = Cocoa.objc_msgSend (Cocoa.C_NSHTTPCookieStorage, Cocoa.S_sharedHTTPCookieStorage); + Cocoa.objc_msgSend (storage, Cocoa.S_setCookie, cookie); + CookieResult = true; + } + }; + } + +public void create (Composite parent, int style) { + /* + * Note. Loading the webkit bundle on Jaguar causes a crash. + * The workaround is to detect any OS prior to 10.30 and fail + * without crashing. + */ + if (OS.VERSION < 0x1030) { + browser.dispose(); + SWT.error(SWT.ERROR_NO_HANDLES); + } + + /* + * Bug in Safari on OSX 10.5 (Leopard) only. VoiceOver no longer follows focus when + * HIWebViewCreate is used to create a WebView. The VoiceOver cursor (activated by + * Control+Alt+arrows) continues to work, but keyboard focus is not tracked. The fix + * is to create the WebView with HICocoaViewCreate (api introduced in OSX 10.5) when + * running on OSX 10.5. + */ + int outControl[] = new int[1]; + if (OS.VERSION >= 0x1050) { + webView = Cocoa.objc_msgSend(Cocoa.objc_msgSend(Cocoa.C_WebView, Cocoa.S_alloc), Cocoa.S_initWithFrame_frameName_groupName, new NSRect(), 0, 0); + if (webView != 0) { + Cocoa.HICocoaViewCreate(webView, 0, outControl); + webViewHandle = outControl[0]; + Cocoa.objc_msgSend(webView, Cocoa.S_release); + } + } else { + Cocoa.HIWebViewCreate(outControl); + webViewHandle = outControl[0]; + if (webViewHandle != 0) { + webView = Cocoa.HIWebViewGetWebView(webViewHandle); + } + } + if (webViewHandle == 0) { + browser.dispose(); + SWT.error(SWT.ERROR_NO_HANDLES); + } + + Display display = browser.getDisplay(); + display.setData(ADD_WIDGET_KEY, new Object[] {new Integer(webViewHandle), browser}); + + /* + * WebKit's DOM listener api became functional in OSX 10.4. If OSX 10.4 or + * later is detected then override the default event mechanism to not send key + * events and some mouse events so that the browser can send them by listening + * to the DOM instead. + */ + if (!(OS.VERSION < 0x1040)) { + browser.setData(SAFARI_EVENTS_FIX_KEY); + } + + /* + * Bug in Safari. For some reason, every application must contain + * a visible window that has never had a WebView or mouse move events + * are not delivered. This seems to happen after a browser has been + * either hidden or disposed in any window. The fix is to create a + * single transparent overlay window that is disposed when the display + * is disposed. + */ + if (display.getData(BROWSER_WINDOW) == null) { + Rect bounds = new Rect (); + OS.SetRect (bounds, (short) 0, (short) 0, (short) 1, (short) 1); + final int[] outWindow = new int[1]; + OS.CreateNewWindow(OS.kOverlayWindowClass, 0, bounds, outWindow); + OS.ShowWindow(outWindow[0]); + OS.HIObjectSetAccessibilityIgnored (outWindow[0], true); + display.disposeExec(new Runnable() { + public void run() { + if (outWindow[0] != 0) { + OS.DisposeWindow(outWindow[0]); + } + outWindow[0] = 0; + } + }); + display.setData(BROWSER_WINDOW, outWindow); + } + + /* + * Bug in Safari. The WebView does not draw properly if it is embedded as + * sub view of the browser handle. The fix is to add the web view to the + * window root control and resize it on top of the browser handle. + * + * Note that when the browser is reparented, the web view has to + * be reparented by hand by hooking kEventControlOwningWindowChanged. + */ + int window = OS.GetControlOwner(browser.handle); + int[] contentView = new int[1]; + OS.HIViewFindByID(OS.HIViewGetRoot(window), OS.kHIViewWindowContentID(), contentView); + OS.HIViewAddSubview(contentView[0], webViewHandle); + OS.HIViewChangeFeatures(webViewHandle, OS.kHIViewFeatureIsOpaque, 0); + + /* + * Bug in Safari. The WebView does not receive mouse and key events when it is added + * to a visible top window. It is assumed that Safari hooks its own event listener + * when the top window emits the kEventWindowShown event. The workaround is to send a + * fake kEventWindowShown event to the top window after the WebView has been added + * to the HIView (after the top window is visible) to give Safari a chance to hook + * events. + */ + OS.HIViewSetVisible(webViewHandle, true); + if (browser.getShell().isVisible()) { + int[] showEvent = new int[1]; + OS.CreateEvent(0, OS.kEventClassWindow, OS.kEventWindowShown, 0.0, OS.kEventAttributeUserEvent, showEvent); + OS.SetEventParameter(showEvent[0], OS.kEventParamDirectObject, OS.typeWindowRef, 4, new int[] {OS.GetControlOwner(browser.handle)}); + OS.SendEventToEventTarget(showEvent[0], OS.GetWindowEventTarget(window)); + if (showEvent[0] != 0) OS.ReleaseEvent(showEvent[0]); + } + + /* + * This code is intentionally commented. Setting a group name is the right thing + * to do in order to avoid multiple open window requests. For some reason, Safari + * crashes when requested to reopen the same window if that window was previously + * closed. This may be because that window was not correctly closed. + */ +// String groupName = "MyDocument"; //$NON-NLS-1$ +// int length = groupName.length(); +// char[] buffer = new char[length]; +// groupName.getChars(0, length, buffer, 0); +// int groupNameString = OS.CFStringCreateWithCharacters(0, buffer, length); +// // [webView setGroupName:@"MyDocument"]; +// WebKit.objc_msgSend(webView, WebKit.S_setGroupName, groupNameString); +// OS.CFRelease(groupNameString); + + final int notificationCenter = Cocoa.objc_msgSend(Cocoa.C_NSNotificationCenter, Cocoa.S_defaultCenter); + + 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; + + OS.RemoveEventHandler(windowBoundsHandler); + windowBoundsHandler = 0; + + e.display.setData(ADD_WIDGET_KEY, new Object[] {new Integer(webViewHandle), null}); + + Cocoa.objc_msgSend(webView, Cocoa.S_setFrameLoadDelegate, 0); + Cocoa.objc_msgSend(webView, Cocoa.S_setResourceLoadDelegate, 0); + Cocoa.objc_msgSend(webView, Cocoa.S_setUIDelegate, 0); + Cocoa.objc_msgSend(webView, Cocoa.S_setPolicyDelegate, 0); + Cocoa.objc_msgSend(webView, Cocoa.S_setDownloadDelegate, 0); + Cocoa.objc_msgSend(notificationCenter, Cocoa.S_removeObserver, delegate); + + Cocoa.objc_msgSend(delegate, Cocoa.S_release); + OS.DisposeControl(webViewHandle); + webView = webViewHandle = 0; + html = null; + lastHoveredLinkURL = lastNavigateURL = null; + + Enumeration elements = functions.elements (); + while (elements.hasMoreElements ()) { + ((BrowserFunction)elements.nextElement ()).dispose (false); + } + functions = null; + + if (preferences != 0) { + Cocoa.objc_msgSend (preferences, Cocoa.S_release); + } + preferences = 0; + break; + } + case SWT.FocusIn: { + hasNewFocusElement = true; + OS.SetKeyboardFocus(OS.GetControlOwner(browser.handle), webViewHandle, (short)-1); + break; + } + } + } + }; + browser.addListener(SWT.Dispose, listener); + browser.addListener(SWT.FocusIn, listener); + browser.addListener(SWT.KeyDown, listener); /* needed to make browser traversable */ + + if (Callback3 == null) Callback3 = new Callback(this.getClass(), "eventProc3", 3); //$NON-NLS-1$ + int callback3Address = Callback3.getAddress(); + if (callback3Address == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + + int[] mask = new int[] { + OS.kEventClassKeyboard, OS.kEventRawKeyDown, + OS.kEventClassControl, OS.kEventControlDraw, + OS.kEventClassControl, OS.kEventControlGetClickActivation, + OS.kEventClassControl, OS.kEventControlSetCursor, + OS.kEventClassTextInput, OS.kEventTextInputUnicodeForKeyEvent, + }; + OS.InstallEventHandler(OS.GetControlEventTarget(webViewHandle), callback3Address, mask.length / 2, mask, webViewHandle, null); + int[] mask1 = new int[] { + OS.kEventClassControl, OS.kEventControlBoundsChanged, + OS.kEventClassControl, OS.kEventControlVisibilityChanged, + OS.kEventClassControl, OS.kEventControlOwningWindowChanged, + }; + OS.InstallEventHandler(OS.GetControlEventTarget(browser.handle), callback3Address, mask1.length / 2, mask1, browser.handle, null); + int[] mask2 = new int[] { + OS.kEventClassWindow, OS.kEventWindowBoundsChanged, + }; + int[] outRef = new int[1]; + OS.InstallEventHandler(OS.GetWindowEventTarget(window), callback3Address, mask2.length / 2, mask2, browser.handle, outRef); + windowBoundsHandler = outRef[0]; + + if (Callback7 == null) Callback7 = new Callback(this.getClass(), "eventProc7", 7); //$NON-NLS-1$ + int callback7Address = Callback7.getAddress(); + if (callback7Address == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + + // delegate = [[WebResourceLoadDelegate alloc] init eventProc]; + delegate = Cocoa.objc_msgSend(Cocoa.C_WebKitDelegate, Cocoa.S_alloc); + delegate = Cocoa.objc_msgSend(delegate, Cocoa.S_initWithProc, callback7Address, webViewHandle); + + // [webView setFrameLoadDelegate:delegate]; + Cocoa.objc_msgSend(webView, Cocoa.S_setFrameLoadDelegate, delegate); + + // [webView setResourceLoadDelegate:delegate]; + Cocoa.objc_msgSend(webView, Cocoa.S_setResourceLoadDelegate, delegate); + + // [webView setUIDelegate:delegate]; + Cocoa.objc_msgSend(webView, Cocoa.S_setUIDelegate, delegate); + + /* register delegate for all notifications sent out from webview */ + Cocoa.objc_msgSend(notificationCenter, Cocoa.S_addObserver_selector_name_object, delegate, Cocoa.S_handleNotification, 0, webView); + + // [webView setPolicyDelegate:delegate]; + Cocoa.objc_msgSend(webView, Cocoa.S_setPolicyDelegate, delegate); + + // [webView setDownloadDelegate:delegate]; + Cocoa.objc_msgSend(webView, Cocoa.S_setDownloadDelegate, delegate); + + // [webView setApplicationNameForUserAgent:applicationName]; + int length = AGENT_STRING.length(); + char[] chars = new char[length]; + AGENT_STRING.getChars(0, length, chars, 0); + int sHandle = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(webView, Cocoa.S_setApplicationNameForUserAgent, sHandle); + OS.CFRelease(sHandle); + + if (OS.VERSION < 0x1050 && display.getActiveShell() == browser.getShell()) { + Cocoa.objc_msgSend(Cocoa.objc_msgSend(webView, Cocoa.S_window), Cocoa.S_makeKeyWindow); + } + + if (!Initialized) { + Initialized = true; + /* disable applets */ + int preferences = Cocoa.objc_msgSend(Cocoa.C_WebPreferences, Cocoa.S_standardPreferences); + Cocoa.objc_msgSend(preferences, Cocoa.S_setJavaEnabled, 0); + } +} + +static int eventProc3(int nextHandler, int theEvent, int userData) { + Widget widget = Display.getCurrent().findWidget(userData); + if (widget instanceof Browser) { + return ((Safari)((Browser)widget).webBrowser).handleCallback(nextHandler, theEvent); + } + return OS.eventNotHandledErr; +} + +static int eventProc7(int webview, int userData, int selector, int arg0, int arg1, int arg2, int arg3) { + Widget widget = Display.getCurrent().findWidget(userData); + if (widget instanceof Browser) { + return ((Safari)((Browser)widget).webBrowser).handleCallback(selector, arg0, arg1, arg2, arg3); + } + return 0; +} + +static String getString (int ptr) { + int length = OS.CFStringGetLength (ptr); + char[] buffer = new char[length]; + CFRange range = new CFRange (); + range.length = length; + OS.CFStringGetCharacters (ptr, range, buffer); + return new String (buffer); +} + +public boolean back() { + html = null; + return Cocoa.objc_msgSend(webView, Cocoa.S_goBack) != 0; +} + +public boolean execute(String script) { + int length = script.length(); + char[] buffer = new char[length]; + script.getChars(0, length, buffer, 0); + int string = OS.CFStringCreateWithCharacters(0, buffer, length); + + int value = Cocoa.objc_msgSend(webView, Cocoa.S_stringByEvaluatingJavaScriptFromString, string); + OS.CFRelease(string); + return value != 0; +} + +public boolean forward() { + html = null; + return Cocoa.objc_msgSend(webView, Cocoa.S_goForward) != 0; +} + +public String getBrowserType () { + return "safari"; //$NON-NLS-1$ +} + +public String getText() { + int mainFrame = Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); + int dataSource = Cocoa.objc_msgSend(mainFrame, Cocoa.S_dataSource); + if (dataSource == 0) return ""; //$NON-NLS-1$ + int representation = Cocoa.objc_msgSend(dataSource, Cocoa.S_representation); + if (representation == 0) return ""; //$NON-NLS-1$ + int source = Cocoa.objc_msgSend(representation, Cocoa.S_documentSource); + if (source == 0) return ""; //$NON-NLS-1$ + int length = OS.CFStringGetLength(source); + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(source, range, buffer); + return new String(buffer); +} + +public String getUrl() { + return url; +} + +int handleCallback(int nextHandler, int theEvent) { + int eventKind = OS.GetEventKind(theEvent); + switch (OS.GetEventClass(theEvent)) { + case OS.kEventClassControl: + switch (eventKind) { + case OS.kEventControlGetClickActivation: { + OS.SetEventParameter (theEvent, OS.kEventParamClickActivation, OS.typeClickActivationResult, 4, new int [] {OS.kActivateAndHandleClick}); + return OS.noErr; + } + case OS.kEventControlSetCursor: { + return OS.noErr; + } + case OS.kEventControlDraw: { + /* + * Bug on Safari. The web view cannot be obscured by other views above it. + * This problem is specified in the apple documentation for HiWebViewCreate. + * The workaround is to don't draw the web view when it is not visible. + */ + if (!browser.isVisible ()) return OS.noErr; + break; + } + case OS.kEventControlOwningWindowChanged: { + /* Reparent the web view handler */ + int window = OS.GetControlOwner(browser.handle); + int[] contentView = new int[1]; + OS.HIViewFindByID(OS.HIViewGetRoot(window), OS.kHIViewWindowContentID(), contentView); + OS.HIViewAddSubview(contentView[0], webViewHandle); + + /* Reset the kEventWindowBoundsChanged handler */ + OS.RemoveEventHandler(windowBoundsHandler); + int[] mask2 = new int[] { + OS.kEventClassWindow, OS.kEventWindowBoundsChanged, + }; + int[] outRef = new int[1]; + OS.InstallEventHandler(OS.GetWindowEventTarget(window), Callback3.getAddress(), mask2.length / 2, mask2, browser.handle, outRef); + windowBoundsHandler = outRef[0]; + break; + } + case OS.kEventControlBoundsChanged: + case OS.kEventControlVisibilityChanged: { + /* + * Bug on Safari. The web view cannot be obscured by other views above it. + * This problem is specified in the apple documentation for HiWebViewCreate. + * The workaround is to hook kEventControlVisibilityChanged on the browser + * and move the browser out of the screen when hidden and restore its bounds + * when shown. + */ + CGRect bounds = new CGRect(); + if (!browser.isVisible()) { + bounds.x = bounds.y = -MIN_SIZE; + bounds.width = bounds.height = MIN_SIZE; + OS.HIViewSetFrame(webViewHandle, bounds); + } else { + OS.HIViewGetBounds(browser.handle, bounds); + int[] contentView = new int[1]; + OS.HIViewFindByID(OS.HIViewGetRoot(OS.GetControlOwner(browser.handle)), OS.kHIViewWindowContentID(), contentView); + OS.HIViewConvertRect(bounds, browser.handle, contentView[0]); + /* + * Bug in Safari. For some reason, the web view will display incorrectly or + * blank depending on its contents, if its size is set to a value smaller than + * MIN_SIZE. It will not display properly even after the size is made larger. + * The fix is to avoid setting sizes smaller than MIN_SIZE. + */ + if (bounds.width <= MIN_SIZE) bounds.width = MIN_SIZE; + if (bounds.height <= MIN_SIZE) bounds.height = MIN_SIZE; + OS.HIViewSetFrame(webViewHandle, bounds); + } + break; + } + } + case OS.kEventClassWindow: + switch (eventKind) { + case OS.kEventWindowBoundsChanged: + /* + * Bug on Safari. Resizing the height of a Shell containing a Browser at + * a fixed location causes the Browser to redraw at a wrong location. + * The web view is a HIView container that internally hosts + * a Cocoa NSView that uses a coordinates system with the origin at the + * bottom left corner of a window instead of the coordinates system used + * in Carbon that starts at the top left corner. The workaround is to + * reposition the web view every time the Shell of the Browser is resized. + * + * Note the size should not be updated if the browser is hidden. + */ + if (browser.isVisible()) { + CGRect oldBounds = new CGRect(); + OS.GetEventParameter (theEvent, OS.kEventParamOriginalBounds, OS.typeHIRect, null, CGRect.sizeof, null, oldBounds); + CGRect bounds = new CGRect(); + OS.GetEventParameter (theEvent, OS.kEventParamCurrentBounds, OS.typeHIRect, null, CGRect.sizeof, null, bounds); + if (oldBounds.height == bounds.height) break; + OS.HIViewGetBounds(browser.handle, bounds); + int[] contentView = new int[1]; + OS.HIViewFindByID(OS.HIViewGetRoot(OS.GetControlOwner(browser.handle)), OS.kHIViewWindowContentID(), contentView); + OS.HIViewConvertRect(bounds, browser.handle, contentView[0]); + /* + * Bug in Safari. For some reason, the web view will display incorrectly or + * blank depending on its contents, if its size is set to a value smaller than + * MIN_SIZE. It will not display properly even after the size is made larger. + * The fix is to avoid setting sizes smaller than MIN_SIZE. + */ + if (bounds.width <= MIN_SIZE) bounds.width = MIN_SIZE; + if (bounds.height <= MIN_SIZE) bounds.height = MIN_SIZE; + bounds.x++; + /* Note that the bounds needs to change */ + OS.HIViewSetFrame(webViewHandle, bounds); + bounds.x--; + OS.HIViewSetFrame(webViewHandle, bounds); + } + } + case OS.kEventClassKeyboard: + switch (eventKind) { + case OS.kEventRawKeyDown: { + /* + * Bug in Safari. The WebView blocks the propagation of certain Carbon events + * such as kEventRawKeyDown. On the Mac, Carbon events propagate from the + * Focus Target Handler to the Control Target Handler, Window Target and finally + * the Application Target Handler. It is assumed that WebView hooks its events + * on the Window Target and does not pass kEventRawKeyDown to the next handler. + * Since kEventRawKeyDown events never make it to the Application Target Handler, + * the Application Target Handler never gets to emit kEventTextInputUnicodeForKeyEvent + * used by SWT to send a SWT.KeyDown event. + * The workaround is to hook kEventRawKeyDown on the Control Target Handler which gets + * called before the WebView hook on the Window Target Handler. Then, forward this event + * directly to the Application Target Handler. Note that if in certain conditions Safari + * does not block the kEventRawKeyDown, then multiple kEventTextInputUnicodeForKeyEvent + * events might be generated as a result of this workaround. + */ + //TEMPORARY CODE +// doit = false; +// OS.SendEventToEventTarget(theEvent, OS.GetApplicationEventTarget()); +// if (!doit) return OS.noErr; + + int[] length = new int[1]; + int status = OS.GetEventParameter (theEvent, OS.kEventParamKeyUnicodes, OS.typeUnicodeText, null, 4, length, (char[])null); + if (status == OS.noErr && length[0] != 0) { + int[] modifiers = new int[1]; + OS.GetEventParameter (theEvent, OS.kEventParamKeyModifiers, OS.typeUInt32, null, 4, null, modifiers); + char[] chars = new char[1]; + OS.GetEventParameter (theEvent, OS.kEventParamKeyUnicodes, OS.typeUnicodeText, null, 2, null, chars); + if ((modifiers[0] & OS.cmdKey) != 0) { + switch (chars[0]) { + case 'v': { + Cocoa.objc_msgSend (webView, Cocoa.S_paste); + return OS.noErr; + } + case 'c': { + Cocoa.objc_msgSend (webView, Cocoa.S_copy); + return OS.noErr; + } + case 'x': { + Cocoa.objc_msgSend (webView, Cocoa.S_cut); + return OS.noErr; + } + } + } + } + /* + * Bug in Carbon. OSX crashes if a HICocoaView is disposed during a key event, + * presumably as a result of attempting to use it after its refcount has reached + * 0. The workaround is to temporarily add an extra ref to the view and its + * ancestor while the DOM listener is handling the event, in case the + * Browser gets disposed in a callback. + */ + int handle = webViewHandle, root = OS.HIViewGetSuperview (webViewHandle); + OS.CFRetain (handle); + OS.CFRetain (root); + int result = OS.CallNextEventHandler (nextHandler, theEvent); + OS.CFRelease (handle); + OS.CFRelease (root); + return result; + } + } + case OS.kEventClassTextInput: + switch (eventKind) { + case OS.kEventTextInputUnicodeForKeyEvent: { + /* + * Note. This event is received from the Window Target therefore after it was received + * by the Focus Target. The SWT.KeyDown event is sent by SWT on the Focus Target. If it + * is received here, then the SWT.KeyDown doit flag must have been left to the value + * true. For package visibility reasons we cannot access the doit flag directly. + * + * Sequence of events when the user presses a key down + * + * .Control Target - kEventRawKeyDown + * .forward to ApplicationEventTarget + * .Focus Target kEventTextInputUnicodeForKeyEvent - SWT emits SWT.KeyDown - + * blocks further propagation if doit false. Browser does not know directly about + * the doit flag value. + * .Window Target kEventTextInputUnicodeForKeyEvent - if received, Browser knows + * SWT.KeyDown is not blocked and event should be sent to WebKit + * Return from Control Target - kEventRawKeyDown: let the event go to WebKit if doit true + * (eventNotHandledErr) or stop it (noErr). + */ + //TEMPORARY CODE +// doit = true; + break; + } + } + } + return OS.eventNotHandledErr; +} + +/* Here we dispatch all WebView upcalls. */ +int handleCallback(int selector, int arg0, int arg1, int arg2, int arg3) { + int ret = 0; + // for meaning of selector see WebKitDelegate methods in webkit.c + switch (selector) { + case 1: didFailProvisionalLoadWithError(arg0, arg1); break; + case 2: didFinishLoadForFrame(arg0); break; + case 3: didReceiveTitle(arg0, arg1); break; + case 4: didStartProvisionalLoadForFrame(arg0); break; + case 5: didFinishLoadingFromDataSource(arg0, arg1); break; + case 6: didFailLoadingWithError(arg0, arg1, arg2); break; + case 7: ret = identifierForInitialRequest(arg0, arg1); break; + case 8: ret = willSendRequest(arg0, arg1, arg2, arg3); break; + case 9: handleNotification(arg0); break; + case 10: didCommitLoadForFrame(arg0); break; + case 11: ret = createWebViewWithRequest(arg0); break; + case 12: webViewShow(arg0); break; + case 13: setFrame(arg0); break; + case 14: webViewClose(); break; + case 15: ret = contextMenuItemsForElement(arg0, arg1); break; + case 16: setStatusBarVisible(arg0); break; + case 17: setResizable(arg0); break; + case 18: setToolbarsVisible(arg0); break; + case 19: decidePolicyForMIMEType(arg0, arg1, arg2, arg3); break; + case 20: decidePolicyForNavigationAction(arg0, arg1, arg2, arg3); break; + case 21: decidePolicyForNewWindowAction(arg0, arg1, arg2, arg3); break; + case 22: unableToImplementPolicyWithError(arg0, arg1); break; + case 23: setStatusText(arg0); break; + case 24: webViewFocus(); break; + case 25: webViewUnfocus(); break; + case 26: runJavaScriptAlertPanelWithMessage(arg0); break; + case 27: ret = runJavaScriptConfirmPanelWithMessage(arg0); break; + case 28: runOpenPanelForFileButtonWithResultListener(arg0); break; + case 29: decideDestinationWithSuggestedFilename(arg0, arg1); break; + case 30: mouseDidMoveOverElement(arg0, arg1); break; + case 31: didChangeLocationWithinPageForFrame(arg0); break; + case 32: handleEvent(arg0); break; + case 33: windowScriptObjectAvailable(arg0); break; + case 34: ret = callJava(arg0, arg1, arg2); break; + case 35: didReceiveAuthenticationChallengefromDataSource(arg0, arg1, arg2); break; + } + return ret; +} + +public boolean isBackEnabled() { + return Cocoa.objc_msgSend(webView, Cocoa.S_canGoBack) != 0; +} + +public boolean isForwardEnabled() { + return Cocoa.objc_msgSend(webView, Cocoa.S_canGoForward) != 0; +} + +public void refresh() { + Cocoa.objc_msgSend(webView, Cocoa.S_reload, 0); +} + +public boolean setText(String html) { + /* + * Bug in Safari. The web view segment faults in some circumstances + * when the text changes during the location changing callback. The + * fix is to defer the work until the callback is done. + */ + if (changingLocation) { + this.html = html; + } else { + _setText(html); + } + return true; +} + +void _setText(String html) { + int length = html.length(); + char[] buffer = new char[length]; + html.getChars(0, length, buffer, 0); + int string = OS.CFStringCreateWithCharacters(0, buffer, length); + + length = URI_FROMMEMORY.length(); + buffer = new char[length]; + URI_FROMMEMORY.getChars(0, length, buffer, 0); + int URLString = OS.CFStringCreateWithCharacters(0, buffer, length); + + /* + * Note. URLWithString uses autorelease. The resulting URL + * does not need to be released. + * URL = [NSURL URLWithString:(NSString *)URLString] + */ + int URL = Cocoa.objc_msgSend(Cocoa.C_NSURL, Cocoa.S_URLWithString, URLString); + OS.CFRelease(URLString); + + //mainFrame = [webView mainFrame]; + int mainFrame = Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); + + //[mainFrame loadHTMLString:(NSString *) string baseURL:(NSURL *)URL]; + Cocoa.objc_msgSend(mainFrame, Cocoa.S_loadHTMLStringbaseURL, string, URL); + OS.CFRelease(string); +} + +public boolean setUrl(String url) { + html = null; + + if (url.indexOf('/') == 0) { + url = PROTOCOL_FILE + url; + } else if (url.indexOf(':') == -1) { + url = PROTOCOL_HTTP + url; + } + + int inURL = 0; + char[] chars = new char[url.length()]; + url.getChars(0, chars.length, chars, 0); + int str = OS.CFStringCreateWithCharacters(0, chars, chars.length); + if (str != 0) { + char[] unescapedChars = new char[] {'%', '#'}; + int unescapedStr = OS.CFStringCreateWithCharacters(0, unescapedChars, unescapedChars.length); + int escapedStr = OS.CFURLCreateStringByAddingPercentEscapes(OS.kCFAllocatorDefault, str, unescapedStr, 0, OS.kCFStringEncodingUTF8); + if (escapedStr != 0) { + inURL = OS.CFURLCreateWithString(OS.kCFAllocatorDefault, escapedStr, 0); + OS.CFRelease(escapedStr); + } + if (unescapedStr != 0) OS.CFRelease(unescapedStr); + OS.CFRelease(str); + } + if (inURL == 0) return false; + + //request = [NSURLRequest requestWithURL:(NSURL*)inURL]; + int request = Cocoa.objc_msgSend(Cocoa.C_NSURLRequest, Cocoa.S_requestWithURL, inURL); + OS.CFRelease(inURL); + + //mainFrame = [webView mainFrame]; + int mainFrame = Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); + + //[mainFrame loadRequest:request]; + Cocoa.objc_msgSend(mainFrame, Cocoa.S_loadRequest, request); + + return true; +} + +public void stop() { + html = null; + Cocoa.objc_msgSend(webView, Cocoa.S_stopLoading, 0); +} + +/* WebFrameLoadDelegate */ +void didChangeLocationWithinPageForFrame(int frame) { + //id url= [[[[frame provisionalDataSource] request] URL] absoluteString]; + int dataSource = Cocoa.objc_msgSend(frame, Cocoa.S_dataSource); + int request = Cocoa.objc_msgSend(dataSource, Cocoa.S_request); + int url = Cocoa.objc_msgSend(request, Cocoa.S_URL); + int s = Cocoa.objc_msgSend(url, Cocoa.S_absoluteString); + int length = OS.CFStringGetLength(s); + if (length == 0) return; + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(s, range, buffer); + String url2 = new String(buffer); + /* + * If the URI indicates that the page is being rendered from memory + * (via setText()) then set it to about:blank to be consistent with IE. + */ + if (url2.equals (URI_FROMMEMORY)) url2 = ABOUT_BLANK; + + final Display display = browser.getDisplay(); + boolean top = frame == Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); + if (top) { + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = display; + statusText.widget = browser; + statusText.text = url2; + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } + } + LocationEvent location = new LocationEvent(browser); + location.display = display; + location.widget = browser; + location.location = url2; + location.top = top; + for (int i = 0; i < locationListeners.length; i++) { + locationListeners[i].changed(location); + } +} + +void didFailProvisionalLoadWithError(int error, int frame) { + if (frame == Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame)) { + /* + * Feature on Safari. The identifier is used here as a marker for the events + * related to the top frame and the URL changes related to that top frame as + * they should appear on the location bar of a browser. It is expected to reset + * the identifier to 0 when the event didFinishLoadingFromDataSource related to + * the identifierForInitialRequest event is received. However, Safari fires + * the didFinishLoadingFromDataSource event before the entire content of the + * top frame is loaded. It is possible to receive multiple willSendRequest + * events in this interval, causing the Browser widget to send unwanted + * Location.changing events. For this reason, the identifier is reset to 0 + * when the top frame has either finished loading (didFinishLoadForFrame + * event) or failed (didFailProvisionalLoadWithError). + */ + identifier = 0; + } +} + +void didFinishLoadForFrame(int frame) { + hookDOMFocusListeners(frame); + hookDOMMouseListeners(frame); + if (frame == Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame)) { + hookDOMKeyListeners(frame); + + final Display display = browser.getDisplay(); + /* + * To be consistent with other platforms a title event should be fired when a + * page has completed loading. A page with a <title> tag will do this + * automatically when the didReceiveTitle callback is received. However a page + * without a <title> tag will not do this by default, so fire the event + * here with the page's url as the title. + */ + int dataSource = Cocoa.objc_msgSend(frame, Cocoa.S_dataSource); + if (dataSource != 0) { + int title = Cocoa.objc_msgSend(dataSource, Cocoa.S_pageTitle); + if (title == 0) { /* page has no title */ + final TitleEvent newEvent = new TitleEvent(browser); + newEvent.display = display; + newEvent.widget = browser; + newEvent.title = url; + for (int i = 0; i < titleListeners.length; i++) { + final TitleListener listener = titleListeners[i]; + /* + * Note on WebKit. Running the event loop from a Browser + * delegate callback breaks the WebKit (stop loading or + * crash). The workaround is to invoke Display.asyncExec() + * so that the Browser does not crash if this is attempted. + */ + display.asyncExec( + new Runnable() { + public void run() { + if (!display.isDisposed() && !browser.isDisposed()) { + listener.changed(newEvent); + } + } + } + ); + } + } + } + final ProgressEvent progress = new ProgressEvent(browser); + progress.display = display; + progress.widget = browser; + progress.current = MAX_PROGRESS; + progress.total = MAX_PROGRESS; + for (int i = 0; i < progressListeners.length; i++) { + final ProgressListener listener = progressListeners[i]; + /* + * Note on WebKit. Running the event loop from a Browser + * delegate callback breaks the WebKit (stop loading or + * crash). The ProgressBar widget currently touches the + * event loop every time the method setSelection is called. + * The workaround is to invoke Display.asyncExec() so that + * the Browser does not crash when the user updates the + * selection of the ProgressBar. + */ + display.asyncExec( + new Runnable() { + public void run() { + if (!display.isDisposed() && !browser.isDisposed()) { + listener.completed(progress); + } + } + } + ); + } + + /* re-install registered functions */ + Enumeration elements = functions.elements (); + while (elements.hasMoreElements ()) { + BrowserFunction function = (BrowserFunction)elements.nextElement (); + execute (function.functionString); + } + + /* + * Feature on Safari. The identifier is used here as a marker for the events + * related to the top frame and the URL changes related to that top frame as + * they should appear on the location bar of a browser. It is expected to reset + * the identifier to 0 when the event didFinishLoadingFromDataSource related to + * the identifierForInitialRequest event is received. Howeever, Safari fires + * the didFinishLoadingFromDataSource event before the entire content of the + * top frame is loaded. It is possible to receive multiple willSendRequest + * events in this interval, causing the Browser widget to send unwanted + * Location.changing events. For this reason, the identifier is reset to 0 + * when the top frame has either finished loading (didFinishLoadForFrame + * event) or failed (didFailProvisionalLoadWithError). + */ + identifier = 0; + } +} + +void hookDOMFocusListeners(int frame) { + /* + * These listeners only need to be hooked for OSX 10.4 (Tiger). The WebKit on + * OSX < 10.4 does not send these DOM events, and tab traversals that exit + * Safari are handled as of OSX 10.5 as a result of using HICocoaViewCreate, + * which makes these listeners unnecessary. + */ + if (!(0x1040 <= OS.VERSION && OS.VERSION < 0x1050)) return; + + int document = Cocoa.objc_msgSend(frame, Cocoa.S_DOMDocument); + if (document == 0) return; + + String string = DOMEVENT_FOCUSIN; + int length = string.length(); + char[] chars = new char[length]; + string.getChars(0, length, chars, 0); + int ptr = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); + OS.CFRelease(ptr); + + string = DOMEVENT_FOCUSOUT; + length = string.length(); + chars = new char[length]; + string.getChars(0, length, chars, 0); + ptr = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); + OS.CFRelease(ptr); +} + +void hookDOMKeyListeners(int frame) { + /* + * WebKit's DOM listener api became functional in OSX 10.4, so if an earlier + * version than this is detected then do not hook the DOM listeners. + */ + if (OS.VERSION < 0x1040) return; + + int document = Cocoa.objc_msgSend(frame, Cocoa.S_DOMDocument); + if (document == 0) return; + + String string = DOMEVENT_KEYDOWN; + int length = string.length(); + char[] chars = new char[length]; + string.getChars(0, length, chars, 0); + int ptr = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); + OS.CFRelease(ptr); + + string = DOMEVENT_KEYUP; + length = string.length(); + chars = new char[length]; + string.getChars(0, length, chars, 0); + ptr = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); + OS.CFRelease(ptr); +} + +void hookDOMMouseListeners(int frame) { + /* + * WebKit's DOM listener api became functional in OSX 10.4, so if an earlier + * version than this is detected then do not hook the DOM listeners. + */ + if (OS.VERSION < 0x1040) return; + + int document = Cocoa.objc_msgSend(frame, Cocoa.S_DOMDocument); + if (document == 0) return; + + String string = DOMEVENT_MOUSEDOWN; + int length = string.length(); + char[] chars = new char[length]; + string.getChars(0, length, chars, 0); + int ptr = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); + OS.CFRelease(ptr); + + string = DOMEVENT_MOUSEUP; + length = string.length(); + chars = new char[length]; + string.getChars(0, length, chars, 0); + ptr = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); + OS.CFRelease(ptr); + + string = DOMEVENT_MOUSEMOVE; + length = string.length(); + chars = new char[length]; + string.getChars(0, length, chars, 0); + ptr = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); + OS.CFRelease(ptr); + + string = DOMEVENT_MOUSEWHEEL; + length = string.length(); + chars = new char[length]; + string.getChars(0, length, chars, 0); + ptr = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); + OS.CFRelease(ptr); +} + +void didReceiveTitle(int title, int frame) { + if (frame == Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame)) { + int length = OS.CFStringGetLength(title); + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(title, range, buffer); + String newTitle = new String(buffer); + TitleEvent newEvent = new TitleEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + newEvent.title = newTitle; + for (int i = 0; i < titleListeners.length; i++) { + titleListeners[i].changed(newEvent); + } + } +} + +void didStartProvisionalLoadForFrame(int frame) { + /* + * This code is intentionally commented. WebFrameLoadDelegate:didStartProvisionalLoadForFrame is + * called before WebResourceLoadDelegate:willSendRequest and + * WebFrameLoadDelegate:didCommitLoadForFrame. The resource count is reset when didCommitLoadForFrame + * is received for the top frame. + */ +// int webView = WebKit.HIWebViewGetWebView(webViewHandle); +// if (frame == WebKit.objc_msgSend(webView, WebKit.S_mainFrame)) { +// /* reset resource status variables */ +// resourceCount= 0; +// } +} + +void didCommitLoadForFrame(int frame) { + //id url= [[[[frame provisionalDataSource] request] URL] absoluteString]; + int dataSource = Cocoa.objc_msgSend(frame, Cocoa.S_dataSource); + int request = Cocoa.objc_msgSend(dataSource, Cocoa.S_request); + int url = Cocoa.objc_msgSend(request, Cocoa.S_URL); + int s = Cocoa.objc_msgSend(url, Cocoa.S_absoluteString); + int length = OS.CFStringGetLength(s); + if (length == 0) return; + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(s, range, buffer); + String url2 = new String(buffer); + /* + * If the URI indicates that the page is being rendered from memory + * (via setText()) then set it to about:blank to be consistent with IE. + */ + if (url2.equals (URI_FROMMEMORY)) url2 = ABOUT_BLANK; + + final Display display = browser.getDisplay(); + boolean top = frame == Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); + if (top) { + /* reset resource status variables */ + resourceCount = 0; + this.url = url2; + + final ProgressEvent progress = new ProgressEvent(browser); + progress.display = display; + progress.widget = browser; + progress.current = 1; + progress.total = MAX_PROGRESS; + for (int i = 0; i < progressListeners.length; i++) { + final ProgressListener listener = progressListeners[i]; + /* + * Note on WebKit. Running the event loop from a Browser + * delegate callback breaks the WebKit (stop loading or + * crash). The widget ProgressBar currently touches the + * event loop every time the method setSelection is called. + * The workaround is to invoke Display.asyncexec so that + * the Browser does not crash when the user updates the + * selection of the ProgressBar. + */ + display.asyncExec( + new Runnable() { + public void run() { + if (!display.isDisposed() && !browser.isDisposed()) + listener.changed(progress); + } + } + ); + } + + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = display; + statusText.widget = browser; + statusText.text = url2; + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } + } + LocationEvent location = new LocationEvent(browser); + location.display = display; + location.widget = browser; + location.location = url2; + location.top = top; + for (int i = 0; i < locationListeners.length; i++) { + locationListeners[i].changed(location); + } +} + +void windowScriptObjectAvailable (int windowScriptObject) { + String objectName = "external"; //$NON-NLS-1$ + char[] chars = new char[objectName.length ()]; + objectName.getChars (0, chars.length, chars, 0); + int str = OS.CFStringCreateWithCharacters (0, chars, chars.length); + if (str != 0) { + Cocoa.objc_msgSend (windowScriptObject, Cocoa.S_setValue, delegate, str); + OS.CFRelease (str); + } +} + +/* WebResourceLoadDelegate */ + +void didFinishLoadingFromDataSource(int identifier, int dataSource) { + /* + * Feature on Safari. The identifier is used here as a marker for the events + * related to the top frame and the URL changes related to that top frame as + * they should appear on the location bar of a browser. It is expected to reset + * the identifier to 0 when the event didFinishLoadingFromDataSource related to + * the identifierForInitialRequest event is received. Howeever, Safari fires + * the didFinishLoadingFromDataSource event before the entire content of the + * top frame is loaded. It is possible to receive multiple willSendRequest + * events in this interval, causing the Browser widget to send unwanted + * Location.changing events. For this reason, the identifier is reset to 0 + * when the top frame has either finished loading (didFinishLoadForFrame + * event) or failed (didFailProvisionalLoadWithError). + */ + // this code is intentionally commented + //if (this.identifier == identifier) this.identifier = 0; +} + +void didFailLoadingWithError(int identifier, int error, int dataSource) { + /* + * Feature on Safari. The identifier is used here as a marker for the events + * related to the top frame and the URL changes related to that top frame as + * they should appear on the location bar of a browser. It is expected to reset + * the identifier to 0 when the event didFinishLoadingFromDataSource related to + * the identifierForInitialRequest event is received. Howeever, Safari fires + * the didFinishLoadingFromDataSource event before the entire content of the + * top frame is loaded. It is possible to receive multiple willSendRequest + * events in this interval, causing the Browser widget to send unwanted + * Location.changing events. For this reason, the identifier is reset to 0 + * when the top frame has either finished loading (didFinishLoadForFrame + * event) or failed (didFailProvisionalLoadWithError). + */ + // this code is intentionally commented + //if (this.identifier == identifier) this.identifier = 0; +} + +void didReceiveAuthenticationChallengefromDataSource (int identifier, int challenge, int dataSource) { + /* + * Do not invoke the listeners if this challenge has been failed too many + * times because a listener is likely giving incorrect credentials repeatedly + * and will do so indefinitely. + */ + int count = Cocoa.objc_msgSend (challenge, Cocoa.S_previousFailureCount); + if (count < 3) { + for (int i = 0; i < authenticationListeners.length; i++) { + AuthenticationEvent event = new AuthenticationEvent (browser); + event.location = lastNavigateURL; + authenticationListeners[i].authenticate (event); + if (!event.doit) { + int challengeSender = Cocoa.objc_msgSend (challenge, Cocoa.S_sender); + Cocoa.objc_msgSend (challengeSender, Cocoa.S_cancelAuthenticationChallenge, challenge); + return; + } + if (event.user != null && event.password != null) { + int challengeSender = Cocoa.objc_msgSend (challenge, Cocoa.S_sender); + int length = event.user.length (); + char[] buffer = new char[length]; + event.user.getChars (0, length, buffer, 0); + int user = OS.CFStringCreateWithCharacters (0, buffer, length); + length = event.password.length (); + buffer = new char[length]; + event.password.getChars (0, length, buffer, 0); + int password = OS.CFStringCreateWithCharacters (0, buffer, length); + int credential = Cocoa.objc_msgSend (Cocoa.C_NSURLCredential, Cocoa.S_credentialWithUser, user, password, Cocoa.NSURLCredentialPersistenceForSession); + Cocoa.objc_msgSend (challengeSender, Cocoa.S_useCredential, credential, challenge); + OS.CFRelease (password); + OS.CFRelease (user); + return; + } + } + } + + /* no listener handled the challenge, so try to invoke the native panel */ + int cls = Cocoa.C_WebPanelAuthenticationHandler; + if (cls != 0) { + int method = Cocoa.class_getClassMethod (cls, Cocoa.S_sharedHandler); + if (method != 0) { + int handler = Cocoa.objc_msgSend (cls, Cocoa.S_sharedHandler); + if (handler != 0) { + int window = Cocoa.objc_msgSend (webView, Cocoa.S_window); + Cocoa.objc_msgSend (handler, Cocoa.S_startAuthentication, challenge, window); + return; + } + } + } + + /* the native panel was not available, so show a custom dialog */ + String[] userReturn = new String[1], passwordReturn = new String[1]; + int proposedCredential = Cocoa.objc_msgSend (challenge, Cocoa.S_proposedCredential); + if (proposedCredential != 0) { + int user = Cocoa.objc_msgSend (proposedCredential, Cocoa.S_user); + userReturn[0] = getString (user); + boolean hasPassword = Cocoa.objc_msgSend (proposedCredential, Cocoa.S_hasPassword) != 0; + if (hasPassword) { + int password = Cocoa.objc_msgSend (proposedCredential, Cocoa.S_password); + passwordReturn[0] = getString (password); + } + } + + int space = Cocoa.objc_msgSend (challenge, Cocoa.S_protectionSpace); + int host = Cocoa.objc_msgSend (space, Cocoa.S_host); + String hostString = getString (host) + ':'; + int port = Cocoa.objc_msgSend (space, Cocoa.S_port); + hostString += port; + int realm = Cocoa.objc_msgSend (space, Cocoa.S_realm); + String realmString = getString (realm); + boolean result = showAuthenticationDialog (userReturn, passwordReturn, hostString, realmString); + int challengeSender = Cocoa.objc_msgSend (challenge, Cocoa.S_sender); + if (!result) { + Cocoa.objc_msgSend (challengeSender, Cocoa.S_cancelAuthenticationChallenge, challenge); + return; + } + + int length = userReturn[0].length (); + char[] buffer = new char[length]; + userReturn[0].getChars (0, length, buffer, 0); + int user = OS.CFStringCreateWithCharacters (0, buffer, length); + length = passwordReturn[0].length (); + buffer = new char[length]; + passwordReturn[0].getChars (0, length, buffer, 0); + int password = OS.CFStringCreateWithCharacters (0, buffer, length); + int credential = Cocoa.objc_msgSend (Cocoa.C_NSURLCredential, Cocoa.S_credentialWithUser, user, password, Cocoa.NSURLCredentialPersistenceForSession); + Cocoa.objc_msgSend (challengeSender, Cocoa.S_useCredential, credential, challenge); + OS.CFRelease (password); + OS.CFRelease (user); +} + +boolean showAuthenticationDialog (final String[] user, final String[] password, String host, String realm) { + final Shell shell = new Shell (browser.getShell ()); + shell.setLayout (new GridLayout ()); + String title = SWT.getMessage ("SWT_Authentication_Required"); //$NON-NLS-1$ + shell.setText (title); + Label label = new Label (shell, SWT.WRAP); + label.setText (Compatibility.getMessage ("SWT_Enter_Username_and_Password", new String[] {realm, host})); //$NON-NLS-1$ + + GridData data = new GridData (); + Monitor monitor = browser.getMonitor (); + int maxWidth = monitor.getBounds ().width * 2 / 3; + int width = label.computeSize (SWT.DEFAULT, SWT.DEFAULT).x; + data.widthHint = Math.min (width, maxWidth); + data.horizontalAlignment = GridData.FILL; + data.grabExcessHorizontalSpace = true; + label.setLayoutData (data); + + Label userLabel = new Label (shell, SWT.NONE); + userLabel.setText (SWT.getMessage ("SWT_Username")); //$NON-NLS-1$ + + final Text userText = new Text (shell, SWT.BORDER); + if (user[0] != null) userText.setText (user[0]); + data = new GridData (); + data.horizontalAlignment = GridData.FILL; + data.grabExcessHorizontalSpace = true; + userText.setLayoutData (data); + + Label passwordLabel = new Label (shell, SWT.NONE); + passwordLabel.setText (SWT.getMessage ("SWT_Password")); //$NON-NLS-1$ + + final Text passwordText = new Text (shell, SWT.PASSWORD | SWT.BORDER); + if (password[0] != null) passwordText.setText (password[0]); + data = new GridData (); + data.horizontalAlignment = GridData.FILL; + data.grabExcessHorizontalSpace = true; + passwordText.setLayoutData (data); + + final boolean[] result = new boolean[1]; + final Button[] buttons = new Button[2]; + Listener listener = new Listener() { + public void handleEvent(Event event) { + user[0] = userText.getText(); + password[0] = passwordText.getText(); + result[0] = event.widget == buttons[1]; + shell.close(); + } + }; + + Composite composite = new Composite (shell, SWT.NONE); + data = new GridData (); + data.horizontalAlignment = GridData.END; + composite.setLayoutData (data); + composite.setLayout (new GridLayout (2, true)); + buttons[0] = new Button (composite, SWT.PUSH); + buttons[0].setText (SWT.getMessage("SWT_Cancel")); //$NON-NLS-1$ + buttons[0].setLayoutData (new GridData (GridData.FILL_HORIZONTAL)); + buttons[0].addListener (SWT.Selection, listener); + buttons[1] = new Button (composite, SWT.PUSH); + buttons[1].setText (SWT.getMessage("SWT_OK")); //$NON-NLS-1$ + buttons[1].setLayoutData (new GridData (GridData.FILL_HORIZONTAL)); + buttons[1].addListener (SWT.Selection, listener); + + shell.setDefaultButton (buttons[1]); + shell.pack (); + shell.open (); + Display display = browser.getDisplay (); + while (!shell.isDisposed ()) { + if (!display.readAndDispatch ()) display.sleep (); + } + + return result[0]; +} + +int identifierForInitialRequest(int request, int dataSource) { + final Display display = browser.getDisplay(); + final ProgressEvent progress = new ProgressEvent(browser); + progress.display = display; + progress.widget = browser; + progress.current = resourceCount; + progress.total = Math.max(resourceCount, MAX_PROGRESS); + for (int i = 0; i < progressListeners.length; i++) { + final ProgressListener listener = progressListeners[i]; + /* + * Note on WebKit. Running the event loop from a Browser + * delegate callback breaks the WebKit (stop loading or + * crash). The widget ProgressBar currently touches the + * event loop every time the method setSelection is called. + * The workaround is to invoke Display.asyncexec so that + * the Browser does not crash when the user updates the + * selection of the ProgressBar. + */ + display.asyncExec( + new Runnable() { + public void run() { + if (!display.isDisposed() && !browser.isDisposed()) + listener.changed(progress); + } + } + ); + } + + /* + * Note. numberWithInt uses autorelease. The resulting object + * does not need to be released. + * identifier = [NSNumber numberWithInt: resourceCount++] + */ + int identifier = Cocoa.objc_msgSend(Cocoa.C_NSNumber, Cocoa.S_numberWithInt, resourceCount++); + + if (this.identifier == 0) { + int frame = Cocoa.objc_msgSend(dataSource, Cocoa.S_webFrame); + if (frame == Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame)) this.identifier = identifier; + } + return identifier; + +} + +int willSendRequest(int identifier, int request, int redirectResponse, int dataSource) { + int url = Cocoa.objc_msgSend (request, Cocoa.S_URL); + boolean isFileURL = Cocoa.objc_msgSend (url, Cocoa.S_isFileURL) != 0; + if (isFileURL) { + int newRequest = Cocoa.objc_msgSend (request, Cocoa.S_mutableCopy); + Cocoa.objc_msgSend (newRequest, Cocoa.S_autorelease); + Cocoa.objc_msgSend (newRequest, Cocoa.S_setCachePolicy, Cocoa.NSURLRequestReloadIgnoringLocalCacheData); + return newRequest; + } + return request; +} + +/* handleNotification */ + +void handleNotification(int notification) { +} + +/* UIDelegate */ +int createWebViewWithRequest(int request) { + WindowEvent newEvent = new WindowEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + newEvent.required = true; + if (openWindowListeners != null) { + for (int i = 0; i < openWindowListeners.length; i++) { + openWindowListeners[i].open(newEvent); + } + } + + int webView = 0; + Browser browser = null; + if (newEvent.browser != null && newEvent.browser.webBrowser instanceof Safari) { + browser = newEvent.browser; + } + if (browser != null && !browser.isDisposed()) { + webView = ((Safari)browser.webBrowser).webView; + + if (request != 0) { + //mainFrame = [webView mainFrame]; + int mainFrame= Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); + + //[mainFrame loadRequest:request]; + Cocoa.objc_msgSend(mainFrame, Cocoa.S_loadRequest, request); + } + } + return webView; +} + +void webViewShow(int sender) { + /* + * Feature on WebKit. The Safari WebKit expects the application + * to create a new Window using the Objective C Cocoa API in response + * to UIDelegate.createWebViewWithRequest. The application is then + * expected to use Objective C Cocoa API to make this window visible + * when receiving the UIDelegate.webViewShow message. For some reason, + * a window created with the Carbon API hosting the new browser instance + * does not redraw until it has been resized. The fix is to increase the + * size of the Shell and restore it to its initial size. + */ + Shell parent = browser.getShell(); + Point pt = parent.getSize(); + parent.setSize(pt.x+1, pt.y); + parent.setSize(pt.x, pt.y); + WindowEvent newEvent = new WindowEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + if (location != null) newEvent.location = location; + if (size != null) newEvent.size = size; + /* + * Feature in Safari. Safari's tool bar contains + * the address bar. The address bar is displayed + * if the tool bar is displayed. There is no separate + * notification for the address bar. + * + * Feature of OSX. The menu bar is always displayed. + * There is no notification to hide the menu bar. + */ + newEvent.addressBar = toolBar; + newEvent.menuBar = true; + newEvent.statusBar = statusBar; + newEvent.toolBar = toolBar; + for (int i = 0; i < visibilityWindowListeners.length; i++) { + visibilityWindowListeners[i].show(newEvent); + } + location = null; + size = null; +} + +void setFrame(int frame) { + float[] dest = new float[4]; + OS.memmove(dest, frame, 16); + /* convert to SWT system coordinates */ + Rectangle bounds = browser.getDisplay().getBounds(); + location = new Point((int)dest[0], bounds.height - (int)dest[1] - (int)dest[3]); + size = new Point((int)dest[2], (int)dest[3]); +} + +void webViewFocus() { +} + +void webViewUnfocus() { +} + +void runJavaScriptAlertPanelWithMessage(int message) { + int length = OS.CFStringGetLength(message); + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(message, range, buffer); + String text = new String(buffer); + + MessageBox messageBox = new MessageBox(browser.getShell(), SWT.OK | SWT.ICON_WARNING); + messageBox.setText("Javascript"); //$NON-NLS-1$ + messageBox.setMessage(text); + messageBox.open(); +} + +int runJavaScriptConfirmPanelWithMessage(int message) { + int length = OS.CFStringGetLength(message); + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(message, range, buffer); + String text = new String(buffer); + + MessageBox messageBox = new MessageBox(browser.getShell(), SWT.OK | SWT.CANCEL | SWT.ICON_QUESTION); + messageBox.setText("Javascript"); //$NON-NLS-1$ + messageBox.setMessage(text); + return messageBox.open() == SWT.OK ? 1 : 0; +} + +void runOpenPanelForFileButtonWithResultListener(int resultListener) { + FileDialog dialog = new FileDialog(browser.getShell(), SWT.NONE); + String result = dialog.open(); + if (result == null) { + Cocoa.objc_msgSend(resultListener, Cocoa.S_cancel); + return; + } + int length = result.length(); + char[] buffer = new char[length]; + result.getChars(0, length, buffer, 0); + int filename = OS.CFStringCreateWithCharacters(0, buffer, length); + Cocoa.objc_msgSend(resultListener, Cocoa.S_chooseFilename, filename); + OS.CFRelease(filename); +} + +void webViewClose() { + Shell parent = browser.getShell(); + 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(); + if (parent.isDisposed()) return; + /* + * Feature on WebKit. The Safari WebKit expects the application + * to create a new Window using the Objective C Cocoa API in response + * to UIDelegate.createWebViewWithRequest. The application is then + * expected to use Objective C Cocoa API to make this window visible + * when receiving the UIDelegate.webViewShow message. For some reason, + * a window created with the Carbon API hosting the new browser instance + * does not redraw until it has been resized. The fix is to increase the + * size of the Shell and restore it to its initial size. + */ + Point pt = parent.getSize(); + parent.setSize(pt.x+1, pt.y); + parent.setSize(pt.x, pt.y); +} + +int contextMenuItemsForElement(int element, int defaultMenuItems) { + org.eclipse.swt.internal.carbon.Point pt = new org.eclipse.swt.internal.carbon.Point(); + OS.GetGlobalMouse(pt); + Event event = new Event(); + event.x = pt.h; + event.y = pt.v; + browser.notifyListeners(SWT.MenuDetect, event); + if (!event.doit || browser.isDisposed()) return 0; + Menu menu = browser.getMenu(); + if (menu != null && !menu.isDisposed()) { + if (event.x != pt.h || event.y != pt.v) { + menu.setLocation(event.x, event.y); + } + menu.setVisible(true); + return 0; + } + return defaultMenuItems; +} + +void setStatusBarVisible(int visible) { + /* Note. Webkit only emits the notification when the status bar should be hidden. */ + statusBar = visible != 0; +} + +void setStatusText(int text) { + int length = OS.CFStringGetLength(text); + if (length == 0) return; + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(text, range, buffer); + + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = browser.getDisplay(); + statusText.widget = browser; + statusText.text = new String(buffer); + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } +} + +void setResizable(int visible) { +} + +void setToolbarsVisible(int visible) { + /* Note. Webkit only emits the notification when the tool bar should be hidden. */ + toolBar = visible != 0; +} + +void mouseDidMoveOverElement (int elementInformation, int modifierFlags) { + if (elementInformation == 0) return; + if (!browser.isEnabled ()) return; + + int length = WebElementLinkURLKey.length(); + char[] chars = new char[length]; + WebElementLinkURLKey.getChars(0, length, chars, 0); + int key = OS.CFStringCreateWithCharacters(0, chars, length); + int value = Cocoa.objc_msgSend(elementInformation, Cocoa.S_valueForKey, key); + OS.CFRelease(key); + if (value == 0) { + /* not currently over a link */ + if (lastHoveredLinkURL == null) return; + lastHoveredLinkURL = null; + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = browser.getDisplay(); + statusText.widget = browser; + statusText.text = ""; //$NON-NLS-1$ + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } + return; + } + + int stringPtr = Cocoa.objc_msgSend(value, Cocoa.S_absoluteString); + length = OS.CFStringGetLength(stringPtr); + String urlString; + if (length == 0) { + urlString = ""; //$NON-NLS-1$ + } else { + chars = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(stringPtr, range, chars); + urlString = new String(chars); + } + if (urlString.equals(lastHoveredLinkURL)) return; + + lastHoveredLinkURL = urlString; + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = browser.getDisplay(); + statusText.widget = browser; + statusText.text = urlString; + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } +} + +/* PolicyDelegate */ + +void decidePolicyForMIMEType(int type, int request, int frame, int listener) { + boolean canShow = Cocoa.objc_msgSend(Cocoa.C_WebView, Cocoa.S_canShowMIMEType, type) != 0; + Cocoa.objc_msgSend(listener, canShow ? Cocoa.S_use : Cocoa.S_download); +} + +void decidePolicyForNavigationAction(int actionInformation, int request, int frame, int listener) { + int url = Cocoa.objc_msgSend(request, Cocoa.S_URL); + if (url == 0) { + /* indicates that a URL with an invalid format was specified */ + Cocoa.objc_msgSend(listener, Cocoa.S_ignore); + return; + } + int s = Cocoa.objc_msgSend(url, Cocoa.S_absoluteString); + int length = OS.CFStringGetLength(s); + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(s, range, buffer); + String url2 = new String(buffer); + /* + * If the URI indicates that the page is being rendered from memory + * (via setText()) then set it to about:blank to be consistent with IE. + */ + if (url2.equals (URI_FROMMEMORY)) url2 = ABOUT_BLANK; + + if (url2.startsWith (URI_APPLEWEBDATA)) { + /* listeners should not be notified of internal transitions like this */ + Cocoa.objc_msgSend(listener, Cocoa.S_use); + } else { + LocationEvent newEvent = new LocationEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + newEvent.location = url2; + newEvent.doit = true; + if (locationListeners != null) { + changingLocation = true; + for (int i = 0; i < locationListeners.length; i++) { + locationListeners[i].changing(newEvent); + } + changingLocation = false; + } + if (newEvent.doit) { + if (jsEnabledChanged) { + jsEnabledChanged = false; + if (preferences == 0) { + preferences = Cocoa.objc_msgSend (Cocoa.C_WebPreferences, Cocoa.S_alloc); + Cocoa.objc_msgSend (preferences, Cocoa.S_init); + Cocoa.objc_msgSend (webView, Cocoa.S_setPreferences, preferences); + } + Cocoa.objc_msgSend (preferences, Cocoa.S_setJavaScriptEnabled, jsEnabled ? 1 : 0); + } + lastNavigateURL = url2; + } + Cocoa.objc_msgSend(listener, newEvent.doit ? Cocoa.S_use : Cocoa.S_ignore); + } + + if (html != null && !browser.isDisposed()) { + String html = this.html; + this.html = null; + _setText(html); + } +} + +void decidePolicyForNewWindowAction(int actionInformation, int request, int frameName, int listener) { + Cocoa.objc_msgSend(listener, Cocoa.S_use); +} + +void unableToImplementPolicyWithError(int error, int frame) { +} + +/* WebDownload */ + +void decideDestinationWithSuggestedFilename (int download, int filename) { + int length = OS.CFStringGetLength(filename); + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(filename, range, buffer); + String name = new String(buffer); + + /* + * Bug in Safari. As of OSX 10.5.5, showing the file dialog here invokes this + * callback a second time when the file dialog runs the event loop, which + * always leads to a crash. The workaround is to choose a location to save + * the file without showing the file dialog. + */ + String path = null; + if (OS.VERSION >= 0x1055) { + int array = Cocoa.NSSearchPathForDirectoriesInDomains (Cocoa.NSDesktopDirectory, Cocoa.NSAllDomainsMask, true); + int count = Cocoa.objc_msgSend (array, Cocoa.S_count); + if (count == 0) { /* should never happen */ + array = Cocoa.NSSearchPathForDirectoriesInDomains (Cocoa.NSDownloadsDirectory, Cocoa.NSAllDomainsMask, true); + count = Cocoa.objc_msgSend (array, Cocoa.S_count); + if (count == 0) { + array = Cocoa.NSSearchPathForDirectoriesInDomains (Cocoa.NSDocumentDirectory, Cocoa.NSAllDomainsMask, true); + count = Cocoa.objc_msgSend (array, Cocoa.S_count); + if (count == 0) { + Cocoa.objc_msgSend (download, Cocoa.S_cancel); + return; + } + } + } + int string = Cocoa.objc_msgSend (array, Cocoa.S_objectAtIndex, 0); + length = OS.CFStringGetLength (string); + buffer = new char[length]; + range = new CFRange (); + range.length = length; + OS.CFStringGetCharacters (string, range, buffer); + path = new String (buffer) + '/' + name; + } else { + FileDialog dialog = new FileDialog(browser.getShell(), SWT.SAVE); + dialog.setText(SWT.getMessage ("SWT_FileDownload")); //$NON-NLS-1$ + dialog.setFileName(name); + path = dialog.open(); + } + + if (path == null) { + /* cancel pressed */ + Cocoa.objc_msgSend(download, Cocoa.S_cancel); + return; + } + length = path.length(); + char[] chars = new char[length]; + path.getChars(0, length, chars, 0); + int result = OS.CFStringCreateWithCharacters(0, chars, length); + Cocoa.objc_msgSend(download, Cocoa.S_setDestinationAllowOverwrite, result, 1); + OS.CFRelease(result); +} + +/* DOMEventListener */ + +void handleEvent(int evt) { + if (!browser.isEnabled ()) return; + + int type = Cocoa.objc_msgSend(evt, Cocoa.S_type); + int length = OS.CFStringGetLength(type); + char[] buffer = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(type, range, buffer); + String typeString = new String(buffer); + + if (typeString.equals(DOMEVENT_FOCUSIN)) { + hasNewFocusElement = true; + return; + } + if (typeString.equals(DOMEVENT_FOCUSOUT)) { + hasNewFocusElement = false; + return; + } + + boolean ctrl = Cocoa.objc_msgSend(evt, Cocoa.S_ctrlKey) != 0; + boolean shift = Cocoa.objc_msgSend(evt, Cocoa.S_shiftKey) != 0; + boolean alt = Cocoa.objc_msgSend(evt, Cocoa.S_altKey) != 0; + boolean meta = Cocoa.objc_msgSend(evt, Cocoa.S_metaKey) != 0; + + if (DOMEVENT_KEYDOWN.equals(typeString) || DOMEVENT_KEYUP.equals(typeString)) { + int keyCode = Cocoa.objc_msgSend(evt, Cocoa.S_keyCode); + int charCode = Cocoa.objc_msgSend(evt, Cocoa.S_charCode); + + Event keyEvent = new Event(); + keyEvent.widget = browser; + if (DOMEVENT_KEYDOWN.equals(typeString)) { + keyEvent.type = SWT.KeyDown; + } else { + keyEvent.type = SWT.KeyUp; + } + keyEvent.keyCode = translateKey(keyCode); + /* + * Safari maps the Delete key's character to 0xf728. Detect + * this key and set its character to SWT.DEL so that the + * Browser's key events are consistent with other controls. + */ + if (keyEvent.keyCode == SWT.DEL) { + keyEvent.character = SWT.DEL; + } else { + keyEvent.character = (char)charCode; + } + keyEvent.stateMask = (alt ? SWT.ALT : 0) | (ctrl ? SWT.CTRL : 0) | (shift ? SWT.SHIFT : 0) | (meta ? SWT.COMMAND : 0); + browser.notifyListeners(keyEvent.type, keyEvent); + if (browser.isDisposed()) { + Cocoa.objc_msgSend(evt, Cocoa.S_preventDefault); + return; + } + + boolean doit = keyEvent.doit; + /* + * Bug in Safari. As a result of using HIWebViewCreate on OSX versions < 10.5 (Leopard), attempting + * to traverse out of Safari backwards (Shift+Tab) leaves it in a strange state where Safari no + * longer has focus but still receives keys. The Carbon-based Safari examples have the same + * problem. The workaround is to only allow forward Tab traversals in the Browser on OSX < 10.5. + */ + if (doit && OS.VERSION < 0x1050 && keyEvent.keyCode == SWT.TAB && (keyEvent.stateMask & SWT.SHIFT) != 0) { + doit = false; + } + if (!doit) { + Cocoa.objc_msgSend(evt, Cocoa.S_preventDefault); + } else { + if (!hasNewFocusElement && keyEvent.keyCode == SWT.TAB && DOMEVENT_KEYUP.equals(typeString)) { + browser.traverse(SWT.TRAVERSE_TAB_NEXT); + hasNewFocusElement = false; + } + } + return; + } + + /* mouse event */ + + int clientX = Cocoa.objc_msgSend(evt, Cocoa.S_clientX); + int clientY = Cocoa.objc_msgSend(evt, Cocoa.S_clientY); + int detail = Cocoa.objc_msgSend(evt, Cocoa.S_detail); + + Event mouseEvent = new Event (); + mouseEvent.widget = browser; + mouseEvent.x = clientX; mouseEvent.y = clientY; + mouseEvent.stateMask = (alt ? SWT.ALT : 0) | (ctrl ? SWT.CTRL : 0) | (shift ? SWT.SHIFT : 0) | (meta ? SWT.COMMAND : 0); + + if (DOMEVENT_MOUSEDOWN.equals (typeString)) { + mouseEvent.type = SWT.MouseDown; + int button = Cocoa.objc_msgSend(evt, Cocoa.S_button); + mouseEvent.button = button + 1; + mouseEvent.count = detail; + } else if (DOMEVENT_MOUSEUP.equals (typeString)) { + mouseEvent.type = SWT.MouseUp; + int button = Cocoa.objc_msgSend(evt, Cocoa.S_button); + mouseEvent.button = button + 1; + mouseEvent.count = detail; + switch (mouseEvent.button) { + case 1: mouseEvent.stateMask |= SWT.BUTTON1; break; + case 2: mouseEvent.stateMask |= SWT.BUTTON2; break; + case 3: mouseEvent.stateMask |= SWT.BUTTON3; break; + case 4: mouseEvent.stateMask |= SWT.BUTTON4; break; + case 5: mouseEvent.stateMask |= SWT.BUTTON5; break; + } + } else if (DOMEVENT_MOUSEMOVE.equals (typeString)) { + /* + * Feature in Safari. Spurious and redundant mousemove events are received in + * various contexts, including following every MouseUp. The workaround is to + * not fire MouseMove events whose x and y values match the last MouseMove. + */ + if (mouseEvent.x == lastMouseMoveX && mouseEvent.y == lastMouseMoveY) return; + mouseEvent.type = SWT.MouseMove; + lastMouseMoveX = mouseEvent.x; lastMouseMoveY = mouseEvent.y; + } else if (DOMEVENT_MOUSEWHEEL.equals (typeString)) { + mouseEvent.type = SWT.MouseWheel; + int delta = Cocoa.objc_msgSend(evt, Cocoa.S_wheelDelta); + mouseEvent.count = delta / 120; + } + + browser.notifyListeners (mouseEvent.type, mouseEvent); + if (browser.isDisposed()) return; + if (detail == 2 && DOMEVENT_MOUSEDOWN.equals (typeString)) { + int button = Cocoa.objc_msgSend(evt, Cocoa.S_button); + mouseEvent = new Event (); + mouseEvent.widget = browser; + mouseEvent.x = clientX; mouseEvent.y = clientY; + mouseEvent.stateMask = (alt ? SWT.ALT : 0) | (ctrl ? SWT.CTRL : 0) | (shift ? SWT.SHIFT : 0) | (meta ? SWT.COMMAND : 0); + mouseEvent.type = SWT.MouseDoubleClick; + mouseEvent.button = button + 1; + mouseEvent.count = detail; + browser.notifyListeners (mouseEvent.type, mouseEvent); + } +} + +/* external */ + +Object convertToJava (int value) { + if (Cocoa.objc_msgSend (value, Cocoa.S_isKindOfClass, Cocoa.C_NSString) != 0) { + int length = Cocoa.objc_msgSend (value, Cocoa.S_length); + char[] buffer = new char[length]; + Cocoa.objc_msgSend (value, Cocoa.S_getCharacters_, buffer); + return new String (buffer); + } + if (Cocoa.objc_msgSend (value, Cocoa.S_isKindOfClass, Cocoa.C_NSNumber) != 0) { + int ptr = Cocoa.objc_msgSend (value, Cocoa.S_objCType); + byte[] type = new byte[1]; + OS.memmove (type, ptr, 1); + if (type[0] == 'c' || type[0] == 'B') { + int result = Cocoa.objc_msgSend (value, Cocoa.S_boolValue); + return new Boolean (result != 0); + } + if ("islqISLQfd".indexOf (type[0]) != -1) { //$NON-NLS-1$ + double result = Cocoa.objc_msgSend_fpret (value, Cocoa.S_doubleValue); + return new Double (result); + } + } + if (Cocoa.objc_msgSend (value, Cocoa.S_isKindOfClass, Cocoa.C_WebScriptObject) != 0) { + String string = "length"; //$NON-NLS-1$ + char[] chars = new char[string.length ()]; + string.getChars (0, chars.length, chars, 0); + int str = OS.CFStringCreateWithCharacters (0, chars, chars.length); + int numberValue = Cocoa.objc_msgSend (value, Cocoa.S_valueForKey, str); + OS.CFRelease (str); + int length = Cocoa.objc_msgSend (numberValue, Cocoa.S_intValue); + Object[] arguments = new Object[length]; + for (int i = 0; i < length; i++) { + int current = Cocoa.objc_msgSend (value, Cocoa.S_webScriptValueAtIndex, i); + if (current != 0) { + arguments[i] = convertToJava (current); + } + } + return arguments; + } + if (Cocoa.objc_msgSend (value, Cocoa.S_isKindOfClass, Cocoa.C_WebUndefined) != 0) { + return null; + } + + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + return null; +} + +int convertToJS (Object value) { + if (value == null) { + return Cocoa.objc_msgSend (Cocoa.C_WebUndefined, Cocoa.S_undefined); + } + if (value instanceof String) { + String result = (String)value; + char[] chars = new char[result.length ()]; + result.getChars (0, chars.length, chars, 0); + return OS.CFStringCreateWithCharacters (0, chars, chars.length); + } + if (value instanceof Boolean) { + int booleanValue = ((Boolean)value).booleanValue () ? 1 : 0; + return Cocoa.objc_msgSend (Cocoa.C_NSNumber, Cocoa.S_numberWithBool, booleanValue); + } + if (value instanceof Number) { + double doubleValue = ((Number)value).doubleValue (); + return Cocoa.objc_msgSend (Cocoa.C_NSNumber, Cocoa.S_numberWithDouble, doubleValue); + } + if (value instanceof Object[]) { + Object[] arrayValue = (Object[])value; + int length = arrayValue.length; + if (length > 0) { + int array = Cocoa.objc_msgSend (Cocoa.C_NSMutableArray, Cocoa.S_arrayWithCapacity, length); + for (int i = 0; i < length; i++) { + Object currentObject = arrayValue[i]; + int jsObject = convertToJS (currentObject); + Cocoa.objc_msgSend (array, Cocoa.S_addObject, jsObject); + } + return array; + } + } + SWT.error (SWT.ERROR_INVALID_RETURN_VALUE); + return 0; +} + +int /*long*/ callJava (int /*long*/ index, int /*long*/ args, int /*long*/ arg1) { + Object returnValue = null; + if (Cocoa.objc_msgSend (index, Cocoa.S_isKindOfClass, Cocoa.C_NSNumber) != 0) { + int functionIndex = Cocoa.objc_msgSend (index, Cocoa.S_intValue); + Object key = new Integer (functionIndex); + BrowserFunction function = (BrowserFunction)functions.get (key); + if (function != null) { + try { + Object temp = convertToJava (args); + if (temp instanceof Object[]) { + Object[] arguments = (Object[])temp; + try { + returnValue = function.function (arguments); + } catch (Exception e) { + /* exception during function invocation */ + returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ()); + } + } + } catch (IllegalArgumentException e) { + /* invalid argument value type */ + if (function.isEvaluate) { + /* notify the evaluate function so that a java exception can be thrown */ + function.function (new String[] {WebBrowser.CreateErrorString (new SWTException (SWT.ERROR_INVALID_RETURN_VALUE).getLocalizedMessage ())}); + } + returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ()); + } + } + } + try { + return convertToJS (returnValue); + } catch (SWTException e) { + return convertToJS (WebBrowser.CreateErrorString (e.getLocalizedMessage ())); + } +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/cocoa/org/eclipse/swt/browser/Safari.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/cocoa/org/eclipse/swt/browser/Safari.java new file mode 100755 index 0000000000..dbc068b3a3 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/cocoa/org/eclipse/swt/browser/Safari.java @@ -0,0 +1,1572 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.browser; + +import java.util.Enumeration; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.cocoa.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +class Safari extends WebBrowser { + WebView webView; + WebPreferences preferences; + SWTWebViewDelegate delegate; + boolean changingLocation; + String lastHoveredLinkURL, lastNavigateURL; + String html; + int /*long*/ identifier; + int resourceCount; + String url = ""; //$NON-NLS-1$ + Point location; + Point size; + boolean statusBar = true, toolBar = true, ignoreDispose; + int lastMouseMoveX, lastMouseMoveY; + //TEMPORARY CODE +// boolean doit; + + static int /*long*/ delegateClass; + static boolean Initialized; + // the following Callbacks are never freed + static Callback Callback3, Callback4, Callback5, Callback6, Callback7; + + static final int MIN_SIZE = 16; + static final int MAX_PROGRESS = 100; + static final String WebElementLinkURLKey = "WebElementLinkURL"; //$NON-NLS-1$ + static final String AGENT_STRING = "Safari/unknown"; //$NON-NLS-1$ + static final String URI_FROMMEMORY = "file:///"; //$NON-NLS-1$ + static final String PROTOCOL_FILE = "file://"; //$NON-NLS-1$ + static final String PROTOCOL_HTTP = "http://"; //$NON-NLS-1$ + static final String ABOUT_BLANK = "about:blank"; //$NON-NLS-1$ + static final String HEADER_SETCOOKIE = "Set-Cookie"; //$NON-NLS-1$ + static final String ADD_WIDGET_KEY = "org.eclipse.swt.internal.addWidget"; //$NON-NLS-1$ + static final String SAFARI_EVENTS_FIX_KEY = "org.eclipse.swt.internal.safariEventsFix"; //$NON-NLS-1$ + static final byte[] SWT_OBJECT = {'S', 'W', 'T', '_', 'O', 'B', 'J', 'E', 'C', 'T', '\0'}; + + /* event strings */ + static final String DOMEVENT_KEYUP = "keyup"; //$NON-NLS-1$ + static final String DOMEVENT_KEYDOWN = "keydown"; //$NON-NLS-1$ + static final String DOMEVENT_MOUSEDOWN = "mousedown"; //$NON-NLS-1$ + static final String DOMEVENT_MOUSEUP = "mouseup"; //$NON-NLS-1$ + static final String DOMEVENT_MOUSEMOVE = "mousemove"; //$NON-NLS-1$ + static final String DOMEVENT_MOUSEWHEEL = "mousewheel"; //$NON-NLS-1$ + + static { + NativeClearSessions = new Runnable() { + public void run() { + NSHTTPCookieStorage storage = NSHTTPCookieStorage.sharedHTTPCookieStorage(); + NSArray cookies = storage.cookies(); + int count = (int)/*64*/cookies.count (); + for (int i = 0; i < count; i++) { + NSHTTPCookie cookie = new NSHTTPCookie(cookies.objectAtIndex(i)); + if (cookie.isSessionOnly()) { + storage.deleteCookie(cookie); + } + } + } + }; + + NativeGetCookie = new Runnable () { + public void run () { + NSHTTPCookieStorage storage = NSHTTPCookieStorage.sharedHTTPCookieStorage (); + NSURL url = NSURL.URLWithString (NSString.stringWith (CookieUrl)); + NSArray cookies = storage.cookiesForURL (url); + int count = (int)/*64*/cookies.count (); + if (count == 0) return; + + NSString name = NSString.stringWith (CookieName); + for (int i = 0; i < count; i++) { + NSHTTPCookie current = new NSHTTPCookie (cookies.objectAtIndex (i)); + if (current.name ().compare (name) == OS.NSOrderedSame) { + CookieValue = current.value ().getString (); + return; + } + } + } + }; + + NativeSetCookie = new Runnable () { + public void run () { + NSURL url = NSURL.URLWithString (NSString.stringWith (CookieUrl)); + NSMutableDictionary headers = NSMutableDictionary.dictionaryWithCapacity (1); + headers.setValue (NSString.stringWith (CookieValue), NSString.stringWith (HEADER_SETCOOKIE)); + NSArray cookies = NSHTTPCookie.cookiesWithResponseHeaderFields (headers, url); + if (cookies.count () == 0) return; + NSHTTPCookieStorage storage = NSHTTPCookieStorage.sharedHTTPCookieStorage (); + NSHTTPCookie cookie = new NSHTTPCookie (cookies.objectAtIndex (0)); + storage.setCookie (cookie); + CookieResult = true; + } + }; + } + +public void create (Composite parent, int style) { + if (delegateClass == 0) { + Class safariClass = this.getClass(); + Callback3 = new Callback(safariClass, "browserProc", 3); //$NON-NLS-1$ + int /*long*/ proc3 = Callback3.getAddress(); + if (proc3 == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + Callback4 = new Callback(safariClass, "browserProc", 4); //$NON-NLS-1$ + int /*long*/ proc4 = Callback4.getAddress(); + if (proc4 == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + Callback5 = new Callback(safariClass, "browserProc", 5); //$NON-NLS-1$ + int /*long*/ proc5 = Callback5.getAddress(); + if (proc5 == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + Callback6 = new Callback(safariClass, "browserProc", 6); //$NON-NLS-1$ + int /*long*/ proc6 = Callback6.getAddress(); + if (proc6 == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + Callback7 = new Callback(safariClass, "browserProc", 7); //$NON-NLS-1$ + int /*long*/ proc7 = Callback7.getAddress(); + if (proc7 == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + int /*long*/ setFrameProc = OS.CALLBACK_webView_setFrame_(proc4); + if (setFrameProc == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + + String className = "SWTWebViewDelegate"; //$NON-NLS-1$ + byte[] types = {'*','\0'}; + int size = C.PTR_SIZEOF, align = C.PTR_SIZEOF == 4 ? 2 : 3; + delegateClass = OS.objc_allocateClassPair (OS.class_NSObject, className, 0); + + OS.class_addIvar(delegateClass, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(delegateClass, OS.sel_webView_didChangeLocationWithinPageForFrame_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_didFailProvisionalLoadWithError_forFrame_, proc5, "@:@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_didFinishLoadForFrame_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_didReceiveTitle_forFrame_, proc5, "@:@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_didStartProvisionalLoadForFrame_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_didCommitLoadForFrame_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_resource_didFinishLoadingFromDataSource_, proc5, "@:@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_resource_didReceiveAuthenticationChallenge_fromDataSource_, proc6, "@:@@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_resource_didFailLoadingWithError_fromDataSource_, proc6, "@:@@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_identifierForInitialRequest_fromDataSource_, proc5, "@:@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_resource_willSendRequest_redirectResponse_fromDataSource_, proc7, "@:@@@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_createWebViewWithRequest_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webViewShow_, proc3, "@:@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webViewClose_, proc3, "@:@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_contextMenuItemsForElement_defaultMenuItems_, proc5, "@:@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_setStatusBarVisible_, proc4, "@:@B"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_setResizable_, proc4, "@:@B"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_setToolbarsVisible_, proc4, "@:@B"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_setStatusText_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webViewFocus_, proc3, "@:@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webViewUnfocus_, proc3, "@:@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_runJavaScriptAlertPanelWithMessage_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_runJavaScriptConfirmPanelWithMessage_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_runOpenPanelForFileButtonWithResultListener_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_mouseDidMoveOverElement_modifierFlags_, proc5, "@:@@I"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_printFrameView_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_decidePolicyForMIMEType_request_frame_decisionListener_, proc7, "@:@@@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_decidePolicyForNavigationAction_request_frame_decisionListener_, proc7, "@:@@@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener_, proc7, "@:@@@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_unableToImplementPolicyWithError_frame_, proc5, "@:@@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_download_decideDestinationWithSuggestedFilename_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_handleEvent_, proc3, "@:@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_setFrame_, setFrameProc, "@:@{NSRect}"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_webView_windowScriptObjectAvailable_, proc4, "@:@@"); //$NON-NLS-1$ + OS.class_addMethod(delegateClass, OS.sel_callJava, proc5, "@:@@@"); //$NON-NLS-1$ + OS.objc_registerClassPair(delegateClass); + + int /*long*/ metaClass = OS.objc_getMetaClass (className); + OS.class_addMethod(metaClass, OS.sel_isSelectorExcludedFromWebScript_, proc3, "@:@"); //$NON-NLS-1$ + OS.class_addMethod(metaClass, OS.sel_webScriptNameForSelector_, proc3, "@:@"); //$NON-NLS-1$ + } + + /* + * Override the default event mechanism to not send key events so + * that the browser can send them by listening to the DOM instead. + */ + browser.setData(SAFARI_EVENTS_FIX_KEY); + + WebView webView = (WebView)new WebView().alloc(); + if (webView == null) SWT.error(SWT.ERROR_NO_HANDLES); + webView.initWithFrame(browser.view.frame(), null, null); + webView.setAutoresizingMask(OS.NSViewWidthSizable | OS.NSViewHeightSizable); + final SWTWebViewDelegate delegate = (SWTWebViewDelegate)new SWTWebViewDelegate().alloc().init(); + Display display = browser.getDisplay(); + display.setData(ADD_WIDGET_KEY, new Object[] {delegate, browser}); + this.delegate = delegate; + this.webView = webView; + browser.view.addSubview(webView); + + Listener listener = new Listener() { + public void handleEvent(Event e) { + switch (e.type) { + case SWT.FocusIn: + Safari.this.webView.window().makeFirstResponder(Safari.this.webView); + break; + 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; + + e.display.setData(ADD_WIDGET_KEY, new Object[] {delegate, null}); + + Safari.this.webView.setFrameLoadDelegate(null); + Safari.this.webView.setResourceLoadDelegate(null); + Safari.this.webView.setUIDelegate(null); + Safari.this.webView.setPolicyDelegate(null); + Safari.this.webView.setDownloadDelegate(null); + + Safari.this.webView.release(); + Safari.this.webView = null; + Safari.this.delegate.release(); + Safari.this.delegate = null; + html = null; + lastHoveredLinkURL = lastNavigateURL = null; + + Enumeration elements = functions.elements (); + while (elements.hasMoreElements ()) { + ((BrowserFunction)elements.nextElement ()).dispose (false); + } + functions = null; + + if (preferences != null) preferences.release (); + preferences = null; + break; + } + } + } + }; + browser.addListener(SWT.Dispose, listener); + /* Needed to be able to tab into the browser */ + browser.addListener(SWT.KeyDown, listener); + browser.addListener(SWT.FocusIn, listener); + + webView.setFrameLoadDelegate(delegate); + webView.setResourceLoadDelegate(delegate); + webView.setUIDelegate(delegate); + webView.setPolicyDelegate(delegate); + webView.setDownloadDelegate(delegate); + webView.setApplicationNameForUserAgent(NSString.stringWith(AGENT_STRING)); + + if (!Initialized) { + Initialized = true; + /* disable applets */ + WebPreferences.standardPreferences().setJavaEnabled(false); + } +} + +public boolean back() { + html = null; + return webView.goBack(); +} + +static int /*long*/ browserProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + if (id == delegateClass) { + if (sel == OS.sel_isSelectorExcludedFromWebScript_) { + return isSelectorExcludedFromWebScript (arg0) ? 1 : 0; + } else if (sel == OS.sel_webScriptNameForSelector_) { + return webScriptNameForSelector (arg0); + } + } + + Widget widget = Display.getCurrent().findWidget(id); + if (widget == null) return 0; + Safari safari = (Safari)((Browser)widget).webBrowser; + if (sel == OS.sel_webViewShow_) { + safari.webViewShow(arg0); + } else if (sel == OS.sel_webViewClose_) { + safari.webViewClose(arg0); + } else if (sel == OS.sel_webViewFocus_) { + safari.webViewFocus(arg0); + } else if (sel == OS.sel_webViewUnfocus_) { + safari.webViewUnfocus(arg0); + } else if (sel == OS.sel_handleEvent_) { + safari.handleEvent(arg0); + } + return 0; +} + +static int /*long*/ browserProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + Widget widget = Display.getCurrent().findWidget(id); + if (widget == null) return 0; + Safari safari = (Safari)((Browser)widget).webBrowser; + if (sel == OS.sel_webView_didChangeLocationWithinPageForFrame_) { + safari.webView_didChangeLocationWithinPageForFrame(arg0, arg1); + } else if (sel == OS.sel_webView_didFinishLoadForFrame_) { + safari.webView_didFinishLoadForFrame(arg0, arg1); + } else if (sel == OS.sel_webView_didStartProvisionalLoadForFrame_) { + safari.webView_didStartProvisionalLoadForFrame(arg0, arg1); + } else if (sel == OS.sel_webView_didCommitLoadForFrame_) { + safari.webView_didCommitLoadForFrame(arg0, arg1); + } else if (sel == OS.sel_webView_setFrame_) { + safari.webView_setFrame(arg0, arg1); + } else if (sel == OS.sel_webView_createWebViewWithRequest_) { + return safari.webView_createWebViewWithRequest(arg0, arg1); + } else if (sel == OS.sel_webView_setStatusBarVisible_) { + safari.webView_setStatusBarVisible(arg0, arg1 != 0); + } else if (sel == OS.sel_webView_setResizable_) { + safari.webView_setResizable(arg0, arg1 != 0); + } else if (sel == OS.sel_webView_setStatusText_) { + safari.webView_setStatusText(arg0, arg1); + } else if (sel == OS.sel_webView_setToolbarsVisible_) { + safari.webView_setToolbarsVisible(arg0, arg1 != 0); + } else if (sel == OS.sel_webView_runJavaScriptAlertPanelWithMessage_) { + safari.webView_runJavaScriptAlertPanelWithMessage(arg0, arg1); + } else if (sel == OS.sel_webView_runJavaScriptConfirmPanelWithMessage_) { + return safari.webView_runJavaScriptConfirmPanelWithMessage(arg0, arg1); + } else if (sel == OS.sel_webView_runOpenPanelForFileButtonWithResultListener_) { + safari.webView_runOpenPanelForFileButtonWithResultListener(arg0, arg1); + } else if (sel == OS.sel_download_decideDestinationWithSuggestedFilename_) { + safari.download_decideDestinationWithSuggestedFilename(arg0, arg1); + } else if (sel == OS.sel_webView_printFrameView_) { + safari.webView_printFrameView(arg0, arg1); + } else if (sel == OS.sel_webView_windowScriptObjectAvailable_) { + safari.webView_windowScriptObjectAvailable (arg0, arg1); + } + return 0; +} + +static int /*long*/ browserProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2) { + Widget widget = Display.getCurrent().findWidget(id); + if (widget == null) return 0; + Safari safari = (Safari)((Browser)widget).webBrowser; + if (sel == OS.sel_webView_didFailProvisionalLoadWithError_forFrame_) { + safari.webView_didFailProvisionalLoadWithError_forFrame(arg0, arg1, arg2); + } else if (sel == OS.sel_webView_didReceiveTitle_forFrame_) { + safari.webView_didReceiveTitle_forFrame(arg0, arg1, arg2); + } else if (sel == OS.sel_webView_resource_didFinishLoadingFromDataSource_) { + safari.webView_resource_didFinishLoadingFromDataSource(arg0, arg1, arg2); + } else if (sel == OS.sel_webView_identifierForInitialRequest_fromDataSource_) { + return safari.webView_identifierForInitialRequest_fromDataSource(arg0, arg1, arg2); + } else if (sel == OS.sel_webView_contextMenuItemsForElement_defaultMenuItems_) { + return safari.webView_contextMenuItemsForElement_defaultMenuItems(arg0, arg1, arg2); + } else if (sel == OS.sel_webView_mouseDidMoveOverElement_modifierFlags_) { + safari.webView_mouseDidMoveOverElement_modifierFlags(arg0, arg1, arg2); + } else if (sel == OS.sel_webView_unableToImplementPolicyWithError_frame_) { + safari.webView_unableToImplementPolicyWithError_frame(arg0, arg1, arg2); + } else if (sel == OS.sel_callJava) { + id result = safari.callJava (arg0, arg1, arg2); + return result == null ? 0 : result.id; + } + return 0; +} + +static int /*long*/ browserProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2, int /*long*/ arg3) { + Widget widget = Display.getCurrent().findWidget(id); + if (widget == null) return 0; + Safari safari = (Safari)((Browser)widget).webBrowser; + if (sel == OS.sel_webView_resource_didFailLoadingWithError_fromDataSource_) { + safari.webView_resource_didFailLoadingWithError_fromDataSource(arg0, arg1, arg2, arg3); + } else if (sel == OS.sel_webView_resource_didReceiveAuthenticationChallenge_fromDataSource_) { + safari.webView_resource_didReceiveAuthenticationChallenge_fromDataSource(arg0, arg1, arg2, arg3); + } + return 0; +} + +static int /*long*/ browserProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2, int /*long*/ arg3, int /*long*/ arg4) { + Widget widget = Display.getCurrent().findWidget(id); + if (widget == null) return 0; + Safari safari = (Safari)((Browser)widget).webBrowser; + if (sel == OS.sel_webView_resource_willSendRequest_redirectResponse_fromDataSource_) { + return safari.webView_resource_willSendRequest_redirectResponse_fromDataSource(arg0, arg1, arg2, arg3, arg4); + } else if (sel == OS.sel_webView_decidePolicyForMIMEType_request_frame_decisionListener_) { + safari.webView_decidePolicyForMIMEType_request_frame_decisionListener(arg0, arg1, arg2, arg3, arg4); + } else if (sel == OS.sel_webView_decidePolicyForNavigationAction_request_frame_decisionListener_) { + safari.webView_decidePolicyForNavigationAction_request_frame_decisionListener(arg0, arg1, arg2, arg3, arg4); + } else if (sel == OS.sel_webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener_) { + safari.webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener(arg0, arg1, arg2, arg3, arg4); + } + return 0; +} + +static boolean isSelectorExcludedFromWebScript (int /*long*/ aSelector) { + return aSelector != OS.sel_callJava; +} + +static int /*long*/ webScriptNameForSelector (int /*long*/ aSelector) { + if (aSelector == OS.sel_callJava) { + return NSString.stringWith ("callJava").id; //$NON-NLS-1$ + } + return 0; +} + +public boolean execute (String script) { + return webView.stringByEvaluatingJavaScriptFromString (NSString.stringWith (script)) != null; +} + +public boolean forward () { + html = null; + return webView.goForward(); +} + +public String getBrowserType () { + return "safari"; //$NON-NLS-1$ +} + +public String getText() { + WebFrame mainFrame = webView.mainFrame(); + WebDataSource dataSource = mainFrame.dataSource(); + if (dataSource == null) return ""; //$NON-NLS-1$ + WebDocumentRepresentation representation = dataSource.representation(); + if (representation == null) return ""; //$NON-NLS-1$ + NSString source = representation.documentSource(); + if (source == null) return ""; //$NON-NLS-1$ + return source.getString(); +} + +public String getUrl() { + return url; +} + +public boolean isBackEnabled() { + return webView.canGoBack(); +} + +public boolean isForwardEnabled() { + return webView.canGoForward(); +} + +public void refresh() { + webView.reload(null); +} + +public boolean setText(String html) { + /* + * Bug in Safari. The web view segment faults in some circumstances + * when the text changes during the location changing callback. The + * fix is to defer the work until the callback is done. + */ + if (changingLocation) { + this.html = html; + } else { + _setText(html); + } + return true; +} + +void _setText(String html) { + NSString string = NSString.stringWith(html); + NSString URLString = NSString.stringWith(URI_FROMMEMORY); + NSURL URL = NSURL.URLWithString(URLString); + WebFrame mainFrame = webView.mainFrame(); + mainFrame.loadHTMLString(string, URL); +} + +public boolean setUrl(String url) { + html = null; + + if (url.indexOf('/') == 0) { + url = PROTOCOL_FILE + url; + } else if (url.indexOf(':') == -1) { + url = PROTOCOL_HTTP + url; + } + + NSString str = NSString.stringWith(url); + NSString unescapedStr = NSString.stringWith("%#"); //$NON-NLS-1$ + int /*long*/ ptr = OS.CFURLCreateStringByAddingPercentEscapes(0, str.id, unescapedStr.id, 0, OS.kCFStringEncodingUTF8); + NSString escapedString = new NSString(ptr); + NSURL inURL = NSURL.URLWithString(escapedString); + OS.CFRelease(ptr); + NSURLRequest request = NSURLRequest.requestWithURL(inURL); + WebFrame mainFrame = webView.mainFrame(); + mainFrame.loadRequest(request); + return true; +} + +public void stop() { + html = null; + webView.stopLoading(null); +} + +/* WebFrameLoadDelegate */ + +void webView_didChangeLocationWithinPageForFrame(int /*long*/ sender, int /*long*/ frameID) { + WebFrame frame = new WebFrame(frameID); + WebDataSource dataSource = frame.dataSource(); + NSURLRequest request = dataSource.request(); + NSURL url = request.URL(); + NSString s = url.absoluteString(); + int length = (int)/*64*/s.length(); + if (length == 0) return; + String url2 = s.getString(); + /* + * If the URI indicates that the page is being rendered from memory + * (via setText()) then set it to about:blank to be consistent with IE. + */ + if (url2.equals (URI_FROMMEMORY)) url2 = ABOUT_BLANK; + + final Display display = browser.getDisplay(); + boolean top = frameID == webView.mainFrame().id; + if (top) { + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = display; + statusText.widget = browser; + statusText.text = url2; + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } + } + + LocationEvent location = new LocationEvent(browser); + location.display = display; + location.widget = browser; + location.location = url2; + location.top = top; + for (int i = 0; i < locationListeners.length; i++) { + locationListeners[i].changed(location); + } +} + +void webView_didFailProvisionalLoadWithError_forFrame(int /*long*/ sender, int /*long*/ error, int /*long*/ frame) { + if (frame == webView.mainFrame().id) { + /* + * Feature on Safari. The identifier is used here as a marker for the events + * related to the top frame and the URL changes related to that top frame as + * they should appear on the location bar of a browser. It is expected to reset + * the identifier to 0 when the event didFinishLoadingFromDataSource related to + * the identifierForInitialRequest event is received. However, Safari fires + * the didFinishLoadingFromDataSource event before the entire content of the + * top frame is loaded. It is possible to receive multiple willSendRequest + * events in this interval, causing the Browser widget to send unwanted + * Location.changing events. For this reason, the identifier is reset to 0 + * when the top frame has either finished loading (didFinishLoadForFrame + * event) or failed (didFailProvisionalLoadWithError). + */ + identifier = 0; + } + + NSError nserror = new NSError(error); + int /*long*/ errorCode = nserror.code(); + if (errorCode <= OS.NSURLErrorBadURL) { + NSString description = nserror.localizedDescription(); + if (description != null) { + String descriptionString = description.getString(); + String urlString = null; + NSDictionary info = nserror.userInfo(); + if (info != null) { + NSString key = new NSString(OS.NSErrorFailingURLStringKey()); + id id = info.valueForKey(key); + if (id != null) { + NSString url = new NSString(id); + urlString = url.getString(); + } + } + String message = urlString != null ? urlString + "\n\n" : ""; //$NON-NLS-1$ //$NON-NLS-2$ + message += Compatibility.getMessage ("SWT_Page_Load_Failed", new Object[] {descriptionString}); //$NON-NLS-1$ + MessageBox messageBox = new MessageBox(browser.getShell(), SWT.OK | SWT.ICON_ERROR); + messageBox.setMessage(message); + messageBox.open(); + } + } +} + +void webView_didFinishLoadForFrame(int /*long*/ sender, int /*long*/ frameID) { + hookDOMMouseListeners(frameID); + if (frameID == webView.mainFrame().id) { + hookDOMKeyListeners(frameID); + + final Display display = browser.getDisplay(); + /* + * To be consistent with other platforms a title event should be fired when a + * page has completed loading. A page with a <title> tag will do this + * automatically when the didReceiveTitle callback is received. However a page + * without a <title> tag will not do this by default, so fire the event + * here with the page's url as the title. + */ + WebFrame frame = new WebFrame(frameID); + WebDataSource dataSource = frame.dataSource(); + if (dataSource != null) { + NSString title = dataSource.pageTitle(); + if (title == null) { /* page has no title */ + final TitleEvent newEvent = new TitleEvent(browser); + newEvent.display = display; + newEvent.widget = browser; + newEvent.title = url; + for (int i = 0; i < titleListeners.length; i++) { + final TitleListener listener = titleListeners[i]; + /* + * Note on WebKit. Running the event loop from a Browser + * delegate callback breaks the WebKit (stop loading or + * crash). The workaround is to invoke Display.asyncExec() + * so that the Browser does not crash if this is attempted. + */ + display.asyncExec( + new Runnable() { + public void run() { + if (!display.isDisposed() && !browser.isDisposed()) { + listener.changed(newEvent); + } + } + } + ); + } + } + } + final ProgressEvent progress = new ProgressEvent(browser); + progress.display = display; + progress.widget = browser; + progress.current = MAX_PROGRESS; + progress.total = MAX_PROGRESS; + for (int i = 0; i < progressListeners.length; i++) { + final ProgressListener listener = progressListeners[i]; + /* + * Note on WebKit. Running the event loop from a Browser + * delegate callback breaks the WebKit (stop loading or + * crash). The ProgressBar widget currently touches the + * event loop every time the method setSelection is called. + * The workaround is to invoke Display.asyncExec() so that + * the Browser does not crash when the user updates the + * selection of the ProgressBar. + */ + display.asyncExec( + new Runnable() { + public void run() { + if (!display.isDisposed() && !browser.isDisposed()) { + listener.completed(progress); + } + } + } + ); + } + + /* + * Feature on Safari. The identifier is used here as a marker for the events + * related to the top frame and the URL changes related to that top frame as + * they should appear on the location bar of a browser. It is expected to reset + * the identifier to 0 when the event didFinishLoadingFromDataSource related to + * the identifierForInitialRequest event is received. However, Safari fires + * the didFinishLoadingFromDataSource event before the entire content of the + * top frame is loaded. It is possible to receive multiple willSendRequest + * events in this interval, causing the Browser widget to send unwanted + * Location.changing events. For this reason, the identifier is reset to 0 + * when the top frame has either finished loading (didFinishLoadForFrame + * event) or failed (didFailProvisionalLoadWithError). + */ + identifier = 0; + } +} + +void hookDOMKeyListeners(int /*long*/ frameID) { + WebFrame frame = new WebFrame(frameID); + DOMDocument document = frame.DOMDocument(); + if (document == null) return; + + NSString type = NSString.stringWith(DOMEVENT_KEYDOWN); + document.addEventListener(type, delegate, false); + + type = NSString.stringWith(DOMEVENT_KEYUP); + document.addEventListener(type, delegate, false); +} + +void hookDOMMouseListeners(int /*long*/ frameID) { + WebFrame frame = new WebFrame(frameID); + DOMDocument document = frame.DOMDocument(); + if (document == null) return; + + NSString type = NSString.stringWith(DOMEVENT_MOUSEDOWN); + document.addEventListener(type, delegate, false); + + type = NSString.stringWith(DOMEVENT_MOUSEUP); + document.addEventListener(type, delegate, false); + + type = NSString.stringWith(DOMEVENT_MOUSEMOVE); + document.addEventListener(type, delegate, false); + + type = NSString.stringWith(DOMEVENT_MOUSEWHEEL); + document.addEventListener(type, delegate, false); +} + +void webView_didReceiveTitle_forFrame(int /*long*/ sender, int /*long*/ titleID, int /*long*/ frameID) { + if (frameID == webView.mainFrame().id) { + NSString title = new NSString(titleID); + String newTitle = title.getString(); + TitleEvent newEvent = new TitleEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + newEvent.title = newTitle; + for (int i = 0; i < titleListeners.length; i++) { + titleListeners[i].changed(newEvent); + } + } +} + +void webView_didStartProvisionalLoadForFrame(int /*long*/ sender, int /*long*/ frameID) { + /* + * This code is intentionally commented. WebFrameLoadDelegate:didStartProvisionalLoadForFrame is + * called before WebResourceLoadDelegate:willSendRequest and + * WebFrameLoadDelegate:didCommitLoadForFrame. The resource count is reset when didCommitLoadForFrame + * is received for the top frame. + */ +// if (frameID == webView.mainFrame().id) { +// /* reset resource status variables */ +// resourceCount= 0; +// } +} + +void webView_didCommitLoadForFrame(int /*long*/ sender, int /*long*/ frameID) { + WebFrame frame = new WebFrame(frameID); + WebDataSource dataSource = frame.dataSource(); + NSURLRequest request = dataSource.request(); + NSURL url = request.URL(); + NSString s = url.absoluteString(); + int length = (int)/*64*/s.length(); + if (length == 0) return; + String url2 = s.getString(); + /* + * If the URI indicates that the page is being rendered from memory + * (via setText()) then set it to about:blank to be consistent with IE. + */ + if (url2.equals (URI_FROMMEMORY)) url2 = ABOUT_BLANK; + + final Display display = browser.getDisplay(); + boolean top = frameID == webView.mainFrame().id; + if (top) { + /* reset resource status variables */ + resourceCount = 0; + this.url = url2; + + /* re-install registered functions */ + Enumeration elements = functions.elements (); + while (elements.hasMoreElements ()) { + BrowserFunction function = (BrowserFunction)elements.nextElement (); + execute (function.functionString); + } + + final ProgressEvent progress = new ProgressEvent(browser); + progress.display = display; + progress.widget = browser; + progress.current = 1; + progress.total = MAX_PROGRESS; + for (int i = 0; i < progressListeners.length; i++) { + final ProgressListener listener = progressListeners[i]; + /* + * Note on WebKit. Running the event loop from a Browser + * delegate callback breaks the WebKit (stop loading or + * crash). The widget ProgressBar currently touches the + * event loop every time the method setSelection is called. + * The workaround is to invoke Display.asyncexec so that + * the Browser does not crash when the user updates the + * selection of the ProgressBar. + */ + display.asyncExec( + new Runnable() { + public void run() { + if (!display.isDisposed() && !browser.isDisposed()) + listener.changed(progress); + } + } + ); + } + + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = display; + statusText.widget = browser; + statusText.text = url2; + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } + } + LocationEvent location = new LocationEvent(browser); + location.display = display; + location.widget = browser; + location.location = url2; + location.top = top; + for (int i = 0; i < locationListeners.length; i++) { + locationListeners[i].changed(location); + } +} + +void webView_windowScriptObjectAvailable (int /*long*/ webView, int /*long*/ windowScriptObject) { + NSObject scriptObject = new NSObject (windowScriptObject); + NSString key = NSString.stringWith ("external"); //$NON-NLS-1$ + scriptObject.setValue (delegate, key); +} + +/* WebResourceLoadDelegate */ + +void webView_resource_didFinishLoadingFromDataSource(int /*long*/ sender, int /*long*/ identifier, int /*long*/ dataSource) { + /* + * Feature on Safari. The identifier is used here as a marker for the events + * related to the top frame and the URL changes related to that top frame as + * they should appear on the location bar of a browser. It is expected to reset + * the identifier to 0 when the event didFinishLoadingFromDataSource related to + * the identifierForInitialRequest event is received. However, Safari fires + * the didFinishLoadingFromDataSource event before the entire content of the + * top frame is loaded. It is possible to receive multiple willSendRequest + * events in this interval, causing the Browser widget to send unwanted + * Location.changing events. For this reason, the identifier is reset to 0 + * when the top frame has either finished loading (didFinishLoadForFrame + * event) or failed (didFailProvisionalLoadWithError). + */ + // this code is intentionally commented + //if (this.identifier == identifier) this.identifier = 0; +} + +void webView_resource_didFailLoadingWithError_fromDataSource(int /*long*/ sender, int /*long*/ identifier, int /*long*/ error, int /*long*/ dataSource) { + /* + * Feature on Safari. The identifier is used here as a marker for the events + * related to the top frame and the URL changes related to that top frame as + * they should appear on the location bar of a browser. It is expected to reset + * the identifier to 0 when the event didFinishLoadingFromDataSource related to + * the identifierForInitialRequest event is received. However, Safari fires + * the didFinishLoadingFromDataSource event before the entire content of the + * top frame is loaded. It is possible to receive multiple willSendRequest + * events in this interval, causing the Browser widget to send unwanted + * Location.changing events. For this reason, the identifier is reset to 0 + * when the top frame has either finished loading (didFinishLoadForFrame + * event) or failed (didFailProvisionalLoadWithError). + */ + // this code is intentionally commented + //if (this.identifier == identifier) this.identifier = 0; +} + +void webView_resource_didReceiveAuthenticationChallenge_fromDataSource (int /*long*/ sender, int /*long*/ identifier, int /*long*/ challenge, int /*long*/ dataSource) { + NSURLAuthenticationChallenge nsChallenge = new NSURLAuthenticationChallenge (challenge); + + /* + * Do not invoke the listeners if this challenge has been failed too many + * times because a listener is likely giving incorrect credentials repeatedly + * and will do so indefinitely. + */ + if (nsChallenge.previousFailureCount () < 3) { + for (int i = 0; i < authenticationListeners.length; i++) { + AuthenticationEvent event = new AuthenticationEvent (browser); + event.location = lastNavigateURL; + authenticationListeners[i].authenticate (event); + if (!event.doit) { + id challengeSender = nsChallenge.sender (); + OS.objc_msgSend (challengeSender.id, OS.sel_cancelAuthenticationChallenge_, challenge); + return; + } + if (event.user != null && event.password != null) { + id challengeSender = nsChallenge.sender (); + NSString user = NSString.stringWith (event.user); + NSString password = NSString.stringWith (event.password); + NSURLCredential credential = NSURLCredential.credentialWithUser (user, password, OS.NSURLCredentialPersistenceForSession); + OS.objc_msgSend (challengeSender.id, OS.sel_useCredential_forAuthenticationChallenge_, credential.id, challenge); + return; + } + } + } + + /* no listener handled the challenge, so try to invoke the native panel */ + int /*long*/ cls = OS.class_WebPanelAuthenticationHandler; + if (cls != 0) { + int /*long*/ method = OS.class_getClassMethod (cls, OS.sel_sharedHandler); + if (method != 0) { + int /*long*/ handler = OS.objc_msgSend (cls, OS.sel_sharedHandler); + if (handler != 0) { + OS.objc_msgSend (handler, OS.sel_startAuthentication, challenge, webView.window ().id); + return; + } + } + } + + /* the native panel was not available, so show a custom dialog */ + String[] userReturn = new String[1], passwordReturn = new String[1]; + NSURLCredential proposedCredential = nsChallenge.proposedCredential (); + if (proposedCredential != null) { + userReturn[0] = proposedCredential.user ().getString (); + if (proposedCredential.hasPassword ()) { + passwordReturn[0] = proposedCredential.password ().getString (); + } + } + NSURLProtectionSpace space = nsChallenge.protectionSpace (); + String host = space.host ().getString () + ':' + space.port (); + String realm = space.realm ().getString (); + boolean result = showAuthenticationDialog (userReturn, passwordReturn, host, realm); + if (!result) { + id challengeSender = nsChallenge.sender (); + OS.objc_msgSend (challengeSender.id, OS.sel_cancelAuthenticationChallenge_, challenge); + return; + } + id challengeSender = nsChallenge.sender (); + NSString user = NSString.stringWith (userReturn[0]); + NSString password = NSString.stringWith (passwordReturn[0]); + NSURLCredential credential = NSURLCredential.credentialWithUser (user, password, OS.NSURLCredentialPersistenceForSession); + OS.objc_msgSend (challengeSender.id, OS.sel_useCredential_forAuthenticationChallenge_, credential.id, challenge); +} + +boolean showAuthenticationDialog (final String[] user, final String[] password, String host, String realm) { + final Shell shell = new Shell (browser.getShell ()); + shell.setLayout (new GridLayout ()); + String title = SWT.getMessage ("SWT_Authentication_Required"); //$NON-NLS-1$ + shell.setText (title); + Label label = new Label (shell, SWT.WRAP); + label.setText (Compatibility.getMessage ("SWT_Enter_Username_and_Password", new String[] {realm, host})); //$NON-NLS-1$ + + GridData data = new GridData (); + Monitor monitor = browser.getMonitor (); + int maxWidth = monitor.getBounds ().width * 2 / 3; + int width = label.computeSize (SWT.DEFAULT, SWT.DEFAULT).x; + data.widthHint = Math.min (width, maxWidth); + data.horizontalAlignment = GridData.FILL; + data.grabExcessHorizontalSpace = true; + label.setLayoutData (data); + + Label userLabel = new Label (shell, SWT.NONE); + userLabel.setText (SWT.getMessage ("SWT_Username")); //$NON-NLS-1$ + + final Text userText = new Text (shell, SWT.BORDER); + if (user[0] != null) userText.setText (user[0]); + data = new GridData (); + data.horizontalAlignment = GridData.FILL; + data.grabExcessHorizontalSpace = true; + userText.setLayoutData (data); + + Label passwordLabel = new Label (shell, SWT.NONE); + passwordLabel.setText (SWT.getMessage ("SWT_Password")); //$NON-NLS-1$ + + final Text passwordText = new Text (shell, SWT.PASSWORD | SWT.BORDER); + if (password[0] != null) passwordText.setText (password[0]); + data = new GridData (); + data.horizontalAlignment = GridData.FILL; + data.grabExcessHorizontalSpace = true; + passwordText.setLayoutData (data); + + final boolean[] result = new boolean[1]; + final Button[] buttons = new Button[2]; + Listener listener = new Listener() { + public void handleEvent(Event event) { + user[0] = userText.getText(); + password[0] = passwordText.getText(); + result[0] = event.widget == buttons[1]; + shell.close(); + } + }; + + Composite composite = new Composite (shell, SWT.NONE); + data = new GridData (); + data.horizontalAlignment = GridData.END; + composite.setLayoutData (data); + composite.setLayout (new GridLayout (2, true)); + buttons[0] = new Button (composite, SWT.PUSH); + buttons[0].setText (SWT.getMessage("SWT_Cancel")); //$NON-NLS-1$ + buttons[0].setLayoutData (new GridData (GridData.FILL_HORIZONTAL)); + buttons[0].addListener (SWT.Selection, listener); + buttons[1] = new Button (composite, SWT.PUSH); + buttons[1].setText (SWT.getMessage("SWT_OK")); //$NON-NLS-1$ + buttons[1].setLayoutData (new GridData (GridData.FILL_HORIZONTAL)); + buttons[1].addListener (SWT.Selection, listener); + + shell.setDefaultButton (buttons[1]); + shell.pack (); + shell.open (); + Display display = browser.getDisplay (); + while (!shell.isDisposed ()) { + if (!display.readAndDispatch ()) display.sleep (); + } + + return result[0]; +} + +int /*long*/ webView_identifierForInitialRequest_fromDataSource(int /*long*/ sender, int /*long*/ request, int /*long*/ dataSourceID) { + final Display display = browser.getDisplay(); + final ProgressEvent progress = new ProgressEvent(browser); + progress.display = display; + progress.widget = browser; + progress.current = resourceCount; + progress.total = Math.max(resourceCount, MAX_PROGRESS); + for (int i = 0; i < progressListeners.length; i++) { + final ProgressListener listener = progressListeners[i]; + /* + * Note on WebKit. Running the event loop from a Browser + * delegate callback breaks the WebKit (stop loading or + * crash). The widget ProgressBar currently touches the + * event loop every time the method setSelection is called. + * The workaround is to invoke Display.asyncexec so that + * the Browser does not crash when the user updates the + * selection of the ProgressBar. + */ + display.asyncExec( + new Runnable() { + public void run() { + if (!display.isDisposed() && !browser.isDisposed()) + listener.changed(progress); + } + } + ); + } + + NSNumber identifier = NSNumber.numberWithInt(resourceCount++); + if (this.identifier == 0) { + WebDataSource dataSource = new WebDataSource(dataSourceID); + WebFrame frame = dataSource.webFrame(); + if (frame.id == webView.mainFrame().id) this.identifier = identifier.id; + } + return identifier.id; + +} + +int /*long*/ webView_resource_willSendRequest_redirectResponse_fromDataSource(int /*long*/ sender, int /*long*/ identifier, int /*long*/ request, int /*long*/ redirectResponse, int /*long*/ dataSource) { + NSURLRequest nsRequest = new NSURLRequest (request); + NSURL url = nsRequest.URL (); + if (url.isFileURL ()) { + NSMutableURLRequest newRequest = new NSMutableURLRequest (nsRequest.mutableCopy ()); + newRequest.autorelease (); + newRequest.setCachePolicy (OS.NSURLRequestReloadIgnoringLocalCacheData); + return newRequest.id; + } + return request; +} + +/* UIDelegate */ + +int /*long*/ webView_createWebViewWithRequest(int /*long*/ sender, int /*long*/ request) { + WindowEvent newEvent = new WindowEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + newEvent.required = true; + if (openWindowListeners != null) { + for (int i = 0; i < openWindowListeners.length; i++) { + openWindowListeners[i].open(newEvent); + } + } + WebView result = null; + Browser browser = null; + if (newEvent.browser != null && newEvent.browser.webBrowser instanceof Safari) { + browser = newEvent.browser; + } + if (browser != null && !browser.isDisposed()) { + result = ((Safari)browser.webBrowser).webView; + if (request != 0) { + WebFrame mainFrame = result.mainFrame(); + mainFrame.loadRequest(new NSURLRequest(request)); + } + } + return result != null ? result.id : 0; +} + +void webViewShow(int /*long*/ sender) { + /* + * Feature on WebKit. The Safari WebKit expects the application + * to create a new Window using the Objective C Cocoa API in response + * to UIDelegate.createWebViewWithRequest. The application is then + * expected to use Objective C Cocoa API to make this window visible + * when receiving the UIDelegate.webViewShow message. For some reason, + * a window created with the Carbon API hosting the new browser instance + * does not redraw until it has been resized. The fix is to increase the + * size of the Shell and restore it to its initial size. + */ + Shell parent = browser.getShell(); + Point pt = parent.getSize(); + parent.setSize(pt.x+1, pt.y); + parent.setSize(pt.x, pt.y); + WindowEvent newEvent = new WindowEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + if (location != null) newEvent.location = location; + if (size != null) newEvent.size = size; + /* + * Feature in Safari. Safari's tool bar contains + * the address bar. The address bar is displayed + * if the tool bar is displayed. There is no separate + * notification for the address bar. + * + * Feature of OSX. The menu bar is always displayed. + * There is no notification to hide the menu bar. + */ + newEvent.addressBar = toolBar; + newEvent.menuBar = true; + newEvent.statusBar = statusBar; + newEvent.toolBar = toolBar; + for (int i = 0; i < visibilityWindowListeners.length; i++) { + visibilityWindowListeners[i].show(newEvent); + } + location = null; + size = null; +} + +void webView_setFrame(int /*long*/ sender, int /*long*/ frame) { + NSRect rect = new NSRect(); + OS.memmove(rect, frame, NSRect.sizeof); + /* convert to SWT system coordinates */ + Rectangle bounds = browser.getDisplay().getBounds(); + location = new Point((int)rect.x, bounds.height - (int)rect.y - (int)rect.height); + size = new Point((int)rect.width, (int)rect.height); +} + +void webViewFocus(int /*long*/ sender) { +} + +void webViewUnfocus(int /*long*/ sender) { +} + +void webView_runJavaScriptAlertPanelWithMessage(int /*long*/ sender, int /*long*/ messageID) { + NSString message = new NSString(messageID); + String text = message.getString(); + + MessageBox messageBox = new MessageBox(browser.getShell(), SWT.OK | SWT.ICON_WARNING); + messageBox.setText("Javascript"); //$NON-NLS-1$ + messageBox.setMessage(text); + messageBox.open(); +} + +int webView_runJavaScriptConfirmPanelWithMessage(int /*long*/ sender, int /*long*/ messageID) { + NSString message = new NSString(messageID); + String text = message.getString(); + + MessageBox messageBox = new MessageBox(browser.getShell(), SWT.OK | SWT.CANCEL | SWT.ICON_QUESTION); + messageBox.setText("Javascript"); //$NON-NLS-1$ + messageBox.setMessage(text); + return messageBox.open() == SWT.OK ? 1 : 0; +} + +void webView_runOpenPanelForFileButtonWithResultListener(int /*long*/ sender, int /*long*/ resultListenerID) { + FileDialog dialog = new FileDialog(browser.getShell(), SWT.NONE); + String result = dialog.open(); + WebOpenPanelResultListener resultListener = new WebOpenPanelResultListener(resultListenerID); + if (result == null) { + resultListener.cancel(); + return; + } + resultListener.chooseFilename(NSString.stringWith(result)); +} + +void webViewClose(int /*long*/ sender) { + Shell parent = browser.getShell(); + 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(); + if (parent.isDisposed()) return; + /* + * Feature on WebKit. The Safari WebKit expects the application + * to create a new Window using the Objective C Cocoa API in response + * to UIDelegate.createWebViewWithRequest. The application is then + * expected to use Objective C Cocoa API to make this window visible + * when receiving the UIDelegate.webViewShow message. For some reason, + * a window created with the Carbon API hosting the new browser instance + * does not redraw until it has been resized. The fix is to increase the + * size of the Shell and restore it to its initial size. + */ + Point pt = parent.getSize(); + parent.setSize(pt.x+1, pt.y); + parent.setSize(pt.x, pt.y); +} + +int /*long*/ webView_contextMenuItemsForElement_defaultMenuItems(int /*long*/ sender, int /*long*/ element, int /*long*/ defaultMenuItems) { + Point pt = browser.getDisplay().getCursorLocation(); + Event event = new Event(); + event.x = pt.x; + event.y = pt.y; + browser.notifyListeners(SWT.MenuDetect, event); + Menu menu = browser.getMenu(); + if (!event.doit) return 0; + if (menu != null && !menu.isDisposed()) { + if (event.x != pt.x || event.y != pt.y) { + menu.setLocation(event.x, event.y); + } + menu.setVisible(true); + return 0; + } + return defaultMenuItems; +} + +void webView_setStatusBarVisible(int /*long*/ sender, boolean visible) { + /* Note. Webkit only emits the notification when the status bar should be hidden. */ + statusBar = visible; +} + +void webView_setStatusText(int /*long*/ sender, int /*long*/ textID) { + NSString text = new NSString(textID); + int length = (int)/*64*/text.length(); + if (length == 0) return; + + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = browser.getDisplay(); + statusText.widget = browser; + statusText.text = text.getString(); + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } +} + +void webView_setResizable(int /*long*/ sender, boolean visible) { +} + +void webView_setToolbarsVisible(int /*long*/ sender, boolean visible) { + /* Note. Webkit only emits the notification when the tool bar should be hidden. */ + toolBar = visible; +} + +void webView_mouseDidMoveOverElement_modifierFlags (int /*long*/ sender, int /*long*/ elementInformationID, int /*long*/ modifierFlags) { + if (elementInformationID == 0) return; + + NSString key = NSString.stringWith(WebElementLinkURLKey); + NSDictionary elementInformation = new NSDictionary(elementInformationID); + id value = elementInformation.valueForKey(key); + if (value == null) { + /* not currently over a link */ + if (lastHoveredLinkURL == null) return; + lastHoveredLinkURL = null; + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = browser.getDisplay(); + statusText.widget = browser; + statusText.text = ""; //$NON-NLS-1$ + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } + return; + } + + NSString url = new NSURL(value.id).absoluteString(); + int length = (int)/*64*/url.length(); + String urlString; + if (length == 0) { + urlString = ""; //$NON-NLS-1$ + } else { + urlString = url.getString(); + } + if (urlString.equals(lastHoveredLinkURL)) return; + + lastHoveredLinkURL = urlString; + StatusTextEvent statusText = new StatusTextEvent(browser); + statusText.display = browser.getDisplay(); + statusText.widget = browser; + statusText.text = urlString; + for (int i = 0; i < statusTextListeners.length; i++) { + statusTextListeners[i].changed(statusText); + } +} + +void webView_printFrameView (int /*long*/ sender, int /*long*/ frameViewID) { + WebFrameView view = new WebFrameView(frameViewID); + boolean viewPrint = view.documentViewShouldHandlePrint(); + if (viewPrint) { + view.printDocumentView(); + return; + } + NSPrintInfo info = NSPrintInfo.sharedPrintInfo(); + NSPrintOperation operation = view.printOperationWithPrintInfo(info); + if (operation != null) operation.runOperation(); +} + +/* PolicyDelegate */ + +void webView_decidePolicyForMIMEType_request_frame_decisionListener(int /*long*/ sender, int /*long*/ type, int /*long*/ request, int /*long*/ frame, int /*long*/ listenerID) { + boolean canShow = WebView.canShowMIMEType(new NSString(type)); + WebPolicyDecisionListener listener = new WebPolicyDecisionListener(listenerID); + if (canShow) { + listener.use(); + } else { + listener.download(); + } +} + +void webView_decidePolicyForNavigationAction_request_frame_decisionListener(int /*long*/ sender, int /*long*/ actionInformation, int /*long*/ request, int /*long*/ frame, int /*long*/ listenerID) { + NSURL url = new NSURLRequest(request).URL(); + WebPolicyDecisionListener listener = new WebPolicyDecisionListener(listenerID); + if (url == null) { + /* indicates that a URL with an invalid format was specified */ + listener.ignore(); + return; + } + NSString s = url.absoluteString(); + String url2 = s.getString(); + /* + * If the URI indicates that the page is being rendered from memory + * (via setText()) then set it to about:blank to be consistent with IE. + */ + if (url2.equals (URI_FROMMEMORY)) url2 = ABOUT_BLANK; + + LocationEvent newEvent = new LocationEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + newEvent.location = url2; + newEvent.doit = true; + if (locationListeners != null) { + changingLocation = true; + for (int i = 0; i < locationListeners.length; i++) { + locationListeners[i].changing(newEvent); + } + changingLocation = false; + } + if (newEvent.doit) { + if (jsEnabledChanged) { + jsEnabledChanged = false; + if (preferences == null) { + preferences = (WebPreferences)new WebPreferences ().alloc ().init (); + webView.setPreferences (preferences); + } + preferences.setJavaScriptEnabled (jsEnabled); + } + listener.use(); + lastNavigateURL = url2; + } else { + listener.ignore(); + } + if (html != null && !browser.isDisposed()) { + String html = this.html; + this.html = null; + _setText(html); + } +} + +void webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener(int /*long*/ sender, int /*long*/ actionInformation, int /*long*/ request, int /*long*/ frameName, int /*long*/ listenerID) { + WebPolicyDecisionListener listener = new WebPolicyDecisionListener(listenerID); + listener.use(); +} + +void webView_unableToImplementPolicyWithError_frame(int /*long*/ sender, int /*long*/ error, int /*long*/ frame) { +} + +/* WebDownload */ + +void download_decideDestinationWithSuggestedFilename(int /*long*/ downloadId, int /*long*/ filename) { + NSString string = new NSString(filename); + String name = string.getString(); + FileDialog dialog = new FileDialog(browser.getShell(), SWT.SAVE); + dialog.setText(SWT.getMessage ("SWT_FileDownload")); //$NON-NLS-1$ + dialog.setFileName(name); + String path = dialog.open(); + NSURLDownload download = new NSURLDownload(downloadId); + if (path == null) { + /* cancel pressed */ + download.cancel(); + return; + } + download.setDestination(NSString.stringWith(path), true); +} + +/* DOMEventListener */ + +void handleEvent(int /*long*/ evtId) { + NSString string = new NSString(OS.objc_msgSend(evtId, OS.sel_type)); + String type = string.getString(); + + if (DOMEVENT_KEYDOWN.equals(type) || DOMEVENT_KEYUP.equals(type)) { + DOMKeyboardEvent event = new DOMKeyboardEvent(evtId); + + boolean ctrl = event.ctrlKey(); + boolean shift = event.shiftKey(); + boolean alt = event.altKey(); + boolean meta = event.metaKey(); + int keyCode = event.keyCode(); + int charCode = event.charCode(); + + Event keyEvent = new Event(); + keyEvent.widget = browser; + int eventType = DOMEVENT_KEYDOWN.equals(type) ? SWT.KeyDown : SWT.KeyUp; + keyEvent.type = eventType; + int translatedKey = translateKey (keyCode); + keyEvent.keyCode = translatedKey; + keyEvent.character = (char)charCode; + int stateMask = (alt ? SWT.ALT : 0) | (ctrl ? SWT.CTRL : 0) | (shift ? SWT.SHIFT : 0) | (meta ? SWT.COMMAND : 0); + keyEvent.stateMask = stateMask; + browser.notifyListeners(keyEvent.type, keyEvent); + if (!keyEvent.doit) { + event.preventDefault(); + } else { + if (eventType == SWT.KeyDown && stateMask == SWT.COMMAND) { + if (translatedKey == 'v') { + webView.paste (webView); + } else if (translatedKey == 'c') { + webView.copy (webView); + } else if (translatedKey == 'x') { + webView.cut (webView); + } + } + } + return; + } + + if (DOMEVENT_MOUSEWHEEL.equals(type)) { + DOMWheelEvent event = new DOMWheelEvent(evtId); + int clientX = event.clientX(); + int clientY = event.clientY(); + int delta = event.wheelDelta(); + boolean ctrl = event.ctrlKey(); + boolean shift = event.shiftKey(); + boolean alt = event.altKey(); + boolean meta = event.metaKey(); + Event mouseEvent = new Event(); + mouseEvent.type = SWT.MouseWheel; + mouseEvent.widget = browser; + mouseEvent.x = clientX; mouseEvent.y = clientY; + mouseEvent.count = delta / 120; + mouseEvent.stateMask = (alt ? SWT.ALT : 0) | (ctrl ? SWT.CTRL : 0) | (shift ? SWT.SHIFT : 0) | (meta ? SWT.COMMAND : 0); + browser.notifyListeners (mouseEvent.type, mouseEvent); + return; + } + + /* mouse event */ + + DOMMouseEvent event = new DOMMouseEvent(evtId); + + int clientX = event.clientX(); + int clientY = event.clientY(); + int detail = event.detail(); + int button = event.button(); + boolean ctrl = event.ctrlKey(); + boolean shift = event.shiftKey(); + boolean alt = event.altKey(); + boolean meta = event.metaKey(); + + Event mouseEvent = new Event (); + mouseEvent.widget = browser; + mouseEvent.x = clientX; mouseEvent.y = clientY; + mouseEvent.stateMask = (alt ? SWT.ALT : 0) | (ctrl ? SWT.CTRL : 0) | (shift ? SWT.SHIFT : 0) | (meta ? SWT.COMMAND : 0); + if (DOMEVENT_MOUSEDOWN.equals (type)) { + mouseEvent.type = SWT.MouseDown; + mouseEvent.button = button + 1; + mouseEvent.count = detail; + } else if (DOMEVENT_MOUSEUP.equals (type)) { + mouseEvent.type = SWT.MouseUp; + mouseEvent.button = button + 1; + mouseEvent.count = detail; + switch (mouseEvent.button) { + case 1: mouseEvent.stateMask |= SWT.BUTTON1; break; + case 2: mouseEvent.stateMask |= SWT.BUTTON2; break; + case 3: mouseEvent.stateMask |= SWT.BUTTON3; break; + case 4: mouseEvent.stateMask |= SWT.BUTTON4; break; + case 5: mouseEvent.stateMask |= SWT.BUTTON5; break; + } + } else if (DOMEVENT_MOUSEMOVE.equals (type)) { + /* + * Bug in Safari. Spurious and redundant mousemove events are received in + * various contexts, including following every MouseUp. The workaround is + * to not fire MouseMove events whose x and y values match the last MouseMove + */ + if (mouseEvent.x == lastMouseMoveX && mouseEvent.y == lastMouseMoveY) return; + mouseEvent.type = SWT.MouseMove; + lastMouseMoveX = mouseEvent.x; lastMouseMoveY = mouseEvent.y; + } + + browser.notifyListeners (mouseEvent.type, mouseEvent); + if (detail == 2 && DOMEVENT_MOUSEDOWN.equals (type)) { + mouseEvent = new Event (); + mouseEvent.widget = browser; + mouseEvent.x = clientX; mouseEvent.y = clientY; + mouseEvent.stateMask = (alt ? SWT.ALT : 0) | (ctrl ? SWT.CTRL : 0) | (shift ? SWT.SHIFT : 0) | (meta ? SWT.COMMAND : 0); + mouseEvent.type = SWT.MouseDoubleClick; + mouseEvent.button = button + 1; + mouseEvent.count = detail; + browser.notifyListeners (mouseEvent.type, mouseEvent); + } +} + +/* external */ + +Object convertToJava (int /*long*/ value) { + NSObject object = new NSObject (value); + int /*long*/ clazz = OS.objc_lookUpClass ("NSString"); //$NON-NLS-1$ + if (object.isKindOfClass (clazz)) { + NSString string = new NSString (value); + return string.getString (); + } + clazz = OS.objc_lookUpClass ("NSNumber"); //$NON-NLS-1$ + if (object.isKindOfClass (clazz)) { + NSNumber number = new NSNumber (value); + int /*long*/ ptr = number.objCType (); + byte[] type = new byte[1]; + OS.memmove (type, ptr, 1); + if (type[0] == 'c' || type[0] == 'B') { + return new Boolean (number.boolValue ()); + } + if ("islqISLQfd".indexOf (type[0]) != -1) { //$NON-NLS-1$ + return new Double (number.doubleValue ()); + } + } + clazz = OS.objc_lookUpClass ("WebScriptObject"); //$NON-NLS-1$ + if (object.isKindOfClass (clazz)) { + WebScriptObject script = new WebScriptObject (value); + id id = script.valueForKey (NSString.stringWith ("length")); //$NON-NLS-1$ + if (id == null) { /* not a JS array */ + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + int length = new NSNumber (id).intValue (); + Object[] arguments = new Object[length]; + for (int i = 0; i < length; i++) { + id current = script.webScriptValueAtIndex (i); + if (current != null) { + arguments[i] = convertToJava (current.id); + } + } + return arguments; + } + clazz = OS.objc_lookUpClass ("WebUndefined"); //$NON-NLS-1$ + if (object.isKindOfClass (clazz)) { + return null; + } + + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + return null; +} + +NSObject convertToJS (Object value) { + if (value == null) { + return WebUndefined.undefined (); + } + if (value instanceof String) { + return NSString.stringWith ((String)value); + } + if (value instanceof Boolean) { + return NSNumber.numberWithBool (((Boolean)value).booleanValue ()); + } + if (value instanceof Number) { + return NSNumber.numberWithDouble (((Number)value).doubleValue ()); + } + if (value instanceof Object[]) { + Object[] arrayValue = (Object[]) value; + int length = arrayValue.length; + if (length > 0) { + NSMutableArray array = NSMutableArray.arrayWithCapacity (length); + for (int i = 0; i < length; i++) { + Object currentObject = arrayValue[i]; + array.addObject (convertToJS (currentObject)); + } + return array; + } + } + SWT.error (SWT.ERROR_INVALID_RETURN_VALUE); + return null; +} + +NSObject callJava (int /*long*/ index, int /*long*/ args, int /*long*/ arg1) { + Object returnValue = null; + NSObject object = new NSObject (index); + int /*long*/ clazz = OS.objc_lookUpClass ("NSNumber"); //$NON-NLS-1$ + if (object.isKindOfClass (clazz)) { + NSNumber number = new NSNumber (index); + Object key = new Integer (number.intValue ()); + BrowserFunction function = (BrowserFunction)functions.get (key); + if (function != null) { + try { + Object temp = convertToJava (args); + if (temp instanceof Object[]) { + Object[] arguments = (Object[])temp; + try { + returnValue = function.function (arguments); + } catch (Exception e) { + /* exception during function invocation */ + returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ()); + } + } + } catch (IllegalArgumentException e) { + /* invalid argument value type */ + if (function.isEvaluate) { + /* notify the evaluate function so that a java exception can be thrown */ + function.function (new String[] {WebBrowser.CreateErrorString (new SWTException (SWT.ERROR_INVALID_RETURN_VALUE).getLocalizedMessage ())}); + } + returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ()); + } + } + } + try { + return convertToJS (returnValue); + } catch (SWTException e) { + /* invalid return value type */ + return convertToJS (WebBrowser.CreateErrorString (e.getLocalizedMessage ())); + } +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/AuthenticationEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/AuthenticationEvent.java new file mode 100644 index 0000000000..9feeec1c71 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/AuthenticationEvent.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 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 org.eclipse.swt.widgets.*; +import org.eclipse.swt.events.*; + +/** + * An <code>AuthenticationEvent</code> is sent by a {@link Browser} + * to {@link AuthenticationListener}'s when the <code>Browser</code> + * navigates to a page that requires authentication. This event allows + * a client to either supply authentication credentials, cancel the + * authentication, or do nothing (which causes an authentication prompter + * to be shown to the user). + * + * @since 3.5 + */ +public class AuthenticationEvent extends TypedEvent { + /** The location that triggered the authentication challenge */ + public String location; + + /** The user name to authenticate with */ + public String user; + + /** The password to authenticate with */ + public String password; + + /** + * A flag indicating whether the authentication should proceed. + * Setting this field to <code>false</code> will cancel the operation. + */ + public boolean doit = true; + + static final long serialVersionUID = -8322331206780057921L; + +/** + * Constructs a new instance of this class. + * + * @param widget the widget that fired the event + * + * @since 3.5 + */ +public AuthenticationEvent(Widget widget) { + super(widget); +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the event + */ +public String toString() { + String string = super.toString (); + return string.substring (0, string.length() - 1) // remove trailing '}' + + " name=" + user + + " password=" + password + + " location=" + location + + "}"; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/AuthenticationListener.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/AuthenticationListener.java new file mode 100644 index 0000000000..4f8d342c33 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/AuthenticationListener.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 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 org.eclipse.swt.internal.SWTEventListener; + +/** + * This listener interface may be implemented in order to receive + * an {@link AuthenticationEvent} notification when the {@link Browser} + * encounters a page that requires authentication. + * + * @see Browser#addAuthenticationListener(AuthenticationListener) + * @see Browser#removeAuthenticationListener(AuthenticationListener) + * + * @since 3.5 + */ +public interface AuthenticationListener extends SWTEventListener { + +/** + * This method is called when a page is navigated to that requires + * authentication. + * <p> + * Setting both the event's <code>user</code> and <code>password</code> + * fields causes these values to be used as credentials for authentication. + * Leaving one or both of these fields as <code>null</code> indicates + * that credentials are not known, so an authentication prompter should + * be shown to the user. Otherwise, setting the event's <code>doit</code> + * field to <code>false</code> cancels the authentication challenge, and + * the page will not be loaded. + * <p> + * + * <p>The following fields in the <code>AuthenticationEvent</code> apply: + * <ul> + * <li>(in) widget the <code>Browser</code> that is attempting to show the + * page that requires authentication + * <li>(in) location the location issuing the authentication challenge + * <li>(in/out) doit can be set to <code>false</code> to cancel the + * authentication challenge + * <li>(out) user the user name to authenticate with + * <li>(out) password the password to authenticate with + * </ul> + * + * @param event the <code>AuthenticationEvent</code> that can be used to + * either supply authentication credentials, defer credential input to + * an authentication prompter, or cancel an authentication challenge. + */ +public void authenticate(AuthenticationEvent event); +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/Browser.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/Browser.java new file mode 100755 index 0000000000..05fcc056e3 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/Browser.java @@ -0,0 +1,992 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 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 org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; + +/** + * Instances of this class implement the browser user interface + * metaphor. It allows the user to visualize and navigate through + * HTML documents. + * <p> + * Note that although this class is a subclass of <code>Composite</code>, + * it does not make sense to set a layout on it. + * </p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>MOZILLA</dd> + * <dt><b>Events:</b></dt> + * <dd>CloseWindowListener, LocationListener, OpenWindowListener, ProgressListener, StatusTextListener, TitleListener, VisibilityWindowListener</dd> + * </dl> + * <p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#browser">Browser snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: ControlExample, BrowserExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + * @noextend This class is not intended to be subclassed by clients. + */ + +public class Browser extends Composite { + WebBrowser webBrowser; + int userStyle; + + static final String PACKAGE_PREFIX = "org.eclipse.swt.browser."; //$NON-NLS-1$ + static final String NO_INPUT_METHOD = "org.eclipse.swt.internal.gtk.noInputMethod"; //$NON-NLS-1$ + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a widget which will be the parent of the new instance (cannot be null) + * @param style the style of widget to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for browser creation</li> + * </ul> + * + * @see Widget#getStyle + * + * @since 3.0 + */ +public Browser (Composite parent, int style) { + super (checkParent (parent), checkStyle (style)); + userStyle = style; + + String platform = SWT.getPlatform (); + Display display = parent.getDisplay (); + if ("gtk".equals (platform)) display.setData (NO_INPUT_METHOD, null); //$NON-NLS-1$ + String className = null; + if ((style & SWT.MOZILLA) != 0) { + className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$ + } else { + if ("win32".equals (platform) || "wpf".equals (platform)) { //$NON-NLS-1$ $NON-NLS-2$ + className = "org.eclipse.swt.browser.IE"; //$NON-NLS-1$ + } else if ("motif".equals (platform)) { //$NON-NLS-1$ + className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$ + } else if ("gtk".equals (platform)) { //$NON-NLS-1$ + className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$ + } else if ("carbon".equals (platform) || "cocoa".equals (platform)) { //$NON-NLS-1$ + className = "org.eclipse.swt.browser.Safari"; //$NON-NLS-1$ + } else if ("photon".equals (platform)) { //$NON-NLS-1$ + className = "org.eclipse.swt.browser.Voyager"; //$NON-NLS-1$ + } else { + dispose (); + SWT.error (SWT.ERROR_NO_HANDLES); + } + } + + try { + Class clazz = Class.forName (className); + webBrowser = (WebBrowser)clazz.newInstance (); + } catch (ClassNotFoundException e) { + } catch (IllegalAccessException e) { + } catch (InstantiationException e) { + } + if (webBrowser == null) { + dispose (); + SWT.error (SWT.ERROR_NO_HANDLES); + } + + webBrowser.setBrowser (this); + webBrowser.create (parent, style); +} + +static Composite checkParent (Composite parent) { + String platform = SWT.getPlatform (); + if (!"gtk".equals (platform)) return parent; //$NON-NLS-1$ + + /* + * Note. Mozilla provides all IM support needed for text input in web pages. + * If SWT creates another input method context for the widget it will cause + * indeterminate results to happen (hangs and crashes). The fix is to prevent + * SWT from creating an input method context for the Browser widget. + */ + if (parent != null && !parent.isDisposed ()) { + Display display = parent.getDisplay (); + if (display != null) { + if (display.getThread () == Thread.currentThread ()) { + display.setData (NO_INPUT_METHOD, "true"); //$NON-NLS-1$ + } + } + } + return parent; +} + +static int checkStyle(int style) { + String platform = SWT.getPlatform (); + if ((style & SWT.MOZILLA) != 0) { + if ("carbon".equals (platform)) return style | SWT.EMBEDDED; //$NON-NLS-1$ + if ("motif".equals (platform)) return style | SWT.EMBEDDED; //$NON-NLS-1$ + return style; + } + + if ("win32".equals (platform)) { //$NON-NLS-1$ + /* + * For IE on win32 the border is supplied by the embedded browser, so remove + * the style so that the parent Composite will not draw a second border. + */ + return style & ~SWT.BORDER; + } else if ("motif".equals (platform)) { //$NON-NLS-1$ + return style | SWT.EMBEDDED; + } + return style; +} + +protected void checkWidget () { + super.checkWidget (); +} + +/** + * Clears all session cookies from all current Browser instances. + * + * @since 3.2 + */ +public static void clearSessions () { + WebBrowser.clearSessions (); +} + +/** + * Returns the value of a cookie that is associated with a URL. + * Note that cookies are shared amongst all Browser instances. + * + * @param name the cookie name + * @param url the URL that the cookie is associated with + * @return the cookie value, or <code>null</code> if no such cookie exists + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the name is null</li> + * <li>ERROR_NULL_ARGUMENT - if the url is null</li> + * </ul> + * + * @since 3.5 + */ +public static String getCookie (String name, String url) { + if (name == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (url == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return WebBrowser.GetCookie (name, url); +} + +/** + * Sets a cookie on a URL. Note that cookies are shared amongst all Browser instances. + * + * The <code>value</code> parameter must be a cookie header string that + * complies with <a href="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</code>. + * The value is passed through to the native browser unchanged. + * <p> + * Example value strings: + * <code>foo=bar</code> (basic session cookie) + * <code>foo=bar; path=/; domain=.eclipse.org</code> (session cookie) + * <code>foo=bar; expires=Thu, 01-Jan-2030 00:00:01 GMT</code> (persistent cookie) + * <code>foo=; expires=Thu, 01-Jan-1970 00:00:01 GMT</code> (deletes cookie <code>foo</code>) + * + * @param value the cookie value + * @param url the URL to associate the cookie with + * @return <code>true</code> if the cookie was successfully set and <code>false</code> otherwise + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the value is null</li> + * <li>ERROR_NULL_ARGUMENT - if the url is null</li> + * </ul> + * + * @since 3.5 + */ +public static boolean setCookie (String value, String url) { + if (value == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (url == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return WebBrowser.SetCookie (value, url); +} + +/** + * Adds the listener to the collection of listeners who will be + * notified when authentication is required. + * <p> + * This notification occurs when a page requiring authentication is + * encountered. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.5 + */ +public void addAuthenticationListener (AuthenticationListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.addAuthenticationListener (listener); +} + +/** + * Adds the listener to the collection of listeners who will be + * notified when the window hosting the receiver should be closed. + * <p> + * This notification occurs when a javascript command such as + * <code>window.close</code> gets executed by a <code>Browser</code>. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void addCloseWindowListener (CloseWindowListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.addCloseWindowListener (listener); +} + +/** + * Adds the listener to the collection of listeners who will be + * notified when the current location has changed or is about to change. + * <p> + * This notification typically occurs when the application navigates + * to a new location with {@link #setUrl(String)} or when the user + * activates a hyperlink. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void addLocationListener (LocationListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.addLocationListener (listener); +} + +/** + * Adds the listener to the collection of listeners who will be + * notified when a new window needs to be created. + * <p> + * This notification occurs when a javascript command such as + * <code>window.open</code> gets executed by a <code>Browser</code>. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void addOpenWindowListener (OpenWindowListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.addOpenWindowListener (listener); +} + +/** + * Adds the listener to the collection of listeners who will be + * notified when a progress is made during the loading of the current + * URL or when the loading of the current URL has been completed. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void addProgressListener (ProgressListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.addProgressListener (listener); +} + +/** + * Adds the listener to the collection of listeners who will be + * notified when the status text is changed. + * <p> + * The status text is typically displayed in the status bar of + * a browser application. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void addStatusTextListener (StatusTextListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.addStatusTextListener (listener); +} + +/** + * Adds the listener to the collection of listeners who will be + * notified when the title of the current document is available + * or has changed. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void addTitleListener (TitleListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.addTitleListener (listener); +} + +/** + * Adds the listener to the collection of listeners who will be + * notified when a window hosting the receiver needs to be displayed + * or hidden. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void addVisibilityWindowListener (VisibilityWindowListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.addVisibilityWindowListener (listener); +} + +/** + * Navigate to the previous session history item. + * + * @return <code>true</code> if the operation was successful and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @see #forward + * + * @since 3.0 + */ +public boolean back () { + checkWidget(); + return webBrowser.back (); +} + +protected void checkSubclass () { + String name = getClass ().getName (); + int index = name.lastIndexOf ('.'); + if (!name.substring (0, index + 1).equals (PACKAGE_PREFIX)) { + SWT.error (SWT.ERROR_INVALID_SUBCLASS); + } +} + +/** + * Executes the specified script. + * <p> + * Executes a script containing javascript commands in the context of the current document. + * If document-defined functions or properties are accessed by the script then this method + * should not be invoked until the document has finished loading (<code>ProgressListener.completed()</code> + * gives notification of this). + * + * @param script the script with javascript commands + * + * @return <code>true</code> if the operation was successful and <code>false</code> otherwise + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the script is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @see ProgressListener#completed(ProgressEvent) + * + * @since 3.1 + */ +public boolean execute (String script) { + checkWidget(); + if (script == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return webBrowser.execute (script); +} + +/** + * Returns the result, if any, of executing the specified script. + * <p> + * Evaluates a script containing javascript commands in the context of + * the current document. If document-defined functions or properties + * are accessed by the script then this method should not be invoked + * until the document has finished loading (<code>ProgressListener.completed()</code> + * gives notification of this). + * </p><p> + * If the script returns a value with a supported type then a java + * representation of the value is returned. The supported + * javascript -> java mappings are: + * <ul> + * <li>javascript null or undefined -> <code>null</code></li> + * <li>javascript number -> <code>java.lang.Double</code></li> + * <li>javascript string -> <code>java.lang.String</code></li> + * <li>javascript boolean -> <code>java.lang.Boolean</code></li> + * <li>javascript array whose elements are all of supported types -> <code>java.lang.Object[]</code></li> + * </ul> + * + * An <code>SWTException</code> is thrown if the return value has an + * unsupported type, or if evaluating the script causes a javascript + * error to be thrown. + * + * @param script the script with javascript commands + * + * @return the return value, if any, of executing the script + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the script is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_FAILED_EVALUATE when the script evaluation causes a javascript error to be thrown</li> + * <li>ERROR_INVALID_RETURN_VALUE when the script returns a value of unsupported type</li> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @see ProgressListener#completed(ProgressEvent) + * + * @since 3.5 + */ +public Object evaluate (String script) throws SWTException { + checkWidget(); + if (script == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return webBrowser.evaluate (script); +} + +/** + * Navigate to the next session history item. + * + * @return <code>true</code> if the operation was successful and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @see #back + * + * @since 3.0 + */ +public boolean forward () { + checkWidget(); + return webBrowser.forward (); +} + +/** + * Returns the type of native browser being used by this instance. + * Examples: "mozilla", "ie", "safari", "voyager" + * + * @return the type of the native browser + * + * @since 3.5 + */ +public String getBrowserType () { + checkWidget(); + return webBrowser.getBrowserType (); +} + +/** + * Returns <code>true</code> if javascript will be allowed to run in pages + * subsequently viewed in the receiver, and <code>false</code> otherwise. + * + * @return the receiver's javascript enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setJavascriptEnabled + * + * @since 3.5 + */ +public boolean getJavascriptEnabled () { + checkWidget(); + return webBrowser.jsEnabled; +} + +public int getStyle () { + /* + * If SWT.BORDER was specified at creation time then getStyle() should answer + * it even though it is removed for IE on win32 in checkStyle(). + */ + return super.getStyle () | (userStyle & SWT.BORDER); +} + +/** + * Returns a string with HTML that represents the content of the current page. + * + * @return HTML representing the current page or an empty <code>String</code> + * if this is empty + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.4 + */ +public String getText () { + checkWidget(); + return webBrowser.getText (); +} + +/** + * Returns the current URL. + * + * @return the current URL or an empty <code>String</code> if there is no current URL + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @see #setUrl + * + * @since 3.0 + */ +public String getUrl () { + checkWidget(); + return webBrowser.getUrl (); +} + +/** + * Returns the JavaXPCOM <code>nsIWebBrowser</code> for the receiver, or <code>null</code> + * if it is not available. In order for an <code>nsIWebBrowser</code> to be returned all + * of the following must be true: <ul> + * <li>the receiver's style must be <code>SWT.MOZILLA</code></li> + * <li>the classes from JavaXPCOM >= 1.8.1.2 must be resolvable at runtime</li> + * <li>the version of the underlying XULRunner must be >= 1.8.1.2</li> + * </ul> + * + * @return the receiver's JavaXPCOM <code>nsIWebBrowser</code> or <code>null</code> + * + * @since 3.3 + */ +public Object getWebBrowser () { + checkWidget(); + return webBrowser.getWebBrowser (); +} + +/** + * Returns <code>true</code> if the receiver can navigate to the + * previous session history item, and <code>false</code> otherwise. + * + * @return the receiver's back command enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #back + */ +public boolean isBackEnabled () { + checkWidget(); + return webBrowser.isBackEnabled (); +} + +public boolean isFocusControl () { + checkWidget(); + if (webBrowser.isFocusControl ()) return true; + return super.isFocusControl (); +} + +/** + * Returns <code>true</code> if the receiver can navigate to the + * next session history item, and <code>false</code> otherwise. + * + * @return the receiver's forward command enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #forward + */ +public boolean isForwardEnabled () { + checkWidget(); + return webBrowser.isForwardEnabled (); +} + +/** + * Refresh the current page. + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void refresh () { + checkWidget(); + webBrowser.refresh (); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when authentication is required. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.5 + */ +public void removeAuthenticationListener (AuthenticationListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.removeAuthenticationListener (listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the window hosting the receiver should be closed. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void removeCloseWindowListener (CloseWindowListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.removeCloseWindowListener (listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the current location is changed or about to be changed. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void removeLocationListener (LocationListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.removeLocationListener (listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when a new window needs to be created. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void removeOpenWindowListener (OpenWindowListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.removeOpenWindowListener (listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when a progress is made during the loading of the current + * URL or when the loading of the current URL has been completed. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void removeProgressListener (ProgressListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.removeProgressListener (listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the status text is changed. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void removeStatusTextListener (StatusTextListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.removeStatusTextListener (listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the title of the current document is available + * or has changed. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void removeTitleListener (TitleListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.removeTitleListener (listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when a window hosting the receiver needs to be displayed + * or hidden. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void removeVisibilityWindowListener (VisibilityWindowListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + webBrowser.removeVisibilityWindowListener (listener); +} + +/** + * Sets whether javascript will be allowed to run in pages subsequently + * viewed in the receiver. Note that setting this value does not affect + * the running of javascript in the current page. + * + * @param enabled the receiver's new javascript enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.5 + */ +public void setJavascriptEnabled (boolean enabled) { + checkWidget(); + webBrowser.jsEnabled = enabled; + webBrowser.jsEnabledChanged = true; +} + +/** + * Renders a string containing HTML. The rendering of the content occurs asynchronously. + * + * <p> + * The html parameter is Unicode encoded since it is a java <code>String</code>. + * As a result, the HTML meta tag charset should not be set. The charset is implied + * by the <code>String</code> itself. + * + * @param html the HTML content to be rendered + * + * @return true if the operation was successful and false otherwise. + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the html is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @see #setUrl + * + * @since 3.0 + */ +public boolean setText (String html) { + checkWidget(); + if (html == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return webBrowser.setText (html); +} + +/** + * Begins loading a URL. The loading of its content occurs asynchronously. + * + * @param url the URL to be loaded + * + * @return true if the operation was successful and false otherwise. + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the url is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @see #getUrl + * + * @since 3.0 + */ +public boolean setUrl (String url) { + checkWidget(); + if (url == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return webBrowser.setUrl (url); +} + +/** + * Stop any loading and rendering activity. + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void stop () { + checkWidget(); + webBrowser.stop (); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/BrowserFunction.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/BrowserFunction.java new file mode 100644 index 0000000000..f87aef0d47 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/BrowserFunction.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2008 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 org.eclipse.swt.*; + +/** + * Instances of this class represent java-side "functions" that + * are invokable from javascript. Browser clients define these + * functions by subclassing <code>BrowserFunction</code> and + * overriding its <code>function(Object[])</code> method. This + * method will be invoked whenever javascript running in the + * Browser makes a call with the function's name. + * + * <p> + * Application code must explicitly invoke the + * <code>BrowserFunction.dispose()</code> method to release the + * resources managed by each instance when those instances are no + * longer required. Since there is usually a correlation between + * the registering of BrowserFunction(s) in a Browser and the + * loading of a page in the Browser that is aware of these + * functions, the <code>LocationListener.changed()</code> listener + * is often a good place to do this. + * </p><p> + * Note that disposing a Browser automatically disposes all + * BrowserFunctions associated with it. + * </p> + * + * @see #dispose() + * @see #function(Object[]) + * @see org.eclipse.swt.browser.LocationListener#changed(LocationEvent) + * + * @since 3.5 + */ +public class BrowserFunction { + Browser browser; + String name; + String functionString; + int index; + boolean isEvaluate; + +/** + * Constructs a new instance of this class, which will be invokable + * by javascript running in the specified Browser. + * <p> + * You must dispose the BrowserFunction when it is no longer required. + * A common place to do this is in a <code>LocationListener.changed()</code> + * listener. + * </p> + * @param browser the browser whose javascript can invoke this function + * @param name the name that javascript will use to invoke this function + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the browser is null</li> + * <li>ERROR_NULL_ARGUMENT - if the name is null</li> + * </ul> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the browser has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #dispose() + * @see org.eclipse.swt.browser.LocationListener#changed(LocationEvent) + */ +public BrowserFunction (Browser browser, String name) { + this (browser, name, true); +} + +BrowserFunction (Browser browser, String name, boolean create) { + super (); + if (browser == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (name == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (browser.isDisposed ()) SWT.error (SWT.ERROR_WIDGET_DISPOSED); + browser.checkWidget (); + this.browser = browser; + this.name = name; + if (create) browser.webBrowser.createFunction (this); +} + +/** + * Disposes of the resources associated with this BrowserFunction. + * Applications must dispose of all BrowserFunctions that they create. + * </p><p> + * Note that disposing a Browser automatically disposes all + * BrowserFunctions associated with it. + * </p> + */ +public void dispose () { + dispose (true); +} + +void dispose (boolean remove) { + if (index < 0) return; + if (remove) browser.webBrowser.destroyFunction (this); + browser = null; + name = functionString = null; + index = -1; +} + +/** + * Subclasses should override this method. This method is invoked when + * the receiver's function is called from javascript. If all of the + * arguments that are passed to the javascript function call are of + * supported types then this method is invoked with the argument values + * converted as follows: + * + * javascript null or undefined -> <code>null</code> + * javascript number -> <code>java.lang.Double</code> + * javascript string -> <code>java.lang.String</code> + * javascript boolean -> <code>java.lang.Boolean</code> + * javascript array whose elements are all of supported types -> <code>java.lang.Object[]</code> + * + * If any of the Javascript arguments are of unsupported types then the + * function invocation will fail and this method will not be called. + * + * This method must return a value with one of these supported types to + * the javascript caller (note that any subclass of <code>java.lang.Number</code> + * will be successfully converted to a javascript number). + * + * @param arguments the javascript arguments converted to java equivalents + * @return the value to return to the javascript caller + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_FUNCTION_DISPOSED when the BrowserFunction has been disposed</li> + * </ul> + */ +public Object function (Object[] arguments) { + if (index < 0) SWT.error (SWT.ERROR_FUNCTION_DISPOSED); + browser.checkWidget (); + return null; +} + +/** + * Returns the Browser whose pages can invoke this BrowserFunction. + * + * @return the Browser associated with this BrowserFunction + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_FUNCTION_DISPOSED when the BrowserFunction has been disposed</li> + * </ul> + */ +public Browser getBrowser () { + if (index < 0) SWT.error (SWT.ERROR_FUNCTION_DISPOSED); + browser.checkWidget (); + return browser; +} + +/** + * Returns the name that javascript can use to invoke this BrowserFunction. + * + * @return the BrowserFunction's name + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> + * <li>ERROR_FUNCTION_DISPOSED when the BrowserFunction has been disposed</li> + * </ul> + */ +public String getName () { + if (index < 0) SWT.error (SWT.ERROR_FUNCTION_DISPOSED); + browser.checkWidget (); + return name; +} + +/** + * Returns <code>true</code> if this BrowserFunction has been disposed + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the BrowserFunction. + * When a BrowserFunction has been disposed it is an error to + * invoke any of its methods. + * </p><p> + * Note that disposing a Browser automatically disposes all + * BrowserFunctions associated with it. + * </p> + * @return <code>true</code> if this BrowserFunction has been disposed + * and <code>false</code> otherwise + */ +public boolean isDisposed () { + return index < 0; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/CloseWindowListener.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/CloseWindowListener.java new file mode 100644 index 0000000000..b545054e7c --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/CloseWindowListener.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 org.eclipse.swt.internal.SWTEventListener; + +/** + * This listener interface may be implemented in order to receive + * a {@link WindowEvent} notification when a {@link Browser} is + * about to be closed and when its host window should be closed + * by the application. + * + * @see Browser#addCloseWindowListener(CloseWindowListener) + * @see Browser#removeCloseWindowListener(CloseWindowListener) + * @see OpenWindowListener + * @see VisibilityWindowListener + * + * @since 3.0 + */ +public interface CloseWindowListener extends SWTEventListener { + +/** + * This method is called when the window hosting a {@link Browser} should be closed. + * Application would typically close the {@link org.eclipse.swt.widgets.Shell} that + * hosts the <code>Browser</code>. The <code>Browser</code> is disposed after this + * notification. + * + * <p>The following fields in the <code>WindowEvent</code> apply: + * <ul> + * <li>(in) widget the <code>Browser</code> that is going to be disposed + * </ul></p> + * + * @param event the <code>WindowEvent</code> that specifies the <code>Browser</code> + * that is going to be disposed + * + * @see org.eclipse.swt.widgets.Shell#close() + * + * @since 3.0 + */ +public void close(WindowEvent event); +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/LocationAdapter.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/LocationAdapter.java new file mode 100644 index 0000000000..1583f43faa --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/LocationAdapter.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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; + +/** + * This adapter class provides default implementations for the + * methods described by the {@link LocationListener} interface. + * <p> + * Classes that wish to deal with {@link LocationEvent}'s can + * extend this class and override only the methods which they are + * interested in. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + */ +public abstract class LocationAdapter implements LocationListener { + +public void changing(LocationEvent event) { +} + +public void changed(LocationEvent event) { +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/LocationEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/LocationEvent.java new file mode 100644 index 0000000000..0fed868b06 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/LocationEvent.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 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 org.eclipse.swt.widgets.*; +import org.eclipse.swt.events.*; + +/** + * A <code>LocationEvent</code> is sent by a {@link Browser} to + * {@link LocationListener}'s when the <code>Browser</code> + * navigates to a different URL. This notification typically + * occurs when the application navigates to a new location with + * {@link Browser#setUrl(String)} or when the user activates a + * hyperlink. + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + */ +public class LocationEvent extends TypedEvent { + /** current location */ + public String location; + + /** + * A flag indicating whether the location opens in the top frame + * or not. + */ + public boolean top; + + /** + * A flag indicating whether the location loading should be allowed. + * Setting this field to <code>false</code> will cancel the operation. + */ + public boolean doit; + + static final long serialVersionUID = 3906644198244299574L; + +/** + * Constructs a new instance of this class. + * + * @param widget the widget that fired the event + * + * @since 3.5 + */ +public LocationEvent(Widget widget) { + super(widget); +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the event + */ +public String toString() { + String string = super.toString (); + return string.substring (0, string.length() - 1) // remove trailing '}' + + " location=" + location + + " top=" + top + + " doit=" + doit + + "}"; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/LocationListener.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/LocationListener.java new file mode 100644 index 0000000000..b34359ba89 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/LocationListener.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 org.eclipse.swt.internal.SWTEventListener; + +/** + * This listener interface may be implemented in order to receive + * a {@link LocationEvent} notification when a {@link Browser} + * navigates to a different URL. + * + * @see Browser#addLocationListener(LocationListener) + * @see Browser#removeLocationListener(LocationListener) + * + * @since 3.0 + */ +public interface LocationListener extends SWTEventListener { + +/** + * This method is called when the current location is about to be changed. + * <p> + * + * <p>The following fields in the <code>LocationEvent</code> apply: + * <ul> + * <li>(in) location the location to be loaded + * <li>(in) widget the <code>Browser</code> whose location is changing + * <li>(in/out) doit can be set to <code>false</code> to prevent the location + * from being loaded + * </ul> + * + * @param event the <code>LocationEvent</code> that specifies the location + * to be loaded by a <code>Browser</code> + * + * @since 3.0 + */ +public void changing(LocationEvent event); + +/** + * This method is called when the current location is changed. + * <p> + * + * <p>The following fields in the <code>LocationEvent</code> apply: + * <ul> + * <li>(in) location the current location + * <li>(in) top <code>true</code> if the location opens in the top frame or + * <code>false</code> otherwise + * <li>(in) widget the <code>Browser</code> whose location has changed + * </ul> + * + * @param event the <code>LocationEvent</code> that specifies the new + * location of a <code>Browser</code> + * + * @since 3.0 + */ +public void changed(LocationEvent event); + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/OpenWindowListener.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/OpenWindowListener.java new file mode 100644 index 0000000000..140f7c3d33 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/OpenWindowListener.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 org.eclipse.swt.internal.SWTEventListener; + +/** + * This listener interface may be implemented in order to receive + * a {@link WindowEvent} notification when a new {@link Browser} + * needs to be provided by the application. + * + * @see Browser#addOpenWindowListener(OpenWindowListener) + * @see Browser#removeOpenWindowListener(OpenWindowListener) + * @see CloseWindowListener + * @see VisibilityWindowListener + * + * @since 3.0 + */ +public interface OpenWindowListener extends SWTEventListener { + +/** + * This method is called when a new window needs to be created. + * <p> + * A particular <code>Browser</code> can be passed to the event.browser + * field to host the content of a new window. + * <p> + * A standalone system browser is used to host the new window + * if the event.required field value is false and if the event.browser + * field is left <code>null</code>. The event.required field + * is true on platforms that don't support a standalone system browser for + * new window requests. + * <p> + * The navigation is cancelled if the event.required field is set to + * true and the event.browser field is left <code>null</code>. + * <p> + * <p>The following fields in the <code>WindowEvent</code> apply: + * <ul> + * <li>(in/out) required true if the platform requires the user to provide a + * <code>Browser</code> to handle the new window or false otherwise. + * <li>(out) browser the new <code>Browser</code> that will host the + * content of the new window. + * <li>(in) widget the <code>Browser</code> that is requesting to open a + * new window + * </ul> + * + * @param event the <code>WindowEvent</code> that needs to be passed a new + * <code>Browser</code> to handle the new window request + * + * @since 3.0 + */ +public void open(WindowEvent event); +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/ProgressAdapter.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/ProgressAdapter.java new file mode 100644 index 0000000000..98995dbe14 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/ProgressAdapter.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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; + +/** + * This adapter class provides default implementations for the + * methods described by the {@link ProgressListener} interface. + * <p> + * Classes that wish to deal with {@link ProgressEvent}'s can + * extend this class and override only the methods which they are + * interested in. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + */ +public abstract class ProgressAdapter implements ProgressListener { + +public void changed(ProgressEvent event) { +} + +public void completed(ProgressEvent event) { +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/ProgressEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/ProgressEvent.java new file mode 100644 index 0000000000..204e2edaa6 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/ProgressEvent.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 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 org.eclipse.swt.widgets.*; +import org.eclipse.swt.events.*; + +/** + * A <code>ProgressEvent</code> is sent by a {@link Browser} to + * {@link ProgressListener}'s when a progress is made during the + * loading of the current URL or when the loading of the current + * URL has been completed. + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + */ +public class ProgressEvent extends TypedEvent { + /** current value */ + public int current; + /** total value */ + public int total; + + static final long serialVersionUID = 3977018427045393972L; + +/** + * Constructs a new instance of this class. + * + * @param widget the widget that fired the event + * + * @since 3.5 + */ +public ProgressEvent(Widget widget) { + super(widget); +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the event + */ +public String toString() { + String string = super.toString (); + return string.substring (0, string.length() - 1) // remove trailing '}' + + " current=" + current + + " total=" + total + + "}"; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/ProgressListener.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/ProgressListener.java new file mode 100644 index 0000000000..06fbc40f55 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/ProgressListener.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 org.eclipse.swt.internal.SWTEventListener; + +/** + * This listener interface may be implemented in order to receive + * a {@link ProgressEvent} notification when a {@link Browser} + * makes a progress in loading the current URL or when the + * current URL has been loaded. + * + * @see Browser#addProgressListener(ProgressListener) + * @see Browser#removeProgressListener(ProgressListener) + * @see Browser#getUrl() + * + * @since 3.0 + */ +public interface ProgressListener extends SWTEventListener { + +/** + * This method is called when a progress is made during the loading of the + * current location. + * <p> + * + * <p>The following fields in the <code>ProgressEvent</code> apply: + * <ul> + * <li>(in) current the progress for the location currently being loaded + * <li>(in) total the maximum progress for the location currently being loaded + * <li>(in) widget the <code>Browser</code> whose current URL is being loaded + * </ul> + * + * @param event the <code>ProgressEvent</code> related to the loading of the + * current location of a <code>Browser</code> + * + * @since 3.0 + */ +public void changed(ProgressEvent event); + +/** + * This method is called when the current location has been completely loaded. + * <p> + * + * <p>The following fields in the <code>ProgressEvent</code> apply: + * <ul> + * <li>(in) widget the <code>Browser</code> whose current URL has been loaded + * </ul> + * + * @param event the <code>ProgressEvent</code> related to the <code>Browser</code> + * that has loaded its current URL. + * + * @since 3.0 + */ +public void completed(ProgressEvent event); +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/StatusTextEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/StatusTextEvent.java new file mode 100644 index 0000000000..995d76498b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/StatusTextEvent.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 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 org.eclipse.swt.widgets.*; +import org.eclipse.swt.events.*; + +/** + * A <code>StatusTextEvent</code> is sent by a {@link Browser} to + * {@link StatusTextListener}'s when the status text is changed. + * The status text is typically displayed in the status bar of + * a browser application. + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + */ +public class StatusTextEvent extends TypedEvent { + /** status text */ + public String text; + + static final long serialVersionUID = 3258407348371600439L; + +/** + * Constructs a new instance of this class. + * + * @param widget the widget that fired the event + * + * @since 3.5 + */ +public StatusTextEvent(Widget widget) { + super(widget); +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the event + */ +public String toString() { + String string = super.toString (); + return string.substring (0, string.length() - 1) // remove trailing '}' + + " text=" + text + + "}"; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/StatusTextListener.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/StatusTextListener.java new file mode 100644 index 0000000000..68e23fbe3c --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/StatusTextListener.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 org.eclipse.swt.internal.SWTEventListener; + +/** + * This listener interface may be implemented in order to receive + * a {@link StatusTextEvent} notification when the status text for + * a {@link Browser} is changed. + * + * @see Browser#addStatusTextListener(StatusTextListener) + * @see Browser#removeStatusTextListener(StatusTextListener) + * + * @since 3.0 + */ +public interface StatusTextListener extends SWTEventListener { + +/** + * This method is called when the status text is changed. The + * status text is typically displayed in the status bar of a browser + * application. + * <p> + * + * <p>The following fields in the <code>StatusTextEvent</code> apply: + * <ul> + * <li>(in) text the modified status text + * <li>(in) widget the <code>Browser</code> whose status text is changed + * </ul> + * + * @param event the <code>StatusTextEvent</code> that contains the updated + * status description of a <code>Browser</code> + * + * @since 3.0 + */ +public void changed(StatusTextEvent event); +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/TitleEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/TitleEvent.java new file mode 100644 index 0000000000..d2d9eacb23 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/TitleEvent.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 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 org.eclipse.swt.widgets.*; +import org.eclipse.swt.events.*; + +/** + * A <code>TitleEvent</code> is sent by a {@link Browser} to + * {@link TitleListener}'s when the title of the current document + * is available or when it is modified. + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + */ +public class TitleEvent extends TypedEvent { + /** the title of the current document */ + public String title; + + static final long serialVersionUID = 4121132532906340919L; + +/** + * Constructs a new instance of this class. + * + * @param widget the widget that fired the event + * + * @since 3.5 + */ +public TitleEvent(Widget widget) { + super(widget); +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the event + */ +public String toString() { + String string = super.toString (); + return string.substring (0, string.length() - 1) // remove trailing '}' + + " title=" + title + + "}"; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/TitleListener.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/TitleListener.java new file mode 100644 index 0000000000..a6cbaeb42b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/TitleListener.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 org.eclipse.swt.internal.SWTEventListener; + +/** + * This listener interface may be implemented in order to receive + * a {@link TitleEvent} notification when the title of the document + * displayed in a {@link Browser} is known or has been changed. + * + * @see Browser#addTitleListener(TitleListener) + * @see Browser#removeTitleListener(TitleListener) + * + * @since 3.0 + */ +public interface TitleListener extends SWTEventListener { + +/** + * This method is called when the title of the current document + * is available or has changed. + * <p> + * + * <p>The following fields in the <code>TitleEvent</code> apply: + * <ul> + * <li>(in) title the title of the current document + * <li>(in) widget the <code>Browser</code> whose current document's + * title is known or modified + * </ul> + * + * @param event the <code>TitleEvent</code> that contains the title + * of the document currently displayed in a <code>Browser</code> + * + * @since 3.0 + */ +public void changed(TitleEvent event); +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/VisibilityWindowAdapter.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/VisibilityWindowAdapter.java new file mode 100644 index 0000000000..62808fa1c0 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/VisibilityWindowAdapter.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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; + +/** + * This adapter class provides default implementations for the + * methods described by the {@link VisibilityWindowListener} interface. + * <p> + * Classes that wish to deal with {@link WindowEvent}'s can + * extend this class and override only the methods which they are + * interested in. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + */ +public abstract class VisibilityWindowAdapter implements VisibilityWindowListener { + +public void hide(WindowEvent event) { +} + +public void show(WindowEvent event) { +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/VisibilityWindowListener.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/VisibilityWindowListener.java new file mode 100644 index 0000000000..78833d93ea --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/VisibilityWindowListener.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2003, 2007 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 org.eclipse.swt.internal.SWTEventListener; + +/** + * This listener interface may be implemented in order to receive + * a {@link WindowEvent} notification when a window hosting a + * {@link Browser} needs to be displayed or hidden. + * + * @see Browser#addVisibilityWindowListener(VisibilityWindowListener) + * @see Browser#removeVisibilityWindowListener(VisibilityWindowListener) + * @see OpenWindowListener + * @see CloseWindowListener + * + * @since 3.0 + */ +public interface VisibilityWindowListener extends SWTEventListener { + +/** + * This method is called when the window hosting a <code>Browser</code> + * is requested to be hidden. Application would typically hide the + * {@link org.eclipse.swt.widgets.Shell} that hosts the <code>Browser</code>. + * <p> + * + * <p>The following fields in the <code>WindowEvent</code> apply: + * <ul> + * <li>(in) widget the <code>Browser</code> that needs to be hidden + * </ul> + * + * @param event the <code>WindowEvent</code> that specifies the + * <code>Browser</code> that needs to be hidden + * + * @see org.eclipse.swt.widgets.Shell#setVisible(boolean) + * + * @since 3.0 + */ +public void hide(WindowEvent event); + +/** + * This method is called when the window hosting a <code>Browser</code> + * is requested to be displayed. Application would typically set the + * location and the size of the {@link org.eclipse.swt.widgets.Shell} + * that hosts the <code>Browser</code>, if a particular location and size + * are specified. The application would then open that <code>Shell</code>. + * <p> + * + * <p>The following fields in the <code>WindowEvent</code> apply: + * <ul> + * <li>(in) widget the <code>Browser</code> to display + * <li>(in) location the requested location for the <code>Shell</code> + * hosting the browser. It is <code>null</code> if no location is set. + * <li>(in) size the requested size for the <code>Browser</code>. + * The client area of the <code>Shell</code> hosting the + * <code>Browser</code> should be large enough to accommodate that size. + * It is <code>null</code> if no size is set. + * <li>(in) addressBar <code>true</code> if the <code>Shell</code> + * hosting the <code>Browser</code> should display an address bar or + * <code>false</code> otherwise + * <li>(in) menuBar <code>true</code> if the <code>Shell</code> + * hosting the <code>Browser</code> should display a menu bar or + * <code>false</code> otherwise + * <li>(in) statusBar <code>true</code> if the <code>Shell</code> + * hosting the <code>Browser</code> should display a status bar or + * <code>false</code> otherwise + * <li>(in) toolBar <code>true</code> if the <code>Shell</code> + * hosting the <code>Browser</code> should display a tool bar or + * <code>false</code> otherwise + * </ul> + * + * @param event the <code>WindowEvent</code> that specifies the + * <code>Browser</code> that needs to be displayed + * + * @see org.eclipse.swt.widgets.Control#setLocation(org.eclipse.swt.graphics.Point) + * @see org.eclipse.swt.widgets.Control#setSize(org.eclipse.swt.graphics.Point) + * @see org.eclipse.swt.widgets.Shell#open() + * + * @since 3.0 + */ +public void show(WindowEvent event); + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WebBrowser.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WebBrowser.java new file mode 100644 index 0000000000..ea8ef4b38a --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WebBrowser.java @@ -0,0 +1,598 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.browser; + +import java.util.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; + +abstract class WebBrowser { + Browser browser; + Hashtable functions = new Hashtable (); + AuthenticationListener[] authenticationListeners = new AuthenticationListener[0]; + CloseWindowListener[] closeWindowListeners = new CloseWindowListener[0]; + LocationListener[] locationListeners = new LocationListener[0]; + OpenWindowListener[] openWindowListeners = new OpenWindowListener[0]; + ProgressListener[] progressListeners = new ProgressListener[0]; + StatusTextListener[] statusTextListeners = new StatusTextListener[0]; + TitleListener[] titleListeners = new TitleListener[0]; + VisibilityWindowListener[] visibilityWindowListeners = new VisibilityWindowListener[0]; + boolean jsEnabled = true; + boolean jsEnabledChanged; + int nextFunctionIndex = 1; + Object evaluateResult; + + static final String ERROR_ID = "org.eclipse.swt.browser.error"; // $NON-NLS-1$ + static final String EXECUTE_ID = "SWTExecuteTemporaryFunction"; // $NON-NLS-1$ + static String CookieName, CookieValue, CookieUrl; + static boolean CookieResult; + static Runnable MozillaClearSessions, NativeClearSessions; + static Runnable MozillaGetCookie, NativeGetCookie; + static Runnable MozillaSetCookie, NativeSetCookie; + + /* Key Mappings */ + static final int [][] KeyTable = { + /* Keyboard and Mouse Masks */ + {18, SWT.ALT}, + {16, SWT.SHIFT}, + {17, SWT.CONTROL}, + {224, SWT.COMMAND}, + + /* Literal Keys */ + {65, 'a'}, + {66, 'b'}, + {67, 'c'}, + {68, 'd'}, + {69, 'e'}, + {70, 'f'}, + {71, 'g'}, + {72, 'h'}, + {73, 'i'}, + {74, 'j'}, + {75, 'k'}, + {76, 'l'}, + {77, 'm'}, + {78, 'n'}, + {79, 'o'}, + {80, 'p'}, + {81, 'q'}, + {82, 'r'}, + {83, 's'}, + {84, 't'}, + {85, 'u'}, + {86, 'v'}, + {87, 'w'}, + {88, 'x'}, + {89, 'y'}, + {90, 'z'}, + {48, '0'}, + {49, '1'}, + {50, '2'}, + {51, '3'}, + {52, '4'}, + {53, '5'}, + {54, '6'}, + {55, '7'}, + {56, '8'}, + {57, '9'}, + {32, ' '}, + {59, ';'}, + {61, '='}, + {188, ','}, + {190, '.'}, + {191, '/'}, + {219, '['}, + {221, ']'}, + {222, '\''}, + {192, '`'}, + {220, '\\'}, + {108, '|'}, + + /* Non-Numeric Keypad Keys */ + {37, SWT.ARROW_LEFT}, + {39, SWT.ARROW_RIGHT}, + {38, SWT.ARROW_UP}, + {40, SWT.ARROW_DOWN}, + {45, SWT.INSERT}, + {36, SWT.HOME}, + {35, SWT.END}, + {46, SWT.DEL}, + {33, SWT.PAGE_UP}, + {34, SWT.PAGE_DOWN}, + + /* Virtual and Ascii Keys */ + {8, SWT.BS}, + {13, SWT.CR}, + {9, SWT.TAB}, + {27, SWT.ESC}, + {12, SWT.DEL}, + + /* Functions Keys */ + {112, SWT.F1}, + {113, SWT.F2}, + {114, SWT.F3}, + {115, SWT.F4}, + {116, SWT.F5}, + {117, SWT.F6}, + {118, SWT.F7}, + {119, SWT.F8}, + {120, SWT.F9}, + {121, SWT.F10}, + {122, SWT.F11}, + {123, SWT.F12}, + {124, SWT.F13}, + {125, SWT.F14}, + {126, SWT.F15}, + {127, 0}, + {128, 0}, + {129, 0}, + {130, 0}, + {131, 0}, + {132, 0}, + {133, 0}, + {134, 0}, + {135, 0}, + + /* Numeric Keypad Keys */ + {96, SWT.KEYPAD_0}, + {97, SWT.KEYPAD_1}, + {98, SWT.KEYPAD_2}, + {99, SWT.KEYPAD_3}, + {100, SWT.KEYPAD_4}, + {101, SWT.KEYPAD_5}, + {102, SWT.KEYPAD_6}, + {103, SWT.KEYPAD_7}, + {104, SWT.KEYPAD_8}, + {105, SWT.KEYPAD_9}, + {14, SWT.KEYPAD_CR}, + {107, SWT.KEYPAD_ADD}, + {109, SWT.KEYPAD_SUBTRACT}, + {106, SWT.KEYPAD_MULTIPLY}, + {111, SWT.KEYPAD_DIVIDE}, + {110, SWT.KEYPAD_DECIMAL}, + + /* Other keys */ + {20, SWT.CAPS_LOCK}, + {144, SWT.NUM_LOCK}, + {145, SWT.SCROLL_LOCK}, + {44, SWT.PRINT_SCREEN}, + {6, SWT.HELP}, + {19, SWT.PAUSE}, + {3, SWT.BREAK}, + + /* Safari-specific */ + {186, ';'}, + {187, '='}, + {189, '-'}, + }; + +public class EvaluateFunction extends BrowserFunction { + public EvaluateFunction (Browser browser, String name) { + super (browser, name, false); + } + public Object function (Object[] arguments) { + if (arguments[0] instanceof String) { + String string = (String)arguments[0]; + if (string.startsWith (ERROR_ID)) { + String errorString = ExtractError (string); + if (errorString.length () > 0) { + evaluateResult = new SWTException (SWT.ERROR_FAILED_EVALUATE, errorString); + } else { + evaluateResult = new SWTException (SWT.ERROR_FAILED_EVALUATE); + } + return null; + } + } + evaluateResult = arguments[0]; + return null; + } +} + +public void addAuthenticationListener (AuthenticationListener listener) { + AuthenticationListener[] newAuthenticationListeners = new AuthenticationListener[authenticationListeners.length + 1]; + System.arraycopy(authenticationListeners, 0, newAuthenticationListeners, 0, authenticationListeners.length); + authenticationListeners = newAuthenticationListeners; + authenticationListeners[authenticationListeners.length - 1] = listener; +} + +public void addCloseWindowListener (CloseWindowListener listener) { + CloseWindowListener[] newCloseWindowListeners = new CloseWindowListener[closeWindowListeners.length + 1]; + System.arraycopy(closeWindowListeners, 0, newCloseWindowListeners, 0, closeWindowListeners.length); + closeWindowListeners = newCloseWindowListeners; + closeWindowListeners[closeWindowListeners.length - 1] = listener; +} + +public void addLocationListener (LocationListener listener) { + LocationListener[] newLocationListeners = new LocationListener[locationListeners.length + 1]; + System.arraycopy(locationListeners, 0, newLocationListeners, 0, locationListeners.length); + locationListeners = newLocationListeners; + locationListeners[locationListeners.length - 1] = listener; +} + +public void addOpenWindowListener (OpenWindowListener listener) { + OpenWindowListener[] newOpenWindowListeners = new OpenWindowListener[openWindowListeners.length + 1]; + System.arraycopy(openWindowListeners, 0, newOpenWindowListeners, 0, openWindowListeners.length); + openWindowListeners = newOpenWindowListeners; + openWindowListeners[openWindowListeners.length - 1] = listener; +} + +public void addProgressListener (ProgressListener listener) { + ProgressListener[] newProgressListeners = new ProgressListener[progressListeners.length + 1]; + System.arraycopy(progressListeners, 0, newProgressListeners, 0, progressListeners.length); + progressListeners = newProgressListeners; + progressListeners[progressListeners.length - 1] = listener; +} + +public void addStatusTextListener (StatusTextListener listener) { + StatusTextListener[] newStatusTextListeners = new StatusTextListener[statusTextListeners.length + 1]; + System.arraycopy(statusTextListeners, 0, newStatusTextListeners, 0, statusTextListeners.length); + statusTextListeners = newStatusTextListeners; + statusTextListeners[statusTextListeners.length - 1] = listener; +} + +public void addTitleListener (TitleListener listener) { + TitleListener[] newTitleListeners = new TitleListener[titleListeners.length + 1]; + System.arraycopy(titleListeners, 0, newTitleListeners, 0, titleListeners.length); + titleListeners = newTitleListeners; + titleListeners[titleListeners.length - 1] = listener; +} + +public void addVisibilityWindowListener (VisibilityWindowListener listener) { + VisibilityWindowListener[] newVisibilityWindowListeners = new VisibilityWindowListener[visibilityWindowListeners.length + 1]; + System.arraycopy(visibilityWindowListeners, 0, newVisibilityWindowListeners, 0, visibilityWindowListeners.length); + visibilityWindowListeners = newVisibilityWindowListeners; + visibilityWindowListeners[visibilityWindowListeners.length - 1] = listener; +} + +public abstract boolean back (); + +public static void clearSessions () { + if (NativeClearSessions != null) NativeClearSessions.run (); + if (MozillaClearSessions != null) MozillaClearSessions.run (); +} + +public static String GetCookie (String name, String url) { + CookieName = name; CookieUrl = url; + if (NativeGetCookie != null) NativeGetCookie.run (); + if (MozillaGetCookie != null) MozillaGetCookie.run (); + String result = CookieValue; + CookieName = CookieValue = CookieUrl = null; + return result; +} + +public static boolean SetCookie (String value, String url) { + CookieValue = value; CookieUrl = url; + CookieResult = false; + if (NativeSetCookie != null) NativeSetCookie.run (); + if (MozillaSetCookie != null) MozillaSetCookie.run (); + CookieValue = CookieUrl = null; + return CookieResult; +} + +public abstract void create (Composite parent, int style); + +static String CreateErrorString (String error) { + return ERROR_ID + error; +} + +static String ExtractError (String error) { + return error.substring (ERROR_ID.length ()); +} + +public void createFunction (BrowserFunction function) { + /* + * If an existing function with the same name is found then + * remove it so that it is not recreated on subsequent pages + * (the new function overwrites the old one). + */ + Enumeration keys = functions.keys (); + while (keys.hasMoreElements ()) { + Object key = keys.nextElement (); + BrowserFunction current = (BrowserFunction)functions.get (key); + if (current.name.equals (function.name)) { + functions.remove (key); + break; + } + } + + function.index = getNextFunctionIndex (); + registerFunction (function); + + StringBuffer buffer = new StringBuffer ("window."); //$NON-NLS-1$ + buffer.append (function.name); + buffer.append (" = function "); //$NON-NLS-1$ + buffer.append (function.name); + buffer.append ("() {var result = window.external.callJava("); //$NON-NLS-1$ + buffer.append (function.index); + buffer.append (",Array.prototype.slice.call(arguments)); if (typeof result == 'string' && result.indexOf('"); //$NON-NLS-1$ + buffer.append (ERROR_ID); + buffer.append ("') == 0) {var error = new Error(result.substring("); //$NON-NLS-1$ + buffer.append (ERROR_ID.length ()); + buffer.append (")); throw error;} return result;};"); //$NON-NLS-1$ + buffer.append ("for (var i = 0; i < frames.length; i++) {try { frames[i]."); //$NON-NLS-1$ + buffer.append (function.name); + buffer.append (" = window."); //$NON-NLS-1$ + buffer.append (function.name); + buffer.append (";} catch (e) {} };"); //$NON-NLS-1$ + function.functionString = buffer.toString (); + execute (function.functionString); +} + +void deregisterFunction (BrowserFunction function) { + functions.remove (new Integer (function.index)); +} + +public void destroyFunction (BrowserFunction function) { + String deleteString = getDeleteFunctionString (function.name); + StringBuffer buffer = new StringBuffer ("for (var i = 0; i < frames.length; i++) {try {frames[i].eval(\""); //$NON-NLS-1$ + buffer.append (deleteString); + buffer.append ("\");} catch (e) {}}"); //$NON-NLS-1$ + execute (buffer.toString ()); + execute (deleteString); + deregisterFunction (function); +} + +public abstract boolean execute (String script); + +public Object evaluate (String script) throws SWTException { + BrowserFunction function = new EvaluateFunction (browser, ""); // $NON-NLS-1$ + int index = getNextFunctionIndex (); + function.index = index; + function.isEvaluate = true; + registerFunction (function); + String functionName = EXECUTE_ID + index; + + StringBuffer buffer = new StringBuffer ("window."); // $NON-NLS-1$ + buffer.append (functionName); + buffer.append (" = function "); // $NON-NLS-1$ + buffer.append (functionName); + buffer.append ("() {\n"); // $NON-NLS-1$ + buffer.append (script); + buffer.append ("\n};"); // $NON-NLS-1$ + execute (buffer.toString ()); + + buffer = new StringBuffer ("if (window."); // $NON-NLS-1$ + buffer.append (functionName); + buffer.append (" == undefined) {window.external.callJava("); // $NON-NLS-1$ + buffer.append (index); + buffer.append (", ['"); // $NON-NLS-1$ + buffer.append (ERROR_ID); + buffer.append ("']);} else {try {var result = "); // $NON-NLS-1$ + buffer.append (functionName); + buffer.append ("(); window.external.callJava("); // $NON-NLS-1$ + buffer.append (index); + buffer.append (", [result]);} catch (e) {window.external.callJava("); // $NON-NLS-1$ + buffer.append (index); + buffer.append (", ['"); // $NON-NLS-1$ + buffer.append (ERROR_ID); + buffer.append ("' + e.message]);}}"); // $NON-NLS-1$ + execute (buffer.toString ()); + execute (getDeleteFunctionString (functionName)); + deregisterFunction (function); + + Object result = evaluateResult; + evaluateResult = null; + if (result instanceof SWTException) throw (SWTException)result; + return result; +} + +public abstract boolean forward (); + +public abstract String getBrowserType (); + +String getDeleteFunctionString (String functionName) { + return "delete window." + functionName; //$NON-NLS-1$ +} + +int getNextFunctionIndex () { + return nextFunctionIndex++; +} + +public abstract String getText (); + +public abstract String getUrl (); + +public Object getWebBrowser () { + return null; +} + +public abstract boolean isBackEnabled (); + +public boolean isFocusControl () { + return false; +} + +public abstract boolean isForwardEnabled (); + +public abstract void refresh (); + +void registerFunction (BrowserFunction function) { + functions.put (new Integer (function.index), function); +} + +public void removeAuthenticationListener (AuthenticationListener listener) { + if (authenticationListeners.length == 0) return; + int index = -1; + for (int i = 0; i < authenticationListeners.length; i++) { + if (listener == authenticationListeners[i]) { + index = i; + break; + } + } + if (index == -1) return; + if (authenticationListeners.length == 1) { + authenticationListeners = new AuthenticationListener[0]; + return; + } + AuthenticationListener[] newAuthenticationListeners = new AuthenticationListener[authenticationListeners.length - 1]; + System.arraycopy (authenticationListeners, 0, newAuthenticationListeners, 0, index); + System.arraycopy (authenticationListeners, index + 1, newAuthenticationListeners, index, authenticationListeners.length - index - 1); + authenticationListeners = newAuthenticationListeners; +} + +public void removeCloseWindowListener (CloseWindowListener listener) { + if (closeWindowListeners.length == 0) return; + int index = -1; + for (int i = 0; i < closeWindowListeners.length; i++) { + if (listener == closeWindowListeners[i]){ + index = i; + break; + } + } + if (index == -1) return; + if (closeWindowListeners.length == 1) { + closeWindowListeners = new CloseWindowListener[0]; + return; + } + CloseWindowListener[] newCloseWindowListeners = new CloseWindowListener[closeWindowListeners.length - 1]; + System.arraycopy (closeWindowListeners, 0, newCloseWindowListeners, 0, index); + System.arraycopy (closeWindowListeners, index + 1, newCloseWindowListeners, index, closeWindowListeners.length - index - 1); + closeWindowListeners = newCloseWindowListeners; +} + +public void removeLocationListener (LocationListener listener) { + if (locationListeners.length == 0) return; + int index = -1; + for (int i = 0; i < locationListeners.length; i++) { + if (listener == locationListeners[i]){ + index = i; + break; + } + } + if (index == -1) return; + if (locationListeners.length == 1) { + locationListeners = new LocationListener[0]; + return; + } + LocationListener[] newLocationListeners = new LocationListener[locationListeners.length - 1]; + System.arraycopy (locationListeners, 0, newLocationListeners, 0, index); + System.arraycopy (locationListeners, index + 1, newLocationListeners, index, locationListeners.length - index - 1); + locationListeners = newLocationListeners; +} + +public void removeOpenWindowListener (OpenWindowListener listener) { + if (openWindowListeners.length == 0) return; + int index = -1; + for (int i = 0; i < openWindowListeners.length; i++) { + if (listener == openWindowListeners[i]){ + index = i; + break; + } + } + if (index == -1) return; + if (openWindowListeners.length == 1) { + openWindowListeners = new OpenWindowListener[0]; + return; + } + OpenWindowListener[] newOpenWindowListeners = new OpenWindowListener[openWindowListeners.length - 1]; + System.arraycopy (openWindowListeners, 0, newOpenWindowListeners, 0, index); + System.arraycopy (openWindowListeners, index + 1, newOpenWindowListeners, index, openWindowListeners.length - index - 1); + openWindowListeners = newOpenWindowListeners; +} + +public void removeProgressListener (ProgressListener listener) { + if (progressListeners.length == 0) return; + int index = -1; + for (int i = 0; i < progressListeners.length; i++) { + if (listener == progressListeners[i]){ + index = i; + break; + } + } + if (index == -1) return; + if (progressListeners.length == 1) { + progressListeners = new ProgressListener[0]; + return; + } + ProgressListener[] newProgressListeners = new ProgressListener[progressListeners.length - 1]; + System.arraycopy (progressListeners, 0, newProgressListeners, 0, index); + System.arraycopy (progressListeners, index + 1, newProgressListeners, index, progressListeners.length - index - 1); + progressListeners = newProgressListeners; +} + +public void removeStatusTextListener (StatusTextListener listener) { + if (statusTextListeners.length == 0) return; + int index = -1; + for (int i = 0; i < statusTextListeners.length; i++) { + if (listener == statusTextListeners[i]){ + index = i; + break; + } + } + if (index == -1) return; + if (statusTextListeners.length == 1) { + statusTextListeners = new StatusTextListener[0]; + return; + } + StatusTextListener[] newStatusTextListeners = new StatusTextListener[statusTextListeners.length - 1]; + System.arraycopy (statusTextListeners, 0, newStatusTextListeners, 0, index); + System.arraycopy (statusTextListeners, index + 1, newStatusTextListeners, index, statusTextListeners.length - index - 1); + statusTextListeners = newStatusTextListeners; +} + +public void removeTitleListener (TitleListener listener) { + if (titleListeners.length == 0) return; + int index = -1; + for (int i = 0; i < titleListeners.length; i++) { + if (listener == titleListeners[i]){ + index = i; + break; + } + } + if (index == -1) return; + if (titleListeners.length == 1) { + titleListeners = new TitleListener[0]; + return; + } + TitleListener[] newTitleListeners = new TitleListener[titleListeners.length - 1]; + System.arraycopy (titleListeners, 0, newTitleListeners, 0, index); + System.arraycopy (titleListeners, index + 1, newTitleListeners, index, titleListeners.length - index - 1); + titleListeners = newTitleListeners; +} + +public void removeVisibilityWindowListener (VisibilityWindowListener listener) { + if (visibilityWindowListeners.length == 0) return; + int index = -1; + for (int i = 0; i < visibilityWindowListeners.length; i++) { + if (listener == visibilityWindowListeners[i]){ + index = i; + break; + } + } + if (index == -1) return; + if (visibilityWindowListeners.length == 1) { + visibilityWindowListeners = new VisibilityWindowListener[0]; + return; + } + VisibilityWindowListener[] newVisibilityWindowListeners = new VisibilityWindowListener[visibilityWindowListeners.length - 1]; + System.arraycopy (visibilityWindowListeners, 0, newVisibilityWindowListeners, 0, index); + System.arraycopy (visibilityWindowListeners, index + 1, newVisibilityWindowListeners, index, visibilityWindowListeners.length - index - 1); + visibilityWindowListeners = newVisibilityWindowListeners; +} + +public void setBrowser (Browser browser) { + this.browser = browser; +} + +public abstract boolean setText (String html); + +public abstract boolean setUrl (String url); + +public abstract void stop (); + +int translateKey (int key) { + for (int i = 0; i < KeyTable.length; i++) { + if (KeyTable[i][0] == key) return KeyTable[i][1]; + } + return 0; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WindowEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WindowEvent.java new file mode 100644 index 0000000000..ae7551533a --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WindowEvent.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 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 org.eclipse.swt.widgets.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; + +/** + * A <code>WindowEvent</code> is sent by a {@link Browser} when + * a new window needs to be created or when an existing window needs to be + * closed. This notification occurs when a javascript command such as + * <code>window.open</code> or <code>window.close</code> gets executed by + * a <code>Browser</code>. + * + * <p> + * The following example shows how <code>WindowEvent</code>'s are typically + * handled. + * + * <code><pre> + * public static void main(String[] args) { + * Display display = new Display(); + * Shell shell = new Shell(display); + * shell.setText("Main Window"); + * shell.setLayout(new FillLayout()); + * Browser browser = new Browser(shell, SWT.NONE); + * initialize(display, browser); + * shell.open(); + * browser.setUrl("http://www.eclipse.org"); + * while (!shell.isDisposed()) { + * if (!display.readAndDispatch()) + * display.sleep(); + * } + * display.dispose(); + * } + * + * static void initialize(final Display display, Browser browser) { + * browser.addOpenWindowListener(new OpenWindowListener() { + * public void open(WindowEvent event) { + * // Certain platforms can provide a default full browser. + * // simply return in that case if the application prefers + * // the default full browser to the embedded one set below. + * if (!event.required) return; + * + * // Embed the new window + * Shell shell = new Shell(display); + * shell.setText("New Window"); + * shell.setLayout(new FillLayout()); + * Browser browser = new Browser(shell, SWT.NONE); + * initialize(display, browser); + * event.browser = browser; + * } + * }); + * browser.addVisibilityWindowListener(new VisibilityWindowListener() { + * public void hide(WindowEvent event) { + * Browser browser = (Browser)event.widget; + * Shell shell = browser.getShell(); + * shell.setVisible(false); + * } + * public void show(WindowEvent event) { + * Browser browser = (Browser)event.widget; + * Shell shell = browser.getShell(); + * if (event.location != null) shell.setLocation(event.location); + * if (event.size != null) { + * Point size = event.size; + * shell.setSize(shell.computeSize(size.x, size.y)); + * } + * if (event.addressBar || event.menuBar || event.statusBar || event.toolBar) { + * // Create widgets for the address bar, menu bar, status bar and/or tool bar + * // leave enough space in the Shell to accommodate a Browser of the size + * // given by event.size + * } + * shell.open(); + * } + * }); + * browser.addCloseWindowListener(new CloseWindowListener() { + * public void close(WindowEvent event) { + * Browser browser = (Browser)event.widget; + * Shell shell = browser.getShell(); + * shell.close(); + * } + * }); + * } + * </pre></code> + * + * The following notifications are emitted when the user selects a hyperlink that targets a new window + * or as the result of a javascript that executes window.open. + * + * <p>Main Browser + * <ul> + * <li>User selects a link that opens in a new window or javascript requests a new window</li> + * <li>OpenWindowListener.open() notified</li> + * <ul> + * <li>Application creates a new Shell and a second Browser inside that Shell</li> + * <li>Application registers WindowListener's on that second Browser, such as VisibilityWindowListener</li> + * <li>Application returns the second Browser as the host for the new window content</li> + * </ul> + * </ul> + * + * <p>Second Browser + * <ul> + * <li>VisibilityWindowListener.show() notified</li> + * <ul> + * <li>Application sets navigation tool bar, status bar, menu bar and Shell size + * <li>Application makes the Shell hosting the second Browser visible + * <li>User now sees the new window + * </ul> + * </ul> + * + * @see CloseWindowListener + * @see OpenWindowListener + * @see VisibilityWindowListener + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + */ +public class WindowEvent extends TypedEvent { + + /** + * Specifies whether the platform requires the user to provide a + * <code>Browser</code> to handle the new window. + * + * @since 3.1 + */ + public boolean required; + + + /** + * <code>Browser</code> provided by the application. + */ + public Browser browser; + + /** + * Requested location for the <code>Shell</code> hosting the <code>Browser</code>. + * It is <code>null</code> if no location has been requested. + */ + public Point location; + + /** + * Requested <code>Browser</code> size. The client area of the <code>Shell</code> + * hosting the <code>Browser</code> should be large enough to accommodate that size. + * It is <code>null</code> if no size has been requested. + */ + public Point size; + + /** + * Specifies whether the <code>Shell</code> hosting the <code>Browser</code> should + * display an address bar. + * + * @since 3.1 + */ + public boolean addressBar; + + /** + * Specifies whether the <code>Shell</code> hosting the <code>Browser</code> should + * display a menu bar. Note that this is always <code>true</code> on OS X. + * + * @since 3.1 + */ + public boolean menuBar; + + /** + * Specifies whether the <code>Shell</code> hosting the <code>Browser</code> should + * display a status bar. + * + * @since 3.1 + */ + public boolean statusBar; + + /** + * Specifies whether the <code>Shell</code> hosting the <code>Browser</code> should + * display a tool bar. + * + * @since 3.1 + */ + public boolean toolBar; + + static final long serialVersionUID = 3617851997387174969L; + +/** + * Constructs a new instance of this class. + * + * @param widget the widget that fired the event + * + * @since 3.5 + */ +public WindowEvent(Widget widget) { + super(widget); +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the event + */ +public String toString() { + String string = super.toString (); + return string.substring (0, string.length() - 1) // remove trailing '}' + + " required=" + required + + " browser=" + browser + + " location=" + location + + " size=" + size + + " addressBar=" + addressBar + + " menuBar=" + menuBar + + " statusBar=" + statusBar + + " toolBar=" + toolBar + + "}"; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/package.html b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/package.html new file mode 100644 index 0000000000..f64b6c4847 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/package.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <meta name="Author" content="IBM"> + <title>Package-level Javadoc</title> +</head> +<body> +SWT Browser widget. +<h2> +Package Specification</h2> +This package provides the classes to implement the browser user interface +metaphor. +</body> +</html> diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/forms/org/eclipse/swt/browser/IE.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/forms/org/eclipse/swt/browser/IE.java new file mode 100644 index 0000000000..4a52398af0 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/forms/org/eclipse/swt/browser/IE.java @@ -0,0 +1,280 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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 org.eclipse.swt.*; +import org.eclipse.swt.internal.wpf.*; +import org.eclipse.swt.widgets.*; + +class IE extends WebBrowser { + + int webBrowser, host, jniRef; + + boolean ignoreDispose; + + static { + NativeClearSessions = new Runnable() { + public void run() { +// OS.InternetSetOption (0, OS.INTERNET_OPTION_END_BROWSER_SESSION, 0, 0); + } + }; + } + +public void create(Composite parent, int style) { + host = OS.gcnew_WindowsFormsHost(); + if (host == 0) SWT.error(SWT.ERROR_NO_HANDLES); + webBrowser = OS.gcnew_WebBrowser(); + if (webBrowser == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.WindowsFormsHost_Child(host, webBrowser); + jniRef = OS.NewGlobalRef(this); + if (jniRef == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.WebBrowser_ScriptErrorsSuppressed(webBrowser, true); + int handler = OS.gcnew_WebBrowserNavigatingEventHandler(jniRef, "HandleNavigating"); + OS.WebBrowser_Navigating(webBrowser, handler); + OS.GCHandle_Free(handler); + handler = OS.gcnew_WebBrowserProgressChangedEventHandler(jniRef, "HandleProgressChanged"); + OS.WebBrowser_ProgressChanged(webBrowser, handler); + OS.GCHandle_Free(handler); + handler = OS.gcnew_EventHandler(jniRef, "HandleStatusTextChanged"); + OS.WebBrowser_StatusTextChanged(webBrowser, handler); + OS.GCHandle_Free(handler); + handler = OS.gcnew_EventHandler(jniRef, "HandleDocumentTitleChanged"); + OS.WebBrowser_DocumentTitleChanged(webBrowser, handler); + OS.GCHandle_Free(handler); + handler = OS.gcnew_WebBrowserDocumentCompletedEventHandler(jniRef, "HandleDocumentCompleted"); + OS.WebBrowser_DocumentCompleted(webBrowser, handler); + OS.GCHandle_Free(handler); + + int parentHandle = browser.handle; + int children = OS.Panel_Children(parentHandle); + OS.UIElementCollection_Insert(children, 0, host); + OS.GCHandle_Free(children); + OS.FrameworkElement_Width(host, OS.FrameworkElement_Width(parentHandle)); + OS.FrameworkElement_Height(host, OS.FrameworkElement_Height(parentHandle)); + + Listener listener = new Listener() { + public void handleEvent(Event event) { + switch (event.type) { + case SWT.Dispose: { + if (ignoreDispose) { + ignoreDispose = false; + break; + } + ignoreDispose = true; + browser.notifyListeners (event.type, event); + event.type = SWT.NONE; + OS.GCHandle_Free(host); + OS.GCHandle_Free(webBrowser); + OS.DeleteGlobalRef(jniRef); + host = webBrowser = jniRef = 0; + break; + } + case SWT.Resize: { + OS.FrameworkElement_Width(host, OS.FrameworkElement_Width(browser.handle)); + OS.FrameworkElement_Height(host, OS.FrameworkElement_Height(browser.handle)); + break; + } + } + } + }; + browser.addListener(SWT.Resize, listener); + browser.addListener(SWT.Dispose, listener); +} + +public boolean back() { + return OS.WebBrowser_GoBack(webBrowser); +} + +public boolean execute(String script) { +// int document = OS.WebBrowser_Document(frame); +// int length = script.length (); +// char [] buffer = new char [length + 1]; +// script.getChars (0, length, buffer, 0); +// int str = OS.gcnew_String (buffer); +// int result = OS.HtmlDocument_InvokeScript(document, str); +// OS.GCHandle_Free(result); +// OS.GCHandle_Free(str); +// OS.GCHandle_Free(document); + return true; +} + +public boolean forward() { + return OS.WebBrowser_GoForward(webBrowser); +} + +public String getBrowserType () { + return "ie"; //$NON-NLS-1$ +} + +public String getText () { + // TODO + return ""; //$NON-NLS-1$ +} + +public String getUrl() { + int uri = OS.WebBrowser_Url(webBrowser); + String url = getUriString (uri); + if (uri != 0) OS.GCHandle_Free(uri); + return url; +} + +String getUriString(int uri) { + if (uri == 0) return null; + int str = OS.Object_ToString(uri); + int charArray = OS.String_ToCharArray(str); + char[] chars = new char[OS.String_Length(str)]; + OS.memcpy(chars, charArray, chars.length * 2); + OS.GCHandle_Free(charArray); + String url = new String(chars); + OS.GCHandle_Free(str); + return url; +} + +void HandleDocumentCompleted(int sender, int e) { + if (webBrowser == 0) return; + int uri = OS.WebBrowserDocumentCompletedEventArgs_Url(e); + String url = getUriString (uri); + if (uri != 0) OS.GCHandle_Free(uri); + 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); + } + if (browser.isDisposed()) return; + ProgressEvent progressEvent = new ProgressEvent(browser); + progressEvent.display = browser.getDisplay(); + progressEvent.widget = browser; + for (int i = 0; i < progressListeners.length; i++) { + progressListeners[i].completed(progressEvent); + } +} + +void HandleDocumentTitleChanged(int sender, int e) { + if (webBrowser == 0) return; + int str = OS.WebBrowser_DocumentTitle(webBrowser); + String title = ""; + if (str != 0) { + int charArray = OS.String_ToCharArray(str); + char[] chars = new char[OS.String_Length(str)]; + OS.memcpy(chars, charArray, chars.length * 2); + OS.GCHandle_Free(charArray); + OS.GCHandle_Free(str); + title = new String(chars); + } + 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); + } +} + +void HandleNavigating(int sender, int e) { + if (webBrowser == 0) return; + int uri = OS.WebBrowserNavigatingEventArgs_Url(e); + String url = getUriString (uri); + if (uri != 0) OS.GCHandle_Free(uri); + 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); + } +} + +void HandleProgressChanged(int sender, int e) { + if (webBrowser == 0) return; + long nProgress = OS.WebBrowserProgressChangedEventArgs_CurrentProgress(e); + long nProgressMax = OS.WebBrowserProgressChangedEventArgs_MaximumProgress(e); + if (nProgress != -1) { + ProgressEvent newEvent = new ProgressEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + newEvent.current = (int)nProgress; + newEvent.total = (int)nProgressMax; + for (int i = 0; i < progressListeners.length; i++) { + progressListeners[i].changed(newEvent); + } + } +} + +void HandleStatusTextChanged(int sender, int e) { + if (webBrowser == 0) return; + int str = OS.WebBrowser_StatusText(webBrowser); + String text = ""; + if (str != 0) { + int charArray = OS.String_ToCharArray(str); + char[] chars = new char[OS.String_Length(str)]; + OS.memcpy(chars, charArray, chars.length * 2); + OS.GCHandle_Free(charArray); + OS.GCHandle_Free(str); + text = new String(chars); + } + 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); + } +} + +public boolean isBackEnabled() { + return OS.WebBrowser_CanGoBack(webBrowser); +} + +public boolean isForwardEnabled() { + return OS.WebBrowser_CanGoForward(webBrowser); +} + +public void refresh() { + OS.WebBrowser_Refresh(webBrowser); +} + +public boolean setText(String html) { + int state = OS.WebBrowser_ReadyState(webBrowser); + if (!(state == OS.WebBrowserReadyState_Uninitialized || state == OS.WebBrowserReadyState_Complete)) { + OS.WebBrowser_Stop(webBrowser); + } + int length = html.length (); + char [] buffer = new char [length + 1]; + html.getChars (0, length, buffer, 0); + int str = OS.gcnew_String (buffer); + if (str == 0) SWT.error (SWT.ERROR_NO_HANDLES); + OS.WebBrowser_DocumentText(webBrowser, str); + OS.GCHandle_Free(str); + return true; +} + +public boolean setUrl(String url) { + int state = OS.WebBrowser_ReadyState(webBrowser); + if (!(state == OS.WebBrowserReadyState_Uninitialized || state == OS.WebBrowserReadyState_Complete)) { + OS.WebBrowser_Stop(webBrowser); + } + int length = url.length (); + char [] buffer = new char [length + 1]; + url.getChars (0, length, buffer, 0); + int str = OS.gcnew_String (buffer); + OS.WebBrowser_Navigate(webBrowser, str); + OS.GCHandle_Free(str); + return true; +} + +public void stop() { + OS.WebBrowser_Stop(webBrowser); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/photon/org/eclipse/swt/browser/Voyager.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/photon/org/eclipse/swt/browser/Voyager.java new file mode 100755 index 0000000000..075ac65fe5 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/photon/org/eclipse/swt/browser/Voyager.java @@ -0,0 +1,469 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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: + * Chris McKillop (QNX Software Systems) - initial implementation + *******************************************************************************/ +package org.eclipse.swt.browser; + +import java.io.File; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.photon.*; +import org.eclipse.swt.widgets.*; + +class Voyager extends WebBrowser { + int webHandle; + String url = ""; //$NON-NLS-1$ + String text = ""; //$NON-NLS-1$ + int textOffset; + int currentProgress; + int totalProgress = 25; + /* browser to redirect content to */ + Browser redirectBrowser; + static int instanceCount = 0; + + /* Package Name */ + static Callback callback; + +public void create(Composite parent, int style) { + /* use Photon's built-in anchoring for resizing */ + int[] args = { + OS.Pt_ARG_ANCHOR_FLAGS, + OS.Pt_TOP_ANCHORED_TOP | OS.Pt_BOTTOM_ANCHORED_BOTTOM | OS.Pt_LEFT_ANCHORED_LEFT | OS.Pt_RIGHT_ANCHORED_RIGHT, + OS.Pt_TOP_ANCHORED_TOP | OS.Pt_BOTTOM_ANCHORED_BOTTOM | OS.Pt_LEFT_ANCHORED_LEFT | OS.Pt_RIGHT_ANCHORED_RIGHT, + OS.Pt_ARG_FILL_COLOR, + 0xFFFFFF, + 0 }; + webHandle = OS.PtCreateWidget(OS.PtWebClient(), browser.handle, args.length / 3, args); + if (webHandle == 0) { + browser.dispose(); + SWT.error (SWT.ERROR_NO_HANDLES); + } + + /* configure the widget with a specific server */ + File netfront = new File("/usr/photon/bin/netfront"); //$NON-NLS-1$ + String name, server; + if (netfront.exists() || (OS.QNX_MAJOR >= 6 && OS.QNX_MINOR >= 3 && OS.QNX_MICRO >= 0)) { + name = "NetfrontServer"; //$NON-NLS-1$ + server = "netfront"; //$NON-NLS-1$ + } else { + name = "VoyagerServer-2"; //$NON-NLS-1$ + server = "vserver"; //$NON-NLS-1$ + } + /* set client name */ + byte[] nameBuffer = Converter.wcsToMbcs(null, name, true); + int namePtr = OS.malloc(nameBuffer.length); + OS.memmove(namePtr, nameBuffer, nameBuffer.length); + OS.PtSetResource(webHandle, OS.Pt_ARG_CLIENT_NAME, namePtr, 0); + OS.free(namePtr); + + /** + * Feature in Photon PtWebClient. If you give a server name + * when the widget is created it will attempt to start a new server + * rather then attaching a new window context to the existing server. + * If you don't connect to the existing one then javascript window + * creation will fail. + */ + if (instanceCount == 0) { + /* select server */ + byte[] serverBuffer = Converter.wcsToMbcs(null, server, true); + int serverPtr = OS.malloc(serverBuffer.length); + OS.memmove(serverPtr, serverBuffer, serverBuffer.length); + OS.PtSetResource(webHandle, OS.Pt_ARG_WEB_SERVER, serverPtr, 0); + OS.free(serverPtr); + } + instanceCount++; + + if (callback == null) callback = new Callback(this.getClass(), "webProc", 3, false); //$NON-NLS-1$ + int webProc = callback.getAddress(); + if (webProc == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + OS.PtAddCallback(webHandle,OS.Pt_CB_WEB_CLOSE_WINDOW, webProc, OS.Pt_CB_WEB_CLOSE_WINDOW); + OS.PtAddCallback(webHandle,OS.Pt_CB_WEB_COMPLETE, webProc, OS.Pt_CB_WEB_COMPLETE); + OS.PtAddCallback(webHandle,OS.Pt_CB_WEB_DATA_REQ, webProc, OS.Pt_CB_WEB_DATA_REQ); + OS.PtAddCallback(webHandle,OS.Pt_CB_WEB_METADATA, webProc, OS.Pt_CB_WEB_METADATA); + OS.PtAddCallback(webHandle,OS.Pt_CB_WEB_NEW_WINDOW, webProc, OS.Pt_CB_WEB_NEW_WINDOW); + OS.PtAddCallback(webHandle,OS.Pt_CB_WEB_START, webProc, OS.Pt_CB_WEB_START); + OS.PtAddCallback(webHandle,OS.Pt_CB_WEB_STATUS, webProc, OS.Pt_CB_WEB_STATUS); + OS.PtAddCallback(webHandle,OS.Pt_CB_WEB_URL, webProc, OS.Pt_CB_WEB_URL); + Listener listener = new Listener() { + public void handleEvent(Event event) { + switch (event.type) { + case SWT.Dispose: onDispose(); break; + case SWT.FocusIn: onFocusGained(event); break; + } + } + }; + int[] folderEvents = new int[]{ + SWT.Dispose, + SWT.FocusIn, + }; + for (int i = 0; i < folderEvents.length; i++) { + browser.addListener(folderEvents[i], listener); + } + OS.PtRealizeWidget(webHandle); +} + +static int webProc(int handle, int data, int info) { + Display display = Display.getCurrent(); + int parent = OS.PtWidgetParent (handle); + Widget widget = display.findWidget(parent); + if (widget != null && widget instanceof Browser) { + Browser browser = (Browser)widget; + return ((Voyager)browser.webBrowser).webProc(data, info); + } + return OS.Pt_CONTINUE; +} + +public boolean back() { + int ptr = OS.malloc(4); + int[] args = new int[]{OS.Pt_ARG_WEB_NAVIGATE_PAGE, ptr, 0}; + OS.PtGetResources(webHandle, args.length / 3, args); + int[] result = new int[1]; + OS.memmove(result, ptr, 4); + OS.memmove(result, result[0], 4); + OS.free(ptr); + if ((result[0] & (1 << OS.Pt_WEB_DIRECTION_BACK)) == 0) return false; + OS.PtSetResource(webHandle, OS.Pt_ARG_WEB_NAVIGATE_PAGE, OS.Pt_WEB_DIRECTION_BACK, 0); + return true; +} + +int webProc(int data, int info) { + switch (data) { + case OS.Pt_CB_WEB_CLOSE_WINDOW: return Pt_CB_WEB_CLOSE_WINDOW(info); + case OS.Pt_CB_WEB_COMPLETE: return Pt_CB_WEB_COMPLETE(info); + case OS.Pt_CB_WEB_DATA_REQ: return Pt_CB_WEB_DATA_REQ(info); + case OS.Pt_CB_WEB_METADATA: return Pt_CB_WEB_METADATA(info); + case OS.Pt_CB_WEB_NEW_WINDOW: return Pt_CB_WEB_NEW_WINDOW(info); + case OS.Pt_CB_WEB_START: return Pt_CB_WEB_START(info); + case OS.Pt_CB_WEB_STATUS: return Pt_CB_WEB_STATUS(info); + case OS.Pt_CB_WEB_URL: return Pt_CB_WEB_URL(info); + } + return OS.Pt_CONTINUE; +} + +public boolean execute(String script) { + return false; +} + +public boolean forward() { + int ptr = OS.malloc(4); + int[] args = new int[]{OS.Pt_ARG_WEB_NAVIGATE_PAGE, ptr, 0}; + OS.PtGetResources(webHandle, args.length / 3, args); + int[] result = new int[1]; + OS.memmove(result, ptr, 4); + OS.memmove(result, result[0], 4); + OS.free(ptr); + if ((result[0] & (1 << OS.Pt_WEB_DIRECTION_FWD)) == 0) return false; + OS.PtSetResource(webHandle, OS.Pt_ARG_WEB_NAVIGATE_PAGE, OS.Pt_WEB_DIRECTION_FWD, 0); + return true; +} + +public String getBrowserType () { + return "voyager"; //$NON-NLS-1$ +} + +public String getText () { + // TODO + return ""; //$NON-NLS-1$ +} + +public String getUrl() { + return url; +} + +public boolean isBackEnabled() { + int ptr = OS.malloc(4); + int[] args = new int[] {OS.Pt_ARG_WEB_NAVIGATE_PAGE, ptr, 0}; + OS.PtGetResources(webHandle, args.length / 3, args); + int[] result = new int[1]; + OS.memmove(result, ptr, 4); + OS.memmove(result, result[0], 4); + OS.free(ptr); + return (result[0] & (1 << OS.Pt_WEB_DIRECTION_BACK)) != 0; +} + +public boolean isForwardEnabled() { + int ptr = OS.malloc(4); + int[] args = new int[]{OS.Pt_ARG_WEB_NAVIGATE_PAGE, ptr, 0}; + OS.PtGetResources(webHandle, args.length / 3, args); + int[] result = new int[1]; + OS.memmove(result, ptr, 4); + OS.memmove(result, result[0], 4); + OS.free(ptr); + return (result[0] & (1 << OS.Pt_WEB_DIRECTION_FWD)) != 0; +} + +void onDispose() { + OS.PtDestroyWidget(webHandle); + webHandle = 0; + instanceCount--; +} + +void onFocusGained(Event e) { + OS.PtContainerGiveFocus(webHandle, null); +} + +int Pt_CB_WEB_CLOSE_WINDOW(int info) { + WindowEvent event = new WindowEvent(browser); + event.display = browser.getDisplay(); + event.widget = browser; + for(int i = 0; i < closeWindowListeners.length; i++ ) + closeWindowListeners[i].close(event); + browser.dispose(); + return OS.Pt_CONTINUE; +} + +int Pt_CB_WEB_COMPLETE(int info) { + Display display = browser.getDisplay(); + LocationEvent event = new LocationEvent(browser); + event.display = display; + event.widget = browser; + event.location = url; + event.top = true; + for (int i = 0; i < locationListeners.length; i++) + locationListeners[i].changed(event); + ProgressEvent progress = new ProgressEvent(browser); + progress.display = display; + progress.widget = browser; + progress.current = totalProgress; + progress.total = totalProgress; + for (int i = 0; i < progressListeners.length; i++) + progressListeners[i].completed(progress); + StatusTextEvent statusevent = new StatusTextEvent(browser); + statusevent.display = display; + statusevent.widget = browser; + statusevent.text = ""; //$NON-NLS-1$ + for (int i = 0; i < statusTextListeners.length; i++) + statusTextListeners[i].changed(statusevent); + return OS.Pt_CONTINUE; +} + +int Pt_CB_WEB_DATA_REQ(int info) { + PtCallbackInfo_t cbinfo_t = new PtCallbackInfo_t(); + OS.memmove(cbinfo_t, info, PtCallbackInfo_t.sizeof); + PtWebDataReqCallback_t dataReq = new PtWebDataReqCallback_t(); + OS.memmove(dataReq, cbinfo_t.cbdata, PtWebDataReqCallback_t.sizeof); + PtWebClient2Data_t clientData = new PtWebClient2Data_t(); + clientData.type = dataReq.type; + clientData.data = 0; + String data = null; + switch (clientData.type) { + case OS.Pt_WEB_DATA_HEADER: + StringBuffer sb = new StringBuffer("Content-Type: text/html\n"); //$NON-NLS-1$ + sb.append("Content-Length: "); //$NON-NLS-1$ + sb.append(text.length()); + sb.append("\n"); //$NON-NLS-1$ + data = sb.toString(); + break; + case OS.Pt_WEB_DATA_BODY: + /* + * Feature on Photon. The PtSetResource() call for PtWebClient data imposes + * a limit on the size of the text buffer being passed. The workaround is + * to break the text into 1KB chunks. + */ + if (text.length() - textOffset > 1024) { + data = text.substring(textOffset, textOffset + 1024); + textOffset += 1024; + } else { + data = text.substring(textOffset); + } + break; + case OS.Pt_WEB_DATA_CLOSE: + text = ""; //$NON-NLS-1$ + break; + } + if (data != null) { + byte[] buffer = Converter.wcsToMbcs(null, data, true); + clientData.data = OS.malloc(buffer.length); + OS.memmove(clientData.data, buffer, buffer.length); + clientData.length = buffer.length - 1; + } + dataReq.url = clientData.url; + int ptr = OS.malloc(PtWebClient2Data_t.sizeof); + OS.memmove(ptr, clientData, PtWebClient2Data_t.sizeof); + OS.PtSetResource(webHandle, OS.Pt_ARG_WEB_DATA, clientData.data, ptr); + OS.free(ptr); + if (clientData.data != 0) OS.free(clientData.data); + return OS.Pt_CONTINUE; +} + +int Pt_CB_WEB_METADATA(int info) { + PtCallbackInfo_t cbinfo_t = new PtCallbackInfo_t(); + OS.memmove(cbinfo_t, info, PtCallbackInfo_t.sizeof); + final PtWebMetaDataCallback_t webmeta_t = new PtWebMetaDataCallback_t(); + OS.memmove(webmeta_t, cbinfo_t.cbdata, PtWebMetaDataCallback_t.sizeof); + String name = new String(webmeta_t.name, 0, OS.strlen(cbinfo_t.cbdata)); + if (name.equals("title")) { //$NON-NLS-1$ + String title = new String(webmeta_t.value, 0, OS.strlen(cbinfo_t.cbdata + webmeta_t.name.length)); + TitleEvent newEvent = new TitleEvent(browser); + newEvent.display = browser.getDisplay(); + newEvent.widget = browser; + newEvent.title = title; + /* + * Feature on Photon. The Voyager Browser updates the title section + * in the window decoration even if the title refers to an inner frame. + * Browsers on other platforms only update the title that refers to + * the top frame. As a result, the title event on Photon is sent for + * both top and inner frames. + */ + for (int i = 0; i < titleListeners.length; i++) + titleListeners[i].changed(newEvent); + } + return OS.Pt_CONTINUE; +} + +int Pt_CB_WEB_NEW_WINDOW(int info) { + PtCallbackInfo_t cbinfo_t = new PtCallbackInfo_t(); + OS.memmove(cbinfo_t, info, PtCallbackInfo_t.sizeof); + final PtWebWindowCallback_t webwin_t = new PtWebWindowCallback_t(); + OS.memmove(webwin_t,cbinfo_t.cbdata,PtWebWindowCallback_t.sizeof); + /* + * Feature on Photon. The server will use the first PtWebClient + * widget created from within the CB_WEB_NEW_WINDOW callback to + * host the new window. The workaround is to create a temporary + * PtWebClient widget everytime the notification is received. + * When its location is known, the Browser provided by the + * application is then redirected. + */ + final Browser hidden = new Browser(browser.getParent(), SWT.NONE); + hidden.addLocationListener(new LocationListener() { + public void changed(org.eclipse.swt.browser.LocationEvent event) { + /* + * Bug on Voyager. The first PtWebClient widget created + * from within the CB_WEB_NEW_WINDOW callback is the one + * hosting the new window. For some reason, this PtWebClient + * widget may or may not receive a Pt_CB_WEB_URL + * notification. It receives a Pt_CB_WEB_COMPLETE in all cases. + * The workaround is to reload the content when this occurs. + * This request causes the Pt_CB_WEB_URL to be correctly sent, + * providing the information required to redirect the browser + * provided by the application. + */ + if (event.location.length() == 0) { + hidden.refresh(); + return; + } + hidden.dispose(); + } + public void changing(final org.eclipse.swt.browser.LocationEvent event) { + Browser redirect = ((Voyager)hidden.webBrowser).redirectBrowser; + /* Forward the link to the Browser actually provided by the user */ + if (redirect != null && !redirect.isDisposed()) { + WindowEvent newEvent = new WindowEvent(redirect); + newEvent.display = browser.getDisplay(); + newEvent.widget = redirect; + newEvent.location = null; + /* Photon sets the size to 0,0 when it isn't specified. */ + newEvent.size = webwin_t.size_w == 0 && webwin_t.size_h == 0 ? null : new Point(webwin_t.size_w, webwin_t.size_h); + for (int i = 0; i < redirect.webBrowser.visibilityWindowListeners.length; i++) + redirect.webBrowser.visibilityWindowListeners[i].show(newEvent); + redirect.setUrl(event.location); + } + } + }); + WindowEvent event = new WindowEvent(browser); + event.display = browser.getDisplay(); + event.widget = browser; + event.required = true; + for (int i = 0; i < openWindowListeners.length; i++) + openWindowListeners[i].open(event); + if (event.browser != null && !event.browser.isDisposed()) ((Voyager)hidden.webBrowser).redirectBrowser = event.browser; + return OS.Pt_CONTINUE; +} + +int Pt_CB_WEB_START(int info) { + currentProgress = 1; + ProgressEvent progress = new ProgressEvent(browser); + progress.display = browser.getDisplay(); + progress.widget = browser; + progress.current = currentProgress; + progress.total = totalProgress; + for (int i = 0; i < progressListeners.length; i++) + progressListeners[i].changed(progress); + return OS.Pt_CONTINUE; +} + +int Pt_CB_WEB_STATUS(int info) { + PtCallbackInfo_t cbinfo_t = new PtCallbackInfo_t(); + PtWebStatusCallback_t webstatus = new PtWebStatusCallback_t(); + OS.memmove(cbinfo_t, info, PtCallbackInfo_t.sizeof); + OS.memmove(webstatus, cbinfo_t.cbdata, PtWebStatusCallback_t.sizeof); + switch (webstatus.type) { + case OS.Pt_WEB_STATUS_MOUSE : + case OS.Pt_WEB_STATUS_PROGRESS : + StatusTextEvent statusevent = new StatusTextEvent(browser); + statusevent.display = browser.getDisplay(); + statusevent.widget = browser; + statusevent.text = new String(webstatus.desc, 0, OS.strlen(cbinfo_t.cbdata)); + for (int i = 0; i < statusTextListeners.length; i++) + statusTextListeners[i].changed(statusevent); + if (webstatus.type == OS.Pt_WEB_STATUS_PROGRESS) { + currentProgress++; + if (currentProgress >= totalProgress) currentProgress = 1; + ProgressEvent progress = new ProgressEvent(browser); + progress.display = browser.getDisplay(); + progress.widget = browser; + progress.current = currentProgress; + progress.total = totalProgress; + for (int i = 0; i < progressListeners.length; i++) + progressListeners[i].changed(progress); + } + break; + } + return OS.Pt_CONTINUE; +} + +int Pt_CB_WEB_URL(int info) { + PtCallbackInfo_t cbinfo_t = new PtCallbackInfo_t(); + OS.memmove(cbinfo_t, info, PtCallbackInfo_t.sizeof); + byte[] buffer = new byte[OS.strlen(cbinfo_t.cbdata) + 1]; + OS.memmove(buffer, cbinfo_t.cbdata, buffer.length); + url = new String(Converter.mbcsToWcs(null, buffer)); + LocationEvent event = new LocationEvent(browser); + event.display = browser.getDisplay(); + event.widget = browser; + event.location = url; + event.doit = true; + for (int i = 0; i < locationListeners.length; i++) + locationListeners[i].changing(event); + /* Widget could have been disposed */ + if (browser.isDisposed()) return OS.Pt_CONTINUE; + if (!event.doit) stop(); + return OS.Pt_CONTINUE; +} + +public void refresh() { + OS.PtSetResource(webHandle, OS.Pt_ARG_WEB_RELOAD, 1, 0); +} + +public boolean setText(String html) { + text = html; + textOffset = 0; + byte[] buffer = Converter.wcsToMbcs(null, "client:", true); //$NON-NLS-1$ + int ptr = OS.malloc(buffer.length); + OS.memmove(ptr, buffer, buffer.length); + OS.PtSetResource(webHandle, OS.Pt_ARG_WEB_GET_URL, ptr, OS.Pt_WEB_ACTION_DISPLAY); + OS.free(ptr); + return true; +} + +public boolean setUrl(String url) { + byte[] buffer = Converter.wcsToMbcs(null, url, true); + int ptr = OS.malloc(buffer.length); + OS.memmove(ptr, buffer, buffer.length); + OS.PtSetResource(webHandle, OS.Pt_ARG_WEB_GET_URL, ptr, OS.Pt_WEB_ACTION_DISPLAY); + OS.free(ptr); + return true; +} + +public void stop() { + OS.PtSetResource(webHandle, OS.Pt_ARG_WEB_STOP, 1, 0); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/IE.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/IE.java new file mode 100644 index 0000000000..7e8d713e4b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/IE.java @@ -0,0 +1,1440 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.browser; + +import java.util.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +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; + OleControlSite site; + OleAutomation auto; + OleListener domListener; + OleAutomation[] documents = new OleAutomation[0]; + + boolean back, forward, navigate, delaySetText, ignoreDispose; + Point location; + Point size; + boolean addressBar = true, menuBar = true, statusBar = true, toolBar = true; + int /*long*/ globalDispatch; + String html, lastNavigateURL; + int style, lastKeyCode, lastCharCode; + int lastMouseMoveX, lastMouseMoveY; + + static boolean IsIE7; + 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 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 short CSC_NAVIGATEFORWARD = 1; + static final short CSC_NAVIGATEBACK = 2; + static final int INET_E_DEFAULT_ACTION = 0x800C0011; + 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_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 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_CLIENTX = "clientX"; //$NON-NLS-1$ + static final String PROPERTY_CLIENTY = "clientY"; //$NON-NLS-1$ + static final String PROPERTY_CTRLKEY = "ctrlKey"; //$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_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$ + int /*long*/ [] phkResult = new int /*long*/ [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); + int major = 0; + try { + major = Integer.valueOf (majorString).intValue (); + } catch (NumberFormatException e) { + /* just continue, version-specific features will not be enabled */ + } + IsIE7 = major >= 7; + } + } + } + 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 int /*long*/ [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$ + int /*long*/ [] phkResult2 = new int /*long*/ [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]); + } + } + +public void create(Composite parent, int style) { + this.style = style; + frame = new OleFrame(browser, SWT.NONE); + + try { + site = new WebSite(frame, SWT.NONE, ProgId); //$NON-NLS-1$ + } catch (SWTException e) { + browser.dispose(); + SWT.error(SWT.ERROR_NO_HANDLES); + } + + 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; + + /* + * 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 = 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; + } + /* + * FocusIn and Traverse are hooked to handle traversal into + * and out of the Browser when it has key listeners. + */ + case SWT.FocusIn: { + site.setFocus(); + break; + } + case SWT.Traverse: { + if (browser.isListening(SWT.KeyDown) || browser.isListening(SWT.KeyUp)) { + if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS) { + browser.traverse(SWT.TRAVERSE_TAB_PREVIOUS); + e.doit = false; + } + } + break; + } + } + } + }; + browser.addListener(SWT.Dispose, listener); + browser.addListener(SWT.FocusIn, listener); + browser.addListener(SWT.Resize, 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: { + Variant varResult = event.arguments[1]; + String url = varResult.getString(); + /* + * Bug in IE. For navigations on the local machine, BeforeNavigate2's url + * field contains a string representing 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$ + url = PROTOCOL_FILE + url.replace('\\', '/'); + } + 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) { + int /*long*/ pCancel = cancel.getByRef(); + COM.MoveMemory(pCancel, new short[] {doit ? COM.VARIANT_FALSE : COM.VARIANT_TRUE}, 2); + } + if (doit) { + lastNavigateURL = url; + varResult = event.arguments[0]; + IDispatch dispatch = varResult.getDispatch(); + Variant variant = new Variant(auto); + 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]; + } + } + 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: { + Variant varResult = event.arguments[0]; + IDispatch dispatch = varResult.getDispatch(); + + varResult = event.arguments[1]; + String url = varResult.getString(); + /* + * Bug in IE. For navigations on the local machine, DocumentComplete's URL + * field contains a string representing 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$ + url = PROTOCOL_FILE + url.replace('\\', '/'); + } + if (html != null && url.equals(ABOUT_BLANK)) { + Runnable runnable = new Runnable () { + public void run() { + if (browser.isDisposed() || html == null) return; + int charCount = html.length(); + char[] chars = new char[charCount]; + html.getChars(0, charCount, chars, 0); + html = null; + int byteCount = OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, charCount, null, 0, null, null); + /* + * Note. 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}; + int /*long*/ 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); + int /*long*/ [] ppstm = new int /*long*/ [1]; + /* + * Note. 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[] {"Document"}); //$NON-NLS-1$ + Variant pVarResult = auto.getProperty(rgdispid[0]); + IDispatch dispatchDocument = pVarResult.getDispatch(); + int /*long*/ [] ppvObject = new int /*long*/ [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(); + /* + * This code is intentionally commented. The IDispatch obtained from a Variant + * did not increase the reference count for the enclosed interface. + */ + //dispatchDocument.Release(); + IUnknown stream = new IUnknown(ppstm[0]); + stream.Release(); + } else { + OS.GlobalFree(hGlobal); + } + } + } + }; + if (delaySetText) { + delaySetText = false; + browser.getDisplay().asyncExec(runnable); + } else { + runnable.run(); + } + } else { + Variant variant = new Variant(auto); + 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; + /* + * This code is intentionally commented. A Variant constructed from an + * OleAutomation object does not increase its reference count. The IDispatch + * obtained from this Variant did not increase the reference count for the + * OleAutomation instance either. + */ + //top.Release(); + //variant.dispose(); + /* + * 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; + + ProgressEvent progressEvent = new ProgressEvent(browser); + progressEvent.display = browser.getDisplay(); + progressEvent.widget = browser; + for (int i = 0; i < progressListeners.length; i++) { + progressListeners[i].completed(progressEvent); + } + } + } + + /* + * This code is intentionally commented. This IDispatch was received + * as an argument from the OleEvent and it will be disposed along with + * the other arguments. + */ + //dispatch.Release(); + break; + } + case NavigateComplete2: { + Variant varResult = event.arguments[0]; + IDispatch dispatch = varResult.getDispatch(); + if (globalDispatch == 0) globalDispatch = dispatch.getAddress(); + + OleAutomation webBrowser = varResult.getAutomation(); + varResult = event.arguments[1]; + Variant variant = new Variant(auto); + IDispatch top = variant.getDispatch(); + boolean isTop = top.getAddress() == dispatch.getAddress(); + if (isTop) { + /* re-install registered functions */ + Enumeration elements = functions.elements (); + while (elements.hasMoreElements ()) { + BrowserFunction function = (BrowserFunction)elements.nextElement (); + execute (function.functionString); + } + } + hookDOMListeners(webBrowser, isTop); + webBrowser.dispose(); + break; + } + case NewWindow2: { + Variant cancel = event.arguments[1]; + int /*long*/ 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) { + Variant variant = new Variant(browser.auto); + IDispatch iDispatch = variant.getDispatch(); + Variant ppDisp = event.arguments[0]; + int /*long*/ byref = ppDisp.getByRef(); + if (byref != 0) COM.MoveMemory(byref, new int /*long*/[] {iDispatch.getAddress()}, OS.PTR_SIZEOF); + /* + * This code is intentionally commented. A Variant constructed from an + * OleAutomation object does not increase its reference count. The IDispatch + * obtained from this Variant did not increase the reference count for the + * OleAutomation instance either. + */ + //variant.dispose(); + //iDispatch.Release(); + } + 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: { + 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: { + 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: { + 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]; + int /*long*/ 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(NavigateComplete2, 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 execute(String script) { + /* get IHTMLDocument2 */ + int[] rgdispid = auto.getIDsOfNames(new String[]{"Document"}); //$NON-NLS-1$ + 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[]{"Document"}); //$NON-NLS-1$ + 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() { + 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(); +} + +public void refresh() { + int[] rgdispid = auto.getIDsOfNames(new String[] { "Refresh" }); //$NON-NLS-1$ + auto.invoke(rgdispid[0]); +} + +public boolean setText(String html) { + /* + * If the html field is non-null then the about:blank page is already being + * loaded, so no Stop or Navigate is required. Just set the html that is to + * be shown. + */ + boolean blankLoading = this.html != null; + this.html = html; + 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. + */ + int[] rgdispid; + if (navigate) { + /* + * 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. + */ + 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]); + } + rgdispid = auto.getIDsOfNames(new String[] { "Navigate", "URL" }); //$NON-NLS-1$ //$NON-NLS-2$ + navigate = true; + 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 && IsIE7) { + 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 && IsIE7) { + 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) { + html = null; + + /* + * 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$ + /* + * Feature in Internet Explorer. Stopping pending requests when no request has been + * issued causes a default 'Action cancelled' page to be displayed. Since Stop must + * be issued here, the workaround is to first Navigate to the about:blank page before + * issuing Stop so that the 'Action cancelled' page is not displayed. + */ + if (!navigate) { + 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 && IsIE7) { + 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); + } + auto.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs); + if (!OS.IsWinCE && IsIE7) { + OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, oldValue); + } + rgvarg[0].dispose(); + } + int[] 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$ + navigate = true; + Variant[] rgvarg = new Variant[1]; + rgvarg[0] = new Variant(url); + int[] rgdispidNamedArgs = new int[1]; + rgdispidNamedArgs[0] = rgdispid[1]; + Variant pVarResult = auto.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs); + rgvarg[0].dispose(); + if (pVarResult == null) return false; + boolean result = pVarResult.getType() == OLE.VT_EMPTY; + pVarResult.dispose(); + return result; +} + +public void stop() { + int[] rgdispid = auto.getIDsOfNames(new String[] { "Stop" }); //$NON-NLS-1$ + auto.invoke(rgdispid[0]); +} + +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(); + + MSG msg = new MSG (); + int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD; + 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 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 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.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; + } + 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(); + } + + 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 OS.VK_RETURN here since + * one is always sent for it from the keydown handler. + */ + if (lastCharCode == OS.VK_RETURN) { + event.dispose(); + return; + } + + Event keyEvent = new Event (); + keyEvent.widget = browser; + keyEvent.type = SWT.KeyDown; + keyEvent.keyCode = lastKeyCode; + keyEvent.character = (char)lastCharCode; + keyEvent.stateMask = mask; + 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(); + } + + 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 x, y, mask = 0; + Event newEvent = new Event(); + newEvent.widget = browser; + + rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CLIENTX }); + dispIdMember = rgdispid[0]; + pVarResult = event.getProperty(dispIdMember); + x = pVarResult.getInt(); + newEvent.x = x; + pVarResult.dispose(); + + rgdispid = event.getIDsOfNames(new String[] { PROPERTY_CLIENTY }); + dispIdMember = rgdispid[0]; + pVarResult = event.getProperty(dispIdMember); + y = pVarResult.getInt(); + newEvent.y = y; + 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_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 = x; newEvent.y = 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[]{ "Document" }); //$NON-NLS-1$ + 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); + } + } +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/WebSite.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/WebSite.java new file mode 100644 index 0000000000..0c2bc056d7 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/WebSite.java @@ -0,0 +1,921 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 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 org.eclipse.swt.*; +import org.eclipse.swt.internal.C; +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.ole.win32.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.internal.win32.*; + +class WebSite extends OleControlSite { + COMObject iDocHostUIHandler; + COMObject iDocHostShowUI; + COMObject iServiceProvider; + COMObject iInternetSecurityManager; + COMObject iOleCommandTarget; + COMObject iAuthenticate; + COMObject iDispatch; + boolean ignoreNextMessage; + Boolean canExecuteApplets; + + static final int OLECMDID_SHOWSCRIPTERROR = 40; + static final short [] ACCENTS = new short [] {'~', '`', '\'', '^', '"'}; + static final String CONSUME_KEY = "org.eclipse.swt.OleFrame.ConsumeKey"; //$NON-NLS-1$ + +public WebSite(Composite parent, int style, String progId) { + super(parent, style, progId); +} + +protected void createCOMInterfaces () { + super.createCOMInterfaces(); + iDocHostUIHandler = new COMObject(new int[]{2, 0, 0, 4, 1, 5, 0, 0, 1, 1, 1, 3, 3, 2, 2, 1, 3, 2}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return ShowContextMenu((int)/*64*/args[0], args[1], args[2], args[3]);} + public int /*long*/ method4(int /*long*/[] args) {return GetHostInfo(args[0]);} + public int /*long*/ method5(int /*long*/[] args) {return ShowUI((int)/*64*/args[0], args[1], args[2], args[3], args[4]);} + public int /*long*/ method6(int /*long*/[] args) {return HideUI();} + public int /*long*/ method7(int /*long*/[] args) {return UpdateUI();} + public int /*long*/ method8(int /*long*/[] args) {return EnableModeless((int)/*64*/args[0]);} + public int /*long*/ method9(int /*long*/[] args) {return OnDocWindowActivate((int)/*64*/args[0]);} + public int /*long*/ method10(int /*long*/[] args) {return OnFrameWindowActivate((int)/*64*/args[0]);} + public int /*long*/ method11(int /*long*/[] args) {return ResizeBorder(args[0], args[1], (int)/*64*/args[2]);} + public int /*long*/ method12(int /*long*/[] args) {return TranslateAccelerator(args[0], args[1], (int)/*64*/args[2]);} + public int /*long*/ method13(int /*long*/[] args) {return GetOptionKeyPath(args[0], (int)/*64*/args[1]);} + public int /*long*/ method14(int /*long*/[] args) {return GetDropTarget(args[0], args[1]);} + public int /*long*/ method15(int /*long*/[] args) {return GetExternal(args[0]);} + public int /*long*/ method16(int /*long*/[] args) {return TranslateUrl((int)/*64*/args[0], args[1], args[2]);} + public int /*long*/ method17(int /*long*/[] args) {return FilterDataObject(args[0], args[1]);} + }; + iDocHostShowUI = new COMObject(new int[]{2, 0, 0, 7, C.PTR_SIZEOF == 4 ? 7 : 6}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return ShowMessage(args[0], args[1], args[2], (int)/*64*/args[3], args[4], (int)/*64*/args[5], args[6]);} + public int /*long*/ method4(int /*long*/[] args) { + if (args.length == 7) { + return ShowHelp(args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3], (int)/*64*/args[4], (int)/*64*/args[5], args[6]); + } else { + return ShowHelp_64(args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3], args[4], args[5]); + } + } + }; + iServiceProvider = new COMObject(new int[]{2, 0, 0, 3}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return QueryService(args[0], args[1], args[2]);} + }; + iInternetSecurityManager = new COMObject(new int[]{2, 0, 0, 1, 1, 3, 4, 8, 7, 3, 3}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return SetSecuritySite(args[0]);} + public int /*long*/ method4(int /*long*/[] args) {return GetSecuritySite(args[0]);} + public int /*long*/ method5(int /*long*/[] args) {return MapUrlToZone(args[0], args[1], (int)/*64*/args[2]);} + public int /*long*/ method6(int /*long*/[] args) {return GetSecurityId(args[0], args[1], args[2], args[3]);} + public int /*long*/ method7(int /*long*/[] args) {return ProcessUrlAction(args[0], (int)/*64*/args[1], args[2], (int)/*64*/args[3], args[4], (int)/*64*/args[5], (int)/*64*/args[6], (int)/*64*/args[7]);} + public int /*long*/ method8(int /*long*/[] args) {return QueryCustomPolicy(args[0], args[1], args[2], args[3], args[4], (int)/*64*/args[5], (int)/*64*/args[6]);} + public int /*long*/ method9(int /*long*/[] args) {return SetZoneMapping((int)/*64*/args[0], args[1], (int)/*64*/args[2]);} + public int /*long*/ method10(int /*long*/[] args) {return GetZoneMappings((int)/*64*/args[0], args[1], (int)/*64*/args[2]);} + }; + iOleCommandTarget = new COMObject(new int[]{2, 0, 0, 4, 5}) { + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return QueryStatus(args[0], (int)/*64*/args[1], args[2], args[3]);} + public int /*long*/ method4(int /*long*/[] args) {return Exec(args[0], (int)/*64*/args[1], (int)/*64*/args[2], args[3], args[4]);} + }; + iAuthenticate = new COMObject(new int[]{2, 0, 0, 3}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return Authenticate(args[0], args[1], args[2]);} + }; + iDispatch = new COMObject (new int[] {2, 0, 0, 1, 3, 5, 8}) { + public int /*long*/ method0 (int /*long*/[] args) { + /* + * IDispatch check must be done here instead of in the shared QueryInterface + * implementation, to avoid answering the superclass's IDispatch implementation + * instead of this one. + */ + GUID guid = new GUID (); + COM.MoveMemory (guid, args[0], GUID.sizeof); + if (COM.IsEqualGUID (guid, COM.IIDIDispatch)) { + COM.MoveMemory (args[1], new int /*long*/[] {iDispatch.getAddress ()}, OS.PTR_SIZEOF); + AddRef (); + return COM.S_OK; + } + return QueryInterface (args[0], args[1]); + } + public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();} + public int /*long*/ method2 (int /*long*/[] args) {return Release ();} + public int /*long*/ method3 (int /*long*/[] args) {return GetTypeInfoCount (args[0]);} + public int /*long*/ method4 (int /*long*/[] args) {return GetTypeInfo ((int)/*64*/args[0], (int)/*64*/args[1], args[2]);} + public int /*long*/ method5 (int /*long*/[] args) {return GetIDsOfNames ((int)/*64*/args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3], args[4]);} + public int /*long*/ method6 (int /*long*/[] args) {return Invoke ((int)/*64*/args[0], (int)/*64*/args[1], (int)/*64*/args[2], (int)/*64*/args[3], args[4], args[5], args[6], args[7]);} + }; +} + +protected void disposeCOMInterfaces() { + super.disposeCOMInterfaces(); + if (iDocHostUIHandler != null) { + iDocHostUIHandler.dispose(); + iDocHostUIHandler = null; + } + if (iDocHostShowUI != null) { + iDocHostShowUI.dispose(); + iDocHostShowUI = null; + } + if (iServiceProvider != null) { + iServiceProvider.dispose(); + iServiceProvider = null; + } + if (iInternetSecurityManager != null) { + iInternetSecurityManager.dispose(); + iInternetSecurityManager = null; + } + if (iOleCommandTarget != null) { + iOleCommandTarget.dispose(); + iOleCommandTarget = null; + } + if (iAuthenticate != null) { + iAuthenticate.dispose(); + iAuthenticate = null; + } + if (iDispatch != null) { + iDispatch.dispose (); + iDispatch = null; + } +} + +protected int AddRef() { + /* Workaround for javac 1.1.8 bug */ + return super.AddRef(); +} + +protected int QueryInterface(int /*long*/ riid, int /*long*/ ppvObject) { + int result = super.QueryInterface(riid, ppvObject); + if (result == COM.S_OK) return result; + if (riid == 0 || ppvObject == 0) return COM.E_INVALIDARG; + GUID guid = new GUID(); + COM.MoveMemory(guid, riid, GUID.sizeof); + if (COM.IsEqualGUID(guid, COM.IIDIDocHostUIHandler)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iDocHostUIHandler.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + if (COM.IsEqualGUID(guid, COM.IIDIDocHostShowUI)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iDocHostShowUI.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + if (COM.IsEqualGUID(guid, COM.IIDIServiceProvider)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iServiceProvider.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + if (COM.IsEqualGUID(guid, COM.IIDIInternetSecurityManager)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iInternetSecurityManager.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + if (COM.IsEqualGUID(guid, COM.IIDIOleCommandTarget)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iOleCommandTarget.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + COM.MoveMemory(ppvObject, new int /*long*/[] {0}, OS.PTR_SIZEOF); + return COM.E_NOINTERFACE; +} + +/* IDocHostUIHandler */ + +int EnableModeless(int EnableModeless) { + return COM.E_NOTIMPL; +} + +int FilterDataObject(int /*long*/ pDO, int /*long*/ ppDORet) { + return COM.E_NOTIMPL; +} + +int GetDropTarget(int /*long*/ pDropTarget, int /*long*/ ppDropTarget) { + return COM.E_NOTIMPL; +} + +int GetExternal(int /*long*/ ppDispatch) { + OS.MoveMemory (ppDispatch, new int /*long*/[] {iDispatch.getAddress()}, C.PTR_SIZEOF); + AddRef (); + return COM.S_OK; +} + +int GetHostInfo(int /*long*/ pInfo) { + int info = IE.DOCHOSTUIFLAG_THEME; + IE browser = (IE)((Browser)getParent().getParent()).webBrowser; + if ((browser.style & SWT.BORDER) == 0) info |= IE.DOCHOSTUIFLAG_NO3DOUTERBORDER; + OS.MoveMemory(pInfo + 4, new int[] {info}, 4); + + /* + * TODO replace the implementation above with the one commented below + * when 32-bit swt starts compiling with a newer mssdk whose definition + * of DOCHOSTUIINFO includes the last two OLECHAR* fields. + */ +// int info = IE.DOCHOSTUIFLAG_THEME; +// Browser browser = (Browser)getParent().getParent(); +// if ((browser.getStyle() & SWT.BORDER) == 0) info |= IE.DOCHOSTUIFLAG_NO3DOUTERBORDER; +// +// DOCHOSTUIINFO uiInfo = new DOCHOSTUIINFO (); +// OS.MoveMemory(uiInfo, pInfo, DOCHOSTUIINFO.sizeof); +// uiInfo.dwFlags = info; +// OS.MoveMemory(pInfo, uiInfo, DOCHOSTUIINFO.sizeof); + return COM.S_OK; +} + +int GetOptionKeyPath(int /*long*/ pchKey, int dw) { + return COM.E_NOTIMPL; +} + +int HideUI() { + return COM.E_NOTIMPL; +} + +int OnDocWindowActivate(int fActivate) { + return COM.E_NOTIMPL; +} + +int OnFrameWindowActivate(int fActivate) { + return COM.E_NOTIMPL; +} + +protected int Release() { + /* Workaround for javac 1.1.8 bug */ + return super.Release(); +} + +int ResizeBorder(int /*long*/ prcBorder, int /*long*/ pUIWindow, int fFrameWindow) { + return COM.E_NOTIMPL; +} + +int ShowContextMenu(int dwID, int /*long*/ ppt, int /*long*/ pcmdtReserved, int /*long*/ pdispReserved) { + Browser browser = (Browser)getParent().getParent(); + Event event = new Event(); + POINT pt = new POINT(); + OS.MoveMemory(pt, ppt, POINT.sizeof); + event.x = pt.x; + event.y = pt.y; + browser.notifyListeners(SWT.MenuDetect, event); + if (!event.doit) return COM.S_OK; + Menu menu = browser.getMenu(); + if (menu != null && !menu.isDisposed ()) { + if (pt.x != event.x || pt.y != event.y) { + menu.setLocation (event.x, event.y); + } + menu.setVisible (true); + return COM.S_OK; + } + /* Show default IE popup menu */ + return COM.S_FALSE; +} + +int ShowUI(int dwID, int /*long*/ pActiveObject, int /*long*/ pCommandTarget, int /*long*/ pFrame, int /*long*/ pDoc) { + return COM.S_FALSE; +} + +int TranslateAccelerator(int /*long*/ lpMsg, int /*long*/ pguidCmdGroup, int nCmdID) { + /* + * Feature on Internet Explorer. By default the embedded Internet Explorer control runs + * the Internet Explorer shortcuts (e.g. Ctrl+F for Find). This overrides the shortcuts + * defined by SWT. The workaround is to forward the accelerator keys to the parent window + * and have Internet Explorer ignore the ones handled by the parent window. + */ + Menu menubar = getShell().getMenuBar(); + if (menubar != null && !menubar.isDisposed() && menubar.isEnabled()) { + Shell shell = menubar.getShell(); + int /*long*/ hwnd = shell.handle; + int /*long*/ hAccel = OS.SendMessage(hwnd, OS.WM_APP+1, 0, 0); + if (hAccel != 0) { + MSG msg = new MSG(); + OS.MoveMemory(msg, lpMsg, MSG.sizeof); + if (OS.TranslateAccelerator(hwnd, hAccel, msg) != 0) return COM.S_OK; + } + } + /* + * By default the IE shortcuts are run. However, F5 causes a refresh, which is not + * appropriate when rendering HTML from memory, and CTRL-N opens a standalone IE, + * which is undesirable and can cause a crash in some contexts. The workaround is + * to block IE from handling these shortcuts by answering COM.S_OK. + */ + int result = COM.S_FALSE; + MSG msg = new MSG(); + OS.MoveMemory(msg, lpMsg, MSG.sizeof); + if (msg.message == OS.WM_KEYDOWN) { + switch ((int)/*64*/msg.wParam) { + case OS.VK_F5: + OleAutomation auto = new OleAutomation(this); + int[] rgdispid = auto.getIDsOfNames(new String[] { "LocationURL" }); //$NON-NLS-1$ + Variant pVarResult = auto.getProperty(rgdispid[0]); + auto.dispose(); + if (pVarResult != null) { + if (pVarResult.getType() == OLE.VT_BSTR) { + String url = pVarResult.getString(); + if (url.equals(IE.ABOUT_BLANK)) result = COM.S_OK; + } + pVarResult.dispose(); + } + break; + case OS.VK_TAB: + /* + * Do not interfere with tab traversal since it's not known + * if it will be within IE or out to another Control. + */ + break; + case OS.VK_RETURN: + /* + * Translating OS.VK_RETURN results in the native control handling it + * twice (eg.- inserting two lines instead of one). So this key is not + * translated here, and instead is explicitly handled in the keypress + * handler. + */ + break; + case OS.VK_N: + if (OS.GetKeyState (OS.VK_CONTROL) < 0 && OS.GetKeyState (OS.VK_MENU) >= 0 && OS.GetKeyState (OS.VK_SHIFT) >= 0) { + frame.setData(CONSUME_KEY, "false"); //$NON-NLS-1$ + result = COM.S_OK; + break; + } + // FALL THROUGH + default: + OS.TranslateMessage(msg); + frame.setData(CONSUME_KEY, "true"); //$NON-NLS-1$ + break; + } + } + + switch (msg.message) { + case OS.WM_KEYDOWN: + case OS.WM_KEYUP: { + if (!OS.IsWinCE) { + boolean isAccent = false; + switch ((int)/*64*/msg.wParam) { + case OS.VK_SHIFT: + case OS.VK_MENU: + case OS.VK_CONTROL: + case OS.VK_CAPITAL: + case OS.VK_NUMLOCK: + case OS.VK_SCROLL: + break; + default: { + /* + * Bug in Windows. The high bit in the result of MapVirtualKey() on + * Windows NT is bit 32 while the high bit on Windows 95 is bit 16. + * They should both be bit 32. The fix is to test the right bit. + */ + int mapKey = OS.MapVirtualKey ((int)/*64*/msg.wParam, 2); + if (mapKey != 0) { + isAccent = (mapKey & (OS.IsWinNT ? 0x80000000 : 0x8000)) != 0; + if (!isAccent) { + for (int i=0; i<ACCENTS.length; i++) { + int value = OS.VkKeyScan (ACCENTS [i]); + if (value != -1 && (value & 0xFF) == msg.wParam) { + int state = value >> 8; + if ((OS.GetKeyState (OS.VK_SHIFT) < 0) == ((state & 0x1) != 0) && + (OS.GetKeyState (OS.VK_CONTROL) < 0) == ((state & 0x2) != 0) && + (OS.GetKeyState (OS.VK_MENU) < 0) == ((state & 0x4) != 0)) { + if ((state & 0x7) != 0) isAccent = true; + break; + } + } + } + } + } + break; + } + }; + if (isAccent) result = COM.S_OK; + } + } + } + return result; +} + +int TranslateUrl(int dwTranslate, int /*long*/ pchURLIn, int /*long*/ ppchURLOut) { + return COM.E_NOTIMPL; +} + +int UpdateUI() { + return COM.E_NOTIMPL; +} + +/* IDocHostShowUI */ + +int ShowMessage(int /*long*/ hwnd, int /*long*/ lpstrText, int /*long*/ lpstrCaption, int dwType, int /*long*/ lpstrHelpFile, int dwHelpContext, int /*long*/ plResult) { + boolean ignore = ignoreNextMessage; + ignoreNextMessage = false; + return ignore ? COM.S_OK : COM.S_FALSE; +} + +int ShowHelp_64(int /*long*/ hwnd, int /*long*/ pszHelpFile, int uCommand, int dwData, long pt, int /*long*/ pDispatchObjectHit) { + POINT point = new POINT(); + OS.MoveMemory(point, new long[]{pt}, 8); + return ShowHelp(hwnd, pszHelpFile, uCommand, dwData, point.x, point.y, pDispatchObjectHit); +} + +/* Note. One of the arguments of ShowHelp is a POINT struct and not a pointer to a POINT struct. Because + * of the way Callback gets int parameters from a va_list of C arguments 2 integer arguments must be declared, + * ptMouse_x and ptMouse_y. Otherwise the Browser crashes when the user presses F1 to invoke + * the help. + */ +int ShowHelp(int /*long*/ hwnd, int /*long*/ pszHelpFile, int uCommand, int dwData, int ptMouse_x, int ptMouse_y, int /*long*/ pDispatchObjectHit) { + Browser browser = (Browser)getParent().getParent(); + Event event = new Event(); + event.type = SWT.Help; + event.display = getDisplay(); + event.widget = browser; + Shell shell = browser.getShell(); + Control control = browser; + do { + if (control.isListening(SWT.Help)) { + control.notifyListeners(SWT.Help, event); + break; + } + if (control == shell) break; + control = control.getParent(); + } while (true); + return COM.S_OK; +} + +/* IServiceProvider */ + +int QueryService(int /*long*/ guidService, int /*long*/ riid, int /*long*/ ppvObject) { + if (riid == 0 || ppvObject == 0) return COM.E_INVALIDARG; + GUID guid = new GUID(); + COM.MoveMemory(guid, riid, GUID.sizeof); + if (COM.IsEqualGUID(guid, COM.IIDIInternetSecurityManager)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iInternetSecurityManager.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + if (COM.IsEqualGUID(guid, COM.IIDIAuthenticate)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iAuthenticate.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + COM.MoveMemory(ppvObject, new int /*long*/[] {0}, OS.PTR_SIZEOF); + return COM.E_NOINTERFACE; +} + +/* IInternetSecurityManager */ + +int SetSecuritySite(int /*long*/ pSite) { + return IE.INET_E_DEFAULT_ACTION; +} + +int GetSecuritySite(int /*long*/ ppSite) { + return IE.INET_E_DEFAULT_ACTION; +} + +int MapUrlToZone(int /*long*/ pwszUrl, int /*long*/ pdwZone, int dwFlags) { + /* + * Feature in IE 6 sp1. HTML rendered in memory + * does not enable local links but the exact same + * HTML document loaded through a local file is + * permitted to follow local links. The workaround is + * to return URLZONE_INTRANET instead of the default + * value URLZONE_LOCAL_MACHINE. + */ + COM.MoveMemory(pdwZone, new int[] {IE.URLZONE_INTRANET}, 4); + return COM.S_OK; +} + +int GetSecurityId(int /*long*/ pwszUrl, int /*long*/ pbSecurityId, int /*long*/ pcbSecurityId, int /*long*/ dwReserved) { + return IE.INET_E_DEFAULT_ACTION; +} + +int ProcessUrlAction(int /*long*/ pwszUrl, int dwAction, int /*long*/ pPolicy, int cbPolicy, int /*long*/ pContext, int cbContext, int dwFlags, int dwReserved) { + ignoreNextMessage = false; + + /* + * Feature in IE 6 sp1. HTML rendered in memory + * containing an OBJECT tag referring to a local file + * brings up a warning dialog asking the user whether + * it should proceed or not. The workaround is to + * set the policy to URLPOLICY_ALLOW in this case (dwAction + * value of 0x1406). + * + * Feature in IE. Security Patches and user settings + * affect the way the embedded web control behaves. The current + * approach is to consider the content trusted and allow + * all URLs by default. + */ + int policy = IE.URLPOLICY_ALLOW; + + if (dwAction >= IE.URLACTION_JAVA_MIN && dwAction <= IE.URLACTION_JAVA_MAX) { + if (canExecuteApplets ()) { + policy = IE.URLPOLICY_JAVA_LOW; + } else { + policy = IE.URLPOLICY_JAVA_PROHIBIT; + ignoreNextMessage = true; + } + } + if (dwAction == IE.URLACTION_ACTIVEX_RUN) { + GUID guid = new GUID(); + COM.MoveMemory(guid, pContext, GUID.sizeof); + if (COM.IsEqualGUID(guid, COM.IIDJavaBeansBridge) && !canExecuteApplets ()) { + policy = IE.URLPOLICY_DISALLOW; + ignoreNextMessage = true; + } + if (COM.IsEqualGUID(guid, COM.IIDShockwaveActiveXControl)) { + policy = IE.URLPOLICY_DISALLOW; + ignoreNextMessage = true; + } + } + if (dwAction == IE.URLACTION_SCRIPT_RUN) { + IE browser = (IE)((Browser)getParent ().getParent ()).webBrowser; + if (!browser.jsEnabled) { + policy = IE.URLPOLICY_DISALLOW; + } + } + + if (cbPolicy >= 4) COM.MoveMemory(pPolicy, new int[] {policy}, 4); + return policy == IE.URLPOLICY_ALLOW ? COM.S_OK : COM.S_FALSE; +} + +boolean canExecuteApplets () { + /* + * Executing an applet in embedded IE will crash if IE's Java plug-in + * launches its jre in IE's process, because this new jre conflicts + * with the one running eclipse. These cases need to be avoided by + * vetoing the running of applets. + * + * However as of Sun jre 1.6u10, applets can be launched in a separate + * process, which avoids the conflict with the jre running eclipse. + * Therefore if this condition is detected, and if the required jar + * libraries are available, then applets can be executed. + */ + + /* + * executing applets with IE6 embedded can crash, so do not + * attempt this if the version is less than IE7 + */ + if (!IE.IsIE7) return false; + + if (canExecuteApplets == null) { + WebBrowser webBrowser = ((Browser)getParent ().getParent ()).webBrowser; + String script = "try {var element = document.createElement('object');element.classid='clsid:CAFEEFAC-DEC7-0000-0000-ABCDEFFEDCBA';return element.object.isPlugin2();} catch (err) {};return false;"; //$NON-NLS-1$ + canExecuteApplets = ((Boolean)webBrowser.evaluate (script)); + if (canExecuteApplets.booleanValue ()) { + try { + Class.forName ("sun.plugin2.main.server.IExplorerPlugin"); /* plugin.jar */ //$NON-NLS-1$ + Class.forName ("com.sun.deploy.services.Service"); /* deploy.jar */ //$NON-NLS-1$ + Class.forName ("com.sun.javaws.Globals"); /* javaws.jar */ //$NON-NLS-1$ + } catch (ClassNotFoundException e) { + /* one or more of the required jar libraries are not available */ + canExecuteApplets = Boolean.FALSE; + } + } + } + return canExecuteApplets.booleanValue (); +} + +int QueryCustomPolicy(int /*long*/ pwszUrl, int /*long*/ guidKey, int /*long*/ ppPolicy, int /*long*/ pcbPolicy, int /*long*/ pContext, int cbContext, int dwReserved) { + return IE.INET_E_DEFAULT_ACTION; +} + +int SetZoneMapping(int dwZone, int /*long*/ lpszPattern, int dwFlags) { + return IE.INET_E_DEFAULT_ACTION; +} + +int GetZoneMappings(int dwZone, int /*long*/ ppenumString, int dwFlags) { + return COM.E_NOTIMPL; +} + +/* IOleCommandTarget */ +int QueryStatus(int /*long*/ pguidCmdGroup, int cCmds, int /*long*/ prgCmds, int /*long*/ pCmdText) { + return COM.E_NOTSUPPORTED; +} + +int Exec(int /*long*/ pguidCmdGroup, int nCmdID, int nCmdExecOpt, int /*long*/ pvaIn, int /*long*/ pvaOut) { + if (pguidCmdGroup != 0) { + GUID guid = new GUID(); + COM.MoveMemory(guid, pguidCmdGroup, GUID.sizeof); + + /* + * If a javascript error occurred then suppress IE's default script error dialog. + */ + if (COM.IsEqualGUID(guid, COM.CGID_DocHostCommandHandler)) { + if (nCmdID == OLECMDID_SHOWSCRIPTERROR) return COM.S_OK; + } + + /* + * Bug in Internet Explorer. OnToolBar TRUE is also fired when any of the + * address bar or menu bar are requested but not the tool bar. A workaround + * has been posted by a Microsoft developer on the public webbrowser_ctl + * newsgroup. The workaround is to implement the IOleCommandTarget interface + * to test the argument of an undocumented command. + */ + if (nCmdID == 1 && COM.IsEqualGUID(guid, COM.CGID_Explorer) && ((nCmdExecOpt & 0xFFFF) == 0xA)) { + IE browser = (IE)((Browser)getParent().getParent()).webBrowser; + browser.toolBar = (nCmdExecOpt & 0xFFFF0000) != 0; + } + } + return COM.E_NOTSUPPORTED; +} + +/* IAuthenticate */ + +int Authenticate (int /*long*/ hwnd, int /*long*/ szUsername, int /*long*/ szPassword) { + IE browser = (IE)((Browser)getParent ().getParent ()).webBrowser; + for (int i = 0; i < browser.authenticationListeners.length; i++) { + AuthenticationEvent event = new AuthenticationEvent (browser.browser); + event.location = browser.lastNavigateURL; + browser.authenticationListeners[i].authenticate (event); + if (!event.doit) return COM.E_ACCESSDENIED; + if (event.user != null && event.password != null) { + TCHAR user = new TCHAR (0, event.user, true); + int size = user.length () * TCHAR.sizeof; + int /*long*/ userPtr = COM.CoTaskMemAlloc (size); + OS.MoveMemory (userPtr, user, size); + TCHAR password = new TCHAR (0, event.password, true); + size = password.length () * TCHAR.sizeof; + int /*long*/ passwordPtr = COM.CoTaskMemAlloc (size); + OS.MoveMemory (passwordPtr, password, size); + C.memmove (hwnd, new int /*long*/[] {0}, C.PTR_SIZEOF); + C.memmove (szUsername, new int /*long*/[] {userPtr}, C.PTR_SIZEOF); + C.memmove (szPassword, new int /*long*/[] {passwordPtr}, C.PTR_SIZEOF); + return COM.S_OK; + } + } + + /* no listener handled the challenge, so defer to the native dialog */ + C.memmove (hwnd, new int /*long*/[] {getShell().handle}, C.PTR_SIZEOF); + return COM.S_OK; +} + +/* IDispatch */ + +int GetTypeInfoCount (int /*long*/ pctinfo) { + C.memmove (pctinfo, new int[] {0}, 4); + return COM.S_OK; +} + +int GetTypeInfo (int iTInfo, int lcid, int /*long*/ ppTInfo) { + return COM.S_OK; +} + +int GetIDsOfNames (int riid, int /*long*/ rgszNames, int cNames, int lcid, int /*long*/ rgDispId) { + int /*long*/[] ptr = new int /*long*/[1]; + OS.MoveMemory (ptr, rgszNames, C.PTR_SIZEOF); + int length = OS.wcslen (ptr[0]); + char[] buffer = new char[length]; + OS.MoveMemory (buffer, ptr[0], length * 2); + String functionName = String.valueOf (buffer); + int result = COM.S_OK; + int[] ids = new int[cNames]; /* DISPIDs */ + if (functionName.equals ("callJava")) { //$NON-NLS-1$ + for (int i = 0; i < cNames; i++) { + ids[i] = i + 1; + } + } else { + result = COM.DISP_E_UNKNOWNNAME; + for (int i = 0; i < cNames; i++) { + ids[i] = COM.DISPID_UNKNOWN; + } + } + OS.MoveMemory (rgDispId, ids, cNames * 4); + return result; +} + +int Invoke (int dispIdMember, int /*long*/ riid, int lcid, int dwFlags, int /*long*/ pDispParams, int /*long*/ pVarResult, int /*long*/ pExcepInfo, int /*long*/ pArgErr) { + DISPPARAMS dispParams = new DISPPARAMS (); + COM.MoveMemory (dispParams, pDispParams, DISPPARAMS.sizeof); + if (dispParams.cArgs != 2) { + if (pVarResult != 0) { + COM.MoveMemory (pVarResult, new int /*long*/[] {0}, C.PTR_SIZEOF); + } + return COM.S_OK; + } + + int /*long*/ ptr = dispParams.rgvarg + Variant.sizeof; + Variant variant = Variant.win32_new (ptr); + int index = variant.getInt (); + variant.dispose (); + if (index <= 0) { + if (pVarResult != 0) { + COM.MoveMemory (pVarResult, new int /*long*/[] {0}, C.PTR_SIZEOF); + } + return COM.S_OK; + } + + variant = Variant.win32_new (dispParams.rgvarg); + IE ie = (IE)((Browser)getParent ().getParent ()).webBrowser; + Object key = new Integer (index); + BrowserFunction function = (BrowserFunction)ie.functions.get (key); + Object returnValue = null; + if (function != null) { + try { + Object temp = convertToJava (variant); + if (temp instanceof Object[]) { + Object[] args = (Object[])temp; + try { + returnValue = function.function (args); + } catch (Exception e) { + /* exception during function invocation */ + returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ()); + } + } + } catch (IllegalArgumentException e) { + /* invalid argument value type */ + if (function.isEvaluate) { + /* notify the function so that a java exception can be thrown */ + function.function (new String[] {WebBrowser.CreateErrorString (new SWTException (SWT.ERROR_INVALID_RETURN_VALUE).getLocalizedMessage ())}); + } + returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ()); + } + } + variant.dispose (); + + if (pVarResult != 0) { + if (returnValue == null) { + COM.MoveMemory (pVarResult, new int /*long*/[] {0}, C.PTR_SIZEOF); + } else { + try { + variant = convertToJS (returnValue); + } catch (SWTException e) { + /* invalid return value type */ + variant = convertToJS (WebBrowser.CreateErrorString (e.getLocalizedMessage ())); + } + Variant.win32_copy (pVarResult, variant); + variant.dispose (); + } + } + return COM.S_OK; +} + +Object convertToJava (Variant variant) { + switch (variant.getType ()) { + case OLE.VT_NULL: return null; + case OLE.VT_BSTR: return variant.getString (); + case OLE.VT_BOOL: return new Boolean (variant.getBoolean ()); + case OLE.VT_I2: + case OLE.VT_I4: + case OLE.VT_I8: + case OLE.VT_R4: + case OLE.VT_R8: + return new Double (variant.getDouble ()); + case OLE.VT_DISPATCH: { + Object[] args = null; + OleAutomation auto = variant.getAutomation (); + TYPEATTR typeattr = auto.getTypeInfoAttributes (); + if (typeattr != null) { + GUID guid = new GUID (); + guid.Data1 = typeattr.guid_Data1; + guid.Data2 = typeattr.guid_Data2; + guid.Data3 = typeattr.guid_Data3; + guid.Data4 = typeattr.guid_Data4; + if (COM.IsEqualGUID (guid, COM.IIDIJScriptTypeInfo)) { + int[] rgdispid = auto.getIDsOfNames (new String[] {"length"}); //$NON-NLS-1$ + if (rgdispid != null) { + Variant varLength = auto.getProperty (rgdispid[0]); + int length = varLength.getInt (); + varLength.dispose (); + args = new Object[length]; + for (int i = 0; i < length; i++) { + rgdispid = auto.getIDsOfNames (new String[] {String.valueOf (i)}); + if (rgdispid != null) { + Variant current = auto.getProperty (rgdispid[0]); + try { + args[i] = convertToJava (current); + current.dispose (); + } catch (IllegalArgumentException e) { + /* invalid argument value type */ + current.dispose (); + auto.dispose (); + throw e; + } + } + } + } + } else { + auto.dispose (); + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + } + auto.dispose (); + return args; + } + } + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + return null; +} + +Variant convertToJS (Object value) { + if (value == null) { + return new Variant (); + } + if (value instanceof String) { + return new Variant ((String)value); + } + if (value instanceof Boolean) { + return new Variant (((Boolean)value).booleanValue ()); + } + if (value instanceof Number) { + return new Variant (((Number)value).doubleValue ()); + } + if (value instanceof Object[]) { + Object[] arrayValue = (Object[])value; + int length = arrayValue.length; + if (length > 0) { + /* get IHTMLDocument2 */ + IE browser = (IE)((Browser)getParent ().getParent ()).webBrowser; + OleAutomation auto = browser.auto; + int[] rgdispid = auto.getIDsOfNames (new String[] {"Document"}); //$NON-NLS-1$ + if (rgdispid == null) return new Variant (); + Variant pVarResult = auto.getProperty (rgdispid[0]); + if (pVarResult == null) return new Variant (); + if (pVarResult.getType () == COM.VT_EMPTY) { + pVarResult.dispose (); + return new Variant (); + } + OleAutomation document = pVarResult.getAutomation (); + pVarResult.dispose (); + + /* get IHTMLWindow2 */ + rgdispid = document.getIDsOfNames (new String[] {"parentWindow"}); //$NON-NLS-1$ + if (rgdispid == null) { + document.dispose (); + return new Variant (); + } + pVarResult = document.getProperty (rgdispid[0]); + if (pVarResult == null || pVarResult.getType () == COM.VT_EMPTY) { + if (pVarResult != null) pVarResult.dispose (); + document.dispose (); + return new Variant (); + } + OleAutomation ihtmlWindow2 = pVarResult.getAutomation (); + pVarResult.dispose (); + document.dispose (); + + /* create a new JS array to be returned */ + rgdispid = ihtmlWindow2.getIDsOfNames (new String[] {"Array"}); //$NON-NLS-1$ + if (rgdispid == null) { + ihtmlWindow2.dispose (); + return new Variant (); + } + Variant arrayType = ihtmlWindow2.getProperty (rgdispid[0]); + ihtmlWindow2.dispose (); + IDispatch arrayTypeDispatch = arrayType.getDispatch (); + arrayType.dispose (); + + int /*long*/[] result = new int /*long*/[1]; + int rc = arrayTypeDispatch.QueryInterface (COM.IIDIDispatchEx, result); + if (rc != COM.S_OK) return new Variant (); + IDispatchEx arrayTypeDispatchEx = new IDispatchEx (result[0]); + result[0] = 0; + int /*long*/ resultPtr = OS.GlobalAlloc (OS.GMEM_FIXED | OS.GMEM_ZEROINIT, VARIANT.sizeof); + DISPPARAMS params = new DISPPARAMS (); + rc = arrayTypeDispatchEx.InvokeEx (COM.DISPID_VALUE, COM.LOCALE_USER_DEFAULT, COM.DISPATCH_CONSTRUCT, params, resultPtr, null, 0); + if (rc != COM.S_OK) { + OS.GlobalFree (resultPtr); + return new Variant (); + } + Variant array = Variant.win32_new (resultPtr); + OS.GlobalFree (resultPtr); + + /* populate the array */ + auto = array.getAutomation (); + int[] rgdispids = auto.getIDsOfNames (new String[] {"push"}); //$NON-NLS-1$ + if (rgdispids != null) { + for (int i = 0; i < length; i++) { + Object currentObject = arrayValue[i]; + try { + Variant variant = convertToJS (currentObject); + auto.invoke (rgdispids[0], new Variant[] {variant}); + variant.dispose (); + } catch (SWTException e) { + /* invalid return value type */ + auto.dispose (); + array.dispose (); + throw e; + } + } + } + auto.dispose (); + return array; + } + } + SWT.error (SWT.ERROR_INVALID_RETURN_VALUE); + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/wpf/org/eclipse/swt/browser/IE.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/wpf/org/eclipse/swt/browser/IE.java new file mode 100644 index 0000000000..babe61d6af --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/wpf/org/eclipse/swt/browser/IE.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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 org.eclipse.swt.*; +import org.eclipse.swt.internal.wpf.*; +import org.eclipse.swt.widgets.*; + +class IE extends WebBrowser { + + int frame; + + boolean ignoreDispose; + + static { + NativeClearSessions = new Runnable() { + public void run() { +// OS.InternetSetOption (0, OS.INTERNET_OPTION_END_BROWSER_SESSION, 0, 0); + } + }; + } + +public void create(Composite parent, int style) { + frame = OS.gcnew_Frame(); + if (frame == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.Frame_NavigationUIVisibility(frame, OS.NavigationUIVisibility_Hidden); + int parentHandle = browser.handle; + int children = OS.Panel_Children(parentHandle); + OS.UIElementCollection_Insert(children, 0, frame); + OS.GCHandle_Free(children); + OS.FrameworkElement_Width(frame, OS.FrameworkElement_Width(parentHandle)); + OS.FrameworkElement_Height(frame, OS.FrameworkElement_Height(parentHandle)); + + Listener listener = new Listener() { + public void handleEvent(Event event) { + switch (event.type) { + case SWT.Dispose: { + if (ignoreDispose) { + ignoreDispose = false; + break; + } + ignoreDispose = true; + browser.notifyListeners (event.type, event); + event.type = SWT.NONE; + OS.GCHandle_Free(frame); + frame = 0; + break; + } + case SWT.Resize: { + OS.FrameworkElement_Width(frame, OS.FrameworkElement_Width(browser.handle)); + OS.FrameworkElement_Height(frame, OS.FrameworkElement_Height(browser.handle)); + break; + } + } + } + }; + browser.addListener(SWT.Resize, listener); + browser.addListener(SWT.Dispose, listener); +} + +public boolean back() { + if (!OS.Frame_CanGoBack(frame)) return false; + OS.Frame_GoBack(frame); + return true; +} + +public boolean execute(String script) { +// /* get IHTMLDocument2 */ +// int[] rgdispid = auto.getIDsOfNames(new String[]{"Document"}); //$NON-NLS-1$ +// int dispIdMember = rgdispid[0]; +// Variant pVarResult = auto.getProperty(dispIdMember); +// if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) 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 (!OS.Frame_CanGoForward(frame)) return false; + OS.Frame_GoForward(frame); + return true; +} + +public String getBrowserType () { + return "ie"; //$NON-NLS-1$ +} + +public String getUrl() { + int uri = OS.Frame_Source(frame); + int str = OS.Object_ToString(uri); + int charArray = OS.String_ToCharArray(str); + char[] chars = new char[OS.String_Length(str)]; + OS.memcpy(chars, charArray, chars.length * 2); + OS.GCHandle_Free(charArray); + String url = new String(chars); + OS.GCHandle_Free(str); + OS.GCHandle_Free(uri); + return url; +} + +public boolean isBackEnabled() { + return OS.Frame_CanGoBack(frame); +} + +public boolean isForwardEnabled() { + return OS.Frame_CanGoForward(frame); +} + +public void refresh() { + OS.Frame_Refresh(frame); +} + +public boolean setText(String html) { + return true; +// /* +// * If the html field is non-null then the about:blank page is already being +// * loaded, so no Stop or Navigate is required. Just set the html that is to +// * be shown. +// */ +// boolean blankLoading = this.html != null; +// this.html = html; +// 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. +// */ +// int[] rgdispid; +// if (navigate) { +// /* +// * 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. +// */ +// 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]); +// } +// rgdispid = auto.getIDsOfNames(new String[] { "Navigate", "URL" }); //$NON-NLS-1$ //$NON-NLS-2$ +// navigate = true; +// Variant[] rgvarg = new Variant[1]; +// rgvarg[0] = new Variant(ABOUT_BLANK); +// int[] rgdispidNamedArgs = new int[1]; +// rgdispidNamedArgs[0] = rgdispid[1]; +// Variant pVarResult = auto.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs); +// rgvarg[0].dispose(); +// if (pVarResult == null) return false; +// boolean result = pVarResult.getType() == OLE.VT_EMPTY; +// pVarResult.dispose(); +// return result; +} + +public boolean setUrl(String url) { + if (url.indexOf(':') == -1) url = "http://" + url; + int length = url.length (); + char [] buffer = new char [length + 1]; + url.getChars (0, length, buffer, 0); + int str = OS.gcnew_String (buffer); + int uri = OS.gcnew_Uri(str, OS.UriKind_RelativeOrAbsolute); + OS.GCHandle_Free(str); + boolean result = OS.Frame_Navigate(frame, uri); + OS.GCHandle_Free(uri); + return result; +} + +public void stop() { + OS.Frame_StopLoading (frame); +} +} |