/*
 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
 * Copyright (C) 2006 David Smith (catfish.man@gmail.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer. 
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution. 
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#import "WebDelegateImplementationCaching.h"

#import "WebKitLogging.h"
#import "WebView.h"
#import "WebViewData.h"
#import <objc/objc-runtime.h>

@implementation WebView (WebDelegateImplementationCaching)

WebResourceDelegateImplementationCache* WebViewGetResourceLoadDelegateImplementations(WebView *webView)
{
    static WebResourceDelegateImplementationCache empty;
    if (!webView)
        return &empty;
    return &webView->_private->resourceLoadDelegateImplementations;
}

WebFrameLoadDelegateImplementationCache* WebViewGetFrameLoadDelegateImplementations(WebView *webView)
{
    static WebFrameLoadDelegateImplementationCache empty;
    if (!webView)
        return &empty;
    return &webView->_private->frameLoadDelegateImplementations;
}

WebScriptDebugDelegateImplementationCache* WebViewGetScriptDebugDelegateImplementations(WebView *webView)
{
    static WebScriptDebugDelegateImplementationCache empty;
    if (!webView)
        return &empty;
    return &webView->_private->scriptDebugDelegateImplementations;
}

WebHistoryDelegateImplementationCache* WebViewGetHistoryDelegateImplementations(WebView *webView)
{
    static WebHistoryDelegateImplementationCache empty;
    if (!webView)
        return &empty;
    return &webView->_private->historyDelegateImplementations;
}

// We use these functions to call the delegates and block exceptions. These functions are
// declared inside a WebView category to get direct access to the delegate data memebers,
// preventing more ObjC message dispatch and compensating for the expense of the @try/@catch.

typedef float (*ObjCMsgSendFPRet)(id, SEL, ...);
#if defined(__i386__)
static const ObjCMsgSendFPRet objc_msgSend_float_return = reinterpret_cast<ObjCMsgSendFPRet>(objc_msgSend_fpret);
#else
static const ObjCMsgSendFPRet objc_msgSend_float_return = reinterpret_cast<ObjCMsgSendFPRet>(objc_msgSend);
#endif

static inline id CallDelegate(WebView *self, id delegate, SEL selector)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return objc_msgSend(delegate, selector, self);
    @try {
        return objc_msgSend(delegate, selector, self);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return objc_msgSend(delegate, selector, self, object);
    @try {
        return objc_msgSend(delegate, selector, self, object);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(WebView *self, id delegate, SEL selector, NSRect rect)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return reinterpret_cast<id (*)(id, SEL, WebView *, NSRect)>(objc_msgSend)(delegate, selector, self, rect);
    @try {
        return reinterpret_cast<id (*)(id, SEL, WebView *, NSRect)>(objc_msgSend)(delegate, selector, self, rect);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object1, id object2)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return objc_msgSend(delegate, selector, self, object1, object2);
    @try {
        return objc_msgSend(delegate, selector, self, object1, object2);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object, BOOL boolean)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return objc_msgSend(delegate, selector, self, object, boolean);
    @try {
        return objc_msgSend(delegate, selector, self, object, boolean);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object1, id object2, id object3)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return objc_msgSend(delegate, selector, self, object1, object2, object3);
    @try {
        return objc_msgSend(delegate, selector, self, object1, object2, object3);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object, NSUInteger integer)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return objc_msgSend(delegate, selector, self, object, integer);
    @try {
        return objc_msgSend(delegate, selector, self, object, integer);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline float CallDelegateReturningFloat(WebView *self, id delegate, SEL selector)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return 0.0f;
    if (!self->_private->catchesDelegateExceptions)
        return objc_msgSend_float_return(delegate, selector, self);
    @try {
        return objc_msgSend_float_return(delegate, selector, self);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return 0.0f;
}

static inline BOOL CallDelegateReturningBoolean(BOOL result, WebView *self, id delegate, SEL selector)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return result;
    if (!self->_private->catchesDelegateExceptions)
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *)>(objc_msgSend)(delegate, selector, self);
    @try {
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *)>(objc_msgSend)(delegate, selector, self);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return result;
}

static inline BOOL CallDelegateReturningBoolean(BOOL result, WebView *self, id delegate, SEL selector, id object)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return result;
    if (!self->_private->catchesDelegateExceptions)
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id)>(objc_msgSend)(delegate, selector, self, object);
    @try {
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id)>(objc_msgSend)(delegate, selector, self, object);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return result;
}

static inline BOOL CallDelegateReturningBoolean(BOOL result, WebView *self, id delegate, SEL selector, id object, BOOL boolean)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return result;
    if (!self->_private->catchesDelegateExceptions)
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, BOOL)>(objc_msgSend)(delegate, selector, self, object, boolean);
    @try {
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, BOOL)>(objc_msgSend)(delegate, selector, self, object, boolean);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return result;
}

static inline BOOL CallDelegateReturningBoolean(BOOL result, WebView *self, id delegate, SEL selector, id object, BOOL boolean, id object2)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return result;
    if (!self->_private->catchesDelegateExceptions)
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, BOOL, id)>(objc_msgSend)(delegate, selector, self, object, boolean, object2);
    @try {
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, BOOL, id)>(objc_msgSend)(delegate, selector, self, object, boolean, object2);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return result;
}

static inline BOOL CallDelegateReturningBoolean(BOOL result, WebView *self, id delegate, SEL selector, id object1, id object2)
{
    if (!delegate || ![delegate respondsToSelector:selector])
        return result;
    if (!self->_private->catchesDelegateExceptions)
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id)>(objc_msgSend)(delegate, selector, self, object1, object2);
    @try {
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id)>(objc_msgSend)(delegate, selector, self, object1, object2);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return result;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self);
    @try {
        return implementation(delegate, selector, self);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object);
    @try {
        return implementation(delegate, selector, self, object);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, id object2)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object1, object2);
    @try {
        return implementation(delegate, selector, self, object1, object2);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, id object2, id object3)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object1, object2, object3);
    @try {
        return implementation(delegate, selector, self, object1, object2, object3);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, id object2, id object3, id object4)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object1, object2, object3, object4);
    @try {
        return implementation(delegate, selector, self, object1, object2, object3, object4);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, NSInteger integer, id object2)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object1, integer, object2);
    @try {
        return implementation(delegate, selector, self, object1, integer, object2);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, NSInteger integer1, NSInteger integer2, id object2)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object1, integer1, integer2, object2);
    @try {
        return implementation(delegate, selector, self, object1, integer1, integer2, object2);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, id object2, NSInteger integer, id object3)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object1, object2, integer, object3);
    @try {
        return implementation(delegate, selector, self, object1, object2, integer, object3);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, NSInteger integer1, id object2, NSInteger integer2, id object3)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object1, integer1, object2, integer2, object3);
    @try {
        return implementation(delegate, selector, self, object1, integer1, object2, integer2, object3);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, NSInteger integer, id object2, id object3, id object4)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object1, integer, object2, object3, object4);
    @try {
        return implementation(delegate, selector, self, object1, integer, object2, object3, object4);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, NSTimeInterval interval, id object2, id object3)
{
    if (!delegate)
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return implementation(delegate, selector, self, object1, interval, object2, object3);
    @try {
        return implementation(delegate, selector, self, object1, interval, object2, object3);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

id CallUIDelegate(WebView *self, SEL selector)
{
    return CallDelegate(self, self->_private->UIDelegate, selector);
}

id CallUIDelegate(WebView *self, SEL selector, id object)
{
    return CallDelegate(self, self->_private->UIDelegate, selector, object);
}

id CallUIDelegate(WebView *self, SEL selector, id object, BOOL boolean)
{
    return CallDelegate(self, self->_private->UIDelegate, selector, object, boolean);
}

id CallUIDelegate(WebView *self, SEL selector, NSRect rect)
{
    return CallDelegate(self, self->_private->UIDelegate, selector, rect);
}

id CallUIDelegate(WebView *self, SEL selector, id object1, id object2)
{
    return CallDelegate(self, self->_private->UIDelegate, selector, object1, object2);
}

id CallUIDelegate(WebView *self, SEL selector, id object1, id object2, id object3)
{
    return CallDelegate(self, self->_private->UIDelegate, selector, object1, object2, object3);
}

id CallUIDelegate(WebView *self, SEL selector, id object, NSUInteger integer)
{
    return CallDelegate(self, self->_private->UIDelegate, selector, object, integer);
}

float CallUIDelegateReturningFloat(WebView *self, SEL selector)
{
    return CallDelegateReturningFloat(self, self->_private->UIDelegate, selector);
}

BOOL CallUIDelegateReturningBoolean(BOOL result, WebView *self, SEL selector)
{
    return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector);
}

BOOL CallUIDelegateReturningBoolean(BOOL result, WebView *self, SEL selector, id object)
{
    return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector, object);
}

BOOL CallUIDelegateReturningBoolean(BOOL result, WebView *self, SEL selector, id object, BOOL boolean)
{
    return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector, object, boolean);
}

BOOL CallUIDelegateReturningBoolean(BOOL result, WebView *self, SEL selector, id object, BOOL boolean, id object2)
{
    return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector, object, boolean, object2);
}

BOOL CallUIDelegateReturningBoolean(BOOL result, WebView *self, SEL selector, id object1, id object2)
{
    return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector, object1, object2);
}

id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector)
{
    return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector);
}

id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object)
{
    return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object);
}

id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2)
{
    return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object1, object2);
}

id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3)
{
    return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object1, object2, object3);
}

id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3, id object4)
{
    return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object1, object2, object3, object4);
}

id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSTimeInterval interval, id object2, id object3)
{
    return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object1, interval, object2, object3);
}

id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2)
{
    return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, object2);
}

id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3)
{
    return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, object2, object3);
}

id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3, id object4)
{
    return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, object2, object3, object4);
}

id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSInteger integer, id object2)
{
    return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, integer, object2);
}

id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, NSInteger integer, id object3)
{
    return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, object2, integer, object3);
}

BOOL CallResourceLoadDelegateReturningBoolean(BOOL result, IMP implementation, WebView *self, SEL selector, id object1, id object2)
{
    if (!self->_private->catchesDelegateExceptions)
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id)>(objc_msgSend)(self->_private->resourceProgressDelegate, selector, self, object1, object2);
    @try {
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id)>(objc_msgSend)(self->_private->resourceProgressDelegate, selector, self, object1, object2);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return result;
}

BOOL CallResourceLoadDelegateReturningBoolean(BOOL result, IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3)
{
    if (!self->_private->catchesDelegateExceptions)
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id, id)>(objc_msgSend)(self->_private->resourceProgressDelegate, selector, self, object1, object2, object3);
    @try {
        return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id, id)>(objc_msgSend)(self->_private->resourceProgressDelegate, selector, self, object1, object2, object3);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return result;
}

id CallScriptDebugDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, NSInteger integer, id object3)
{
    return CallDelegate(implementation, self, self->_private->scriptDebugDelegate, selector, object1, object2, integer, object3);
}

id CallScriptDebugDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSInteger integer1, id object2, NSInteger integer2, id object3)
{
    return CallDelegate(implementation, self, self->_private->scriptDebugDelegate, selector, object1, integer1, object2, integer2, object3);
}

id CallScriptDebugDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSInteger integer, id object2, id object3, id object4)
{
    return CallDelegate(implementation, self, self->_private->scriptDebugDelegate, selector, object1, integer, object2, object3, object4);
}

id CallScriptDebugDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSInteger integer1, NSInteger integer2, id object2)
{
    return CallDelegate(implementation, self, self->_private->scriptDebugDelegate, selector, object1, integer1, integer2, object2);
}

id CallHistoryDelegate(IMP implementation, WebView *self, SEL selector)
{
    return CallDelegate(implementation, self, self->_private->historyDelegate, selector);
}

id CallHistoryDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2)
{
    return CallDelegate(implementation, self, self->_private->historyDelegate, selector, object1, object2);
}

id CallHistoryDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3)
{
    return CallDelegate(implementation, self, self->_private->historyDelegate, selector, object1, object2, object3);
}

// The form delegate needs to have it's own implementation, because the first argument is never the WebView

id CallFormDelegate(WebView *self, SEL selector, id object1, id object2)
{
    id delegate = self->_private->formDelegate;
    if (!delegate || ![delegate respondsToSelector:selector])
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return objc_msgSend(delegate, selector, object1, object2);
    @try {
        return objc_msgSend(delegate, selector, object1, object2);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

id CallFormDelegate(WebView *self, SEL selector, id object1, id object2, id object3, id object4, id object5)
{
    id delegate = self->_private->formDelegate;
    if (!delegate || ![delegate respondsToSelector:selector])
        return nil;
    if (!self->_private->catchesDelegateExceptions)
        return objc_msgSend(delegate, selector, object1, object2, object3, object4, object5);
    @try {
        return objc_msgSend(delegate, selector, object1, object2, object3, object4, object5);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return nil;
}

BOOL CallFormDelegateReturningBoolean(BOOL result, WebView *self, SEL selector, id object1, SEL selectorArg, id object2)
{
    id delegate = self->_private->formDelegate;
    if (!delegate || ![delegate respondsToSelector:selector])
        return result;
    if (!self->_private->catchesDelegateExceptions)
        return reinterpret_cast<BOOL (*)(id, SEL, id, SEL, id)>(objc_msgSend)(delegate, selector, object1, selectorArg, object2);
    @try {
        return reinterpret_cast<BOOL (*)(id, SEL, id, SEL, id)>(objc_msgSend)(delegate, selector, object1, selectorArg, object2);
    } @catch(id exception) {
        ReportDiscardedDelegateException(selector, exception);
    }
    return result;
}

@end
