/******************************************************************************* * Copyright (c) 2011, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.browser; import java.io.UnsupportedEncodingException; 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 WebKit extends WebBrowser { /* Objective-C WebView delegate */ int delegate; /* Carbon HIView handle */ int webViewHandle, webView; int windowBoundsHandler; int preferences; boolean loadingText, hasNewFocusElement, untrustedText; 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_FILEROOT = "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 POST = "POST"; //$NON-NLS-1$ static final String USER_AGENT = "user-agent"; //$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 WEBKIT_EVENTS_FIX_KEY = "org.eclipse.swt.internal.webKitEventsFix"; //$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 { Cocoa.WebInitForCarbon(); 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 urlString = createNSString (CookieUrl); 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; int name = createNSString (CookieName); 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); int length = OS.CFStringGetLength (value); char[] 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 urlString = createNSString(CookieUrl); int url = Cocoa.objc_msgSend (Cocoa.C_NSURL, Cocoa.S_URLWithString, urlString); OS.CFRelease (urlString); int value = createNSString (CookieValue); int key = createNSString (HEADER_SETCOOKIE); 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; } }; if (NativePendingCookies != null) { SetPendingCookies (NativePendingCookies); } NativePendingCookies = null; } 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 WebKit 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(WEBKIT_EVENTS_FIX_KEY); } /* * Bug in WebKit. 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 WebKit. 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 WebKit. The WebView does not receive mouse and key events when it is added * to a visible top window. It is assumed that WebKit 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 WebKit 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, WebKit * 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; /* invoke onbeforeunload handlers */ if (!browser.isClosing && !browser.isDisposed()) { close (false); } 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 sHandle = createNSString(AGENT_STRING); 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 ((WebKit)((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 ((WebKit)((Browser)widget).webBrowser).handleCallback(selector, arg0, arg1, arg2, arg3); } return 0; } static int createNSString(String string) { int length = string.length (); char[] buffer = new char[length]; string.getChars (0, length, buffer, 0); return OS.CFStringCreateWithCharacters (0, buffer, length); } 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 close () { return close (true); } boolean close (boolean showPrompters) { if (!jsEnabled) return true; String functionName = EXECUTE_ID + "CLOSE"; // $NON-NLS-1$ StringBuffer buffer = new StringBuffer ("function "); // $NON-NLS-1$ buffer.append (functionName); buffer.append ("(win) {\n"); // $NON-NLS-1$ buffer.append ("var fn = win.onbeforeunload; if (fn != null) {try {var str = fn(); "); // $NON-NLS-1$ if (showPrompters) { buffer.append ("if (str != null) { "); // $NON-NLS-1$ buffer.append ("var result = window.external.callRunBeforeUnloadConfirmPanelWithMessage(str);"); // $NON-NLS-1$ buffer.append ("if (!result) return false;}"); // $NON-NLS-1$ } buffer.append ("} catch (e) {}}"); // $NON-NLS-1$ buffer.append ("try {for (var i = 0; i < win.frames.length; i++) {var result = "); // $NON-NLS-1$ buffer.append (functionName); buffer.append ("(win.frames[i]); if (!result) return false;}} catch (e) {} return true;"); // $NON-NLS-1$ buffer.append ("\n};"); // $NON-NLS-1$ execute (buffer.toString ()); Boolean result = (Boolean)evaluate ("return " + functionName +"(window);"); // $NON-NLS-1$ // $NON-NLS-2$ if (result == null) return false; return result.booleanValue (); } public boolean execute(String script) { int frame = Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); int context = Cocoa.objc_msgSend(frame, Cocoa.S_globalContext); byte[] bytes = null; try { bytes = (script + '\0').getBytes("UTF-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { bytes = (script + '\0').getBytes(); } int scriptString = OS.JSStringCreateWithUTF8CString(bytes); try { bytes = (getUrl() + '\0').getBytes("UTF-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { bytes = (getUrl() + '\0').getBytes(); } int urlString = OS.JSStringCreateWithUTF8CString(bytes); int result = OS.JSEvaluateScript(context, scriptString, 0, urlString, 0, null); OS.JSStringRelease(urlString); OS.JSStringRelease(scriptString); return result != 0; } public boolean forward() { html = null; return Cocoa.objc_msgSend(webView, Cocoa.S_goForward) != 0; } public String getBrowserType () { return "webkit"; //$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() { /* WebKit auto-navigates to about:blank at startup */ if (url.length() == 0) return ABOUT_BLANK; 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 WebKit. 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 WebKit. 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 WebKit. 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 WebKit. 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 WebKit. 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 WebKit. 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 WebKit * 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, arg3); break; case 35: didReceiveAuthenticationChallengefromDataSource(arg0, arg1, arg2); break; case 36: ret = runBeforeUnloadConfirmPanelWithMessage(arg0, arg1); break; case 37: ret = callRunBeforeUnloadConfirmPanelWithMessage(arg0, arg1); break; case 38: createPanelDidEnd(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() { html = null; Cocoa.objc_msgSend(webView, Cocoa.S_reload, 0); } public boolean setText(String html, boolean trusted) { /* * If this.html is not null then the about:blank page is already being loaded, * so no navigate is required. Just set the html that is to be shown. */ boolean blankLoading = this.html != null; this.html = html; untrustedText = !trusted; if (blankLoading) return true; int str = createNSString(ABOUT_BLANK); int inURL = Cocoa.objc_msgSend(Cocoa.C_NSURL, Cocoa.S_URLWithString, str); /* autoreleased */ OS.CFRelease (str); int request = Cocoa.objc_msgSend(Cocoa.C_NSURLRequest, Cocoa.S_requestWithURL, inURL); int mainFrame = Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); Cocoa.objc_msgSend(mainFrame, Cocoa.S_loadRequest, request); return true; } public boolean setUrl(String url, String postData, String[] headers) { html = null; if (url.indexOf('/') == 0) { url = PROTOCOL_FILE + url; } else if (url.indexOf(':') == -1) { url = PROTOCOL_HTTP + url; } int inURL = 0; int str = createNSString(url); 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; int request = Cocoa.objc_msgSend(Cocoa.C_NSMutableURLRequest, Cocoa.S_requestWithURL, inURL); OS.CFRelease(inURL); if (postData != null) { int post = createNSString(POST); Cocoa.objc_msgSend(request, Cocoa.S_setHTTPMethod, post); OS.CFRelease (post); byte[] bytes = postData.getBytes(); int data = Cocoa.objc_msgSend(Cocoa.C_NSData, Cocoa.S_dataWithBytes, bytes, bytes.length); Cocoa.objc_msgSend(request, Cocoa.S_setHTTPBody, data); } if (headers != null) { for (int i = 0; i < headers.length; i++) { String current = headers[i]; if (current != null) { int index = current.indexOf(':'); if (index != -1) { String key = current.substring(0, index).trim(); String value = current.substring(index + 1).trim(); if (key.length() > 0 && value.length() > 0) { if (key.equalsIgnoreCase(USER_AGENT)) { /* * Feature of WebKit. The user-agent header value cannot be overridden * here. The workaround is to temporarily set the value on the WebView * and then remove it after the loading of the request has begun. */ int string = createNSString(value); Cocoa.objc_msgSend(webView, Cocoa.S_setCustomUserAgent, string); OS.CFRelease (string); } else { int keyString = createNSString(key); int valueString = createNSString(value); Cocoa.objc_msgSend(request, Cocoa.S_setValueForHTTPHeaderField, valueString, keyString); OS.CFRelease (valueString); OS.CFRelease (keyString); } } } } } } int mainFrame = Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); Cocoa.objc_msgSend(mainFrame, Cocoa.S_loadRequest, request); Cocoa.objc_msgSend(webView, Cocoa.S_setCustomUserAgent, 0); return true; } public void stop() { html = null; Cocoa.objc_msgSend(webView, Cocoa.S_stopLoading, 0); } boolean translateMnemonics() { return false; } /* 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_FILEROOT)) { url2 = ABOUT_BLANK; } else { length = URI_FILEROOT.length (); if (url2.startsWith (URI_FILEROOT) && url2.charAt (length) == '#') { url2 = ABOUT_BLANK + url2.substring (length); } } 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 WebKit. 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, WebKit 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; } int errorCode = Cocoa.objc_msgSend(error, Cocoa.S_code); if (Cocoa.NSURLErrorBadURL < errorCode) return; int failingURL = 0; int info = Cocoa.objc_msgSend(error, Cocoa.S_userInfo); if (info != 0) { int keyString = createNSString("NSErrorFailingURLKey"); //$NON-NLS-1$ failingURL = Cocoa.objc_msgSend(info, Cocoa.S_valueForKey, keyString); OS.CFRelease(keyString); } if (failingURL != 0 && Cocoa.NSURLErrorServerCertificateNotYetValid <= errorCode && errorCode <= Cocoa.NSURLErrorSecureConnectionFailed) { /* handle invalid certificate error */ int keyString = createNSString("NSErrorPeerCertificateChainKey"); //$NON-NLS-1$ int certificates = Cocoa.objc_msgSend(info, Cocoa.S_objectForKey, keyString); OS.CFRelease(keyString); int[] policySearch = new int[1]; int[] policyRef = new int[1]; int[] trustRef = new int[1]; boolean success = false; int result = OS.SecPolicySearchCreate(OS.CSSM_CERT_X_509v3, 0, 0, policySearch); if (result == 0 && policySearch[0] != 0) { result = OS.SecPolicySearchCopyNext(policySearch[0], policyRef); if (result == 0 && policyRef[0] != 0) { result = OS.SecTrustCreateWithCertificates(certificates, policyRef[0], trustRef); if (result == 0 && trustRef[0] != 0) { int panel = Cocoa.objc_msgSend(Cocoa.C_SFCertificateTrustPanel, Cocoa.S_sharedCertificateTrustPanel); String failingUrlString = getString(Cocoa.objc_msgSend(failingURL, Cocoa.S_absoluteString)); String message = Compatibility.getMessage("SWT_InvalidCert_Message", new Object[] {failingUrlString}); //$NON-NLS-1$ int nsString = createNSString(Compatibility.getMessage("SWT_Cancel")); //$NON-NLS-1$ Cocoa.objc_msgSend(panel, Cocoa.S_setAlternateButtonTitle, nsString); OS.CFRelease(nsString); Cocoa.objc_msgSend(panel, Cocoa.S_setShowsHelp, 1); Cocoa.objc_msgSend(failingURL, Cocoa.S_retain); int window = Cocoa.objc_msgSend(webView, Cocoa.S_window); nsString = createNSString(message); Cocoa.objc_msgSend(panel, Cocoa.S_beginSheetForWindow, window, delegate, Cocoa.S_createPanelDidEnd, failingURL, trustRef[0], nsString); OS.CFRelease(nsString); success = true; } } } if (trustRef[0] != 0) OS.CFRelease(trustRef[0]); if (policyRef[0] != 0) OS.CFRelease(policyRef[0]); if (policySearch[0] != 0) OS.CFRelease(policySearch[0]); if (success) return; } /* handle other types of errors */ int description = Cocoa.objc_msgSend(error, Cocoa.S_localizedDescription); if (description != 0) { String descriptionString = getString(description); String message = failingURL != 0 ? getString(Cocoa.objc_msgSend(failingURL, Cocoa.S_absoluteString)) + "\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 createPanelDidEnd(int sheet, int returnCode, int contextInfo) { Cocoa.objc_msgSend(contextInfo, Cocoa.S_autorelease); if (returnCode != Cocoa.NSFileHandlingPanelOKButton) return; /* nothing more to do */ long /*int*/ method = Cocoa.class_getClassMethod(Cocoa.C_NSURLRequest, Cocoa.S_setAllowsAnyHTTPSCertificate); if (method != 0) { int host = Cocoa.objc_msgSend(contextInfo, Cocoa.S_host); int urlString = Cocoa.objc_msgSend(contextInfo, Cocoa.S_absoluteString); Cocoa.objc_msgSend(Cocoa.C_NSURLRequest, Cocoa.S_setAllowsAnyHTTPSCertificate, 1, host); setUrl(getString(urlString), null, null); } } void didFinishLoadForFrame(int frame) { if (frame == Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame)) { /* * If html is not null then there is html from a previous setText() call * waiting to be set into the about:blank page once it has completed loading. */ if (html != null) { if (getUrl().startsWith(ABOUT_BLANK)) { loadingText = true; int htmlString = createNSString(html); int urlString; if (untrustedText) { urlString = createNSString(ABOUT_BLANK); } else { urlString = createNSString(URI_FILEROOT); } int url = Cocoa.objc_msgSend(Cocoa.C_NSURL, Cocoa.S_URLWithString, urlString); /* autoreleased */ int mainFrame = Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame); Cocoa.objc_msgSend(mainFrame, Cocoa.S_loadHTMLStringBaseURL, htmlString, url); OS.CFRelease(urlString); OS.CFRelease(htmlString); html = null; } } /* * The loadHTMLStringBaseURL invocation above will trigger a second didFinishLoadForFrame * callback when it is completed. If text was just set into the browser then wait * for this second callback to come before sending the title or completed events. */ if (!loadingText) { /* * 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. */ final Display display = browser.getDisplay(); 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 = getUrl(); for (int i = 0; i < titleListeners.length; i++) { titleListeners[i].changed(newEvent); } if (browser.isDisposed()) return; } } 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++) { progressListeners[i].completed(progress); } } loadingText = false; if (browser.isDisposed()) return; /* * Feature on WebKit. 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, WebKit 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 * WebKit 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; int ptr = createNSString(DOMEVENT_FOCUSIN); Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); OS.CFRelease(ptr); ptr = createNSString(DOMEVENT_FOCUSOUT); 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; int ptr = createNSString(DOMEVENT_KEYDOWN); Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); OS.CFRelease(ptr); ptr = createNSString(DOMEVENT_KEYUP); 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; int ptr = createNSString(DOMEVENT_MOUSEDOWN); Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); OS.CFRelease(ptr); ptr = createNSString(DOMEVENT_MOUSEUP); Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); OS.CFRelease(ptr); ptr = createNSString(DOMEVENT_MOUSEMOVE); Cocoa.objc_msgSend(document, Cocoa.S_addEventListener, ptr, delegate, 0); OS.CFRelease(ptr); ptr = createNSString(DOMEVENT_MOUSEWHEEL); 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_FILEROOT)) { url2 = ABOUT_BLANK; } else { length = URI_FILEROOT.length (); if (url2.startsWith (URI_FILEROOT) && url2.charAt (length) == '#') { url2 = ABOUT_BLANK + url2.substring (length); } } 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; /* * Each invocation of setText() causes didCommitLoadForFrame to be invoked twice, * once for the initial navigate to about:blank, and once for the auto-navigate * to about:blank that WebKit does when loadHTMLStringBaseURL is invoked. If * this is the first didCommitLoadForFrame callback received for a setText() * invocation then do not send any events or re-install registered BrowserFunctions. */ if (url2.startsWith(ABOUT_BLANK) && html != null) return; 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++) { progressListeners[i].changed(progress); } if (browser.isDisposed()) return; 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); } if (browser.isDisposed()) return; hookDOMKeyListeners(frame); } hookDOMFocusListeners(frame); hookDOMMouseListeners(frame); 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) { int str = createNSString("external"); //$NON-NLS-1$ if (str != 0) { Cocoa.objc_msgSend (windowScriptObject, Cocoa.S_setValue, delegate, str); OS.CFRelease (str); } } /* WebResourceLoadDelegate */ void didFinishLoadingFromDataSource(int identifier, int dataSource) { /* * Feature on WebKit. 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, WebKit 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 WebKit. 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, WebKit 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 user = createNSString(event.user); int password = createNSString(event.password); 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 user = createNSString(userReturn[0]); int password = createNSString(passwordReturn[0]); 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++) { progressListeners[i].changed(progress); } if (browser.isDisposed()) return 0; /* * 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 WebKit) { browser = newEvent.browser; } if (browser != null && !browser.isDisposed()) { webView = ((WebKit)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. 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 WebKit. WebKit'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() { } int callRunBeforeUnloadConfirmPanelWithMessage(long /*int*/ messageID, long /*int*/ arg) { int result = runBeforeUnloadConfirmPanelWithMessage (messageID, 0); return Cocoa.objc_msgSend (Cocoa.C_NSNumber, Cocoa.S_numberWithBool, result); } int runBeforeUnloadConfirmPanelWithMessage(int message, int frame) { StringBuffer text = new StringBuffer(Compatibility.getMessage("SWT_OnBeforeUnload_Message1")); //$NON-NLS-1$ text.append ("\n\n"); //$NON-NLS-1$ int length = OS.CFStringGetLength(message); char[] buffer = new char[length]; CFRange range = new CFRange(); range.length = length; OS.CFStringGetCharacters(message, range, buffer); text.append(new String(buffer)); text.append("\n\n"); //$NON-NLS-1$ text.append(Compatibility.getMessage("SWT_OnBeforeUnload_Message2")); //$NON-NLS-1$ MessageBox messageBox = new MessageBox(browser.getShell(), SWT.OK | SWT.CANCEL | SWT.ICON_QUESTION); messageBox.setMessage(text.toString()); return messageBox.open() == SWT.OK ? 1 : 0; } 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 filename = createNSString(result); 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. 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 key = createNSString(WebElementLinkURLKey); 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); int length = OS.CFStringGetLength(stringPtr); String urlString; if (length == 0) { urlString = ""; //$NON-NLS-1$ } else { char[] 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 (loadingText) { /* * WebKit is auto-navigating to about:blank in response to a loadHTMLString() * invocation. This navigate should always proceed without sending an event * since it is preceded by an explicit navigate to about:blank in setText(). */ Cocoa.objc_msgSend(listener, Cocoa.S_use); return; } if (url == 0) { /* indicates that a URL with an invalid format was specified */ Cocoa.objc_msgSend(listener, Cocoa.S_ignore); return; } boolean isFileURL = Cocoa.objc_msgSend(url, Cocoa.S_isFileURL) != 0; if (isFileURL && getUrl().startsWith(ABOUT_BLANK) && untrustedText) { /* indicates an attempt to access the local file system from untrusted content */ 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_FILEROOT)) { url2 = ABOUT_BLANK; } else { length = URI_FILEROOT.length (); if (url2.startsWith (URI_FILEROOT) && url2.charAt (length) == '#') { url2 = ABOUT_BLANK + url2.substring (length); } } 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) { for (int i = 0; i < locationListeners.length; i++) { locationListeners[i].changing(newEvent); } } if (newEvent.doit) { if (jsEnabled != jsEnabledOnNextPage) { jsEnabled = jsEnabledOnNextPage; 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); } } 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 WebKit. 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; } int result = createNSString(path); 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); /* * WebKit 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); boolean doit; if (keyEvent.type == SWT.KeyDown) { doit = sendKeyEvent(keyEvent); } else { /* SWT.KeyUp */ browser.notifyListeners(keyEvent.type, keyEvent); doit = keyEvent.doit; } if (browser.isDisposed()) { Cocoa.objc_msgSend(evt, Cocoa.S_preventDefault); return; } /* * Bug in WebKit. As a result of using HIWebViewCreate on OSX versions < 10.5 (Leopard), attempting * to traverse out of WebKit backwards (Shift+Tab) leaves it in a strange state where WebKit no * longer has focus but still receives keys. The Carbon-based WebKit 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)) { hasNewFocusElement = false; } } return; } /* mouse event */ /* * The position of mouse events is received in screen-relative coordinates * in order to handle pages with frames, since frames express their event * coordinates relative to themselves rather than relative to their top- * level page. Convert screen-relative coordinates to be browser-relative. */ int screenX = Cocoa.objc_msgSend(evt, Cocoa.S_screenX); int screenY = Cocoa.objc_msgSend(evt, Cocoa.S_screenY); Point position = new Point(screenX, screenY); position = browser.getDisplay().map(null, browser, position); int detail = Cocoa.objc_msgSend(evt, Cocoa.S_detail); Event mouseEvent = new Event(); mouseEvent.widget = browser; mouseEvent.x = position.x; mouseEvent.y = position.y; 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 WebKit. 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 = position.x; mouseEvent.y = position.y; 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) { int str = createNSString ("length"); //$NON-NLS-1$ 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) { return createNSString((String)value); } 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; 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; } long /*int*/ callJava (long /*int*/ index, long /*int*/ token, long /*int*/ args, long /*int*/ 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); if (Cocoa.objc_msgSend (token, Cocoa.S_isKindOfClass, Cocoa.C_NSString) != 0) { int length = Cocoa.objc_msgSend (token, Cocoa.S_length); char[] buffer = new char[length]; Cocoa.objc_msgSend (token, Cocoa.S_getCharacters_, buffer); String tokenString = new String (buffer); Object key = new Integer (functionIndex); BrowserFunction function = (BrowserFunction)functions.get (key); if (function != null && tokenString.equals (function.token)) { 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 ())); } } }