/*
SRMainWindowControllerWebView.m

Author: Makoto Kinoshita

Copyright 2004 The Shiira Project. All rights reserved.

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.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``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 THE SHIIRA PROJECT OR 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 "SRDefaultsKey.h"
#import "SRContextMenu.h"

#import "SRAppDelegate.h"
#import "SRDocumentController.h"
#import "SRMainDocument.h"
#import "SRMainWindowController.h"
#import "SRAuthenticationController.h"
#import "SRSearchFieldController.h"
#import "SRSearchEnginesController.h"
#import "SRBookmark.h"
#import "SRPreferencesController.h"
#import "SRAutofillController.h"

#import "SRTabView.h"
#import "SRURLComboBox.h"
#import "SRTextInputPanelManager.h"
#import "SRRSSRepresentation.h"

#import "SRUtil.h"
#import "FoundationEx.h"
#import "WebKitEx.h"
#import "SRDOMFilter.h"

@implementation SRMainWindowController (WebViewDelegateMethods)

//--------------------------------------------------------------//
#pragma mark -- WebFrameLoadDelegate --
//--------------------------------------------------------------//

- (void)webView:(WebView*)webView 
        didStartProvisionalLoadForFrame:(WebFrame*)frame
{
    // Resign first responder
    if (webView == [self selectedWebView]) {
        [[self window] makeFirstResponder:nil];
    }
    
    // For main frame
    if ([webView mainFrame] == frame) {
        // Get provisional data source
        WebDataSource*  provisionalDataSource;
        provisionalDataSource = [frame provisionalDataSource];
        
        // Get URL string
        NSString*   URLString;
        URLString = [[[provisionalDataSource request] URL] _web_userVisibleString];
        
        // Set tab title
        int index;
        index = [_srTabView indexOfView:webView];
        if (index != -1) {
            [_srTabView setLabel:URLString atIndex:index];
        }
        
        // Compare with forward one
        if ([self selectedWebView] == webView) {
            // Set URL String
            [self setValue:URLString forKey:SRMainWebViewURLString];
            
            // Set icon
            [[self URLComboBox] setImage:
                    [[WebIconDatabase sharedIconDatabase] defaultIconWithSize:NSMakeSize(16, 16)]];
            
            // Set status message
            NSString*   status;
            status = NSLocalizedStringFromTable(UTF8STR("Connecting “%@”"), nil, nil);
            if (status) {
                [self setValue:[NSString stringWithFormat:status, URLString]
                        forKey:SRMainWebViewStatus];
            }
            
            // Set loading
            [self setValue:[NSNumber numberWithBool:YES] 
                    forKey:SRMainWebViewIsIndeterminate];
            [self setValue:[NSNumber numberWithBool:YES] 
                    forKey:SRMainWebViewIsLoading];
            
            // Set window title
            [self setValue:URLString forKey:SRMainWebViewTitle];
            
#ifdef SR_SUPPORT_RSS
            // RSS button
            [_RSSButton setHidden:YES];
#endif // SR_SUPPORT_RSS
        }
    }
}

- (void)webView:(WebView*)webView 
        didCommitLoadForFrame:(WebFrame*)frame
{
    // Get request, response, and URL
    WebDataSource*  dataSource;
    NSURLRequest*   request;
    NSURLResponse*  response;
    dataSource = [frame dataSource];
    request = [dataSource request];
    response = [dataSource response];
    
    // Compare with forward one
    if ([self selectedWebView] == webView) {
        
        // For main frame
        if ([webView mainFrame] == frame) {
            // Get URL string
            NSURL*      url;
            NSString*   URLString;
            url = [request URL];
            URLString = [url _web_userVisibleString];
            
            // Set URL String
            [self setValue:URLString forKey:SRMainWebViewURLString];
            
            // Set status message
            NSString*   status;
            status = NSLocalizedStringFromTable(UTF8STR("Loading “%@”"), nil, nil);
            if (status) {
                [self setValue:[NSString stringWithFormat:status, URLString]
                        forKey:SRMainWebViewStatus];
            }
            
            // Set loading
            [self setValue:[NSNumber numberWithBool:NO] 
                    forKey:SRMainWebViewIsIndeterminate];
            [self setValue:[NSNumber numberWithBool:[dataSource isLoading]] 
                    forKey:SRMainWebViewIsLoading];
            
            // Set secure
            [self setValue:[NSNumber numberWithBool:[[url scheme] isEqualToString:@"https"]] 
                    forKey:SRMainWebViewIsSecured];
            [_lockIconView setHidden:![[url scheme] isEqualToString:@"https"]];
        }
        
        // Check focus
        NSWindow*       window;
        WebFrameView*   webFrameView;
        window = [self window];
        webFrameView = [frame frameView];
        if ([window firstResponder] != webFrameView) {
            [window makeFirstResponder:webFrameView];
        }
    }
    
    // Check MIME type
    NSString*   MIMEType;
    MIMEType = [response MIMEType];
    if ([[SRRSSRepresentation RSSMimes] containsObject:MIMEType]) {
        NSString*   initialURLString;
        initialURLString = [[[dataSource initialRequest] URL] absoluteString];
        if (_openingBookmark && 
            [[_openingBookmark URLString] isEqualToString:initialURLString] && 
            [_openingBookmark type] != SRBookmarkTypeRSS)
        {
            [_openingBookmark setType:SRBookmarkTypeRSS];
        }
    }
}

- (void)webView:(WebView*)webView 
        didFinishLoadForFrame:(WebFrame*)frame
{
#if 0
DOMDocument*    document;
document = [frame DOMDocument];
DOMNodeIterator*    iterator;
iterator = [document createNodeIterator:[document documentElement] :DOM_SHOW_ALL :nil :NO];

NSMutableString*    htmlText;
htmlText = [NSMutableString string];
DOMNode*    node;
while (node = [iterator nextNode]) {
    if ([node respondsToSelector:@selector(data)]) {
        NSString*   string;
        string = [(DOMCharacterData*)node data];
        if (string && [string length] > 0) {
            [htmlText appendFormat:@"%@ ", string];
        }
    }
    //if ([node isKindOfClass:[DOMHTMLBodyElement class]]) {
    //    NSLog(@"text %@", [(DOMHTMLBodyElement*)node text]);
    //}
    //NSLog(@"%@", [node nodeValue]);
}
NSLog(@"%@", htmlText);
#endif
    // Compare with forward one
    if ([self selectedWebView] == webView) {
        
        // For main frame
        if ([webView mainFrame] == frame) {
            // Get data source
            WebDataSource*  dataSource;
            dataSource = [frame dataSource];
            
            // Set status message
            [self setValue:@"" forKey:SRMainWebViewStatus];
            
            // Set loading
            [self setValue:[NSNumber numberWithBool:[dataSource isLoading]] 
                    forKey:SRMainWebViewIsLoading];
            
#ifdef SR_SUPPORT_RSS
            // RSS button
            [_RSSButton setHidden:![self hasRSSLinkAtWebView:webView]];
#endif // SR_SUPPORT_RSS
        }
    }
    
#ifdef SR_SUPPORT_RSS
    // For RSS MIME
    if ([[SRRSSRepresentation RSSMimes] containsObject:[[[frame dataSource] response] MIMEType]]) {
        // Reload title
        NSString*   title;
        title = [[frame dataSource] pageTitle];
        if (title) {
            [self webView:webView didReceiveTitle:title forFrame:frame];
        }
    }
#endif // SR_SUPPORT_RSS
    
    // Get stauts code
    int statusCode = 0;
    id  response;
    response = [[frame dataSource] response];
    if ([response respondsToSelector:@selector(statusCode)]) {
        statusCode = [response statusCode];
    }
    
    // For sound
    if ([[NSUserDefaults standardUserDefaults] boolForKey:SRSoundUseSoundEffect]) {
        if ([webView mainFrame] == frame) {
            // Get sound action ID
            SystemSoundActionID soundActionId;
            if (statusCode == 200) {
                soundActionId = [[NSApp delegate] soundForAction:SRSoundPageLoadDone];
            }
            else if (statusCode != 0) {
                soundActionId = [[NSApp delegate] soundForAction:SRSoundPageLoadError];
            }
            if (soundActionId > 0) {
                SystemSoundPlay(soundActionId);
            }
        }
    }
    
    // For auto fill
    if ([webView mainFrame] == frame) {
        [[SRAutofillController sharedInstance] fillFormsWithWebView:webView];
    }
}

- (void)webView:(WebView*)webView 
        willCloseFrame:(WebFrame*)frame
{
    // Compare with forward one
    if ([self selectedWebView] == webView) {
    }
}

- (void)webView:(WebView*)webView 
        didChangeLocationWithinPageForFrame:(WebFrame*)frame
{
    // Notify change
    [[NSNotificationCenter defaultCenter] 
            postNotificationName:SRMainWindowDidChangeLocationWithinPage object:webView];
}

- (void)webView:(WebView*)webView 
        didReceiveTitle:(NSString*)title 
        forFrame:(WebFrame*)frame
{
    // For all main frames
    if ([webView mainFrame] == frame) {
        // Set tab title
        int index;
        index = [_srTabView indexOfView:webView];
        if (index != -1) {
            [_srTabView setLabel:title atIndex:index];
        }
    }
    
    // Compare with forward one
    if ([self selectedWebView] == webView) {
        // For main frame
        if ([webView mainFrame] == frame) {
            // Set window title
            [self setValue:title forKey:SRMainWebViewTitle];
        }
    }
}

- (void)webView:(WebView*)webView 
        didReceiveIcon:(NSImage*)icon 
        forFrame:(WebFrame*)frame
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon];
    
    // Compare with forward one
    if ([self selectedWebView] == webView) {
        
        // For main frame
        if ([webView mainFrame] == frame) {
            // Set icon
            if (isFaviconUsed) {
                if (!icon || ![icon isValid] || NSEqualSizes([icon size], NSZeroSize)) {
                    icon = [[WebIconDatabase sharedIconDatabase] defaultIconWithSize:NSMakeSize(16, 16)];
                }
                [[self URLComboBox] setImage:icon];
            }
        }
    }
    
    // For searching
    NSString*   initialRequestURLString;
    NSString*   templateURL;
    initialRequestURLString = [[[[frame dataSource] initialRequest] URL] absoluteString];
    templateURL = [_searchingURLDict objectForKey:initialRequestURLString];
    if (templateURL) {
        // Get icon URL
        NSString*   iconURLString;
        iconURLString = [[WebIconDatabase sharedIconDatabase] iconURLForURL:
                [[[[frame dataSource] request] URL] absoluteString]];
        
        // Register icon
        if (iconURLString) {
            [[WebIconDatabase sharedIconDatabase] _setIconURL:iconURLString forURL:templateURL];
        }
        
        // Remove template
        if (initialRequestURLString) {
            [_searchingURLDict removeObjectForKey:initialRequestURLString];
        }
    }
}

- (void)showAlertWithError:(NSError*)error forFrame:(WebFrame*)frame
{
    // Set loading
    [self setValue:[NSNumber numberWithBool:NO] 
            forKey:SRMainWebViewIsIndeterminate];
    [self setValue:[NSNumber numberWithBool:NO] 
            forKey:SRMainWebViewIsLoading];
    
    // Get error domain and code
    NSString*   domain;
    int         code;
    domain = [error domain];
    code = [error code];
    
    // Get error message
    NSString*   msgText = nil;
    NSString*   infoText = nil;
    NSString*   statusText = nil;
    SRGetErrorMessage(error, frame, &msgText, &infoText, &statusText);
    
    // For certification error
    if ([domain isEqualToString:NSURLErrorDomain] && 
        (code == NSURLErrorServerCertificateHasBadDate || 
         code == NSURLErrorServerCertificateHasUnknownRoot || 
         code == NSURLErrorServerCertificateUntrusted))
    {
        // Show dialog for certifacte allowing
        if (msgText && infoText) {
            // Get host
            NSURLRequest*   request;
            NSURL*          URL;
            NSString*       host;
            request = [[frame provisionalDataSource] request];
            if (!request) {
                request = [[frame dataSource] request];
            }
            URL = [request URL];
            host = [URL host];
            if (!host || [host length] == 0) {
                request = [[frame provisionalDataSource] initialRequest];
                URL = [request URL];
                host = [URL host];
            }
            
            // Create alert
            NSAlert*    alert;
            alert = [[NSAlert alloc] init];
            [alert autorelease];
            [alert setMessageText:msgText];
            [alert setInformativeText:infoText];
            [alert setAlertStyle:NSWarningAlertStyle];
            [alert addButtonWithTitle:NSLocalizedString(@"Continue", nil)];
            [alert addButtonWithTitle:NSLocalizedString(@"Cancel", nil)];
            
            // Show alert
            [alert beginSheetModalForWindow:[self window] 
                    modalDelegate:self 
                    didEndSelector:@selector(_certificateAllowingAlertDidEnd:returnCode:contextInfo:) 
                    contextInfo:[[URL _web_userVisibleString] retain]];
        }
    }
    else {
        // Show alert
        if (msgText && infoText) {
            // Create alert
            NSAlert*    alert;
            alert = [[NSAlert alloc] init];
            [alert autorelease];
            [alert setMessageText:msgText];
            [alert setInformativeText:infoText];
            [alert setAlertStyle:NSWarningAlertStyle];
            
            // Show alert
            [alert beginSheetModalForWindow:[self window] 
                    modalDelegate:self 
                    didEndSelector:@selector(_failureAlertDidEnd:returnCode:contextInfo:) 
                    contextInfo:NULL];
        }
    }
    
    // Show status
    if (statusText) {
        [self setValue:statusText forKey:SRMainWebViewStatus];
    }
    else {
        [self setValue:@"" forKey:SRMainWebViewStatus];
    }
}

- (void)_certificateAllowingAlertDidEnd:(NSAlert*)alert 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Focus on URL text field
    [[self window] makeFirstResponder:[self URLComboBox]];
    
    // For continue
    if (returnCode == NSAlertFirstButtonReturn) {
        // Get URL string
        NSString*   URLString;
        URLString = (NSString*)contextInfo;
        
        if (URLString) {
            // Allow any certifiate from this host
            [NSURLRequest setAllowsAnyHTTPSCertificate:YES 
                    forHost:[[NSURL _web_URLWithUserTypedString:URLString] host]];
            
            // Reload
            [self openURLString:URLString];
        }
        
        [(id)contextInfo release];
    }
}

- (void)_failureAlertDidEnd:(NSAlert*)alert 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Focus on URL text field
    [[self window] makeFirstResponder:[self URLComboBox]];
}

- (void)_soundEffectForWebView:(WebView*)webView error:(NSError*)error frame:(WebFrame*)frame
{
    // Get status code
    int statusCode = 0;
    id  response;
    response = [[frame dataSource] response];
    if ([response respondsToSelector:@selector(statusCode)]) {
        statusCode = [response statusCode];
    }
    if (statusCode == 200) {
        return;
    }
    
    // For sound
    if ([[NSUserDefaults standardUserDefaults] boolForKey:SRSoundUseSoundEffect]) {
        if ([webView mainFrame] == frame && 
			(!response || 
			 statusCode != 0 || 
			 [error code] != NSURLErrorCancelled))
		{
            SystemSoundActionID soundActionId;
            soundActionId = [[NSApp delegate] soundForAction:SRSoundPageLoadError];
            if (soundActionId > 0) {
                SystemSoundPlay(soundActionId);
            }
        }
    }
}

- (void)webView:(WebView*)webView 
        didFailProvisionalLoadWithError:(NSError*)error 
        forFrame:(WebFrame*)frame
{
    // Sound effect
    [self _soundEffectForWebView:webView error:error frame:frame];
    
    // Compare with forward one
    if ([self selectedWebView] == webView) {
        if ([webView mainFrame] == frame) {
            // Show alert
            [self showAlertWithError:error forFrame:frame];
        }
    }
    // Store this error in tab view item
    else {
        // Get tab view item
        int             index;
        SRTabViewItem*  tabViewItem;
        index = [_srTabView indexOfView:webView];
        if (index == -1) {
            return;
        }
        tabViewItem = (SRTabViewItem*)[[_srTabView tabView] tabViewItemAtIndex:index];
        if (!tabViewItem) {
            return;
        }
        
        // Set error as user info
        if (error && frame) {
            NSDictionary*   errorInfo;
            errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                    error, @"error", 
                    frame, @"frame", 
                    nil];
            [tabViewItem setUserInfo:errorInfo];
        }
    }
}

- (void)webView:(WebView*)webView 
        didFailLoadWithError:(NSError*)error 
        forFrame:(WebFrame*)frame
{
    // Sound effect
    [self _soundEffectForWebView:webView error:error frame:frame];
    
    // Compare with forward one
    if ([self selectedWebView] == webView && [webView mainFrame] == frame) {
        // Show alert
        [self showAlertWithError:error forFrame:frame];
    }
}

- (void)webView:(WebView*)sender 
        didCancelClientRedirectForFrame:(WebFrame*)frame
{
}

- (void)webView:(WebView*)webView 
        willPerformClientRedirectToURL:(NSURL*)URL 
        delay:(NSTimeInterval)seconds 
        fireDate:(NSDate*)date 
        forFrame:(WebFrame*)frame
{
    // Get original request
    NSString*   originlURL;
    originlURL = [[[[frame dataSource] request] URL] absoluteString];
    
    // Swap searching URL
    NSString*   templateURL;
    templateURL = [_searchingURLDict objectForKey:originlURL];
    if (templateURL) {
        [_searchingURLDict setObject:templateURL forKey:[URL absoluteString]];
    }
}

- (void)webView:(WebView*)webView 
        didReceiveServerRedirectForProvisionalLoadForFrame:(WebFrame*)frame
{
}

//--------------------------------------------------------------//
#pragma mark -- WebResourceLoadDelegate --
//--------------------------------------------------------------//

- (NSURLRequest*)webView:(WebView*)webView 
        resource:(id)identifier 
        willSendRequest:(NSURLRequest*)request 
        redirectResponse:(NSURLResponse*)redirectResponse 
        fromDataSource:(WebDataSource*)dataSource
{
    // Defer loading
    if ([self selectedWebView] != webView) {
        [[[webView mainFrame] _bridge] setDefersLoading:YES];
        
        // Retain web view
        [webView retain];
        
        // Resume later
        [self performSelector:@selector(_resumeLoading:) withObject:webView afterDelay:1.0f];
    }
    
    // Reset timeout interval
    int timeout;
    timeout = [[NSUserDefaults standardUserDefaults] integerForKey:SRGeneralTimeout];
    if ([request timeoutInterval] != timeout) {
        NSMutableURLRequest*    newRequest;
        newRequest = [[request mutableCopy] autorelease];
        [newRequest setTimeoutInterval:timeout];
        return newRequest;
    }
    
    return request;
}

- (void)_resumeLoading:(WebView*)webView
{
    // Resume loading
    [[[webView mainFrame] _bridge] setDefersLoading:NO];
    
    // Release web view
    [webView release];
}

- (void)webView:(WebView*)webView 
        resource:(id)identifier 
        didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge 
        fromDataSource:(WebDataSource*)dataSource
{
    // Show authentication panel
    SRAuthenticationController* authController;
    authController = [self authenticationController];
    [authController setAuthenticationChallenge:challenge];
    [authController windowWillShow];
    [NSApp beginSheet:[authController window] 
            modalForWindow:[self window] 
            modalDelegate:self 
            didEndSelector:@selector(_didEndAuthPanel:returnCode:contextInfo:) 
            contextInfo:NULL];
}

#define _CANCEL_BUTTON -1
#define _LOG_IN_BUTTON 0

- (void)_didEndAuthPanel:(NSWindow*)sheet 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Close sheet
    [sheet orderOut:self];
    
    // Get challenge
    SRAuthenticationController*     authController;
    NSURLAuthenticationChallenge*   challenge;
    authController = [self authenticationController];
    challenge = [authController authenticationChallenge];
    // Check return code
    if (returnCode != _LOG_IN_BUTTON) {
        // Cancel challenge
        [[challenge sender] cancelAuthenticationChallenge:challenge];
        
        return;
    }
    
    // Create credential
    NSString*           username;
    NSString*           password;
    NSURLCredential*    credential;
    username = [authController valueForKey:@"username"];
    password = [authController password];
    credential = [NSURLCredential credentialWithUser:username 
            password:password 
            persistence:NSURLCredentialPersistenceForSession];
    
    // Get protection space
    NSURLProtectionSpace*   protectionSpace;
    protectionSpace = [challenge protectionSpace];
    
    // Register to Keychain
    if ([[authController valueForKey:@"isPasswordRemembered"] boolValue]) {
        // Get host
        NSString*   host;
        host = [protectionSpace host];
        
        // Get realm
        NSString*   realm;
        realm = [protectionSpace realm];
        
        // Decide protocol type
        SecProtocolType protocolType = kSecProtocolTypeHTTP;
        
        NSString*   protocol;
        NSString*   proxyType;
        protocol = [protectionSpace protocol];
        proxyType = [protectionSpace proxyType];
        if (protocol) {
            if ([protocol isEqualToString:@"https"]) {
                protocolType = kSecProtocolTypeHTTPS;
            }
            else if ([protocol isEqualToString:@"ftp"]) {
                protocolType = kSecProtocolTypeFTP;
            }
        }
        else if (proxyType) {
            if ([proxyType isEqualToString:NSURLProtectionSpaceHTTPProxy]) {
                protocolType = kSecProtocolTypeHTTPProxy;
            }
            else if ([proxyType isEqualToString:NSURLProtectionSpaceHTTPSProxy]) {
                protocolType = kSecProtocolTypeHTTPSProxy;
            }
            else if ([proxyType isEqualToString:NSURLProtectionSpaceFTPProxy]) {
                protocolType = kSecProtocolTypeFTPProxy;
            }
        }
        
        // Decide authentication type
        SecAuthenticationType   authType = kSecAuthenticationTypeDefault;
// Default authentication always use default type
#if 0
        NSString*   authMethod;
        authMethod = [protectionSpace authenticationMethod];
        if ([authMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]) {
            authType = kSecAuthenticationTypeHTTPBasic;
        }
        else if ([authMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]) {
            authType = kSecAuthenticationTypeHTTPDigest;
        }
        else if ([authMethod isEqualToString:NSURLAuthenticationMethodHTMLForm]) {
            authType = kSecAuthenticationTypeHTMLForm;
        }
#endif
        
        if (host && [host length] > 0 && username && [username length] > 0 && password && [password length] > 0) {
            // Register to keychain
            OSStatus    status;
            status = SecKeychainAddInternetPassword(
                    NULL, 
                    strlen([host UTF8String]), 
                    [host UTF8String], 
                    strlen([realm UTF8String]), 
                    [realm UTF8String], 
                    strlen([username UTF8String]), 
                    [username UTF8String], 
                    0, 
                    NULL, 
                    [protectionSpace port], 
                    protocolType, 
                    authType, 
                    strlen([password UTF8String]), 
                    [password UTF8String], 
                    NULL);
            if (status != noErr) {
                // Error
                NSLog(@"Failed to SecKeychainAddInternetPassword, %d", status);
            }
        }
    }
    
    // Challenge it
    [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    [authController setAuthenticationChallenge:nil];
}

- (void)webView:(WebView*)sender 
        resource:(id)identifier 
        didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge 
        fromDataSource:(WebDataSource*)dataSource
{
}

//--------------------------------------------------------------//
#pragma mark -- WebPolicyDelegate --
//--------------------------------------------------------------//

- (void)webView:(WebView*)webView 
        decidePolicyForMIMEType:(NSString*)type 
        request:(NSURLRequest*)request 
        frame:(WebFrame*)frame 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    // Check HTTP header fields
    id  response;
    response = [[frame provisionalDataSource] response];
    if (response && [response respondsToSelector:@selector(allHeaderFields)]) {
        NSDictionary*   headerFields;
        headerFields = [response allHeaderFields];
        
        // For Content-Disposition
        NSString*   contentDisposition;
        contentDisposition = [headerFields objectForKey:@"Content-Disposition"];
        if (contentDisposition && [contentDisposition rangeOfString:@"attachment"].location != NSNotFound) {
            // Download
            [listener download];
            return;
        }
        
        // For Content-Type
        NSString*   contentType;
        contentType = [headerFields objectForKey:@"Content-Type"];
        if (contentType && [contentType rangeOfString:@"application/octet-stream"].location != NSNotFound) {
            // Download
            [listener download];
            return;
        }
    }
    
    // Check path extension
    NSString*   extension;
    extension = [[[request URL] path] pathExtension];
    if ([extension isEqualToString:@"html"] || [extension isEqualToString:@"htm"]) {
        // Use this resource
        [listener use];
        return;
    }
    
    // Check MIME type WebView can show
    if ([WebView canShowMIMEType:type]) {
        // Use this MIME type
        [listener use];
        return;
    }
    
    // Download
    [listener download];
}

- (BOOL)_webView:(WebView*)webView 
        decideBrowserModePolicyAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        frameName:(NSString*)frameName 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get tab browsing
    BOOL    isTabbedBrowsing;
    isTabbedBrowsing = [defaults boolForKey:SRTabEnableTabbedBrowsing];
    
    // Check modifier key flag
    unsigned int    modifierFlags = 0;
    modifierFlags = [[info objectForKey:WebActionModifierFlagsKey] unsignedIntValue];
    if ((modifierFlags & (NSAlphaShiftKeyMask | 
                          NSShiftKeyMask | 
                          NSControlKeyMask | 
                          NSAlternateKeyMask | 
                          NSCommandKeyMask)) != 0)
    {
        return NO;
    }
    
    // Get browse mode
    int browseMode;
    browseMode = [defaults integerForKey:SRBrowseMode];
    switch (browseMode) {
    case SRAlwaysInSameTabBrowseMode: {
        [self openRequest:request];
        [listener ignore];
        return YES;
    }
    case SRAlwaysInNewTabBrowseMode: {
        if (isTabbedBrowsing) {
            [self openInNewTabRequest:request frameName:frameName groupName:nil select:YES];
            [listener ignore];
            return YES;
        }
        
        break;
    }
    case SRAlwaysInNewBackgroundTabBrowseMode: {
        if (isTabbedBrowsing) {
            [self openInNewTabRequest:request frameName:frameName groupName:nil select:NO];
            [listener ignore];
            return YES;
        }
        
        break;
    }
    }
    
    return NO;
}

- (BOOL)_webView:(WebView*)webView 
        decideTabPolicyAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        frameName:(NSString*)frameName 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get modifier key flag
    unsigned int    modifierFlags = 0;
    modifierFlags = [[info objectForKey:WebActionModifierFlagsKey] unsignedIntValue];
    if (!modifierFlags) {
        return NO;
    }
    
    // Get open action
    SROpenActionType    openAction;
    openAction = SROpenActionTypeFromModifierFlags(modifierFlags);
    
    // Check open action
    switch (openAction) {
    case SROpenAction: {
        // Do nothing
        break;
    }
    // For opening with tab or window
    case SROpenInNewTabAction:
    case SROpenInNewBackgroundTabAction:
    case SROpenInNewWindowAction:
    case SROpenInNewBackgroundWindowAction: {
        [self openRequest:request frameName:frameName groupName:nil withOpenAction:openAction];
        [listener ignore];
        return YES;
    }
    case SROpenOptionAction: {
        [listener download];
        return YES;
    }
    }
    
    return NO;
}

- (void)webView:(WebView*)webView 
        decidePolicyForNavigationAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        frame:(WebFrame*)frame 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get navigation type
    int navigationType;
    navigationType = [[info objectForKey:WebActionNavigationTypeKey] intValue];
    
    // Get action element
    NSDictionary*   element;
    NSURL*          linkURL;
    NSString*       linkTargetFrame;
    element = [info objectForKey:WebActionElementKey];
    linkURL = [info objectForKey:WebActionOriginalURLKey];;
    linkTargetFrame = [[element objectForKey:WebElementLinkTargetFrameKey] name];
    
    // Check scheme
    NSString*	scheme;
    NSString*   lowercase;
    scheme = [linkURL scheme];
    lowercase = [scheme lowercaseString];
    if (scheme && ![scheme isEqualToString:lowercase]) {
        // Change scheme
        NSMutableString*    URLString;
        NSRange             range;
        URLString = [NSMutableString stringWithString:[linkURL absoluteString]];
        range = [URLString rangeOfString:scheme];
        if (range.location != NSNotFound) {
            [URLString replaceCharactersInRange:range withString:lowercase];
            linkURL = [NSURL URLWithString:URLString];
        }
    }
    
    // For about scheme
    if ([[linkURL scheme] isEqualToString:@"about"]) {
        // Just use
        [listener use];
        return;
    }
    
    // For other schemes
    if (![self isSchemeSupported:[linkURL scheme]]) {
        // only if it's mailto, or if the default is set to allow all schemes
        if ([[linkURL scheme] isEqualToString:@"mailto"] || 
            [defaults boolForKey:SRSecurityAllowAllURLSchemes])
        {
            // Launch external viewer
            [[NSWorkspace sharedWorkspace] openURL:linkURL];
            [listener ignore];
            return;
        }
    }
    
    // For link click
    if (navigationType == WebNavigationTypeLinkClicked) {
        // Check browse mode
        if ([self _webView:webView 
            decideBrowserModePolicyAction:info 
            request:request 
            frameName:linkTargetFrame 
            decisionListener:listener])
        {
            return;
        }
        
        // Check tab policy
        if ([self _webView:webView 
                decideTabPolicyAction:info 
                request:request 
                frameName:linkTargetFrame 
                decisionListener:listener])
        {
            return;
        }
    }
    // For submit
    if (navigationType == WebNavigationTypeFormSubmitted) {
        // Register form contents
        [[SRAutofillController sharedInstance] registerFormsWithWebView:webView];
    }
    // For resubmit
    if (navigationType == WebNavigationTypeFormResubmitted) {
#if 0
        // Show alert
        NSAlert*    alert;
        alert = [[NSAlert alloc] init];
        [alert autorelease];
        [alert addButtonWithTitle:NSLocalizedString(@"Yes", nil)];
        [alert addButtonWithTitle:NSLocalizedString(@"No", nil)];
        [alert setMessageText:NSLocalizedString(@"Are you sure you want to send a form again?", nil)];
        [alert setInformativeText:NSLocalizedString(@"Are you sure you want to send a form again?", nil)];
        [alert setAlertStyle:NSWarningAlertStyle];
        
        [alert beginSheetModalForWindow:[self window] 
                modalDelegate:self 
                didEndSelector:@selector(_didEndResubmitPanel:returnCode:contextInfo:) 
                contextInfo:listener];
        return;
#endif
    }
    // For other
    if (navigationType == WebNavigationTypeOther) {
        if ([self _webView:webView 
                decideTabPolicyAction:nil 
                request:request 
                frameName:nil 
                decisionListener:listener])
        {
            return;
        }
    }
    
#if 0
    // Use this navigation
    if ([self selectedWebView] != webView) {
        // Wait a moment
        [(NSObject*)listener performSelector:@selector(use) withObject:nil afterDelay:0.1];
        return;
    }
#endif
    
    // If listener is nil, it should come from representation view
    if (!listener) {
        [self openRequest:request];
    }
    
    [listener use];
}

- (void)webView:(WebView*)webView 
        decidePolicyForNewWindowAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        newFrameName:(NSString*)frameName 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // For tabbed browsing
    if ([self _webView:webView 
            decideTabPolicyAction:info 
            request:request 
            frameName:frameName 
            decisionListener:listener])
    {
        return;
    }
    
    // Check browse mode
    if ([self _webView:webView 
        decideBrowserModePolicyAction:info 
        request:request 
        frameName:frameName 
        decisionListener:listener])
    {
        return;
    }
    
    // Find named frame
    NSEnumerator*   enumerator;
    id              document;
    enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator];
    while (document = [enumerator nextObject]) {
        if (![document isKindOfClass:[SRMainDocument class]]) {
            continue;
        }
        
        // Get window controller
        SRMainWindowController* windowController;
        windowController = [document mainWindowController];
        if (!windowController) {
            continue;
        }
        
        // Check all web view
        SRTabView*      srTabView;
        NSEnumerator*   tabViewItemEnumerator;
        NSTabViewItem*  tabViewItem;
        srTabView = [windowController srTabView];
        tabViewItemEnumerator = [[[srTabView tabView] tabViewItems] objectEnumerator];
        while (tabViewItem = [tabViewItemEnumerator nextObject]) {
            
            // Find named frame
            WebView*    webView;
            WebFrame*   namedFrame;
            webView = [tabViewItem view];
            if (!webView) {
                continue;
            }
            
            namedFrame = [[webView mainFrame] srFindFrameNamed:frameName];
            
            // Open in named frame
            if (namedFrame) {
                [listener ignore];
                [namedFrame loadRequest:request];
                [[srTabView tabView] selectTabViewItem:tabViewItem];
                
                // Make window front
                [[windowController window] makeKeyAndOrderFront:self];
                
                return;
            }
        }
    }
    
    // Open target link in tab
    if ([defaults boolForKey:SRTabEnableTabbedBrowsing] && 
        [defaults boolForKey:SRTabTargetLinkUseTab])
    {
        [self openRequest:request 
                frameName:frameName 
                groupName:nil 
                withOpenAction:SROpenActionTypeFromModifierFlags(NSCommandKeyMask)];
        
        [listener ignore];
        return;
    }
    
    // Always ignore
    [listener ignore];
    
    // Open in new background
    [self openInNewWindowRequest:request frameName:frameName groupName:nil];
}

- (void)_didEndResubmitPanel:(NSAlert*)alert 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Cast context info
    id<WebPolicyDecisionListener>   listener;
    listener = (id<WebPolicyDecisionListener>)contextInfo;
    
    if (returnCode == NSAlertFirstButtonReturn) {
        [listener use];
        return;
    }
    [listener ignore];
}

// - webView:unableToImplementPolicy:error:forURL:frame:

//--------------------------------------------------------------//
#pragma mark -- WebUIDelegate --
//--------------------------------------------------------------//

- (WebView*)webView:(WebView*)webView
        createWebViewWithRequest:(NSURLRequest*)request
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get tab browsing
    BOOL    isTabbedBrowsing;
    isTabbedBrowsing = [defaults boolForKey:SRTabEnableTabbedBrowsing];
    if (isTabbedBrowsing) {
        // Get browse mode
        int browseMode;
        browseMode = [defaults integerForKey:SRBrowseMode];
        switch (browseMode) {
        case SRDefaultBrowseMode: {
            // Check target link opening way
            BOOL    doseTargetLinkUseTab;
            doseTargetLinkUseTab = [defaults boolForKey:SRTabTargetLinkUseTab];
            if (doseTargetLinkUseTab) {
                WebView*    webView;
                webView = [self openInNewTabRequest:request frameName:nil groupName:nil select:YES];
                return webView;
            }
            
            break;
        }
        case SRAlwaysInSameTabBrowseMode: {
            [self openRequest:request];
            return webView;
        }
        case SRAlwaysInNewTabBrowseMode: {
            if (isTabbedBrowsing) {
                WebView*    webView;
                webView = [self openInNewTabRequest:request frameName:nil groupName:nil select:YES];
                return webView;
            }
            
            break;
        }
        case SRAlwaysInNewBackgroundTabBrowseMode: {
            if (isTabbedBrowsing) {
                WebView*    webView;
                webView = [self openInNewTabRequest:request frameName:nil groupName:nil select:NO];
                return webView;
            }
            
            break;
        }
        }
    }
    
    // Create new document
    SRDocumentController*   documentController;
    SRMainDocument*         document;
    documentController = [NSDocumentController sharedDocumentController];
    document = [documentController makeUntitledDocumentOfType:SRHTMLDocumentType];
    [documentController addDocument:document];
    
    // Make window controllers
    [document makeWindowControllers];
    
    // Get main window controller
    SRMainWindowController* mainWindowController;
    mainWindowController = [document mainWindowController];
    
    // Open request in new window, 
    // but not show it yet
    [mainWindowController openRequest:request];
    
    return [mainWindowController selectedWebView];
}

- (void)webViewClose:(WebView*)webView
{
    if ([_srTabView numberOfItems] < 2) {
        // Get document from web view
        SRMainDocument* document;
        document = [[NSDocumentController sharedDocumentController] 
                documentForWindow:[webView window]];
        
        // Close window
        [document close];
    } else {
        // Search Tab for webView
        NSArray*    tabViewItems=[[_srTabView tabView] tabViewItems];
        int     i,cnt=[tabViewItems count];
        for(i=0;i<cnt;i++){
            // Get web view
            NSTabViewItem*  tabViewItem=[tabViewItems objectAtIndex:i];
            NSView*    aView=[tabViewItem view];
            if(aView==webView){
                [_srTabView removeItemAtIndex:i];
                return;
            }
        }
    }
}

// - webViewIsResizable
// - webView:setResizable

- (void)webView:(WebView*)webView 
        setFrame:(NSRect)frame
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get browse mode
    int browseMode;
    browseMode = [defaults integerForKey:SRBrowseMode];
    if (browseMode != SRDefaultBrowseMode) {
        // Ignore it
        return;
    }
    
    // Check opened in tab
    BOOL    doseTargetLinkUseTab;
    doseTargetLinkUseTab = [defaults boolForKey:SRTabTargetLinkUseTab];
    if (doseTargetLinkUseTab) {
        // Ignore it
        return;
    }
    if ([_srTabView numberOfItems] > 1) {
        // Ignore it
        return;
    }
    
    // Set flag
    _isResizedByJavaScript = YES;
    
    // Resize window
    [[self window] setFrame:frame display:NO];
}

- (NSRect)webViewFrame:(WebView*)webView
{
    return [[webView window] frame];
}

//- webView:setContentRect:
//- webViewContentRect

//- (void)webViewFocus:(WebView*)webView
//- (void)webViewUnfocus:(WebView*)webView

- (void)webViewShow:(WebView*)webView
{
    // Get document from web view
    SRMainDocument* document;
    document = [[NSDocumentController sharedDocumentController] 
            documentForWindow:[webView window]];
    
    // Show window
    [document showWindows];
}

- (NSResponder*)webViewFirstResponder:(WebView*)sender
{
    return [[self window] firstResponder];
}

- (void)webView:(WebView*) 
        makeFirstResponder:(NSResponder*)responder
{
    [[self window] makeFirstResponder:responder];
}

- (void)webView:(WebView*)webView 
        mouseDidMoveOverElement:(NSDictionary*)info 
        modifierFlags:(unsigned int)flags
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    NSString*   status = nil;
    
    // Get URL string
    NSString*   URLString;
    URLString = [[info objectForKey:WebElementLinkURLKey] _web_userVisibleString];
    
    if (URLString) {
        // Get scheme
        NSURL*      URL;
        NSString*   scheme;
        URL = [NSURL _web_URLWithUserTypedString:URLString];
        scheme = [URL scheme];
        
        // For mailto:
        if ([scheme isEqualToString:@"mailto"]) {
            status = NSLocalizedString(UTF8STR("Send e-mail to “%@”"), nil);
            goto finalize;
        }
        
        // Modifier flag is specified
        if (flags) {
            // Check key mask
            unsigned int    cmdFlag, optionFlag, shiftFlag;
            cmdFlag = flags & NSCommandKeyMask;
            optionFlag = flags & NSAlternateKeyMask;
            shiftFlag = flags & NSShiftKeyMask;
            
            // Download linked file
            if (!cmdFlag && optionFlag && !shiftFlag) {
                status = NSLocalizedString(UTF8STR("Download “%@”"), nil);
                goto finalize;
            }
            
            // Check tab browsing
            BOOL    enableTabbedBrowsing, selectNewTabs;
            enableTabbedBrowsing = [defaults boolForKey:SRTabEnableTabbedBrowsing];
            selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
            
            // If tabbed browsing is enable
            if (enableTabbedBrowsing) {
                // If select new tabs
                if (selectNewTabs) {
                    // Open in new tabs and select it
                    if (cmdFlag && !optionFlag && !shiftFlag) {
                        status = NSLocalizedString(UTF8STR("Open “%@” in a new tab"), nil);
                        goto finalize;
                    }
                    // Open in new tabs
                    else if (cmdFlag && !optionFlag && shiftFlag) {
                        status = NSLocalizedString(UTF8STR("Open “%@” in a new background tab"), nil);
                        goto finalize;
                    }
                    // Open in new window
                    else if (cmdFlag && optionFlag && !shiftFlag) {
                        status = NSLocalizedString(UTF8STR("Open “%@” in a new window"), nil);
                        goto finalize;
                    }
                    // Open in new window in background
                    else if (cmdFlag && optionFlag && shiftFlag) {
                        status = NSLocalizedString(UTF8STR("Open “%@” in a new background window"), nil);
                        goto finalize;
                    }
                }
                // If not select new tabs
                else {
                    // Open in new tabs
                    if (cmdFlag && !optionFlag && !shiftFlag) {
                        status = NSLocalizedString(UTF8STR("Open “%@” in a new background tab"), nil);
                        goto finalize;
                    }
                    // Open in new tabs and select it
                    else if (cmdFlag && !optionFlag && shiftFlag) {
                        status = NSLocalizedString(UTF8STR("Open “%@” in a new tab"), nil);
                        goto finalize;
                    }
                    // Open in new window in background
                    else if (cmdFlag && optionFlag && !shiftFlag) {
                        status = NSLocalizedString(UTF8STR("Open “%@” in a new background window"), nil);
                        goto finalize;
                    }
                    // Open in new window
                    else if (cmdFlag && optionFlag && shiftFlag) {
                        status = NSLocalizedString(UTF8STR("Open “%@” in a new window"), nil);
                        goto finalize;
                    }
                }
            }
            // If tabbed browsing is disable
            else {
                // Open in new window
                if (cmdFlag && !shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new window"), nil);
                    goto finalize;
                }
                // Open in new window in background
                else if (cmdFlag && shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new background window"), nil);
                    goto finalize;
                }
            }
        }
        
        // Get target
        WebFrame*   target;
        target = [info objectForKey:WebElementLinkTargetFrameKey];
        
        // Target is in this frame
        if (target) {
            // Go to URL
            status = NSLocalizedString(UTF8STR("Go to “%@”"), nil);
            goto finalize;
        }
        else {
            if([defaults boolForKey:SRTabEnableTabbedBrowsing] && [defaults boolForKey:SRTabTargetLinkUseTab]){
                //target link use tab
                status = NSLocalizedString(UTF8STR("Open “%@” in a new tab"), nil);
            }else{
                // Open page in new window
                status = NSLocalizedString(UTF8STR("Open “%@” in a new window"), nil);
            }
            goto finalize;
        }
    }
    
finalize:
    if (status) {
        [self setValue:[NSString stringWithFormat:status, URLString]
                forKey:SRMainWebViewStatus];
    }
    else {
        [self setValue:@"" forKey:SRMainWebViewStatus];
    }
}

- (NSMenuItem*)_createSerachMenuItemByEngine:(NSDictionary*)engine 
        searchString:(NSString*)searchString
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get menu item for finding by search engine
    NSMenu*     menu;
    NSMenuItem* menuItem;
    menu = [SRContextMenu webViewContextMenu];
    menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SRFindBySearchEngineTag target:nil];
    
    // Get engine name
    NSString*   engineName;
    engineName = [engine objectForKey:@"title"];
    
    // Craate search URL string
    NSString*   URLString;
    URLString = SRCreateSearchURLStringFromSearchEngine(searchString, engine);
    
    // Create menu title
    NSString*   menuTitle;
    menuTitle = [NSString stringWithFormat:NSLocalizedString(@"Search using %@", nil), engineName];
    
    // Create search item
    NSDictionary*   searchItem;
    searchItem = [NSDictionary dictionaryWithObjectsAndKeys:
            searchString, @"string", 
            engineName, @"title", 
            URLString, @"URLString", 
            nil];
    
    // Create 'Search by serach engine' menu item
    menuItem = [[NSMenuItem alloc] initWithTitle:menuTitle 
            action:[menuItem action] 
            keyEquivalent:[menuItem keyEquivalent]];
    [menuItem setRepresentedObject:searchItem];
    
    // Get icon
    if ([defaults boolForKey:SRIconUseFavicon] && 
        [defaults boolForKey:SRIconUseFaviconSearchEngine])
    {
        NSString*   URLString;
        URLString = [engine objectForKey:@"templateURL"];
        
        NSImage*    icon;
        icon = [[WebIconDatabase sharedIconDatabase] iconForURL:URLString withSize:NSMakeSize(16, 16)];
        if (icon) {
            [menuItem setImage:icon];
        }
    }
    
    return menuItem;
}

- (NSArray*)webView:(WebView*)webView 
        contextMenuItemsForElement:(NSDictionary*)element 
        defaultMenuItems:(NSArray*)defaultMenuItems
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get element info
    NSURL*  linkURL;
    NSURL*  imageURL;
    BOOL    isSelected;
    
    linkURL = [element objectForKey:WebElementLinkURLKey];
    imageURL = [element objectForKey:WebElementImageURLKey];
    isSelected = [[element objectForKey:WebElementIsSelectedKey] boolValue];
    
    // Get modifier key flag
    unsigned int    modifierFlags;
    unsigned int    cmdFlag, optionFlag, shiftFlag;
    modifierFlags = [[NSApp currentEvent] modifierFlags];
    cmdFlag = modifierFlags & NSCommandKeyMask;
    optionFlag = modifierFlags & NSAlternateKeyMask;
    shiftFlag = modifierFlags & NSShiftKeyMask;
    
    // Check tab browsing
    BOOL    enableTabbedBrowsing, selectNewTabs;
    enableTabbedBrowsing = [defaults boolForKey:SRTabEnableTabbedBrowsing];
    selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
    
    // Create array
    NSMutableArray* items;
    items = [NSMutableArray array];
    
    // Get web view context menu
    NSMenu*     menu;
    NSMenuItem* menuItem;
    menu = [SRContextMenu webViewContextMenu];
    
    NSMutableArray* allowedDefaultMenuItems;
    allowedDefaultMenuItems = [NSMutableArray array];
    
    // Check default menu items
    NSEnumerator*   enumerator;
    enumerator = [defaultMenuItems objectEnumerator];
    while (menuItem = [enumerator nextObject]) {
        int tag;
        tag = [menuItem tag];
        
        // For Spotlight, Google Search, and looking up in dictionary
        if (tag >= 1000) {
            // Do not include them
            continue;
        }
        
        // For separator
        if ([menuItem isSeparatorItem]) {
            if ([allowedDefaultMenuItems count] == 0 || 
                [[allowedDefaultMenuItems lastObject] isSeparatorItem])
            {
                continue;
            }
        }
        [allowedDefaultMenuItems addObject:menuItem];
        
        // For 'Open the link in a new window'
        if (tag == WebMenuItemTagOpenLinkInNewWindow) {
            // Change title, action and target
            [menuItem setAction:@selector(openLinkInNewWindowAction:)];
            [menuItem setTarget:nil];
            [menuItem setRepresentedObject:linkURL];
            continue;
        }
        
        // For 'Open the image in a new window'
        if (tag == WebMenuItemTagOpenImageInNewWindow) {
            // Change action and target
            [menuItem setAction:@selector(openLinkInNewWindowAction:)];
            [menuItem setTarget:nil];
            [menuItem setRepresentedObject:imageURL];
            continue;
        }
    }
    
    // Get selected links
    DOMRange*       range;
    DOMNode*        startContainer = nil;
    NSMutableArray* linkURLStrings;
    range = [webView selectedDOMRange];
    startContainer = [range startContainer];
    linkURLStrings = [NSMutableArray array];
    if (startContainer) {
        SRDOMRangeFilter*   filter;
        DOMNodeIterator*    iterator;
        DOMNode*            node;
        filter = [[SRDOMRangeFilter alloc] initWithDOMRange:range nodeName:@"A"];
        iterator = [[[webView mainFrame] DOMDocument] createNodeIterator:startContainer :DOM_SHOW_ALL :filter :NO];
        while (node = [iterator nextNode]) {
            // Get URL string
            NSString*   URLString = nil;
            if ([node respondsToSelector:@selector(href)]) {
                URLString = [(DOMHTMLLinkElement*)node href];
            }
            if (URLString) {
                // Check scheme
                NSString*   scheme;
                scheme = [[NSURL URLWithString:URLString] scheme];
                if (![scheme isEqualToString:@"javascript"]) {
                    [linkURLStrings addObject:URLString];
                }
            }
        }
        [filter release];
    }
    
    // Link is contained
    if (linkURL) {
        // Copy default menu item
        [items addObjectsFromArray:allowedDefaultMenuItems];
        
        // Add 'Add Link to Bookmarks'
        menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SRAddLinkToBokkmarksTag target:nil];
        [menuItem setRepresentedObject:element];
        [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:WebMenuItemTagOpenLinkInNewWindow];
        
        // Check scheme
        if (![[linkURL scheme] isEqualToString:@"javascript"]) {
            // For tabbed browsing
            if (enableTabbedBrowsing) {
                if ((selectNewTabs && !shiftFlag) || 
                    (!selectNewTabs && shiftFlag))
                {
                    // Add 'Open Link in New Tab'
                    menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenLinkInNewTabTag target:nil];
                    [menuItem setRepresentedObject:linkURL];
                    [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:WebMenuItemTagOpenLinkInNewWindow];
                    
                    // Add 'Open All Links in New Tabs'
                    if ([linkURLStrings count] > 1) {
                        menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenAllLinksInNewTabsTag target:nil];
                        [menuItem setTitle:[NSString stringWithFormat:[menuItem title], [linkURLStrings count]]];
                        [menuItem setRepresentedObject:linkURLStrings];
                        [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:SROpenLinkInNewTabTag];
                    }
                }
                else {
                    // Add 'Open Link in New Background Window'
                    menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenLinkInNewBackgroundWindowTag target:nil];
                    [menuItem setRepresentedObject:linkURL];
                    [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:WebMenuItemTagOpenLinkInNewWindow];
                    
                    // Add 'Open Link in New Background Tab'
                    menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenLinkInNewBackgroundTabTag target:nil];
                    [menuItem setRepresentedObject:linkURL];
                    [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:SROpenLinkInNewBackgroundWindowTag];
                    
                    // Add 'Open All Links in New Background Tabs'
                    if ([linkURLStrings count] > 1) {
                        menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenAllLinksInNewBackgroundTabsTag target:nil];
                        [menuItem setTitle:[NSString stringWithFormat:[menuItem title], [linkURLStrings count]]];
                        [menuItem setRepresentedObject:linkURLStrings];
                        [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:SROpenLinkInNewBackgroundTabTag];
                    }
                    
                    // Remove 'Open Link in New Window'
                    [SRContextMenu removeMenuItem:menuItem fromItems:items ofTag:WebMenuItemTagOpenLinkInNewWindow];
                }
            }
            else {
                if (shiftFlag) {
                    // Add 'Open Link in New Background Window'
                    menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenLinkInNewBackgroundWindowTag target:nil];
                    [menuItem setRepresentedObject:linkURL];
                    [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:WebMenuItemTagOpenLinkInNewWindow];
                    
                    // Remove 'Open Link in New Window'
                    [SRContextMenu removeMenuItem:menuItem fromItems:items ofTag:WebMenuItemTagOpenLinkInNewWindow];
                }
            }
        }
    }
    
    // Link is not contained
    else {
        // Text is selected
        if (isSelected) {
            // Copy default menu item
            [items addObjectsFromArray:allowedDefaultMenuItems];
            
            // Get selected string
            NSView*         documentView;
            NSString*       selectedString = nil;
            documentView = [[[element objectForKey:WebElementFrameKey] frameView] documentView];
            if ([documentView respondsToSelector:@selector(selectedString)]) {
                selectedString = [documentView performSelector:@selector(selectedString)];
            }
            
            // Get current search engine
            NSDictionary*   currentEngine;
            currentEngine = [_searchFieldController currentEngine];
            
            if (selectedString && currentEngine) {
                // Add 'Search by serach engine'
                NSMenuItem* searchMenuItem;
                searchMenuItem = [self _createSerachMenuItemByEngine:currentEngine 
                        searchString:selectedString];
                [items addObject:searchMenuItem];
                
                // Find other search engines
                NSMutableArray* otherEngines;
                NSArray*        engines;
                NSEnumerator*   enumerator;
                NSDictionary*   engine;
                otherEngines = [NSMutableArray array];
                engines = [[SRSearchEnginesManager sharedInstance] searchEngines];
                enumerator = [engines objectEnumerator];
                while (engine = [enumerator nextObject]) {
                    if ([[engine objectForKey:@"isUsing"] boolValue] && 
                        ![[engine objectForKey:@"title"] isEqualToString:[currentEngine objectForKey:@"title"]])
                    {
                        [otherEngines addObject:engine];
                    }
                }
                
                // Add search by other engines menu
                if ([otherEngines count] > 0) {
                    // Add 'Search by other'
                    menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SRFindByOtherSearchEngineTag target:nil];
                    [items addObject:menuItem];
                    
                    // Create submenu
                    NSMenu* otherEnginesSubmenu;
                    otherEnginesSubmenu = [[NSMenu alloc] initWithTitle:@"Other engine"];
                    [menuItem setSubmenu:otherEnginesSubmenu];
                    
                    enumerator = [otherEngines objectEnumerator];
                    while (engine = [enumerator nextObject]) {
                        // Add 'Search by serach engine'
                        NSMenuItem* searchMenuItem;
                        searchMenuItem = [self _createSerachMenuItemByEngine:engine 
                                searchString:selectedString];
                        [otherEnginesSubmenu addItem:searchMenuItem];
                    }
                }
            }
            
            // For link nodes
            if ([linkURLStrings count] > 1) {
                [items addObject:[NSMenuItem separatorItem]];
                
                if ((selectNewTabs && !shiftFlag) || 
                    (!selectNewTabs && shiftFlag))
                {
                    // Add open links in tabs
                    menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenAllLinksInNewTabsTag target:nil];
                    [menuItem setTitle:[NSString stringWithFormat:[menuItem title], [linkURLStrings count]]];
                    [menuItem setRepresentedObject:linkURLStrings];
                    [items addObject:menuItem];
                }
                else {
                    // Add open links in tabs
                    menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenAllLinksInNewBackgroundTabsTag target:nil];
                    [menuItem setTitle:[NSString stringWithFormat:[menuItem title], [linkURLStrings count]]];
                    [menuItem setRepresentedObject:linkURLStrings];
                    [items addObject:menuItem];
                }
            }
        }
        
        // Text is not selected
        else {
            // Copy default menu item
            [items addObjectsFromArray:allowedDefaultMenuItems];
            
            // Add 'View Soruce'
            if ([[webView mainFrame] dataSource]) {
                if ([items count] > 0 && ![[items lastObject] isSeparatorItem]) {
                    [items addObject:[NSMenuItem separatorItem]];
                }
                
                menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SRViewSourceTag target:nil];
                [menuItem setRepresentedObject:[element objectForKey:WebElementFrameKey]]; // WebFrame
                [items addObject:menuItem];
            }
        }
    }
    
    // Image is selected
    if (imageURL) {
        // For tabbed browsing
        if (enableTabbedBrowsing) {
            if ((selectNewTabs && !shiftFlag) || 
                (!selectNewTabs && shiftFlag))
            {
                // Add 'Open Image in New Tab'
                menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenImageInNewTabTag target:nil];
                [menuItem setRepresentedObject:imageURL];
                [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:WebMenuItemTagOpenImageInNewWindow];
            }
            else {
                // Add 'Open Image in New Background Window'
                menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenImageInNewBackgroundWindowTag target:nil];
                [menuItem setRepresentedObject:imageURL];
                [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:WebMenuItemTagOpenImageInNewWindow];
                
                // Add 'Open Image in New Background Tab'
                menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenImageInNewBackgroundTabTag target:nil];
                [menuItem setRepresentedObject:imageURL];
                [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:SROpenImageInNewBackgroundWindowTag];
                
                // Remove 'Open Image in New Window'
                [SRContextMenu removeMenuItem:menuItem fromItems:items ofTag:WebMenuItemTagOpenImageInNewWindow];
            }
        }
        else {
            if (shiftFlag) {
                // Add 'Open Image in New Background Window'
                menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenImageInNewBackgroundWindowTag target:nil];
                [menuItem setRepresentedObject:imageURL];
                [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:WebMenuItemTagOpenImageInNewWindow];
                
                // Remove 'Open Image in New Window'
                [SRContextMenu removeMenuItem:menuItem fromItems:items ofTag:WebMenuItemTagOpenImageInNewWindow];
            }
        }
    }
    
    return items;
}

- (void)webView:(WebView*)sender 
        runJavaScriptAlertPanelWithMessage:(NSString*)message
{
    // Create alert panel
    NSAlert*    alert;
    alert = [[NSAlert alloc] init];
    [alert autorelease];
    
    // Configure alert panel
    [alert setMessageText:@"JavaScript"];
    [alert setInformativeText:message];
    [alert addButtonWithTitle:@"OK"];
    
    // Beep
    if ([[NSUserDefaults standardUserDefaults] boolForKey:SREnableJavaScriptDialogAlert]) {
        NSBeep();
    }
    
    // Display alert panel
    [alert runModal];
}

- (BOOL)webView:(WebView*)sender 
        runJavaScriptConfirmPanelWithMessage:(NSString*)message
{
    // Create alert panel
    NSAlert*    alert;
    alert = [[NSAlert alloc] init];
    [alert autorelease];
    
    // Configure alert panel
    [alert setMessageText:@"JavaScript"];
    [alert setInformativeText:message];
    [alert addButtonWithTitle:@"OK"];
    [alert addButtonWithTitle:@"Cancel"];
    
    // Display alert panel
    int result;
    result = [alert runModal];
    
    // Return result
    return result == NSAlertFirstButtonReturn;
}

- (NSString*)webView:(WebView*)sender 
        runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt 
        defaultText:(NSString*)defaultText
{
    // Create panel for text input
    SRTextInputPanelManager*    inputMgr;
    inputMgr = [[SRTextInputPanelManager alloc] init];
    [inputMgr autorelease];
    
    // Configure text input panel
    [inputMgr setPrompt:prompt];
    [inputMgr setMessage:defaultText];
    
    // Display text input panel
    int result;
    result = [inputMgr runModal];
    
    // Return result
    if (result == SRTextInputOK) {
        return [inputMgr message];
    }
    else {
        return nil;
    }
}

- (void)webView:(WebView*)sender 
        runOpenPanelForFileButtonWithResultListener:(id<WebOpenPanelResultListener>)listener
{
    // Create open panel
    NSOpenPanel*    openPanel;
    openPanel = [NSOpenPanel openPanel];
    
    // Display open panel
    int result;
    result = [openPanel runModal];
    
    // Return result
    if (result == NSOKButton) {
        [listener chooseFilename:[openPanel filename]];
    }
    else {
        [listener cancel];
    }
}

- (void)webView:(WebView*)webView 
        setStatusText:(NSString*)text
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Ignore JavaScript status text if it is disabled
    if (![defaults boolForKey:SREnableJavaScirptStatusMessage]) {
        return;
    }
    
    // For selected web view
    if ([self selectedWebView] == webView) {
        [self setValue:text forKey:SRMainWebViewStatus];
    }
}

- (NSString*)webViewStatusText:(WebView*)webView
{
    return [self valueForKey:SRMainWebViewStatus];
}

- (BOOL)webViewAreToolbarsVisible:(WebView*)webView
{
    return [[[self window] toolbar] isVisible];
}

- (void)webView:(WebView*)webView 
        setToolbarsVisible:(BOOL)visible
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get browse mode
    int browseMode;
    browseMode = [defaults integerForKey:SRBrowseMode];
    if (browseMode != SRDefaultBrowseMode) {
        // Ignore it
        return;
    }
    
    // Check opened in tab
    BOOL    doseTargetLinkUseTab;
    doseTargetLinkUseTab = [defaults boolForKey:SRTabTargetLinkUseTab];
    if (doseTargetLinkUseTab) {
        // Ignore it
        return;
    }
    if ([_srTabView numberOfItems] > 1) {
        // Ignore it
        return;
    }
    
    // Set flag, it might be requested by Java Script
    _isResizedByJavaScript = YES;
    
    // Set visiblity of toolbar, bookmarks bar, and tabs
    [[[self window] toolbar] setAutosavesConfiguration:NO];
    [[[self window] toolbar] setVisible:visible];
    [self setBookmarksBarVisible:visible display:NO];
    [self setTabVisible:visible display:NO];
}

- (BOOL)webViewIsStatusBarVisible:(WebView*)webView
{
    return [self isStatusBarVisible];
}

- (void)webView:(WebView*)webView 
        setStatusBarVisible:(BOOL)visible
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get browse mode
    int browseMode;
    browseMode = [defaults integerForKey:SRBrowseMode];
    if (browseMode != SRDefaultBrowseMode) {
        // Ignore it
        return;
    }
    
    // Check opened in tab
    BOOL    doseTargetLinkUseTab;
    doseTargetLinkUseTab = [defaults boolForKey:SRTabTargetLinkUseTab];
    if (doseTargetLinkUseTab) {
        // Ignore it
        return;
    }
    if ([_srTabView numberOfItems] > 1) {
        // Ignore it
        return;
    }
    
    // Set flag, it might be requested by Java Script
    _isResizedByJavaScript = YES;
    
    [self setStatusBarVisible:visible display:NO];
}

//--------------------------------------------------------------//
#pragma mark -- WebView notification --
//--------------------------------------------------------------//

- (void)webViewProgressStarted:(NSNotification*)notification
{
    // Get notification web view
    WebView*    webView;
    webView = [notification object];
    
    // Compare with forward one
    if ([self selectedWebView] == webView) {
        [self setValue:[NSNumber numberWithBool:YES] forKey:SRMainWebViewIsLoading];
        [self setValue:[NSNumber numberWithBool:NO] forKey:SRMainWebViewIsIndeterminate];
        [self setValue:[NSNumber numberWithDouble:0.0] 
                forKey:SRMainWebViewEstimatedProgress];
    }
}

- (void)webViewProgressEstimateChanged:(NSNotification*)notification
{
    // Get notification web view
    WebView*    webView;
    webView = [notification object];
    
    // Compare with forward one
    if ([self selectedWebView] == webView) {
        [self setValue:[NSNumber numberWithDouble:[webView estimatedProgress]] 
                forKey:SRMainWebViewEstimatedProgress];
    }
}

- (void)webViewProgressFinished:(NSNotification*)notification
{
    // Get notification web view
    WebView*    webView;
    webView = [notification object];
    
    // Compare with forward one
    if ([self selectedWebView] == webView) {
        [self setValue:[NSNumber numberWithBool:NO] forKey:SRMainWebViewIsLoading];
        [self setValue:[NSNumber numberWithBool:NO] forKey:SRMainWebViewIsIndeterminate];
        [self setValue:[NSNumber numberWithDouble:0.0] 
                forKey:SRMainWebViewEstimatedProgress];
    }
}

@end

