blob: b2cb38a89af3efb968b15fd7beae5f251873ce0a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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.*;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.Callback;
import org.eclipse.swt.internal.carbon.*;
import org.eclipse.swt.internal.cocoa.*;
import org.eclipse.swt.widgets.*;
class Safari extends WebBrowser {
/* Objective-C WebView delegate */
int delegate;
/* Carbon HIView handle */
int webViewHandle;
int windowBoundsHandler;
boolean changingLocation;
String lastHoveredLinkURL;
String html;
int identifier;
int resourceCount;
String url = "";
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 URI_FROMMEMORY = "file:///"; //$NON-NLS-1$
static final String ABOUT_BLANK = "about:blank"; //$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 {
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);
}
}
}
};
}
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);
}
int outControl[] = new int[1];
try {
Cocoa.HIWebViewCreate(outControl);
} catch (UnsatisfiedLinkError e) {
browser.dispose();
SWT.error(SWT.ERROR_NO_HANDLES);
}
webViewHandle = outControl[0];
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});
/*
* 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]);
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 reparent the browser is reparented, the web view has to
* be reparent 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]);
}
final int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
/*
* 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(notificationCenter, Cocoa.S_removeObserver, delegate);
Cocoa.objc_msgSend(delegate, Cocoa.S_release);
OS.DisposeControl(webViewHandle);
html = null;
lastHoveredLinkURL = null;
break;
}
}
}
};
browser.addListener(SWT.Dispose, listener);
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.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);
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;
}
public boolean back() {
html = null;
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
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 webView = Cocoa.HIWebViewGetWebView(webViewHandle);
int value = Cocoa.objc_msgSend(webView, Cocoa.S_stringByEvaluatingJavaScriptFromString, string);
OS.CFRelease(string);
return value != 0;
}
public boolean forward() {
html = null;
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
return Cocoa.objc_msgSend(webView, Cocoa.S_goForward) != 0;
}
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.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': {
int webView = Cocoa.HIWebViewGetWebView (webViewHandle);
Cocoa.objc_msgSend (webView, Cocoa.S_paste);
return OS.noErr;
}
case 'c': {
int webView = Cocoa.HIWebViewGetWebView (webViewHandle);
Cocoa.objc_msgSend (webView, Cocoa.S_copy);
return OS.noErr;
}
case 'x': {
int webView = Cocoa.HIWebViewGetWebView (webViewHandle);
Cocoa.objc_msgSend (webView, Cocoa.S_cut);
return OS.noErr;
}
}
}
}
break;
}
}
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;
}
return ret;
}
public boolean isBackEnabled() {
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
return Cocoa.objc_msgSend(webView, Cocoa.S_canGoBack) != 0;
}
public boolean isForwardEnabled() {
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
return Cocoa.objc_msgSend(webView, Cocoa.S_canGoForward) != 0;
}
public void refresh() {
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
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);
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
//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;
StringBuffer buffer = new StringBuffer();
if (url.indexOf('/') == 0) buffer.append("file://"); //$NON-NLS-1$ //$NON-NLS-2$
else if (url.indexOf(':') == -1) buffer.append("http://"); //$NON-NLS-1$
for (int i = 0; i < url.length(); i++) {
char c = url.charAt(i);
if (c == ' ') buffer.append("%20"); //$NON-NLS-1$ //$NON-NLS-2$
else buffer.append(c);
}
int length = buffer.length();
char[] chars = new char[length];
buffer.getChars(0, length, chars, 0);
int sHandle = OS.CFStringCreateWithCharacters(0, chars, length);
/*
* Note. URLWithString uses autorelease. The resulting URL
* does not need to be released.
* inURL = [NSURL URLWithString:(NSString *)sHandle]
*/
int inURL= Cocoa.objc_msgSend(Cocoa.C_NSURL, Cocoa.S_URLWithString, sHandle);
OS.CFRelease(sHandle);
//request = [NSURLRequest requestWithURL:(NSURL*)inURL];
int request= Cocoa.objc_msgSend(Cocoa.C_NSURLRequest, Cocoa.S_requestWithURL, inURL);
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
//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;
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
Cocoa.objc_msgSend(webView, Cocoa.S_stopLoading, 0);
}
/* WebFrameLoadDelegate */
void didFailProvisionalLoadWithError(int error, int frame) {
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
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. 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 didFinishLoadForFrame(int frame) {
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
if (frame == Cocoa.objc_msgSend(webView, Cocoa.S_mainFrame)) {
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);
}
}
}
);
}
/*
* 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 didReceiveTitle(int title, int frame) {
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
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) {
int webView = Cocoa.HIWebViewGetWebView(webViewHandle);
//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);
}
}
/* 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;
}
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 webView = Cocoa.HIWebViewGetWebView(webViewHandle);
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) {
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 = newEvent.browser;
if (browser != null && !browser.isDisposed()) {
webView = Cocoa.HIWebViewGetWebView(((Safari)browser.webBrowser).webViewHandle);
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 in Safari. 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);
Menu menu = browser.getMenu();
if (!event.doit) return 0;
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;
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;
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;
}
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);
FileDialog dialog = new FileDialog(browser.getShell(), SWT.SAVE);
dialog.setText(SWT.getMessage ("SWT_FileDownload")); //$NON-NLS-1$
dialog.setFileName(name);
String 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);
}
}