diff dwt/browser/Safari.d @ 0:380af2bdd8e5

Upload of whole dwt tree
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sat, 09 Aug 2008 17:00:02 +0200
parents
children f565d3a95c0a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/browser/Safari.d	Sat Aug 09 17:00:02 2008 +0200
@@ -0,0 +1,1566 @@
+/*******************************************************************************
+ * 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
+ *     
+ * Port to the D Programming language:
+ *     Jacob Carlborg <jacob.carlborg@gmail.com>
+ *******************************************************************************/
+module dwt.browser.Safari;
+
+import dwt.DWT;
+import dwt.browser.LocationEvent;
+import dwt.browser.ProgressEvent;
+import dwt.browser.ProgressListener;
+import dwt.browser.StatusTextEvent;
+import dwt.browser.TitleEvent;
+import dwt.browser.TitleListener;
+import dwt.browser.WebBrowser;
+import dwt.dwthelper.utils;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.Callback;
+import dwt.internal.cocoa.DOMDocument;
+import dwt.internal.cocoa.DOMEvent;
+import dwt.internal.cocoa.DOMKeyboardEvent;
+import dwt.internal.cocoa.DOMMouseEvent;
+import dwt.internal.cocoa.DOMWheelEvent;
+import dwt.internal.cocoa.NSArray;
+import dwt.internal.cocoa.NSDictionary;
+import dwt.internal.cocoa.NSHTTPCookie;
+import dwt.internal.cocoa.NSHTTPCookieStorage;
+import dwt.internal.cocoa.NSNotificationCenter;
+import dwt.internal.cocoa.NSNumber;
+import dwt.internal.cocoa.NSPrintInfo;
+import dwt.internal.cocoa.NSPrintOperation;
+import dwt.internal.cocoa.NSString;
+import dwt.internal.cocoa.NSURL;
+import dwt.internal.cocoa.NSURLDownload;
+import dwt.internal.cocoa.NSURLRequest;
+import dwt.internal.cocoa.OS;
+import dwt.internal.cocoa.DWTWebViewDelegate;
+import dwt.internal.cocoa.WebDataSource;
+import dwt.internal.cocoa.WebDocumentRepresentation;
+import dwt.internal.cocoa.WebFrame;
+import dwt.internal.cocoa.WebFrameView;
+import dwt.internal.cocoa.WebOpenPanelResultListener;
+import dwt.internal.cocoa.WebPolicyDecisionListener;
+import dwt.internal.cocoa.WebPreferences;
+import dwt.internal.cocoa.WebView;
+import dwt.internal.cocoa.id;
+
+import dwt.widgets.Composite;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.FileDialog;
+import dwt.widgets.Listener;
+import dwt.widgets.Menu;
+import dwt.widgets.MessageBox;
+import dwt.widgets.Shell;
+
+import Math = tango.math.Math;
+
+import dwt.dwthelper.Runnable;
+
+class Safari : WebBrowser
+{
+    WebView webView;
+    DWTWebViewDelegate delegatee;
+    int jniRef;
+    bool changingLocation;
+    String lastHoveredLinkURL;
+    String html;
+    int identifier;
+    int resourceCount;
+    String url = "";
+    Point location;
+    Point size;
+    bool statusBar = true, toolBar = true, ignoreDispose;
+    int lastMouseMoveX, lastMouseMoveY;
+    //TEMPORARY CODE
+    //  bool doit;
+
+    static bool Initialized;
+    static Callback Callback2, Callback3, Callback4, Callback5, Callback6, Callback7;
+
+    static const int MIN_SIZE = 16;
+    static const int MAX_PROGRESS = 100;
+    static const String WebElementLinkURLKey = "WebElementLinkURL"; //$NON-NLS-1$
+    static const String AGENT_STRING = "Safari/unknown"; //$NON-NLS-1$
+    static const String URI_FROMMEMORY = "file:///"; //$NON-NLS-1$
+    static const String PROTOCOL_FILE = "file:"; //$NON-NLS-1$
+    static const String PROTOCOL_HTTP = "http:"; //$NON-NLS-1$
+    static const String ABOUT_BLANK = "about:blank"; //$NON-NLS-1$
+    static const String SAFARI_EVENTS_FIX_KEY = "dwt.internal.safariEventsFix"; //$NON-NLS-1$
+
+    /* event Strings */
+    static const String DOMEVENT_KEYUP = "keyup"; //$NON-NLS-1$
+    static const String DOMEVENT_KEYDOWN = "keydown"; //$NON-NLS-1$
+    static const String DOMEVENT_MOUSEDOWN = "mousedown"; //$NON-NLS-1$
+    static const String DOMEVENT_MOUSEUP = "mouseup"; //$NON-NLS-1$
+    static const String DOMEVENT_MOUSEMOVE = "mousemove"; //$NON-NLS-1$
+    static const String DOMEVENT_MOUSEWHEEL = "mousewheel"; //$NON-NLS-1$
+
+    static this ()
+    {
+        NativeClearSessions = new class Runnable
+        {
+            public void run ()
+            {
+                NSHTTPCookieStorage storage = NSHTTPCookieStorage.sharedHTTPCookieStorage();
+                NSArray cookies = storage.cookies();
+                int count = cookies.count();
+                
+                for (int i = 0; i < count; i++)
+                {
+                    NSHTTPCookie cookie = new NSHTTPCookie(cookies.objectAtIndex(i));
+                    if (cookie.isSessionOnly())
+                    {
+                        storage.deleteCookie(cookie);
+                    }
+                }
+            }
+        };
+    }
+
+    public void create (Composite parent, int style)
+    {
+
+        String className = "DWTWebViewDelegate";
+        if (OS.objc_lookUpClass(className) == 0)
+        {
+            ClassInfo safaryClass = this.classinfo;
+            Callback2 = new Callback(safaryClass, "browserProc", 2);
+            int proc2 = Callback2.getAddress();
+            if (proc2 is 0)
+                DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
+            Callback3 = new Callback(safaryClass, "browserProc", 3);
+            int proc3 = Callback3.getAddress();
+            if (proc3 is 0)
+                DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
+            Callback4 = new Callback(safaryClass, "browserProc", 4);
+            int proc4 = Callback4.getAddress();
+            if (proc4 is 0)
+                DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
+            Callback5 = new Callback(safaryClass, "browserProc", 5);
+            int proc5 = Callback5.getAddress();
+            if (proc5 is 0)
+                DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
+            Callback6 = new Callback(safaryClass, "browserProc", 6);
+            int proc6 = Callback6.getAddress();
+            if (proc6 is 0)
+                DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
+            Callback7 = new Callback(safaryClass, "browserProc", 7);
+            int proc7 = Callback7.getAddress();
+            if (proc7 is 0)
+                DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
+
+            int cls = OS.objc_allocateClassPair(OS.class_WebView, className, 0);
+            OS.class_addIvar(cls, "tag", OS.PTR_SIZEOF, cast(byte) (Math.log(OS.PTR_SIZEOF) / Math.log(2)), "i");
+            OS.class_addMethod(cls, OS.sel_tag, proc2, "@:");
+            OS.class_addMethod(cls, OS.sel_setTag_1, proc3, "@:i");
+            OS.class_addMethod(cls, OS.sel_webView_1didChangeLocationWithinPageForFrame_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webView_1didFailProvisionalLoadWithError_1forFrame_1, proc5, "@:@@@");
+            OS.class_addMethod(cls, OS.sel_webView_1didFinishLoadForFrame_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webView_1didReceiveTitle_1forFrame_1, proc5, "@:@@@");
+            OS.class_addMethod(cls, OS.sel_webView_1didStartProvisionalLoadForFrame_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webView_1didCommitLoadForFrame_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webView_1resource_1didFinishLoadingFromDataSource_1, proc5, "@:@@@");
+            OS.class_addMethod(cls, OS.sel_webView_1resource_1didFailLoadingWithError_1fromDataSource_1, proc6, "@:@@@@");
+            OS.class_addMethod(cls, OS.sel_webView_1identifierForInitialRequest_1fromDataSource_1, proc5, "@:@@@");
+            OS.class_addMethod(cls, OS.sel_webView_1resource_1willSendRequest_1redirectResponse_1fromDataSource_1, proc7, "@:@@@@@");
+            OS.class_addMethod(cls, OS.sel_handleNotification_1, proc3, "@:@");
+            OS.class_addMethod(cls, OS.sel_webView_1createWebViewWithRequest_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webViewShow_1, proc3, "@:@");
+            OS.class_addMethod(cls, OS.sel_webView_1setFrame_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webViewClose_1, proc3, "@:@");
+            OS.class_addMethod(cls, OS.sel_webView_1contextMenuItemsForElement_1defaultMenuItems_1, proc5, "@:@@@");
+            OS.class_addMethod(cls, OS.sel_webView_1setStatusBarVisible_1, proc4, "@:@B");
+            OS.class_addMethod(cls, OS.sel_webView_1setResizable_1, proc4, "@:@B");
+            OS.class_addMethod(cls, OS.sel_webView_1setToolbarsVisible_1, proc4, "@:@B");
+            OS.class_addMethod(cls, OS.sel_webView_1setStatusText_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webViewFocus_1, proc3, "@:@");
+            OS.class_addMethod(cls, OS.sel_webViewUnfocus_1, proc3, "@:@");
+            OS.class_addMethod(cls, OS.sel_webView_1runJavaScriptAlertPanelWithMessage_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webView_1runJavaScriptConfirmPanelWithMessage_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webView_1runOpenPanelForFileButtonWithResultListener_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webView_1mouseDidMoveOverElement_1modifierFlags_1, proc5, "@:@@I");
+            OS.class_addMethod(cls, OS.sel_webView_1printFrameView_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_webView_1decidePolicyForMIMEType_1request_1frame_1decisionListener_1, proc7, "@:@@@@@");
+            OS.class_addMethod(cls, OS.sel_webView_1decidePolicyForNavigationAction_1request_1frame_1decisionListener_1, proc7, "@:@@@@@");
+            OS.class_addMethod(cls, OS.sel_webView_1decidePolicyForNewWindowAction_1request_1newFrameName_1decisionListener_1, proc7, "@:@@@@@");
+            OS.class_addMethod(cls, OS.sel_webView_1unableToImplementPolicyWithError_1frame_1, proc5, "@:@@@");
+            OS.class_addMethod(cls, OS.sel_download_1decideDestinationWithSuggestedFilename_1, proc4, "@:@@");
+            OS.class_addMethod(cls, OS.sel_handleEvent_1, proc3, "@:@");
+            OS.objc_registerClassPair(cls);
+        }
+
+        /*
+         * Override the default event mechanism to not send key events so
+         * that the browser can send them by listening to the DOM instead.
+         */
+        browser.setData(SAFARI_EVENTS_FIX_KEY);
+
+        WebView webView = cast(WebView) (new WebView()).alloc();
+        if (webView is null)
+            DWT.error(DWT.ERROR_NO_HANDLES);
+        webView.initWithFrame(browser.view.frame(), null, null);
+        webView.setAutoresizingMask(OS.NSViewWidthSizable | OS.NSViewHeightSizable);
+        jniRef = OS.NewGlobalRef(this);
+        if (jniRef is 0)
+            DWT.error(DWT.ERROR_NO_HANDLES);
+        const DWTWebViewDelegate delegatee = cast(DWTWebViewDelegate) (new DWTWebViewDelegate()).alloc().init();
+        delegatee.setTag(jniRef);
+        this.delegatee = delegatee;
+        this.webView = webView;
+        browser.view.addSubview_(webView);
+
+        const NSNotificationCenter notificationCenter = NSNotificationCenter.defaultCenter();
+
+        Listener listener = new class (notificationCenter) Listener
+        {
+            NSNotificationCenter notificationCenter;
+            
+            this (NSNotificationCenter notificationCenter)
+            {
+                this.notificationCenter = notificationCenter;
+            }
+            
+            public void handleEvent (Event e)
+            {
+                switch (e.type)
+                {
+                    case DWT.Dispose:
+                    {
+                        /* make this handler run after other dispose listeners */
+                        if (ignoreDispose)
+                        {
+                            ignoreDispose = false;
+                            break;
+                        }
+                        ignoreDispose = true;
+                        browser.notifyListeners(e.type, e);
+                        e.type = DWT.NONE;
+
+                        webView.setFrameLoadDelegate(null);
+                        webView.setResourceLoadDelegate(null);
+                        webView.setUIDelegate(null);
+                        webView.setPolicyDelegate(null);
+                        webView.setDownloadDelegate(null);
+                        notificationCenter.removeObserver(delegatee);
+
+                        webView.release();
+                        webView = null;
+                        delegatee.release();
+                        delegatee = null;
+                        OS.DeleteGlobalRef(jniRef);
+                        jniRef = 0;
+                        html = null;
+                        lastHoveredLinkURL = null;
+                        break;
+                    }
+                }
+            }
+        };
+        browser.addListener(DWT.Dispose, listener);
+
+        webView.setFrameLoadDelegate(delegatee);
+        webView.setResourceLoadDelegate(delegatee);
+        webView.setUIDelegate(delegatee);
+        notificationCenter.addObserver(delegatee, OS.sel_handleNotification_1, null, webView);
+        webView.setPolicyDelegate(delegatee);
+        webView.setDownloadDelegate(delegatee);
+        webView.setApplicationNameForUserAgent(NSString.StringWith(AGENT_STRING));
+
+        if (!Initialized)
+        {
+            Initialized = true;
+            /* disable applets */
+            WebPreferences.standardPreferences().setJavaEnabled(false);
+        }
+    }
+
+    public bool back ()
+    {
+        html = null;
+        return webView.goBack();
+    }
+
+    static int browserProc (int delegatee, int sel)
+    {
+        if (sel is OS.sel_tag)
+        {
+            int[] tag = new int[1];
+            OS.object_getInstanceVariable(delegatee, "tag", tag);
+            return tag[0];
+        }
+        return 0;
+    }
+
+    static int browserProc (int id, int sel, int arg0)
+    {
+        if (sel is OS.sel_setTag_1)
+        {
+            OS.object_setInstanceVariable(id, "tag", arg0);
+            return 0;
+        }
+        int jniRef = OS.objc_msgSend(id, OS.sel_tag);
+        if (jniRef is 0 || jniRef is -1)
+            return 0;
+        Safari widget = cast(Safari) OS.JNIGetObject(jniRef);
+        if (widget is null)
+            return 0;
+        if (sel is OS.sel_handleNotification_1)
+        {
+            widget.handleNotification(arg0);
+        }
+        else if (sel is OS.sel_webViewShow_1)
+        {
+            widget.webViewShow(arg0);
+        }
+        else if (sel is OS.sel_webViewClose_1)
+        {
+            widget.webViewClose(arg0);
+        }
+        else if (sel is OS.sel_webViewFocus_1)
+        {
+            widget.webViewFocus(arg0);
+        }
+        else if (sel is OS.sel_webViewUnfocus_1)
+        {
+            widget.webViewUnfocus(arg0);
+        }
+        else if (sel is OS.sel_handleEvent_1)
+        {
+            widget.handleEvent(arg0);
+        }
+        return 0;
+    }
+
+    static int browserProc (int id, int sel, int arg0, int arg1)
+    {
+        if (sel is OS.sel_setTag_1)
+        {
+            OS.object_setInstanceVariable(id, "tag", arg0);
+            return 0;
+        }
+        int jniRef = OS.objc_msgSend(id, OS.sel_tag);
+        if (jniRef is 0 || jniRef is -1)
+            return 0;
+        Safari widget = cast(Safari) OS.JNIGetObject(jniRef);
+        if (widget is null)
+            return 0;
+        if (sel is OS.sel_webView_1didChangeLocationWithinPageForFrame_1)
+        {
+            widget.webView_didChangeLocationWithinPageForFrame(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1didFinishLoadForFrame_1)
+        {
+            widget.webView_didFinishLoadForFrame(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1didStartProvisionalLoadForFrame_1)
+        {
+            widget.webView_didStartProvisionalLoadForFrame(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1didCommitLoadForFrame_1)
+        {
+            widget.webView_didCommitLoadForFrame(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1setFrame_1)
+        {
+            widget.webView_setFrame(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1createWebViewWithRequest_1)
+        {
+            return widget.webView_createWebViewWithRequest(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1setStatusBarVisible_1)
+        {
+            widget.webView_setStatusBarVisible(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1setResizable_1)
+        {
+            widget.webView_setResizable(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1setStatusText_1)
+        {
+            widget.webView_setStatusText(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1setToolbarsVisible_1)
+        {
+            widget.webView_setToolbarsVisible(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1runJavaScriptAlertPanelWithMessage_1)
+        {
+            widget.webView_runJavaScriptAlertPanelWithMessage(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1runJavaScriptConfirmPanelWithMessage_1)
+        {
+            return widget.webView_runJavaScriptConfirmPanelWithMessage(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1runOpenPanelForFileButtonWithResultListener_1)
+        {
+            widget.webView_runOpenPanelForFileButtonWithResultListener(arg0, arg1);
+        }
+        else if (sel is OS.sel_download_1decideDestinationWithSuggestedFilename_1)
+        {
+            widget.download_decideDestinationWithSuggestedFilename(arg0, arg1);
+        }
+        else if (sel is OS.sel_webView_1printFrameView_1)
+        {
+            widget.webView_printFrameView(arg0, arg1);
+        }
+        return 0;
+    }
+
+    static int browserProc (int id, int sel, int arg0, int arg1, int arg2)
+    {
+        int jniRef = OS.objc_msgSend(id, OS.sel_tag);
+        if (jniRef is 0 || jniRef is -1)
+            return 0;
+        Safari widget = cast(Safari) OS.JNIGetObject(jniRef);
+        if (widget is null)
+            return 0;
+        if (sel is OS.sel_webView_1didFailProvisionalLoadWithError_1forFrame_1)
+        {
+            widget.webView_didFailProvisionalLoadWithError_forFrame(arg0, arg1, arg2);
+        }
+        else if (sel is OS.sel_webView_1didReceiveTitle_1forFrame_1)
+        {
+            widget.webView_didReceiveTitle_forFrame(arg0, arg1, arg2);
+        }
+        else if (sel is OS.sel_webView_1resource_1didFinishLoadingFromDataSource_1)
+        {
+            widget.webView_resource_didFinishLoadingFromDataSource(arg0, arg1, arg2);
+        }
+        else if (sel is OS.sel_webView_1identifierForInitialRequest_1fromDataSource_1)
+        {
+            return widget.webView_identifierForInitialRequest_fromDataSource(arg0, arg1, arg2);
+        }
+        else if (sel is OS.sel_webView_1contextMenuItemsForElement_1defaultMenuItems_1)
+        {
+            return widget.webView_contextMenuItemsForElement_defaultMenuItems(arg0, arg1, arg2);
+        }
+        else if (sel is OS.sel_webView_1mouseDidMoveOverElement_1modifierFlags_1)
+        {
+            widget.webView_mouseDidMoveOverElement_modifierFlags(arg0, arg1, arg2);
+        }
+        else if (sel is OS.sel_webView_1unableToImplementPolicyWithError_1frame_1)
+        {
+            widget.webView_unableToImplementPolicyWithError_frame(arg0, arg1, arg2);
+        }
+        return 0;
+    }
+
+    static int browserProc (int id, int sel, int arg0, int arg1, int arg2, int arg3)
+    {
+        int jniRef = OS.objc_msgSend(id, OS.sel_tag);
+        if (jniRef is 0 || jniRef is -1)
+            return 0;
+        Safari widget = cast(Safari) OS.JNIGetObject(jniRef);
+        if (widget is null)
+            return 0;
+        if (sel is OS.sel_webView_1resource_1didFailLoadingWithError_1fromDataSource_1)
+        {
+            widget.webView_resource_didFailLoadingWithError_fromDataSource(arg0, arg1, arg2, arg3);
+        }
+        return 0;
+    }
+
+    static int browserProc (int id, int sel, int arg0, int arg1, int arg2, int arg3, int arg4)
+    {
+        int jniRef = OS.objc_msgSend(id, OS.sel_tag);
+        if (jniRef is 0 || jniRef is -1)
+            return 0;
+        Safari widget = cast(Safari) OS.JNIGetObject(jniRef);
+        if (widget is null)
+            return 0;
+        if (sel is OS.sel_webView_1resource_1willSendRequest_1redirectResponse_1fromDataSource_1)
+        {
+            return widget.webView_resource_willSendRequest_redirectResponse_fromDataSource(arg0, arg1, arg2, arg3, arg4);
+        }
+        else if (sel is OS.sel_webView_1decidePolicyForMIMEType_1request_1frame_1decisionListener_1)
+        {
+            widget.webView_decidePolicyForMIMEType_request_frame_decisionListener(arg0, arg1, arg2, arg3, arg4);
+        }
+        else if (sel is OS.sel_webView_1decidePolicyForNavigationAction_1request_1frame_1decisionListener_1)
+        {
+            widget.webView_decidePolicyForNavigationAction_request_frame_decisionListener(arg0, arg1, arg2, arg3, arg4);
+        }
+        else if (sel is OS.sel_webView_1decidePolicyForNewWindowAction_1request_1newFrameName_1decisionListener_1)
+        {
+            widget.webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener(arg0, arg1, arg2, arg3, arg4);
+        }
+        return 0;
+    }
+
+    public bool execute (String script)
+    {
+        return webView.StringByEvaluatingJavaScriptFromString(NSString.StringWith(script)) !is null;
+    }
+
+    public bool forward ()
+    {
+        html = null;
+        return webView.goForward();
+    }
+
+    public String getText ()
+    {
+        WebFrame mainFrame = webView.mainFrame();
+        WebDataSource dataSource = mainFrame.dataSource();
+        if (dataSource is null)
+            return ""; //$NON-NLS-1$
+        WebDocumentRepresentation representation = dataSource.representation();
+        if (representation is null)
+            return ""; //$NON-NLS-1$
+        NSString source = representation.documentSource();
+        if (source is null)
+            return ""; //$NON-NLS-1$
+        char[] buffer = new char[source.length()];
+        source.getCharacters_(buffer);
+        return new String(buffer);
+    }
+
+    public String getUrl ()
+    {
+        return url;
+    }
+
+    public bool isBackEnabled ()
+    {
+        return webView.canGoBack();
+    }
+
+    public bool isForwardEnabled ()
+    {
+        return webView.canGoForward();
+    }
+
+    public void refresh ()
+    {
+        webView.reload(null);
+    }
+
+    public bool setText (String html)
+    {
+        /*
+         * Bug in Safari.  The web view segment faults in some circumstances
+         * when the text changes during the location changing callback.  The
+         * fix is to defer the work until the callback is done. 
+         */
+        if (changingLocation)
+        {
+            this.html = html;
+        }
+        else
+        {
+            _setText(html);
+        }
+        return true;
+    }
+
+    void _setText (String html)
+    {
+        NSString String = NSString.StringWith(html);
+        NSString URLString = NSString.StringWith(URI_FROMMEMORY);
+        NSURL URL = NSURL.static_URLWithString_(URLString);
+        WebFrame mainFrame = webView.mainFrame();
+        mainFrame.loadHTMLString(String, URL);
+    }
+
+    public bool setUrl (String url)
+    {
+        html = null;
+
+        NSURL inURL;
+        if (url.startsWith(PROTOCOL_FILE))
+        {
+            url = url.substring(PROTOCOL_FILE.length());
+        }
+        bool isHttpURL = url.indexOf('/') !is 0;
+        if (isHttpURL)
+        {
+            if (url.indexOf(':') is -1)
+            {
+                url = PROTOCOL_HTTP + "//" + url; //$NON-NLS-1$
+            }
+            inURL = NSURL.static_URLWithString_(NSString.StringWith(url.toString()));
+        }
+        else
+        {
+            inURL = NSURL.static_fileURLWithPath_(NSString.StringWith(url.toString()));
+        }
+        if (inURL is null)
+            return false;
+
+        NSURLRequest request = NSURLRequest.static_requestWithURL_(inURL);
+        WebFrame mainFrame = webView.mainFrame();
+        mainFrame.loadRequest(request);
+        return true;
+    }
+
+    public void stop ()
+    {
+        html = null;
+        webView.stopLoading(null);
+    }
+
+    /* WebFrameLoadDelegate */
+
+    void webView_didChangeLocationWithinPageForFrame (int sender, int frameID)
+    {
+        WebFrame frame = new WebFrame(frameID);
+        WebDataSource dataSource = frame.dataSource();
+        NSURLRequest request = dataSource.request();
+        NSURL url = request.URL();
+        NSString s = url.absoluteString();
+        int length = s.length();
+        if (length is 0)
+            return;
+        char[] buffer = new char[length];
+        s.getCharacters_(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.opEquals(URI_FROMMEMORY))
+            url2 = ABOUT_BLANK;
+
+        const Display display = browser.getDisplay();
+        bool top = frameID is webView.mainFrame().id;
+        if (top)
+        {
+            StatusTextEvent statusText = new StatusTextEvent(browser);
+            statusText.display = display;
+            statusText.widget = browser;
+            statusText.text = url2;
+            for (int i = 0; i < statusTextListeners.length; i++)
+            {
+                statusTextListeners[i].changed(statusText);
+            }
+        }
+
+        LocationEvent location = new LocationEvent(browser);
+        location.display = display;
+        location.widget = browser;
+        location.location = url2;
+        location.top = top;
+        for (int i = 0; i < locationListeners.length; i++)
+        {
+            locationListeners[i].changed(location);
+        }
+    }
+
+    void webView_didFailProvisionalLoadWithError_forFrame (int sender, int error, int frame)
+    {
+        if (frame is webView.mainFrame().id)
+        {
+            /*
+             * Feature on Safari.  The identifier is used here as a marker for the events 
+             * related to the top frame and the URL changes related to that top frame as 
+             * they should appear on the location bar of a browser.  It is expected to reset
+             * the identifier to 0 when the event didFinishLoadingFromDataSource related to 
+             * the identifierForInitialRequest event is received.  However, Safari fires
+             * the didFinishLoadingFromDataSource event before the entire content of the
+             * top frame is loaded.  It is possible to receive multiple willSendRequest 
+             * events in this interval, causing the Browser widget to send unwanted
+             * Location.changing events.  For this reason, the identifier is reset to 0
+             * when the top frame has either finished loading (didFinishLoadForFrame
+             * event) or failed (didFailProvisionalLoadWithError).
+             */
+            identifier = 0;
+        }
+    }
+
+    void webView_didFinishLoadForFrame (int sender, int frameID)
+    {
+        hookDOMMouseListeners(frameID);
+        if (frameID is webView.mainFrame().id)
+        {
+            hookDOMKeyListeners(frameID);
+
+            const Display display = browser.getDisplay();
+            /*
+             * To be consistent with other platforms a title event should be fired when a
+             * page has completed loading.  A page with a <title> tag will do this
+             * automatically when the didReceiveTitle callback is received.  However a page
+             * without a <title> tag will not do this by default, so fire the event
+             * here with the page's url as the title.
+             */
+            WebFrame frame = new WebFrame(frameID);
+            WebDataSource dataSource = frame.dataSource();
+            if (dataSource !is null)
+            {
+                NSString title = dataSource.pageTitle();
+                if (title is null)
+                {   /* page has no title */
+                    const TitleEvent newEvent = new TitleEvent(browser);
+                    newEvent.display = display;
+                    newEvent.widget = browser;
+                    newEvent.title = url;
+                    for (int i = 0; i < titleListeners.length; i++)
+                    {
+                        const TitleListener listener = titleListeners[i];
+                        /*
+                         * Note on WebKit.  Running the event loop from a Browser
+                         * delegatee 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 class (display, listener) Runnable
+                        {
+                            Display display;
+                            TitleListener listener;
+                            
+                            this (Display display, TitleListener listener)
+                            {
+                                this.display = display;
+                                this.listener = listener;
+                            }
+                            
+                            public void run ()
+                            {
+                                if (!display.isDisposed() && !browser.isDisposed())
+                                {
+                                    listener.changed(newEvent);
+                                }
+                            }
+                        });
+                    }
+                }
+            }
+            const 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++)
+            {
+                const ProgressListener listener = progressListeners[i];
+                /*
+                 * Note on WebKit.  Running the event loop from a Browser
+                 * delegatee 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 class (display, listener) Runnable
+                {
+                    Display display;
+                    ProgressListener listener;
+                    
+                    this (Display display, ProgressListener listener)
+                    {
+                        this.display = display;
+                        this.listener = listener;
+                    }
+                    
+                    public void run ()
+                    {
+                        if (!display.isDisposed() && !browser.isDisposed())
+                        {
+                            listener.completed(progress);
+                        }
+                    }
+                });
+            }
+            /*
+             * Feature on Safari.  The identifier is used here as a marker for the events 
+             * related to the top frame and the URL changes related to that top frame as 
+             * they should appear on the location bar of a browser.  It is expected to reset
+             * the identifier to 0 when the event didFinishLoadingFromDataSource related to 
+             * the identifierForInitialRequest event is received.  However, Safari fires
+             * the didFinishLoadingFromDataSource event before the entire content of the
+             * top frame is loaded.  It is possible to receive multiple willSendRequest 
+             * events in this interval, causing the Browser widget to send unwanted
+             * Location.changing events.  For this reason, the identifier is reset to 0
+             * when the top frame has either finished loading (didFinishLoadForFrame
+             * event) or failed (didFailProvisionalLoadWithError).
+             */
+            identifier = 0;
+        }
+    }
+
+    void hookDOMKeyListeners (int frameID)
+    {
+        WebFrame frame = new WebFrame(frameID);
+        DOMDocument document = frame.DOMDocument();
+
+        NSString type = NSString.StringWith(DOMEVENT_KEYDOWN);
+        document.addEventListener_listener_useCapture(type, delegatee, false);
+
+        type = NSString.StringWith(DOMEVENT_KEYUP);
+        document.addEventListener_listener_useCapture(type, delegatee, false);
+    }
+
+    void hookDOMMouseListeners (int frameID)
+    {
+        WebFrame frame = new WebFrame(frameID);
+        DOMDocument document = frame.DOMDocument();
+
+        NSString type = NSString.StringWith(DOMEVENT_MOUSEDOWN);
+        document.addEventListener_listener_useCapture(type, delegatee, false);
+
+        type = NSString.StringWith(DOMEVENT_MOUSEUP);
+        document.addEventListener_listener_useCapture(type, delegatee, false);
+
+        type = NSString.StringWith(DOMEVENT_MOUSEMOVE);
+        document.addEventListener_listener_useCapture(type, delegatee, false);
+
+        type = NSString.StringWith(DOMEVENT_MOUSEWHEEL);
+        document.addEventListener_listener_useCapture(type, delegatee, false);
+    }
+
+    void webView_didReceiveTitle_forFrame (int sender, int titleID, int frameID)
+    {
+        if (frameID is webView.mainFrame().id)
+        {
+            NSString title = new NSString(titleID);
+            char[] buffer = new char[title.length()];
+            title.getCharacters_(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 webView_didStartProvisionalLoadForFrame (int sender, int frameID)
+    {
+    /* 
+     * This code is intentionally commented.  WebFrameLoadDelegate:didStartProvisionalLoadForFrame is
+     * called before WebResourceLoadDelegate:willSendRequest and
+     * WebFrameLoadDelegate:didCommitLoadForFrame.  The resource count is reset when didCommitLoadForFrame
+     * is received for the top frame.
+     */
+    //  if (frameID is webView.mainFrame().id) {
+    //      /* reset resource status variables */
+    //      resourceCount= 0;
+    //  }
+    }
+
+    void webView_didCommitLoadForFrame (int sender, int frameID)
+    {
+        WebFrame frame = new WebFrame(frameID);
+        WebDataSource dataSource = frame.dataSource();
+        NSURLRequest request = dataSource.request();
+        NSURL url = request.URL();
+        NSString s = url.absoluteString();
+        int length = s.length();
+        if (length is 0)
+            return;
+        char[] buffer = new char[length];
+        s.getCharacters_(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.opEquals(URI_FROMMEMORY))
+            url2 = ABOUT_BLANK;
+
+        const Display display = browser.getDisplay();
+        bool top = frameID is webView.mainFrame().id;
+        if (top)
+        {
+            /* reset resource status variables */
+            resourceCount = 0;
+            this.url = url2;
+
+            const 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++)
+            {
+                const ProgressListener listener = progressListeners[i];
+                /*
+                 * Note on WebKit.  Running the event loop from a Browser
+                 * delegatee 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 class (display, listener) Runnable
+                {
+                    Display display;
+                    ProgressListener listener;
+                    
+                    this (Display display, ProgressListener listener)
+                    {
+                        this.display = display;
+                        this.listener = listener;
+                    }
+                    
+                    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 webView_resource_didFinishLoadingFromDataSource (int sender, 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.  However, Safari fires
+     * the didFinishLoadingFromDataSource event before the entire content of the
+     * top frame is loaded.  It is possible to receive multiple willSendRequest 
+     * events in this interval, causing the Browser widget to send unwanted
+     * Location.changing events.  For this reason, the identifier is reset to 0
+     * when the top frame has either finished loading (didFinishLoadForFrame
+     * event) or failed (didFailProvisionalLoadWithError).
+     */
+    // this code is intentionally commented
+    //if (this.identifier is identifier) this.identifier = 0;
+    }
+
+    void webView_resource_didFailLoadingWithError_fromDataSource (int sender, 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.  However, Safari fires
+     * the didFinishLoadingFromDataSource event before the entire content of the
+     * top frame is loaded.  It is possible to receive multiple willSendRequest 
+     * events in this interval, causing the Browser widget to send unwanted
+     * Location.changing events.  For this reason, the identifier is reset to 0
+     * when the top frame has either finished loading (didFinishLoadForFrame
+     * event) or failed (didFailProvisionalLoadWithError).
+     */
+    // this code is intentionally commented
+    //if (this.identifier is identifier) this.identifier = 0;
+    }
+
+    int webView_identifierForInitialRequest_fromDataSource (int sender, int request, int dataSourceID)
+    {
+        const Display display = browser.getDisplay();
+        const 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++)
+        {
+            const ProgressListener listener = progressListeners[i];
+            /*
+             * Note on WebKit.  Running the event loop from a Browser
+             * delegatee 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 class (display, listener) Runnable
+            {
+                Display display;
+                ProgressListener listener;
+                
+                this (Display display, ProgressListener listener)
+                {
+                    this.display = display;
+                    this.listener = listener;
+                }
+                
+                public void run ()
+                {
+                    if (!display.isDisposed() && !browser.isDisposed())
+                        listener.changed(progress);
+                }
+            });
+        }
+
+        NSNumber identifier = NSNumber.numberWithInt(resourceCount++);
+        if (this.identifier is 0)
+        {
+            WebDataSource dataSource = new WebDataSource(dataSourceID);
+            WebFrame frame = dataSource.webFrame();
+            if (frame.id is webView.mainFrame().id)
+                this.identifier = identifier.id;
+        }
+        return identifier.id;
+
+    }
+
+    int webView_resource_willSendRequest_redirectResponse_fromDataSource (int sender, int identifier, int request, int redirectResponse,
+            int dataSource)
+    {
+        return request;
+    }
+
+    /* handleNotification */
+
+    void handleNotification (int notification)
+    {
+    }
+
+    /* UIDelegate */
+    int webView_createWebViewWithRequest (int sender, int request)
+    {
+        WindowEvent newEvent = new WindowEvent(browser);
+        newEvent.display = browser.getDisplay();
+        newEvent.widget = browser;
+        newEvent.required = true;
+        if (openWindowListeners !is null)
+        {
+            for (int i = 0; i < openWindowListeners.length; i++)
+            {
+                openWindowListeners[i].open(newEvent);
+            }
+        }
+        Browser browser = null;
+        if (newEvent.browser !is null && cast(Safari) newEvent.browser.webBrowser)
+        {
+            browser = newEvent.browser;
+        }
+        if (browser !is null && !browser.isDisposed())
+        {
+            if (request !is 0)
+            {
+                WebFrame mainFrame = webView.mainFrame();
+                mainFrame.loadRequest(new NSURLRequest(request));
+            }
+        }
+        return webView.id;
+    }
+
+    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 !is null)
+            newEvent.location = location;
+        if (size !is 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 webView_setFrame (int sender, int frame)
+    {
+        float[] dest = new float[4];
+        OS.memmove(dest, frame, 16);
+        /* convert to DWT system coordinates */
+        Rectangle bounds = browser.getDisplay().getBounds();
+        location = new Point(cast(int) dest[0], bounds.height - cast(int) dest[1] - cast(int) dest[3]);
+        size = new Point(cast(int) dest[2], cast(int) dest[3]);
+    }
+
+    void webViewFocus (int sender)
+    {
+    }
+
+    void webViewUnfocus (int sender)
+    {
+    }
+
+    void webView_runJavaScriptAlertPanelWithMessage (int sender, int messageID)
+    {
+        NSString message = new NSString(messageID);
+        char[] buffer = new char[message.length()];
+        message.getCharacters_(buffer);
+        String text = new String(buffer);
+
+        MessageBox messageBox = new MessageBox(browser.getShell(), DWT.OK | DWT.ICON_WARNING);
+        messageBox.setText("Javascript"); //$NON-NLS-1$
+        messageBox.setMessage(text);
+        messageBox.open();
+    }
+
+    int webView_runJavaScriptConfirmPanelWithMessage (int sender, int messageID)
+    {
+        NSString message = new NSString(messageID);
+        char[] buffer = new char[message.length()];
+        message.getCharacters_(buffer);
+        String text = new String(buffer);
+
+        MessageBox messageBox = new MessageBox(browser.getShell(), DWT.OK | DWT.CANCEL | DWT.ICON_QUESTION);
+        messageBox.setText("Javascript"); //$NON-NLS-1$
+        messageBox.setMessage(text);
+        return messageBox.open() is DWT.OK ? 1 : 0;
+    }
+
+    void webView_runOpenPanelForFileButtonWithResultListener (int sender, int resultListenerID)
+    {
+        FileDialog dialog = new FileDialog(browser.getShell(), DWT.NONE);
+        String result = dialog.open();
+        WebOpenPanelResultListener resultListener = new WebOpenPanelResultListener(resultListenerID);
+        if (result is null)
+        {
+            resultListener.cancel();
+            return;
+        }
+        resultListener.chooseFilename(NSString.StringWith(result));
+    }
+
+    void webViewClose (int sender)
+    {
+        Shell parent = browser.getShell();
+        WindowEvent newEvent = new WindowEvent(browser);
+        newEvent.display = browser.getDisplay();
+        newEvent.widget = browser;
+        for (int i = 0; i < closeWindowListeners.length; i++)
+        {
+            closeWindowListeners[i].close(newEvent);
+        }
+        browser.dispose();
+        if (parent.isDisposed())
+            return;
+        /*
+         * Feature on WebKit.  The Safari WebKit expects the application
+         * to create a new Window using the Objective C Cocoa API in response
+         * to UIDelegate.createWebViewWithRequest. The application is then
+         * expected to use Objective C Cocoa API to make this window visible
+         * when receiving the UIDelegate.webViewShow message.  For some reason,
+         * a window created with the Carbon API hosting the new browser instance
+         * does not redraw until it has been resized.  The fix is to increase the
+         * size of the Shell and restore it to its initial size.
+         */
+        Point pt = parent.getSize();
+        parent.setSize(pt.x + 1, pt.y);
+        parent.setSize(pt.x, pt.y);
+    }
+
+    int webView_contextMenuItemsForElement_defaultMenuItems (int sender, int element, int defaultMenuItems)
+    {
+        Point pt = browser.getDisplay().getCursorLocation();
+        Event event = new Event();
+        event.x = pt.x;
+        event.y = pt.y;
+        browser.notifyListeners(DWT.MenuDetect, event);
+        Menu menu = browser.getMenu();
+        if (!event.doit)
+            return 0;
+        if (menu !is null && !menu.isDisposed())
+        {
+            if (event.x !is pt.x || event.y !is pt.y)
+            {
+                menu.setLocation(event.x, event.y);
+            }
+            menu.setVisible(true);
+            return 0;
+        }
+        return defaultMenuItems;
+    }
+
+    void webView_setStatusBarVisible (int sender, int visible)
+    {
+        /* Note.  Webkit only emits the notification when the status bar should be hidden. */
+        statusBar = visible !is 0;
+    }
+
+    void webView_setStatusText (int sender, int textID)
+    {
+        NSString text = new NSString(textID);
+        int length = text.length();
+        if (length is 0)
+            return;
+        char[] buffer = new char[length];
+        text.getCharacters_(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 webView_setResizable (int sender, int visible)
+    {
+    }
+
+    void webView_setToolbarsVisible (int sender, int visible)
+    {
+        /* Note.  Webkit only emits the notification when the tool bar should be hidden. */
+        toolBar = visible !is 0;
+    }
+
+    void webView_mouseDidMoveOverElement_modifierFlags (int sender, int elementInformationID, int modifierFlags)
+    {
+        if (elementInformationID is 0)
+            return;
+
+        NSString key = NSString.StringWith(WebElementLinkURLKey);
+        NSDictionary elementInformation = new NSDictionary(elementInformationID);
+        id value = elementInformation.valueForKey(key);
+        if (value is null)
+        {
+            /* not currently over a link */
+            if (lastHoveredLinkURL is null)
+                return;
+            lastHoveredLinkURL = null;
+            StatusTextEvent statusText = new StatusTextEvent(browser);
+            statusText.display = browser.getDisplay();
+            statusText.widget = browser;
+            statusText.text = ""; //$NON-NLS-1$
+            for (int i = 0; i < statusTextListeners.length; i++)
+            {
+                statusTextListeners[i].changed(statusText);
+            }
+            return;
+        }
+
+        NSString url = (new NSURL(value.id)).absoluteString();
+        int length = url.length();
+        String urlString;
+        if (length is 0)
+        {
+            urlString = ""; //$NON-NLS-1$
+        }
+        else
+        {
+            char[] buffer = new char[length];
+            url.getCharacters_(buffer);
+            urlString = new String(buffer);
+        }
+        if (urlString.opEquals(lastHoveredLinkURL))
+            return;
+
+        lastHoveredLinkURL = urlString;
+        StatusTextEvent statusText = new StatusTextEvent(browser);
+        statusText.display = browser.getDisplay();
+        statusText.widget = browser;
+        statusText.text = urlString;
+        for (int i = 0; i < statusTextListeners.length; i++)
+        {
+            statusTextListeners[i].changed(statusText);
+        }
+    }
+
+    void webView_printFrameView (int sender, int frameViewID)
+    {
+        WebFrameView view = new WebFrameView(frameViewID);
+        bool viewPrint = view.documentViewShouldHandlePrint();
+        if (viewPrint)
+        {
+            view.printDocumentView();
+            return;
+        }
+        NSPrintInfo info = NSPrintInfo.sharedPrintInfo();
+        NSPrintOperation operation = view.printOperationWithPrintInfo(info);
+        if (operation !is null)
+            operation.runOperation();
+    }
+
+    /* PolicyDelegate */
+
+    void webView_decidePolicyForMIMEType_request_frame_decisionListener (int sender, int type, int request, int frame, int listenerID)
+    {
+        bool canShow = WebView.canShowMIMEType(new NSString(type));
+        WebPolicyDecisionListener listener = new WebPolicyDecisionListener(listenerID);
+        if (canShow)
+        {
+            listener.use();
+        }
+        else
+        {
+            listener.download();
+        }
+    }
+
+    void webView_decidePolicyForNavigationAction_request_frame_decisionListener (int sender, int actionInformation, int request, int frame,
+            int listenerID)
+    {
+        NSURL url = (new NSURLRequest(request)).URL();
+        WebPolicyDecisionListener listener = new WebPolicyDecisionListener(listenerID);
+        if (url is null)
+        {
+            /* indicates that a URL with an invalid format was specified */
+            listener.ignore();
+            return;
+        }
+        NSString s = url.absoluteString();
+        char[] buffer = new char[s.length()];
+        s.getCharacters_(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.opEquals(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 !is null)
+        {
+            changingLocation = true;
+            for (int i = 0; i < locationListeners.length; i++)
+            {
+                locationListeners[i].changing(newEvent);
+            }
+            changingLocation = false;
+        }
+        if (newEvent.doit)
+        {
+            listener.use();
+        }
+        else
+        {
+            listener.ignore();
+        }
+        if (html !is null && !browser.isDisposed())
+        {
+            String html = this.html;
+            this.html = null;
+            _setText(html);
+        }
+    }
+
+    void webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener (int sender, int actionInformation, int request, int frameName,
+            int listenerID)
+    {
+        WebPolicyDecisionListener listener = new WebPolicyDecisionListener(listenerID);
+        listener.use();
+    }
+
+    void webView_unableToImplementPolicyWithError_frame (int sender, int error, int frame)
+    {
+    }
+
+    /* WebDownload */
+
+    void download_decideDestinationWithSuggestedFilename (int downloadId, int filename)
+    {
+        NSString String = new NSString(filename);
+        char[] buffer = new char[String.length()];
+        String.getCharacters_(buffer);
+        String name = new String(buffer);
+        FileDialog dialog = new FileDialog(browser.getShell(), DWT.SAVE);
+        dialog.setText(DWT.getMessage("DWT_FileDownload")); //$NON-NLS-1$
+        dialog.setFileName(name);
+        String path = dialog.open();
+        NSURLDownload download = new NSURLDownload(downloadId);
+        if (path is null)
+        {
+            /* cancel pressed */
+            download.cancel();
+            return;
+        }
+        download.setDestination(NSString.StringWith(path), true);
+    }
+
+    /* DOMEventListener */
+
+    void handleEvent (int evtId)
+    {
+        DOMEvent evt = new DOMEvent(evtId);
+        NSString String = evt.type();
+        char[] buffer = new char[String.length()];
+        String.getCharacters_(buffer);
+        String type = new String(buffer);
+
+        if (DOMEVENT_KEYDOWN.opEquals(type) || DOMEVENT_KEYUP.opEquals(type))
+        {
+            DOMKeyboardEvent event = new DOMKeyboardEvent(evtId);
+
+            bool ctrl = event.ctrlKey();
+            bool shift = event.shiftKey();
+            bool alt = event.altKey();
+            bool meta = event.metaKey();
+            int keyCode = event.keyCode();
+            int charCode = event.charCode();
+
+            Event keyEvent = new Event();
+            keyEvent.widget = browser;
+            if (DOMEVENT_KEYDOWN.opEquals(type))
+            {
+                keyEvent.type = DWT.KeyDown;
+            }
+            else
+            {
+                keyEvent.type = DWT.KeyUp;
+            }
+            keyEvent.keyCode = translateKey(keyCode);
+            keyEvent.character = cast(char) charCode;
+            keyEvent.stateMask = (alt ? DWT.ALT : 0) | (ctrl ? DWT.CTRL : 0) | (shift ? DWT.SHIFT : 0) | (meta ? DWT.COMMAND : 0);
+            browser.notifyListeners(keyEvent.type, keyEvent);
+            if (!keyEvent.doit)
+            {
+                event.preventDefault();
+            }
+            return;
+        }
+
+        if (DOMEVENT_MOUSEWHEEL.opEquals(type))
+        {
+            DOMWheelEvent event = new DOMWheelEvent(evtId);
+            int clientX = event.clientX();
+            int clientY = event.clientY();
+            int delta = event.wheelDelta();
+            bool ctrl = event.ctrlKey();
+            bool shift = event.shiftKey();
+            bool alt = event.altKey();
+            bool meta = event.metaKey();
+            Event mouseEvent = new Event();
+            mouseEvent.type = DWT.MouseWheel;
+            mouseEvent.widget = browser;
+            mouseEvent.x = clientX;
+            mouseEvent.y = clientY;
+            mouseEvent.count = delta / 120;
+            mouseEvent.stateMask = (alt ? DWT.ALT : 0) | (ctrl ? DWT.CTRL : 0) | (shift ? DWT.SHIFT : 0) | (meta ? DWT.COMMAND : 0);
+            browser.notifyListeners(mouseEvent.type, mouseEvent);
+            return;
+        }
+
+        /* mouse event */
+
+        DOMMouseEvent event = new DOMMouseEvent(evtId);
+
+        int clientX = event.clientX();
+        int clientY = event.clientY();
+        int detail = event.detail();
+        int button = event.button();
+        bool ctrl = event.ctrlKey();
+        bool shift = event.shiftKey();
+        bool alt = event.altKey();
+        bool meta = event.metaKey();
+
+        Event mouseEvent = new Event();
+        mouseEvent.widget = browser;
+        mouseEvent.x = clientX;
+        mouseEvent.y = clientY;
+        mouseEvent.stateMask = (alt ? DWT.ALT : 0) | (ctrl ? DWT.CTRL : 0) | (shift ? DWT.SHIFT : 0) | (meta ? DWT.COMMAND : 0);
+        if (DOMEVENT_MOUSEDOWN.opEquals(type))
+        {
+            mouseEvent.type = DWT.MouseDown;
+            mouseEvent.button = button + 1;
+            mouseEvent.count = detail;
+        }
+        else if (DOMEVENT_MOUSEUP.opEquals(type))
+        {
+            mouseEvent.type = DWT.MouseUp;
+            mouseEvent.button = button + 1;
+            mouseEvent.count = detail;
+            switch (mouseEvent.button)
+            {
+                case 1:
+                    mouseEvent.stateMask |= DWT.BUTTON1;
+                break;
+                case 2:
+                    mouseEvent.stateMask |= DWT.BUTTON2;
+                break;
+                case 3:
+                    mouseEvent.stateMask |= DWT.BUTTON3;
+                break;
+                case 4:
+                    mouseEvent.stateMask |= DWT.BUTTON4;
+                break;
+                case 5:
+                    mouseEvent.stateMask |= DWT.BUTTON5;
+                break;
+            }
+        }
+        else if (DOMEVENT_MOUSEMOVE.opEquals(type))
+        {
+            /*
+             * Bug in Safari.  Spurious and redundant mousemove events are received in
+             * various contexts, including following every MouseUp.  The workaround is
+             * to not fire MouseMove events whose x and y values match the last MouseMove  
+             */
+            if (mouseEvent.x is lastMouseMoveX && mouseEvent.y is lastMouseMoveY)
+                return;
+            mouseEvent.type = DWT.MouseMove;
+            lastMouseMoveX = mouseEvent.x;
+            lastMouseMoveY = mouseEvent.y;
+        }
+
+        browser.notifyListeners(mouseEvent.type, mouseEvent);
+        if (detail is 2 && DOMEVENT_MOUSEDOWN.opEquals(type))
+        {
+            mouseEvent = new Event();
+            mouseEvent.widget = browser;
+            mouseEvent.x = clientX;
+            mouseEvent.y = clientY;
+            mouseEvent.stateMask = (alt ? DWT.ALT : 0) | (ctrl ? DWT.CTRL : 0) | (shift ? DWT.SHIFT : 0) | (meta ? DWT.COMMAND : 0);
+            mouseEvent.type = DWT.MouseDoubleClick;
+            mouseEvent.button = button + 1;
+            mouseEvent.count = detail;
+            browser.notifyListeners(mouseEvent.type, mouseEvent);
+        }
+    }
+}