view dwt/browser/Safari.d @ 156:969e7de37c3d default tip

Fixes to get dwt to work with dmd and ldc
author Jacob Carlborg <doob@me.com>
date Wed, 08 Jul 2009 21:56:44 +0200
parents 38807a925e24
children
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2000, 2008 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 <doob@me.com>
 *******************************************************************************/
module dwt.browser.Safari;

import dwt.dwthelper.utils;

import dwt.DWT;
import dwt.graphics.Point;
import dwt.graphics.Rectangle;
import dwt.internal.C;
import dwt.internal.Compatibility;
import dwt.internal.cocoa.DOMDocument;
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.NSError;
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.NSRect;
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.SWTWebViewDelegate;
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 dwt.widgets.Widget;

import dwt.browser.Browser;
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.browser.WindowEvent;
import Carbon = dwt.internal.c.Carbon;
import dwt.internal.objc.cocoa.Cocoa;
import objc = dwt.internal.objc.runtime;

class Safari : WebBrowser {
    WebView webView;
    SWTWebViewDelegate delegate_;
    bool changingLocation;
    String lastHoveredLinkURL;
    String html;
    objc.id identifier;
    int resourceCount;
    String url = ""; //$NON-NLS-1$
    Point location;
    Point size;
    bool statusBar = true, toolBar = true, ignoreDispose;
    int lastMouseMoveX, lastMouseMoveY;
    //TEMPORARY CODE
//  bool doit;

