view 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 source

/*******************************************************************************
 * 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);
        }
    }
}