blob: 4f0b314f635a485bdc2087289e1b12158f58c520 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.browser;
import org.eclipse.swt.*;
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.widgets.*;
/**
* Instances of this class implement the browser user interface
* metaphor. It allows the user to visualize and navigate through
* HTML documents.
* <p>
* Note that although this class is a subclass of <code>Composite</code>,
* it does not make sense to set a layout on it.
* </p><p>
* IMPORTANT: This class is <em>not</em> intended to be subclassed.
* </p>
*
* @since 3.0
*/
public class Browser extends Composite {
/* Package Name */
static final String PACKAGE_PREFIX = "org.eclipse.swt.browser."; //$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 int MAX_PROGRESS = 100;
/* External Listener management */
CloseWindowListener[] closeWindowListeners = new CloseWindowListener[0];
LocationListener[] locationListeners = new LocationListener[0];
OpenWindowListener[] openWindowListeners = new OpenWindowListener[0];
ProgressListener[] progressListeners = new ProgressListener[0];
StatusTextListener[] statusTextListeners = new StatusTextListener[0];
TitleListener[] titleListeners = new TitleListener[0];
VisibilityWindowListener[] visibilityWindowListeners = new VisibilityWindowListener[0];
static Callback Callback3, Callback7;
/* Objective-C WebView delegate */
int delegate;
/* Carbon HIView handle */
int webViewHandle;
boolean changingLocation;
String html;
int identifier;
int resourceCount;
String url = "";
Point location;
Point size;
boolean statusBar = true, toolBar = true;
//TEMPORARY CODE
// boolean doit;
static final int MIN_SIZE = 16;
/**
* Constructs a new instance of this class given its parent
* and a style value describing its behavior and appearance.
* <p>
* The style value is either one of the style constants defined in
* class <code>SWT</code> which is applicable to instances of this
* class, or must be built by <em>bitwise OR</em>'ing together
* (that is, using the <code>int</code> "|" operator) two or more
* of those <code>SWT</code> style constants. The class description
* lists the style constants that are applicable to the class.
* Style bits are also inherited from superclasses.
* </p>
*
* @param parent a widget which will be the parent of the new instance (cannot be null)
* @param style the style of widget to construct
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
* </ul>
* @exception SWTError <ul>
* <li>ERROR_NO_HANDLES if a handle could not be obtained for browser creation</li>
* </ul>
*
* @see Widget#getStyle
*
* @since 3.0
*/
public Browser(Composite parent, int style) {
super(parent, 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.
*/
int[] response = new int[1];
int err = OS.Gestalt(OS.gestaltSystemVersion, response);
if (err != OS.noErr || ((response[0] & 0xffff) < 0x1030)) {
dispose();
SWT.error(SWT.ERROR_NO_HANDLES);
}
int outControl[] = new int[1];
try {
WebKit.HIWebViewCreate(outControl);
} catch (UnsatisfiedLinkError e) {
dispose();
SWT.error(SWT.ERROR_NO_HANDLES);
}
webViewHandle = outControl[0];
if (webViewHandle == 0) {
dispose();
SWT.error(SWT.ERROR_NO_HANDLES);
}
Display display = getDisplay();
display.setData(ADD_WIDGET_KEY, new Object[] {new Integer(webViewHandle), this});
/*
* 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 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.HIViewAddSubview(handle, webViewHandle);
OS.HIViewSetVisible(webViewHandle, true);
if (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(handle)});
OS.SendEventToEventTarget(showEvent[0], OS.GetWindowEventTarget(OS.GetControlOwner(handle)));
if (showEvent[0] != 0) OS.ReleaseEvent(showEvent[0]);
}
final int webView = WebKit.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 = WebKit.objc_msgSend(WebKit.C_NSNotificationCenter, WebKit.S_defaultCenter);
Listener listener = new Listener() {
public void handleEvent(Event e) {
switch (e.type) {
case SWT.Dispose: {
Shell shell = getShell();
shell.removeListener(SWT.Resize, this);
shell.removeListener(SWT.Show, this);
shell.removeListener(SWT.Hide, this);
Control c = Browser.this;
do {
c.removeListener(SWT.Show, this);
c.removeListener(SWT.Hide, this);
c = c.getParent();
} while (c != shell);
e.display.setData(ADD_WIDGET_KEY, new Object[] {new Integer(webViewHandle), null});
WebKit.objc_msgSend(webView, WebKit.S_setFrameLoadDelegate, 0);
WebKit.objc_msgSend(webView, WebKit.S_setResourceLoadDelegate, 0);
WebKit.objc_msgSend(webView, WebKit.S_setUIDelegate, 0);
WebKit.objc_msgSend(webView, WebKit.S_setPolicyDelegate, 0);
WebKit.objc_msgSend(notificationCenter, WebKit.S_removeObserver, delegate);
WebKit.objc_msgSend(delegate, WebKit.S_release);
html = null;
break;
}
case SWT.Hide: {
/*
* 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 Hide and Show events on the browser's parents
* and set its size to 0 in Hide and to restore its size in Show.
*/
CGRect bounds = new CGRect();
bounds.x = bounds.y = -MIN_SIZE;
bounds.width = bounds.height = MIN_SIZE;
OS.HIViewSetFrame(webViewHandle, bounds);
break;
}
case SWT.Show: {
/*
* 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 Hide and Show events on the browser's parents
* and set its size to 0 in Hide and to restore its size in Show.
*/
CGRect bounds = new CGRect();
OS.HIViewGetFrame(handle, bounds);
/*
* 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 SWT.Resize: {
/* Do not update size when it is not visible */
if (!isVisible()) return;
/*
* 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.
*/
CGRect bounds = new CGRect();
OS.HIViewGetFrame(handle, bounds);
/*
* 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;
if (e.widget == getShell()) {
bounds.x++;
/* Note that the bounds needs to change */
OS.HIViewSetFrame(webViewHandle, bounds);
bounds.x--;
}
OS.HIViewSetFrame(webViewHandle, bounds);
break;
}
}
}
};
addListener(SWT.Dispose, listener);
addListener(SWT.Resize, listener);
Shell shell = getShell();
shell.addListener(SWT.Resize, listener);
shell.addListener(SWT.Show, listener);
shell.addListener(SWT.Hide, listener);
Control c = this;
do {
c.addListener(SWT.Show, listener);
c.addListener(SWT.Hide, listener);
c = c.getParent();
} while (c != shell);
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.kEventClassTextInput, OS.kEventTextInputUnicodeForKeyEvent,
};
int controlTarget = OS.GetControlEventTarget(webViewHandle);
OS.InstallEventHandler(controlTarget, callback3Address, mask.length / 2, mask, webViewHandle, null);
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 = WebKit.objc_msgSend(WebKit.C_WebKitDelegate, WebKit.S_alloc);
delegate = WebKit.objc_msgSend(delegate, WebKit.S_initWithProc, callback7Address, webViewHandle);
// [webView setFrameLoadDelegate:delegate];
WebKit.objc_msgSend(webView, WebKit.S_setFrameLoadDelegate, delegate);
// [webView setResourceLoadDelegate:delegate];
WebKit.objc_msgSend(webView, WebKit.S_setResourceLoadDelegate, delegate);
// [webView setUIDelegate:delegate];
WebKit.objc_msgSend(webView, WebKit.S_setUIDelegate, delegate);
/* register delegate for all notifications send out from webview */
WebKit.objc_msgSend(notificationCenter, WebKit.S_addObserver_selector_name_object, delegate, WebKit.S_handleNotification, 0, webView);
// [webView setPolicyDelegate:delegate];
WebKit.objc_msgSend(webView, WebKit.S_setPolicyDelegate, delegate);
}
static int eventProc3(int nextHandler, int theEvent, int userData) {
Widget widget = Display.getCurrent().findWidget(userData);
if (widget instanceof Browser)
return ((Browser)widget).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 ((Browser)widget).handleCallback(selector, arg0, arg1, arg2, arg3);
return 0;
}
/**
* Adds the listener to the collection of listeners who will be
* notified when the window hosting the receiver should be closed.
* <p>
* This notification occurs when a javascript command such as
* <code>window.close</code> gets executed by a <code>Browser</code>.
* </p>
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void addCloseWindowListener(CloseWindowListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
CloseWindowListener[] newCloseWindowListeners = new CloseWindowListener[closeWindowListeners.length + 1];
System.arraycopy(closeWindowListeners, 0, newCloseWindowListeners, 0, closeWindowListeners.length);
closeWindowListeners = newCloseWindowListeners;
closeWindowListeners[closeWindowListeners.length - 1] = listener;
}
/**
* Adds the listener to the collection of listeners who will be
* notified when the current location has changed or is about to change.
* <p>
* This notification typically occurs when the application navigates
* to a new location with {@link #setUrl(String)} or when the user
* activates a hyperlink.
* </p>
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void addLocationListener(LocationListener listener) {
checkWidget();
if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
LocationListener[] newLocationListeners = new LocationListener[locationListeners.length + 1];
System.arraycopy(locationListeners, 0, newLocationListeners, 0, locationListeners.length);
locationListeners = newLocationListeners;
locationListeners[locationListeners.length - 1] = listener;
}
/**
* Adds the listener to the collection of listeners who will be
* notified when a new window needs to be created.
* <p>
* This notification occurs when a javascript command such as
* <code>window.open</code> gets executed by a <code>Browser</code>.
* </p>
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void addOpenWindowListener(OpenWindowListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
OpenWindowListener[] newOpenWindowListeners = new OpenWindowListener[openWindowListeners.length + 1];
System.arraycopy(openWindowListeners, 0, newOpenWindowListeners, 0, openWindowListeners.length);
openWindowListeners = newOpenWindowListeners;
openWindowListeners[openWindowListeners.length - 1] = listener;
}
/**
* Adds the listener to the collection of listeners who will be
* notified when a progress is made during the loading of the current
* URL or when the loading of the current URL has been completed.
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void addProgressListener(ProgressListener listener) {
checkWidget();
if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
ProgressListener[] newProgressListeners = new ProgressListener[progressListeners.length + 1];
System.arraycopy(progressListeners, 0, newProgressListeners, 0, progressListeners.length);
progressListeners = newProgressListeners;
progressListeners[progressListeners.length - 1] = listener;
}
/**
* Adds the listener to the collection of listeners who will be
* notified when the status text is changed.
* <p>
* The status text is typically displayed in the status bar of
* a browser application.
* </p>
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void addStatusTextListener(StatusTextListener listener) {
checkWidget();
if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
StatusTextListener[] newStatusTextListeners = new StatusTextListener[statusTextListeners.length + 1];
System.arraycopy(statusTextListeners, 0, newStatusTextListeners, 0, statusTextListeners.length);
statusTextListeners = newStatusTextListeners;
statusTextListeners[statusTextListeners.length - 1] = listener;
}
/**
* Adds the listener to the collection of listeners who will be
* notified when the title of the current document is available
* or has changed.
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void addTitleListener(TitleListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
TitleListener[] newTitleListeners = new TitleListener[titleListeners.length + 1];
System.arraycopy(titleListeners, 0, newTitleListeners, 0, titleListeners.length);
titleListeners = newTitleListeners;
titleListeners[titleListeners.length - 1] = listener;
}
/**
* Adds the listener to the collection of listeners who will be
* notified when a window hosting the receiver needs to be displayed
* or hidden.
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void addVisibilityWindowListener(VisibilityWindowListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
VisibilityWindowListener[] newVisibilityWindowListeners = new VisibilityWindowListener[visibilityWindowListeners.length + 1];
System.arraycopy(visibilityWindowListeners, 0, newVisibilityWindowListeners, 0, visibilityWindowListeners.length);
visibilityWindowListeners = newVisibilityWindowListeners;
visibilityWindowListeners[visibilityWindowListeners.length - 1] = listener;
}
/**
* Navigate to the previous session history item.
*
* @return <code>true</code> if the operation was successful and <code>false</code> otherwise
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @see #forward
*
* @since 3.0
*/
public boolean back() {
checkWidget();
html = null;
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
return WebKit.objc_msgSend(webView, WebKit.S_goBack) != 0;
}
protected void checkSubclass () {
String name = getClass().getName();
int index = name.lastIndexOf('.');
if (!name.substring(0, index + 1).equals(PACKAGE_PREFIX)) {
SWT.error(SWT.ERROR_INVALID_SUBCLASS);
}
}
/**
* Execute the specified script.
*
* <p>
* Execute a script containing javascript commands in the context of the current document.
*
* @param script the script with javascript commands
*
* @return <code>true</code> if the operation was successful and <code>false</code> otherwise
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the script is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.1
*/
public boolean execute(String script) {
checkWidget();
if (script == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
int length = script.length();
char[] buffer = new char[length];
script.getChars(0, length, buffer, 0);
int string = OS.CFStringCreateWithCharacters(0, buffer, length);
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
int value = WebKit.objc_msgSend(webView, WebKit.S_stringByEvaluatingJavaScriptFromString, string);
OS.CFRelease(string);
return value != 0;
}
/**
* Navigate to the next session history item.
*
* @return <code>true</code> if the operation was successful and <code>false</code> otherwise
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @see #back
*
* @since 3.0
*/
public boolean forward() {
checkWidget();
html = null;
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
return WebKit.objc_msgSend(webView, WebKit.S_goForward) != 0;
}
/**
* Returns the current URL.
*
* @return the current URL or an empty <code>String</code> if there is no current URL
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @see #setUrl
*
* @since 3.0
*/
public String getUrl() {
checkWidget();
return url;
}
int handleCallback(int nextHandler, int theEvent) {
int eventKind = OS.GetEventKind(theEvent);
switch (eventKind) {
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 (!isVisible ()) return OS.noErr;
break;
}
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;
break;
}
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: ret = webViewFirstResponder(); break;
case 27: makeFirstResponder(arg0); break;
}
return ret;
}
/**
* Returns <code>true</code> if the receiver can navigate to the
* previous session history item, and <code>false</code> otherwise.
*
* @return the receiver's back command enabled state
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see #back
*/
public boolean isBackEnabled() {
checkWidget();
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
return WebKit.objc_msgSend(webView, WebKit.S_canGoBack) != 0;
}
/**
* Returns <code>true</code> if the receiver can navigate to the
* next session history item, and <code>false</code> otherwise.
*
* @return the receiver's forward command enabled state
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see #forward
*/
public boolean isForwardEnabled() {
checkWidget();
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
return WebKit.objc_msgSend(webView, WebKit.S_canGoForward) != 0;
}
/**
* Refresh the current page.
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void refresh() {
checkWidget();
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
WebKit.objc_msgSend(webView, WebKit.S_reload, 0);
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the window hosting the receiver should be closed.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void removeCloseWindowListener(CloseWindowListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (closeWindowListeners.length == 0) return;
int index = -1;
for (int i = 0; i < closeWindowListeners.length; i++) {
if (listener == closeWindowListeners[i]){
index = i;
break;
}
}
if (index == -1) return;
if (closeWindowListeners.length == 1) {
closeWindowListeners = new CloseWindowListener[0];
return;
}
CloseWindowListener[] newCloseWindowListeners = new CloseWindowListener[closeWindowListeners.length - 1];
System.arraycopy(closeWindowListeners, 0, newCloseWindowListeners, 0, index);
System.arraycopy(closeWindowListeners, index + 1, newCloseWindowListeners, index, closeWindowListeners.length - index - 1);
closeWindowListeners = newCloseWindowListeners;
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the current location is changed or about to be changed.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void removeLocationListener(LocationListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (locationListeners.length == 0) return;
int index = -1;
for (int i = 0; i < locationListeners.length; i++) {
if (listener == locationListeners[i]){
index = i;
break;
}
}
if (index == -1) return;
if (locationListeners.length == 1) {
locationListeners = new LocationListener[0];
return;
}
LocationListener[] newLocationListeners = new LocationListener[locationListeners.length - 1];
System.arraycopy(locationListeners, 0, newLocationListeners, 0, index);
System.arraycopy(locationListeners, index + 1, newLocationListeners, index, locationListeners.length - index - 1);
locationListeners = newLocationListeners;
}
/**
* Removes the listener from the collection of listeners who will
* be notified when a new window needs to be created.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void removeOpenWindowListener(OpenWindowListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (openWindowListeners.length == 0) return;
int index = -1;
for (int i = 0; i < openWindowListeners.length; i++) {
if (listener == openWindowListeners[i]){
index = i;
break;
}
}
if (index == -1) return;
if (openWindowListeners.length == 1) {
openWindowListeners = new OpenWindowListener[0];
return;
}
OpenWindowListener[] newOpenWindowListeners = new OpenWindowListener[openWindowListeners.length - 1];
System.arraycopy(openWindowListeners, 0, newOpenWindowListeners, 0, index);
System.arraycopy(openWindowListeners, index + 1, newOpenWindowListeners, index, openWindowListeners.length - index - 1);
openWindowListeners = newOpenWindowListeners;
}
/**
* Removes the listener from the collection of listeners who will
* be notified when a progress is made during the loading of the current
* URL or when the loading of the current URL has been completed.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void removeProgressListener(ProgressListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (progressListeners.length == 0) return;
int index = -1;
for (int i = 0; i < progressListeners.length; i++) {
if (listener == progressListeners[i]){
index = i;
break;
}
}
if (index == -1) return;
if (progressListeners.length == 1) {
progressListeners = new ProgressListener[0];
return;
}
ProgressListener[] newProgressListeners = new ProgressListener[progressListeners.length - 1];
System.arraycopy(progressListeners, 0, newProgressListeners, 0, index);
System.arraycopy(progressListeners, index + 1, newProgressListeners, index, progressListeners.length - index - 1);
progressListeners = newProgressListeners;
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the status text is changed.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void removeStatusTextListener(StatusTextListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (statusTextListeners.length == 0) return;
int index = -1;
for (int i = 0; i < statusTextListeners.length; i++) {
if (listener == statusTextListeners[i]){
index = i;
break;
}
}
if (index == -1) return;
if (statusTextListeners.length == 1) {
statusTextListeners = new StatusTextListener[0];
return;
}
StatusTextListener[] newStatusTextListeners = new StatusTextListener[statusTextListeners.length - 1];
System.arraycopy(statusTextListeners, 0, newStatusTextListeners, 0, index);
System.arraycopy(statusTextListeners, index + 1, newStatusTextListeners, index, statusTextListeners.length - index - 1);
statusTextListeners = newStatusTextListeners;
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the title of the current document is available
* or has changed.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void removeTitleListener(TitleListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (titleListeners.length == 0) return;
int index = -1;
for (int i = 0; i < titleListeners.length; i++) {
if (listener == titleListeners[i]){
index = i;
break;
}
}
if (index == -1) return;
if (titleListeners.length == 1) {
titleListeners = new TitleListener[0];
return;
}
TitleListener[] newTitleListeners = new TitleListener[titleListeners.length - 1];
System.arraycopy(titleListeners, 0, newTitleListeners, 0, index);
System.arraycopy(titleListeners, index + 1, newTitleListeners, index, titleListeners.length - index - 1);
titleListeners = newTitleListeners;
}
/**
* Removes the listener from the collection of listeners who will
* be notified when a window hosting the receiver needs to be displayed
* or hidden.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void removeVisibilityWindowListener(VisibilityWindowListener listener) {
checkWidget();
if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (visibilityWindowListeners.length == 0) return;
int index = -1;
for (int i = 0; i < visibilityWindowListeners.length; i++) {
if (listener == visibilityWindowListeners[i]){
index = i;
break;
}
}
if (index == -1) return;
if (visibilityWindowListeners.length == 1) {
visibilityWindowListeners = new VisibilityWindowListener[0];
return;
}
VisibilityWindowListener[] newVisibilityWindowListeners = new VisibilityWindowListener[visibilityWindowListeners.length - 1];
System.arraycopy(visibilityWindowListeners, 0, newVisibilityWindowListeners, 0, index);
System.arraycopy(visibilityWindowListeners, index + 1, newVisibilityWindowListeners, index, visibilityWindowListeners.length - index - 1);
visibilityWindowListeners = newVisibilityWindowListeners;
}
/**
* Renders HTML.
*
* <p>
* The html parameter is Unicode encoded since it is a java <code>String</code>.
* As a result, the HTML meta tag charset should not be set. The charset is implied
* by the <code>String</code> itself.
*
* @param html the HTML content to be rendered
*
* @return true if the operation was successful and false otherwise.
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the html is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @see #setUrl
*
* @since 3.0
*/
public boolean setText(String html) {
checkWidget();
if (html == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
/*
* Bug in Safari. The web view segment faults in some circunstances
* 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);
String baseURL = "about:blank"; //$NON-NLS-1$
length = baseURL.length();
buffer = new char[length];
baseURL.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 = WebKit.objc_msgSend(WebKit.C_NSURL, WebKit.S_URLWithString, URLString);
OS.CFRelease(URLString);
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
//mainFrame = [webView mainFrame];
int mainFrame = WebKit.objc_msgSend(webView, WebKit.S_mainFrame);
//[mainFrame loadHTMLString:(NSString *) string baseURL:(NSURL *)URL];
WebKit.objc_msgSend(mainFrame, WebKit.S_loadHTMLStringbaseURL, string, URL);
OS.CFRelease(string);
}
/**
* Loads a URL.
*
* @param url the URL to be loaded
*
* @return true if the operation was successful and false otherwise.
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the url is null</li>
* </ul>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @see #getUrl
*
* @since 3.0
*/
public boolean setUrl(String url) {
checkWidget();
if (url == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
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= WebKit.objc_msgSend(WebKit.C_NSURL, WebKit.S_URLWithString, sHandle);
OS.CFRelease(sHandle);
//request = [NSURLRequest requestWithURL:(NSURL*)inURL];
int request= WebKit.objc_msgSend(WebKit.C_NSURLRequest, WebKit.S_requestWithURL, inURL);
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
//mainFrame = [webView mainFrame];
int mainFrame= WebKit.objc_msgSend(webView, WebKit.S_mainFrame);
//[mainFrame loadRequest:request];
WebKit.objc_msgSend(mainFrame, WebKit.S_loadRequest, request);
return true;
}
/**
* Stop any loading and rendering activity.
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* </ul>
*
* @since 3.0
*/
public void stop() {
checkWidget();
html = null;
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
WebKit.objc_msgSend(webView, WebKit.S_stopLoading, 0);
}
/* WebFrameLoadDelegate */
void didFailProvisionalLoadWithError(int error, int frame) {
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
if (frame == WebKit.objc_msgSend(webView, WebKit.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 = WebKit.HIWebViewGetWebView(webViewHandle);
if (frame == WebKit.objc_msgSend(webView, WebKit.S_mainFrame)) {
final Display display= getDisplay();
final ProgressEvent progress = new ProgressEvent(this);
progress.display = getDisplay();
progress.widget = this;
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 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() && !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 = WebKit.HIWebViewGetWebView(webViewHandle);
if (frame == WebKit.objc_msgSend(webView, WebKit.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.this);
newEvent.display = getDisplay();
newEvent.widget = this;
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 = WebKit.HIWebViewGetWebView(webViewHandle);
//id url= [[[[frame provisionalDataSource] request] URL] absoluteString];
int dataSource = WebKit.objc_msgSend(frame, WebKit.S_dataSource);
int request = WebKit.objc_msgSend(dataSource, WebKit.S_request);
int url = WebKit.objc_msgSend(request, WebKit.S_URL);
int s = WebKit.objc_msgSend(url, WebKit.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);
final Display display = getDisplay();
boolean top = frame == WebKit.objc_msgSend(webView, WebKit.S_mainFrame);
if (top) {
/* reset resource status variables */
resourceCount = 0;
this.url = url2;
final ProgressEvent progress = new ProgressEvent(this);
progress.display = display;
progress.widget = this;
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() && !isDisposed())
listener.changed(progress);
}
}
);
}
StatusTextEvent statusText = new StatusTextEvent(this);
statusText.display = display;
statusText.widget = this;
statusText.text = url2;
for (int i = 0; i < statusTextListeners.length; i++)
statusTextListeners[i].changed(statusText);
}
LocationEvent location = new LocationEvent(Browser.this);
location.display = display;
location.widget = this;
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 = getDisplay();
final ProgressEvent progress = new ProgressEvent(this);
progress.display = display;
progress.widget = this;
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() && !isDisposed())
listener.changed(progress);
}
}
);
}
/*
* Note. numberWithInt uses autorelease. The resulting object
* does not need to be released.
* identifier = [NSNumber numberWithInt: resourceCount++]
*/
int identifier = WebKit.objc_msgSend(WebKit.C_NSNumber, WebKit.S_numberWithInt, resourceCount++);
if (this.identifier == 0) {
int webView = WebKit.HIWebViewGetWebView(webViewHandle);
int frame = WebKit.objc_msgSend(dataSource, WebKit.S_webFrame);
if (frame == WebKit.objc_msgSend(webView, WebKit.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.this);
newEvent.display = getDisplay();
newEvent.widget = this;
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 = WebKit.HIWebViewGetWebView(browser.webViewHandle);
if (request != 0) {
//mainFrame = [webView mainFrame];
int mainFrame= WebKit.objc_msgSend(webView, WebKit.S_mainFrame);
//[mainFrame loadRequest:request];
WebKit.objc_msgSend(mainFrame, WebKit.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 = getShell();
Point pt = parent.getSize();
parent.setSize(pt.x+1, pt.y);
parent.setSize(pt.x, pt.y);
WindowEvent newEvent = new WindowEvent(this);
newEvent.display = getDisplay();
newEvent.widget = this;
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.memcpy(dest, frame, 16);
/* convert to SWT system coordinates */
Rectangle bounds = 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 webViewFirstResponder() {
/*
* Note. A web page can request focus through javascript to an internal
* element. For some reason, when this occurs, Carbon SWT widget previously
* focused continues to believe it has focus, causing graphical problems
* (text widget shows flashing caret) and keyboard grab (keyboard continues
* to go to WebKit even if the Browser widget is hidden and another Carbon
* SWT widget has been selected by the user. The workaround is to not allow
* WebKit to give focus to an internal element by returning 0 in this
* callback and by not doing anything in the makeFirstResponder callback.
*/
return 0;
}
void makeFirstResponder(int responder) {
}
void webViewClose() {
Shell parent = getShell();
WindowEvent newEvent = new WindowEvent(this);
newEvent.display = getDisplay();
newEvent.widget = this;
for (int i = 0; i < closeWindowListeners.length; i++)
closeWindowListeners[i].close(newEvent);
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;
notifyListeners(SWT.MenuDetect, event);
Menu menu = 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(this);
statusText.display = getDisplay();
statusText.widget = this;
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;
}
/* PolicyDelegate */
void decidePolicyForMIMEType(int type, int request, int frame, int listener) {
WebKit.objc_msgSend(listener, WebKit.S_use);
}
void decidePolicyForNavigationAction(int actionInformation, int request, int frame, int listener) {
int url = WebKit.objc_msgSend(request, WebKit.S_URL);
int s = WebKit.objc_msgSend(url, WebKit.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);
LocationEvent newEvent = new LocationEvent(this);
newEvent.display = getDisplay();
newEvent.widget = this;
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;
}
WebKit.objc_msgSend(listener, newEvent.doit ? WebKit.S_use : WebKit.S_ignore);
if (html != null && !isDisposed()) {
String html = this.html;
this.html = null;
_setText(html);
}
}
void decidePolicyForNewWindowAction(int actionInformation, int request, int frameName, int listener) {
}
void unableToImplementPolicyWithError(int error, int frame) {
}
}