    static bool Initialized;

    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 ADD_WIDGET_KEY = "dwt.internal.addWidget"; //$NON-NLS-1$
    static const String SAFARI_EVENTS_FIX_KEY = "dwt.internal.safariEventsFix"; //$NON-NLS-1$
    static const String DWT_OBJECT = "DWT_OBJECT"; //$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();
                NSUInteger count = cookies.count();
                for (NSUInteger 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 = "SWTWebViewDelegate"; //$NON-NLS-1$
    if (OS.objc_lookUpClass(className) is null) {
        Class safariClass = this.classinfo;
        objc.IMP proc3;// = cast(objc.IMP) &browserProc3;
        objc.IMP proc4;// = cast(objc.IMP) &browserProc4;
        objc.IMP proc5;// = cast(objc.IMP) &browserProc5;
        objc.IMP proc6;// = cast(objc.IMP) &browserProc6;
        objc.IMP proc7;// = cast(objc.IMP) &browserProc7;
        objc.IMP setFrameProc = OS.webView_setFrame_CALLBACK(proc4);

        String types = "*"; //$NON-NLS-1$
        size_t size = C.PTR_SIZEOF, align_ = C.PTR_SIZEOF is 4 ? 2 : 3;

        objc.Class cls = OS.objc_allocateClassPair(cast(objc.Class) OS.class_NSObject, className, 0);
        OS.class_addIvar(cls, DWT_OBJECT, size, cast(byte)align_, types);
        OS.class_addMethod(cls, OS.sel_webView_didChangeLocationWithinPageForFrame_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_didFailProvisionalLoadWithError_forFrame_, proc5, "@:@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_didFinishLoadForFrame_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_didReceiveTitle_forFrame_, proc5, "@:@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_didStartProvisionalLoadForFrame_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_didCommitLoadForFrame_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_resource_didFinishLoadingFromDataSource_, proc5, "@:@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_resource_didFailLoadingWithError_fromDataSource_, proc6, "@:@@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_identifierForInitialRequest_fromDataSource_, proc5, "@:@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_resource_willSendRequest_redirectResponse_fromDataSource_, proc7, "@:@@@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_handleNotification_, proc3, "@:@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_createWebViewWithRequest_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webViewShow_, proc3, "@:@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webViewClose_, proc3, "@:@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_contextMenuItemsForElement_defaultMenuItems_, proc5, "@:@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_setStatusBarVisible_, proc4, "@:@B"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_setResizable_, proc4, "@:@B"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_setToolbarsVisible_, proc4, "@:@B"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_setStatusText_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webViewFocus_, proc3, "@:@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webViewUnfocus_, proc3, "@:@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_runJavaScriptAlertPanelWithMessage_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_runJavaScriptConfirmPanelWithMessage_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_runOpenPanelForFileButtonWithResultListener_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_mouseDidMoveOverElement_modifierFlags_, proc5, "@:@@I"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_printFrameView_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_decidePolicyForMIMEType_request_frame_decisionListener_, proc7, "@:@@@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_decidePolicyForNavigationAction_request_frame_decisionListener_, proc7, "@:@@@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener_, proc7, "@:@@@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_unableToImplementPolicyWithError_frame_, proc5, "@:@@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_download_decideDestinationWithSuggestedFilename_, proc4, "@:@@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_handleEvent_, proc3, "@:@"); //$NON-NLS-1$
        OS.class_addMethod(cls, OS.sel_webView_setFrame_, setFrameProc, "@:@{NSRect}"); //$NON-NLS-1$
        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(new ArrayWrapperString(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);
    final SWTWebViewDelegate delegate_ = cast(SWTWebViewDelegate)(new SWTWebViewDelegate()).alloc().init();
    Display display = browser.getDisplay();
    display.setData(ADD_WIDGET_KEY, new ArrayWrapperObject([cast(Object) delegate_, browser]));
    this.delegate_ = delegate_;
    this.webView = webView;
    browser.view.addSubview(webView);

    final NSNotificationCenter notificationCenter = NSNotificationCenter.defaultCenter();

    Listener listener = new class () Listener {
        public void handleEvent(Event e) {
            switch (e.type) {
                case DWT.FocusIn:
                    this.outer.webView.window().makeFirstResponder(this.outer.webView);
                    break;
                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;

                    e.display.setData(ADD_WIDGET_KEY, new ArrayWrapperObject([delegate_, null]));

                    this.outer.webView.setFrameLoadDelegate(null);
                    this.outer.webView.setResourceLoadDelegate(null);
                    this.outer.webView.setUIDelegate(null);
                    this.outer.webView.setPolicyDelegate(null);
                    this.outer.webView.setDownloadDelegate(null);
                    notificationCenter.removeObserver(delegate_);

                    this.outer.webView.release();
                    this.outer.webView = null;
                    this.outer.delegate_.release();
                    this.outer.delegate_ = null;
                    html = null;
                    lastHoveredLinkURL = null;
                    break;
                }
                default:
            }
        }
    };
    browser.addListener(DWT.Dispose, listener);
    /* Needed to be able to tab into the browser */
    browser.addListener(DWT.KeyDown, listener);
    browser.addListener(DWT.FocusIn, listener);

    webView.setFrameLoadDelegate(delegate_);
    webView.setResourceLoadDelegate(delegate_);
    webView.setUIDelegate(delegate_);    
    notificationCenter.addObserver(delegate_, OS.sel_handleNotification_, null, webView);
    webView.setPolicyDelegate(delegate_);
    webView.setDownloadDelegate(delegate_);
    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 objc.id browserProc3(objc.id id, objc.SEL sel, objc.id arg0) {
    Widget widget = Display.getCurrent().findWidget(id);
    if (widget is null) return null;
    Safari safari = cast(Safari)(cast(Browser)widget).webBrowser;
    if (sel is OS.sel_handleNotification_) {
        safari.handleNotification(arg0);
    } else if (sel is OS.sel_webViewShow_) {
        safari.webViewShow(arg0);
    } else if (sel is OS.sel_webViewClose_) {
        safari.webViewClose(arg0);
    } else if (sel is OS.sel_webViewFocus_) {
        safari.webViewFocus(arg0);
    } else if (sel is OS.sel_webViewUnfocus_) {
        safari.webViewUnfocus(arg0);
    } else if (sel is OS.sel_handleEvent_) {
        safari.handleEvent(arg0);
    }
    return null;
}

static objc.id browserProc4(objc.id id, objc.SEL sel, objc.id arg0, objc.id arg1) {
    Widget widget = Display.getCurrent().findWidget(id);
    if (widget is null) return null;
    Safari safari = cast(Safari)(cast(Browser)widget).webBrowser;
    if (sel is OS.sel_webView_didChangeLocationWithinPageForFrame_) {
        safari.webView_didChangeLocationWithinPageForFrame(arg0, arg1);
    } else if (sel is OS.sel_webView_didFinishLoadForFrame_) {
        safari.webView_didFinishLoadForFrame(arg0, arg1);
    } else if (sel is OS.sel_webView_didStartProvisionalLoadForFrame_) {
        safari.webView_didStartProvisionalLoadForFrame(arg0, arg1);
    } else if (sel is OS.sel_webView_didCommitLoadForFrame_) {
        safari.webView_didCommitLoadForFrame(arg0, arg1);
    } else if (sel is OS.sel_webView_setFrame_) {
        safari.webView_setFrame(arg0, arg1);
    } else if (sel is OS.sel_webView_createWebViewWithRequest_) {
        return safari.webView_createWebViewWithRequest(arg0, arg1);     
    } else if (sel is OS.sel_webView_setStatusBarVisible_) {
        safari.webView_setStatusBarVisible(arg0, arg1 !is null);
    } else if (sel is OS.sel_webView_setResizable_) {
        safari.webView_setResizable(arg0, arg1 !is null);
    } else if (sel is OS.sel_webView_setStatusText_) {
        safari.webView_setStatusText(arg0, arg1);
    } else if (sel is OS.sel_webView_setToolbarsVisible_) {
        safari.webView_setToolbarsVisible(arg0, arg1 !is null);
    } else if (sel is OS.sel_webView_runJavaScriptAlertPanelWithMessage_) {
        safari.webView_runJavaScriptAlertPanelWithMessage(arg0, arg1);
    } else if (sel is OS.sel_webView_runJavaScriptConfirmPanelWithMessage_) {
        return safari.webView_runJavaScriptConfirmPanelWithMessage(arg0, arg1);
    } else if (sel is OS.sel_webView_runOpenPanelForFileButtonWithResultListener_) {
        safari.webView_runOpenPanelForFileButtonWithResultListener(arg0, arg1);
    } else if (sel is OS.sel_download_decideDestinationWithSuggestedFilename_) {
        safari.download_decideDestinationWithSuggestedFilename(arg0, arg1);
    } else if (sel is OS.sel_webView_printFrameView_) {
        safari.webView_printFrameView(arg0, arg1);
    }
    return null;
}

static objc.id browserProc5(objc.id id, objc.SEL sel, objc.id arg0, objc.id arg1, objc.id arg2) {
    Widget widget = Display.getCurrent().findWidget(id);
    if (widget is null) return null;
    Safari safari = cast(Safari)(cast(Browser)widget).webBrowser;
    if (sel is OS.sel_webView_didFailProvisionalLoadWithError_forFrame_) {
        safari.webView_didFailProvisionalLoadWithError_forFrame(arg0, arg1, arg2);
    } else if (sel is OS.sel_webView_didReceiveTitle_forFrame_) {
        safari.webView_didReceiveTitle_forFrame(arg0, arg1, arg2);
    } else if (sel is OS.sel_webView_resource_didFinishLoadingFromDataSource_) {
        safari.webView_resource_didFinishLoadingFromDataSource(arg0, arg1, arg2);
    } else if (sel is OS.sel_webView_identifierForInitialRequest_fromDataSource_) {
        return safari.webView_identifierForInitialRequest_fromDataSource(arg0, arg1, arg2);
    } else if (sel is OS.sel_webView_contextMenuItemsForElement_defaultMenuItems_) {
        return safari.webView_contextMenuItemsForElement_defaultMenuItems(arg0, arg1, arg2);
    } else if (sel is OS.sel_webView_mouseDidMoveOverElement_modifierFlags_) {
        safari.webView_mouseDidMoveOverElement_modifierFlags(arg0, arg1, arg2);
    } else if (sel is OS.sel_webView_unableToImplementPolicyWithError_frame_) {
        safari.webView_unableToImplementPolicyWithError_frame(arg0, arg1, arg2);
    }
    return null;
}

static objc.id browserProc6(objc.id id, objc.SEL sel, objc.id arg0, objc.id arg1, objc.id arg2, objc.id arg3) {
    Widget widget = Display.getCurrent().findWidget(id);
    if (widget is null) return null;
    Safari safari = cast(Safari)(cast(Browser)widget).webBrowser;
    if (sel is OS.sel_webView_resource_didFailLoadingWithError_fromDataSource_) {
        safari.webView_resource_didFailLoadingWithError_fromDataSource(arg0, arg1, arg2, arg3);
    }   
    return null;
}

static objc.id browserProc7(objc.id id, objc.SEL sel, objc.id arg0, objc.id arg1, objc.id arg2, objc.id arg3, objc.id arg4) {
    Widget widget = Display.getCurrent().findWidget(id);
    if (widget is null) return null;
    Safari safari = cast(Safari)(cast(Browser)widget).webBrowser;
    if (sel is OS.sel_webView_resource_willSendRequest_redirectResponse_fromDataSource_) {
        return safari.webView_resource_willSendRequest_redirectResponse_fromDataSource(arg0, arg1, arg2, arg3, arg4);
    } else if (sel is OS.sel_webView_decidePolicyForMIMEType_request_frame_decisionListener_) {
        safari.webView_decidePolicyForMIMEType_request_frame_decisionListener(arg0, arg1, arg2, arg3, arg4);
    } else if (sel is OS.sel_webView_decidePolicyForNavigationAction_request_frame_decisionListener_) {
        safari.webView_decidePolicyForNavigationAction_request_frame_decisionListener(arg0, arg1, arg2, arg3, arg4);
    } else if (sel is OS.sel_webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener_) {
        safari.webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener(arg0, arg1, arg2, arg3, arg4);
    }
    return null;
}

public bool execute(String script) {
    return webView.stringByEvaluatingJavaScriptFromString(NSString.stringWith(script)) !is null;
}

public bool forward() {
    html = null;
    return webView.goForward();
}

public String getBrowserType () {
    return "safari"; //$NON-NLS-1$
}

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$
    return source.getString();
}

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.URLWithString(URLString);
    WebFrame mainFrame = webView.mainFrame();
    mainFrame.loadHTMLString(string, URL);
}

public bool setUrl(String url) {
    html = null;

    if (url.indexOf('/') is 0) {
        url = PROTOCOL_FILE ~ url;
    } else if (url.indexOf(':') is -1) {
        url = PROTOCOL_HTTP ~ url;
    }

    NSString str = NSString.stringWith(url);
    NSString unescapedStr = NSString.stringWith("%#"); //$NON-NLS-1$
    Carbon.CFStringRef ptr = OS.CFURLCreateStringByAddingPercentEscapes(null, cast(Carbon.CFStringRef) str.id, cast(Carbon.CFStringRef) unescapedStr.id, null, OS.kCFStringEncodingUTF8);
    NSString escapedString = new NSString(cast(objc.id) ptr);
    NSURL inURL = NSURL.URLWithString(escapedString);
    OS.CFRelease(ptr);
    NSURLRequest request = NSURLRequest.requestWithURL(inURL);
    WebFrame mainFrame = webView.mainFrame();
    mainFrame.loadRequest(request);
    return true;
}

public void stop() {
    html = null;
    webView.stopLoading(null);
}

/* WebFrameLoadDelegate */

void webView_didChangeLocationWithinPageForFrame(objc.id sender, objc.id frameID) {
    WebFrame frame = new WebFrame(frameID);
    WebDataSource dataSource = frame.dataSource();
    NSURLRequest request = dataSource.request();
    NSURL url = request.URL();
    NSString s = url.absoluteString();
    NSUInteger length = s.length();
    if (length is 0) return;
    String url2 = s.getString();
    /*
     * If the URI indicates that the page is being rendered from memory
     * (via setText()) then set it to about:blank to be consistent with IE.
     */
    if (url2.equals (URI_FROMMEMORY)) url2 = ABOUT_BLANK;

    final Display display = browser.getDisplay();
    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(objc.id sender, objc.id error, objc.id 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 = null;
    }

    NSError nserror = new NSError(error);
    NSInteger errorCode = nserror.code();
    if (errorCode <= OS.NSURLErrorBadURL) {
        NSString description = nserror.localizedDescription();
        if (description !is null) {
            String descriptionString = description.getString();
            String urlString = null;
            NSDictionary info = nserror.userInfo();
            if (info !is null) {
                NSString key = new NSString(OS.NSErrorFailingURLStringKey_);
                id id = info.valueForKey(key);
                if (id !is null) {
                    NSString url = new NSString(id);
                    urlString = url.getString();
                }
            }
            String message = urlString !is null ? urlString ~ "\n\n" : ""; //$NON-NLS-1$ //$NON-NLS-2$
            message ~= Compatibility.getMessage ("DWT_Page_Load_Failed", [new ArrayWrapperString(descriptionString)]); //$NON-NLS-1$
            MessageBox messageBox = new MessageBox(browser.getShell(), DWT.OK | DWT.ICON_ERROR);
            messageBox.setMessage(message);
            messageBox.open();
        }
    }
}

void webView_didFinishLoadForFrame(objc.id sender, objc.id frameID) {
    hookDOMMouseListeners(frameID);
    if (frameID is webView.mainFrame().id) {
        hookDOMKeyListeners(frameID);

        final Display display = browser.getDisplay();
        /*
        * To be consistent with other platforms a title event should be fired when a
        * page has completed loading.  A page with a <title> tag will do this
        * automatically when the didReceiveTitle callback is received.  However a page
        * without a <title> tag will not do this by default, so fire the event
        * here with the page's url as the title.
        */
        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 */
                final TitleEvent newEvent = new TitleEvent(browser);
                newEvent.display = display;
                newEvent.widget = browser;
                newEvent.title = url;
                for (int i = 0; i < titleListeners.length; i++) {
                    final TitleListener listener = titleListeners[i];
                    /*
                    * Note on WebKit.  Running the event loop from a Browser
                    * delegate callback breaks the WebKit (stop loading or
                    * crash).  The workaround is to invoke Display.asyncExec()
                    * so that the Browser does not crash if this is attempted.
                    */
                    display.asyncExec(
                        new class (display, browser, listener) Runnable {
                            
                            Display display;
                            Browser browser;
                            TitleListener listener;
                            
                            this (Display display, Browser browser, TitleListener listener)
                            {
                                this.display = display;
                                this.browser = browser;
                                this.listener = listener;
                            }
                            
                            public void run() {
                                if (!display.isDisposed() && !browser.isDisposed()) {
                                    listener.changed(newEvent);
                                }
                            }
                        }
                    );
                }
            }
        }
        final ProgressEvent progress = new ProgressEvent(browser);
        progress.display = display;
        progress.widget = browser;
        progress.current = MAX_PROGRESS;
        progress.total = MAX_PROGRESS;
        for (int i = 0; i < progressListeners.length; i++) {
            final ProgressListener listener = progressListeners[i];
            /*
            * Note on WebKit.  Running the event loop from a Browser
            * delegate callback breaks the WebKit (stop loading or
            * crash).  The ProgressBar widget currently touches the
            * event loop every time the method setSelection is called.  
            * The workaround is to invoke Display.asyncExec() so that
            * the Browser does not crash when the user updates the 
            * selection of the ProgressBar.
            */
            display.asyncExec(
                new class (display, browser, listener) Runnable {
                    
                    Display display;
                    Browser browser;
                    ProgressListener listener;
                    
                    this (Display display, Browser browser, ProgressListener listener)
                    {
                        this.display = display;
                        this.browser = browser;
                        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 = null;
    }
}

void hookDOMKeyListeners(objc.id frameID) {
    WebFrame frame = new WebFrame(frameID);
    DOMDocument document = frame.DOMDocument_();

    NSString type = NSString.stringWith(DOMEVENT_KEYDOWN);
    document.addEventListener(type, delegate_, false);

    type = NSString.stringWith(DOMEVENT_KEYUP);
    document.addEventListener(type, delegate_, false);
}

void hookDOMMouseListeners(objc.id frameID) {
    WebFrame frame = new WebFrame(frameID);
    DOMDocument document = frame.DOMDocument_();

    NSString type = NSString.stringWith(DOMEVENT_MOUSEDOWN);
    document.addEventListener(type, delegate_, false);

    type = NSString.stringWith(DOMEVENT_MOUSEUP);
    document.addEventListener(type, delegate_, false);

    type = NSString.stringWith(DOMEVENT_MOUSEMOVE);
    document.addEventListener(type, delegate_, false);

    type = NSString.stringWith(DOMEVENT_MOUSEWHEEL);
    document.addEventListener(type, delegate_, false);
}

void webView_didReceiveTitle_forFrame(objc.id sender, objc.id titleID, objc.id frameID) {
    if (frameID is webView.mainFrame().id) {
        NSString title = new NSString(titleID);
        String newTitle = title.getString();
        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(objc.id sender, objc.id 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(objc.id sender, objc.id frameID) {
    WebFrame frame = new WebFrame(frameID);
    WebDataSource dataSource = frame.dataSource();
    NSURLRequest request = dataSource.request();
    NSURL url = request.URL();
    NSString s = url.absoluteString();
    NSUInteger length = s.length();
    if (length is 0) return;
    String url2 = s.getString();
    /*
     * If the URI indicates that the page is being rendered from memory
     * (via setText()) then set it to about:blank to be consistent with IE.
     */
    if (url2.equals (URI_FROMMEMORY)) url2 = ABOUT_BLANK;

    final Display display = browser.getDisplay();
    bool top = frameID is webView.mainFrame().id;
    if (top) {
        /* reset resource status variables */
        resourceCount = 0;      
        this.url = url2;

        final ProgressEvent progress = new ProgressEvent(browser);
        progress.display = display;
        progress.widget = browser;
        progress.current = 1;
        progress.total = MAX_PROGRESS;
        for (int i = 0; i < progressListeners.length; i++) {
            final ProgressListener listener = progressListeners[i];
            /*
            * Note on WebKit.  Running the event loop from a Browser
            * delegate callback breaks the WebKit (stop loading or
            * crash).  The widget ProgressBar currently touches the
            * event loop every time the method setSelection is called.  
            * The workaround is to invoke Display.asyncexec so that
            * the Browser does not crash when the user updates the 
            * selection of the ProgressBar.
            */
            display.asyncExec(
                new class (display, browser, listener) Runnable {
                    
                    Display display;
                    Browser browser;
                    ProgressListener listener;
                    
                    this (Display display, Browser browser, ProgressListener listener)
                    {
                        this.display = display;
                        this.browser = browser;
                        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(objc.id sender, objc.id identifier, objc.id 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(objc.id sender, objc.id identifier, objc.id error, objc.id 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;
}

objc.id webView_identifierForInitialRequest_fromDataSource(objc.id sender, objc.id request, objc.id dataSourceID) {
    final Display display = browser.getDisplay();
    final ProgressEvent progress = new ProgressEvent(browser);
    progress.display = display;
    progress.widget = browser;
    progress.current = resourceCount;
    progress.total = Math.max(resourceCount, MAX_PROGRESS);
    for (int i = 0; i < progressListeners.length; i++) {
        final ProgressListener listener = progressListeners[i];
        /*
        * Note on WebKit.  Running the event loop from a Browser
        * delegate callback breaks the WebKit (stop loading or
        * crash).  The widget ProgressBar currently touches the
        * event loop every time the method setSelection is called.  
        * The workaround is to invoke Display.asyncexec so that
        * the Browser does not crash when the user updates the 
        * selection of the ProgressBar.
        */
        display.asyncExec(
            new class (display, browser, listener) Runnable {
                
                Display display;
                Browser browser;
                ProgressListener listener;
                
                this (Display display, Browser browser, ProgressListener listener)
                {
                    this.display = display;
                    this.browser = browser;
                    this.listener = listener;
                }
                
                public void run() {
                    if (!display.isDisposed() && !browser.isDisposed())
                        listener.changed(progress);
                }
            }
        );
    }

    NSNumber identifier = NSNumber.numberWithInt(resourceCount++);
    if (this.identifier is null) {
        WebDataSource dataSource = new WebDataSource(dataSourceID);
        WebFrame frame = dataSource.webFrame();
        if (frame.id is webView.mainFrame().id) this.identifier = identifier.id;
    }
    return identifier.id;
        
}

objc.id webView_resource_willSendRequest_redirectResponse_fromDataSource(objc.id sender, objc.id identifier, objc.id request, objc.id redirectResponse, objc.id dataSource) {
    return request;
}

/* handleNotification */

void handleNotification(objc.id notification) {    
}

/* UIDelegate */

objc.id webView_createWebViewWithRequest(objc.id sender, objc.id 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);
        }
    }
    WebView result = null;
    Browser browser = null;
    if (newEvent.browser !is null && cast(Safari) newEvent.browser.webBrowser) {
        browser = newEvent.browser;
    }
    if (browser !is null && !browser.isDisposed()) {
        result = (cast(Safari)browser.webBrowser).webView;
        if (request !is null) {
            WebFrame mainFrame = webView.mainFrame();
            mainFrame.loadRequest(new NSURLRequest(request));
        }
    }
    return result !is null ? result.id : null;
}

void webViewShow(objc.id 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(objc.id sender, objc.id frame) {
    NSRect rect = NSRect();
    OS.memmove(&rect, frame, NSRect.sizeof);
    /* convert to DWT system coordinates */
    Rectangle bounds = browser.getDisplay().getBounds();
    location = new Point(cast(int)rect.x, bounds.height - cast(int)rect.y - cast(int)rect.height);
    size = new Point(cast(int)rect.width, cast(int)rect.height);
}

void webViewFocus(objc.id sender) {
}

void webViewUnfocus(objc.id sender) {
}

void webView_runJavaScriptAlertPanelWithMessage(objc.id sender, objc.id messageID) {
    NSString message = new NSString(messageID);
    String text = message.getString();

    MessageBox messageBox = new MessageBox(browser.getShell(), DWT.OK | DWT.ICON_WARNING);
    messageBox.setText("Javascript");   //$NON-NLS-1$
    messageBox.setMessage(text);
    messageBox.open();
}

objc.id webView_runJavaScriptConfirmPanelWithMessage(objc.id sender, objc.id messageID) {
    NSString message = new NSString(messageID);
    String text = message.getString();

    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 ? cast(objc.id) 1 : null;
}

void webView_runOpenPanelForFileButtonWithResultListener(objc.id sender, objc.id 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(objc.id 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);
}

objc.id webView_contextMenuItemsForElement_defaultMenuItems(objc.id sender, objc.id element, objc.id 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 null;
    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 null;
    }
    return defaultMenuItems;
}

void webView_setStatusBarVisible(objc.id sender, bool visible) {
    /* Note.  Webkit only emits the notification when the status bar should be hidden. */
    statusBar = visible;
}

void webView_setStatusText(objc.id sender, objc.id textID) {
    NSString text = new NSString(textID);
    NSUInteger length = text.length();
    if (length is 0) return;

    StatusTextEvent statusText = new StatusTextEvent(browser);
    statusText.display = browser.getDisplay();
    statusText.widget = browser;
    statusText.text = text.getString();
    for (int i = 0; i < statusTextListeners.length; i++) {
        statusTextListeners[i].changed(statusText);
    }
}

void webView_setResizable(objc.id sender, bool visible) {
}

void webView_setToolbarsVisible(objc.id sender, bool visible) {
    /* Note.  Webkit only emits the notification when the tool bar should be hidden. */
    toolBar = visible;
}

void webView_mouseDidMoveOverElement_modifierFlags (objc.id sender, objc.id elementInformationID, objc.id modifierFlags) {
    if (elementInformationID is null) 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();
    NSUInteger length = url.length();
    String urlString;
    if (length is 0) {
        urlString = ""; //$NON-NLS-1$
    } else {
        urlString = url.getString();
    }
    if (urlString.equals(lastHoveredLinkURL)) return;

    lastHoveredLinkURL = urlString;
    StatusTextEvent statusText = new StatusTextEvent(browser);
    statusText.display = browser.getDisplay();
    statusText.widget = browser;
    statusText.text = urlString;
    for (int i = 0; i < statusTextListeners.length; i++) {
        statusTextListeners[i].changed(statusText);
    }
}

void webView_printFrameView (objc.id sender, objc.id 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(objc.id sender, objc.id type, objc.id request, objc.id frame, objc.id 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(objc.id sender, objc.id actionInformation, objc.id request, objc.id frame, objc.id 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();
    String url2 = s.getString();
    /*
     * If the URI indicates that the page is being rendered from memory
     * (via setText()) then set it to about:blank to be consistent with IE.
     */
    if (url2.equals (URI_FROMMEMORY)) url2 = ABOUT_BLANK;

    LocationEvent newEvent = new LocationEvent(browser);
    newEvent.display = browser.getDisplay();
    newEvent.widget = browser;
    newEvent.location = url2;
    newEvent.doit = true;
    if (locationListeners !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(objc.id sender, objc.id actionInformation, objc.id request, objc.id frameName, objc.id listenerID) {
    WebPolicyDecisionListener listener = new WebPolicyDecisionListener(listenerID);
    listener.use();
}

void webView_unableToImplementPolicyWithError_frame(objc.id sender, objc.id error, objc.id frame) {
}

/* WebDownload */

void download_decideDestinationWithSuggestedFilename(objc.id downloadId, objc.id filename) {
    NSString string = new NSString(filename);
    String name = string.getString();
    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(objc.id evtId) {
    NSString string = new NSString(OS.objc_msgSend(evtId, OS.sel_type));
    String type = string.getString();

    if (DOMEVENT_KEYDOWN.equals(type) || DOMEVENT_KEYUP.equals(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.equals(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.equals(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.equals (type)) {
        mouseEvent.type = DWT.MouseDown;
        mouseEvent.button = button + 1;
        mouseEvent.count = detail;
    } else if (DOMEVENT_MOUSEUP.equals (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;
            default:
        }
    } else if (DOMEVENT_MOUSEMOVE.equals (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.equals (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);
    }
}
}