/*
SRPageController.m

Author: Makoto Kinoshita

Copyright 2004-2006 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 "SRSearchEngine.h"

#import "SRAppController.h"
#import "SRBrowserController.h"
#import "SRBrowserDocument.h"
#import "SRDefaultKeys.h"
#import "SRDocumentController.h"
#import "SRDownloadManager.h"
#import "SRPageController.h"
#import "SRSearchFieldController.h"

#import "SRWebViewContextMenu.h"

#import "SRPreference.h"
#import "SRPrefDefaultKeys.h"

#import "SRShelfController.h"
#import "SRShelfRepresentation.h"

#import "SRWebView.h"

// Notifications
NSString*   SRPageDidStartProvisionalLoad = @"SRPageDidStartProvisionalLoad";
NSString*   SRPageDidCommitLoad = @"SRPageDidCommitLoad";
NSString*   SRPageDidFinishLoad = @"SRPageDidFinishLoad";
NSString*   SRPageDidReceiveServerRedirect = @"SRPageDidReceiveServerRedirect";
NSString*   SRPageTitleReceived = @"SRPageTitleReceived";
NSString*   SRPageIconReceived = @"SRPageIconReceived";
NSString*   SRPageProgressStart = @"SRPageProgressStart";
NSString*   SRPageProgressEstimateChanged = @"SRPageProgressEstimateChanged";
NSString*   SRPageProgressFinish = @"SRPageProgressFinish";

static NSArray* _templateKeys = nil;

@interface SRPageController (Private)
- (void)_setUserAgentOfWebView:(WebView*)webView;
@end

@implementation SRPageController

//--------------------------------------------------------------//
#pragma -- Initialize --
//--------------------------------------------------------------//

- (id)initWithBrowserController:(SRBrowserController*)browserController 
        label:(NSString*)label frameName:(NSString*)frameName groupName:(NSString*)groupName
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize instance varibles
    _browserController = browserController;
    _documentViewNeedsUpdate = YES;
    _webViewNeedsUpdate = YES;
    
    // Create web view
    WebView*    webView;
    webView = [[SRWebView alloc] initWithFrame:NSZeroRect 
            frameName:frameName groupName:groupName];
    
    [webView autorelease];
    [webView setHostWindow:[_browserController window]];  // For Plug-in, JavaScript
    [webView setFrameLoadDelegate:self];
    [webView setResourceLoadDelegate:self];
    [webView setPolicyDelegate:self];
    [webView setUIDelegate:self];
    [webView setDownloadDelegate:[SRDownloadManager sharedInstance]];
    [webView setPreferences:[SRPreference webPreferences]];
    
    // Set user agent
    [self _setUserAgentOfWebView:webView];
    
    // Dummy
    WebPreferences* pref;
    int             defaultFontSize;
    pref = [webView preferences];
    defaultFontSize = [pref defaultFontSize];
    [pref setDefaultFontSize:defaultFontSize + 1];
    [pref setDefaultFontSize:defaultFontSize];
    
    // Create tab view item
    _tabViewItem = [[HMTabViewItem alloc] initWithIdentifier:@""];
    [_tabViewItem setLabel:label];
    [_tabViewItem setView:webView];
    
    // Register notification
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(webViewProgressStarted:) 
            name:WebViewProgressStartedNotification object:webView];
    [center addObserver:self selector:@selector(webViewProgressEstimateChanged:) 
            name:WebViewProgressEstimateChangedNotification object:webView];
    [center addObserver:self selector:@selector(webViewProgressFinished:) 
            name:WebViewProgressFinishedNotification object:webView];
    
    // Create template keys
    if (!_templateKeys) {
        _templateKeys = [[NSArray arrayWithObjects:
                @"$ERROR_TITLE$", @"$ERROR_MESSAGE$", nil] retain];
    }
    
    return self;
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    // Stop loading
    WebView* webView;
    webView = [self webView];
    [webView stopLoading:self];
    
    // To close web view
	[webView setHostWindow:nil];
    [webView setFrameLoadDelegate:nil];
    [webView setResourceLoadDelegate:nil];
    [webView setPolicyDelegate:nil];
    [webView setUIDelegate:nil];
    [webView setDownloadDelegate:nil];
    
    // Close web view
    // I'm not sure I need to call it...
    [webView _close];
    
    [_documentViewImage release], _documentViewImage = nil;
    [_documentViewImageRep release], _documentViewImageRep = nil;
    [_webViewImage release], _webViewImage = nil;
    [_webViewImageRep release], _webViewImageRep = nil;
    [_tabViewItem release], _tabViewItem = nil;
    [_shelfIdWillOpen release], _shelfIdWillOpen = nil;
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Controller --
//--------------------------------------------------------------//

- (SRBrowserController*)browserController
{
    return _browserController;
}

//--------------------------------------------------------------//
#pragma mark -- Tab view item --
//--------------------------------------------------------------//

- (HMTabViewItem*)tabViewItem
{
    return _tabViewItem;
}

//--------------------------------------------------------------//
#pragma mark -- Web view --
//--------------------------------------------------------------//

- (WebView*)webView
{
    return (WebView*)[_tabViewItem view];
}

//--------------------------------------------------------------//
#pragma mark -- Opening page with request --
//--------------------------------------------------------------//

- (WebView*)openRequest:(NSURLRequest*)request
{
    // Get web view
    WebView*    webView;
    webView = [self webView];
    
    // Evaluate JavaScript
    NSString*   URLString;
    URLString = [[request URL] absoluteString];
    if ([URLString hasPrefix:@"javascript:"]) {
        NSString*   script;
        NSString*   result;
        script = [URLString substringWithRange:NSMakeRange(11, [URLString length] - 11)]; // Remove 'javascript:'
        script = [script stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        result = [webView stringByEvaluatingJavaScriptFromString:script];
        
        return webView;
    }
    
    // Open request
    [[webView mainFrame] loadRequest:request];
    
#if 0
    // Set icon
    if ([self selectedWebView] == webView) {
        [[self URLComboBox] setImage:[[WebIconDatabase sharedIconDatabase] 
                defaultIconWithSize:NSMakeSize(16, 16)]];
    }
#endif
    
    return webView;
}

//--------------------------------------------------------------//
#pragma mark -- Page info --
//--------------------------------------------------------------//

- (float)loadingTime
{
    return _loadingTime;
}

//--------------------------------------------------------------//
#pragma mark -- Page image --
//--------------------------------------------------------------//

static float    _ratio = 1.0f;

- (void)_updateDocumentViewImage
{
    // Check needs update
    if (!_documentViewNeedsUpdate) {
        return;
    }
    _documentViewNeedsUpdate = NO;
    
    // Get web view and document view
    WebView*    webView;
    id          documentView;
    webView = [self webView];
    documentView = [[[webView mainFrame] frameView] documentView];
    if (!documentView) {
        return;
    }
    
    // Decide document frame and image frame
    NSRect  documentFrame, imageFrame;
    documentFrame = [documentView frame];
    documentFrame.origin = NSZeroPoint;
    imageFrame = documentFrame;
    if (imageFrame.size.height > imageFrame.size.width * _ratio) {
        float   oldHeight;
        oldHeight = imageFrame.size.height;
        imageFrame.size.height = floor(imageFrame.size.width * _ratio);
    }
    
    // Check size
    if (NSEqualSizes(documentFrame.size, NSZeroSize)) {
        return;
    }
    
    // Create bitmap image representation
    if (!_documentViewImageRep || !NSEqualSizes([_documentViewImageRep size], documentFrame.size)) {
        [_documentViewImage release], _documentViewImage = nil;
        [_documentViewImageRep release], _documentViewImageRep = nil;
        
        _documentViewImageRep = [[documentView bitmapImageRepForCachingDisplayInRect:documentFrame] retain];
        _documentViewImage = [[NSImage alloc] initWithSize:documentFrame.size];
    }
    
    if (![[_documentViewImage representations] containsObject:_documentViewImageRep]) {
        [_documentViewImage addRepresentation:_documentViewImageRep];
    }
    
    // Layout if needed
    if ([documentView respondsToSelector:@selector(_layoutIfNeeded)]) {
        [documentView _layoutIfNeeded];
    }
    
    // Remove subviews temporally
    NSMutableArray* subviews;
    NSEnumerator*   enumerator;
    NSView*         subview;
    subviews = [NSMutableArray array];
    enumerator = [[documentView subviews] objectEnumerator];
    while (subview = [enumerator nextObject]) {
        [subviews addObject:subview];
        [subview removeFromSuperviewWithoutNeedingDisplay];
    }
    
    // Cache bitmap
    [documentView cacheDisplayInRect:imageFrame toBitmapImageRep:_documentViewImageRep];
    
    // Add subviews again
    enumerator = [subviews objectEnumerator];
    while (subview = [enumerator nextObject]) {
        [documentView addSubview:subview];
    }
}

- (void)_updateWebViewImage
{
    // Check needs update
    if (!_webViewNeedsUpdate) {
        return;
    }
    _webViewNeedsUpdate = NO;
    
    // Get web view and document view
    WebView*    webView;
    id          documentView;
    webView = [self webView];
    documentView = [[[webView mainFrame] frameView] documentView];
    if (!webView) {
        return;
    }
    
    // Get web bounds
    NSRect  webBounds;
    webBounds = [webView bounds];
    
    // Check size
    if (NSEqualSizes(webBounds.size, NSZeroSize)) {
        return;
    }
    
    // Create bitmap image representation
    if (!_webViewImageRep || !NSEqualSizes([_webViewImageRep size], webBounds.size)) {
        [_webViewImage release], _webViewImage = nil;
        [_webViewImageRep release], _webViewImageRep = nil;
        
        _webViewImageRep = [[webView bitmapImageRepForCachingDisplayInRect:webBounds] retain];
        _webViewImage = [[NSImage alloc] initWithSize:webBounds.size];
    }
    
    if (![[_webViewImage representations] containsObject:_webViewImageRep]) {
        [_webViewImage addRepresentation:_webViewImageRep];
    }
    
#if 1
    // Remove document view's subviews temporally
    NSMutableArray* subviews;
    NSEnumerator*   enumerator;
    NSView*         subview;
    subviews = [NSMutableArray array];
    enumerator = [[documentView subviews] objectEnumerator];
    while (subview = [enumerator nextObject]) {
        [subviews addObject:subview];
        [subview removeFromSuperviewWithoutNeedingDisplay];
    }
#endif
    
    // Cache bitmap
    [webView cacheDisplayInRect:webBounds toBitmapImageRep:_webViewImageRep];
    
#if 1
    // Add subviews again
    enumerator = [subviews objectEnumerator];
    while (subview = [enumerator nextObject]) {
        [documentView addSubview:subview];
    }
#endif
}

- (NSImage*)documentViewImage
{
    // Update document view image
    [self _updateDocumentViewImage];
    
    return _documentViewImage;
}

- (NSBitmapImageRep*)documentViewImageBitmapRep
{
    // Update document view image
    [self _updateDocumentViewImage];
    
    return _documentViewImageRep;
}

- (NSImage*)webViewImage
{
    // Force update
    _webViewNeedsUpdate = YES;
    
    // Update web view image
    [self _updateWebViewImage];
    
    return _webViewImage;
}

- (NSBitmapImageRep*)webViewImageBitmapRep
{
    // Update web view image
    [self _updateWebViewImage];
    
    return _webViewImageRep;
}

- (void)updateDocumentViewImage
{
    // Update document view image
    NSImage*    image;
    image = [self documentViewImage];
    
    // Set it tab view item
    [_tabViewItem setThumbnailImage:image];
}

- (void)updateWebViewImage
{
}

- (void)clearPageImage
{
    // Set page image
    [_tabViewItem setThumbnailImage:_documentViewImage];
    
    // Remove image
    [_documentViewImage release], _documentViewImage = nil;
    [_documentViewImageRep release], _documentViewImageRep = nil;
    _documentViewNeedsUpdate = YES;
    [_webViewImage release], _webViewImage = nil;
    [_webViewImageRep release], _webViewImageRep = nil;
    _webViewNeedsUpdate = YES;
}

//--------------------------------------------------------------//
#pragma mark -- User agent --
//--------------------------------------------------------------//

- (void)_setUserAgentOfWebView:(WebView*)webView
{
#if 1
    [webView setCustomUserAgent:nil];
    
    // Get version
    NSBundle*   bundle;
    NSString*   version;
    NSString*   userAgentName = @"Shiira";
    bundle = [NSBundle mainBundle];
    version = [[bundle infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    if (version) {
        userAgentName = [NSString stringWithFormat:@"%@/%@", userAgentName, version];
    }
    [webView setApplicationNameForUserAgent:userAgentName];
    
    // Get composited user agent name
    NSString*   compUserAgentName;
    compUserAgentName = [webView userAgentForURL:[NSURL URLWithString:@"http://local"]];
    
    // If user agent name dose not contain 'Safari', add it
    if ([compUserAgentName rangeOfString:@"Safari"].location == NSNotFound) {
        compUserAgentName = [NSString stringWithFormat:@"%@ Safari/125", compUserAgentName];
    }
    [webView setCustomUserAgent:compUserAgentName];
#else
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Set user agent
    int         userAgent;
    NSString*   customUserAgentName;
    userAgent = [defaults integerForKey:SRUserAgent];
    customUserAgentName = [defaults stringForKey:SRUserAgentOtherName];
    
    // For Shiira user agent
    if (userAgent == SRUserAgentShiira) {
        [webView setCustomUserAgent:nil];
        
        // Get version
        NSBundle*   bundle;
        NSString*   version;
        NSString*   userAgentName = @"Shiira";
        bundle = [NSBundle mainBundle];
        version = [[bundle infoDictionary] objectForKey:@"CFBundleShortVersionString"];
        if (version) {
            userAgentName = [NSString stringWithFormat:@"%@/%@", userAgentName, version];
        }
        [webView setApplicationNameForUserAgent:userAgentName];
        
        // Get composited user agent name
        NSString*   compUserAgentName;
        compUserAgentName = [webView userAgentForURL:[NSURL URLWithString:@"http://local"]];
        
        // If user agent name dose not contain 'Safari', add it
        if ([compUserAgentName rangeOfString:@"Safari"].location == NSNotFound) {
            compUserAgentName = [NSString stringWithFormat:@"%@ Safari/125", compUserAgentName];
        }
        [webView setCustomUserAgent:compUserAgentName];
        
        // Show user agent name on preferences panel
        [defaults setValue:compUserAgentName forKey:SRUserAgentShiiraName];
    }
    // For default user agents
    if (userAgent > SRUserAgentShiira && userAgent < SRUserAgentOther) {
        // Get user agents plist
        NSArray*    userAgents;
        userAgents = [SRPreferencesController userAgents];
        if (userAgents && [userAgents count] > userAgent - 1) {
            NSDictionary*   userAgentInfo;
            NSString*       userAgentName;
            userAgentInfo = [userAgents objectAtIndex:userAgent - 1];
            userAgentName = [userAgentInfo objectForKey:@"userAgent"];
            if (userAgentName) {
                [webView setCustomUserAgent:userAgentName];
                
                // Show user agent name on preferences panel
                [defaults setValue:userAgentName forKey:SRUserAgentShiiraName];
            }
        }
    }
    // For other
    if (userAgent == SRUserAgentOther) {
        [webView setCustomUserAgent:customUserAgentName];
        
        // Set other user agent name on preferences panel
        [defaults setValue:customUserAgentName forKey:SRUserAgentShiiraName];
    }
#endif
}

- (void)setUserAgent:(int)userAgent
{
#if 0
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get custom user agent name
    NSString*   customUserAgentName;
    customUserAgentName = [defaults stringForKey:SRUserAgentOtherName];
    
    // Set it web view
    NSEnumerator*   enumerator;
    NSTabViewItem*  tabViewItem;
    enumerator = [[[_srTabView tabView] tabViewItems] objectEnumerator];
    while (tabViewItem = [enumerator nextObject]) {
        // Get web view
        WebView*    webView;
        webView = (WebView*)[tabViewItem view];
        
        // Set user agent
        [self _setUserAgentOfWebView:webView];
    }
#endif
}

//--------------------------------------------------------------//
#pragma mark -- Shelf --
//--------------------------------------------------------------//

- (void)setSelectedShelfId:(NSString*)shelfId
{
    // Get web view and data source
    WebView*        webView;
    WebDataSource*  dataSource;
    webView = [self webView];
    dataSource = [[webView mainFrame] dataSource];
    
    // Get shelf representation
    id  representation;
    representation = [dataSource representation];
    if (!representation) {
        // Store shelf ID
        [_shelfIdWillOpen release], _shelfIdWillOpen = nil;
        _shelfIdWillOpen = [shelfId copy];
    }
    else {
        if ([representation isKindOfClass:[SRShelfRepresentation class]]) {
            // Select Shelf
            SRShelfController*  shelfController;
            shelfController = [representation shelfController];
            [shelfController setSelectedShelfId:shelfId];
        }
    }
}

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

- (void)webView:(WebView*)webView 
        didStartProvisionalLoadForFrame:(WebFrame*)frame
{
    // Resign first responder
    if (self == [_browserController selectedPageController]) {
        [[webView 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 label
        [_tabViewItem setLabel:URLString];
        [_tabViewItem setSubLabel:URLString];
        
        // Notify change
        [[NSNotificationCenter defaultCenter] 
                postNotificationName:SRPageDidStartProvisionalLoad object:self];
    }
}

- (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];
    
    // When this page is selected
    if ([_browserController selectedPageController] == self) {
        // Check focus
        NSWindow*       window;
        WebFrameView*   webFrameView;
        window = [webView window];
        webFrameView = [frame frameView];
        if ([window firstResponder] != webFrameView) {
            [window makeFirstResponder:webFrameView];
        }
    }
    
    // For main frame
    if ([webView mainFrame] == frame) {
        // Notify progress
        [[NSNotificationCenter defaultCenter] 
                postNotificationName:SRPageDidCommitLoad object:self];
    }
}

- (void)webView:(WebView*)webView 
        didFinishLoadForFrame:(WebFrame*)frame
{
    // For main frame
    if ([webView mainFrame] == frame) {
        // Notify progress
        [[NSNotificationCenter defaultCenter] 
                postNotificationName:SRPageDidFinishLoad object:self];
        
        // For shelf
        if (_shelfIdWillOpen) {
            WebDataSource*  dataSource;
            dataSource = [frame dataSource];
            if ([[[[dataSource initialRequest] URL] absoluteString] isEqualToString:@"shiira://shelf"]) {
                // Get shelf representation
                id  representation;
                representation = [dataSource representation];
                if ([representation isKindOfClass:[SRShelfRepresentation class]]) {
                    // Select Shelf
                    SRShelfController*  shelfController;
                    shelfController = [representation shelfController];
                    [shelfController setSelectedShelfId:_shelfIdWillOpen];
                }
            }
            
            [_shelfIdWillOpen release], _shelfIdWillOpen = nil;
        }
    }
}

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

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

- (void)webView:(WebView*)webView 
        didReceiveTitle:(NSString*)title 
        forFrame:(WebFrame*)frame
{
    // For main frame
    if ([webView mainFrame] == frame) {
        // Set tab label
        [_tabViewItem setLabel:title];
        
        // Notify change
        [[NSNotificationCenter defaultCenter] 
                postNotificationName:SRPageTitleReceived object:self];
    }
}

- (void)webView:(WebView*)webView 
        didReceiveIcon:(NSImage*)icon 
        forFrame:(WebFrame*)frame
{
    // For main frame
    if ([webView mainFrame] == frame) {
        // Notify change
        [[NSNotificationCenter defaultCenter] 
                postNotificationName:SRPageIconReceived object:self];
    }
}

- (NSString*)_valueForTemplateKey:(NSString*)key error:(NSError*)error frame:(WebFrame*)frame
{
    // 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;
    HMGetWebKitErrorMessage(error, frame, &msgText, &infoText, &statusText);
    
    // title
    if ([key isEqualToString:@"$ERROR_TITLE$"]) {
        return msgText;
    }
    // message
    if ([key isEqualToString:@"$ERROR_MESSAGE$"]) {
        return infoText;
    }
    
    return nil;
}

- (NSString*)_createHTMLWithError:(NSError*)error frame:(WebFrame*)frame
{
    // Check arguments
    if (!error) {
        return nil;
    }
    
    // Load template
    NSString*   resPath;
    NSString*   path;
    NSString*   errorTemplate;
    resPath = [[NSBundle mainBundle] resourcePath];
    path = [resPath stringByAppendingPathComponent:@"template/ErrorTemplate.html"];
    errorTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
    if (!errorTemplate) {
        // Error
        NSLog(@"Can't load ErrorTemplate.html");
        return nil;
    }
    
    // Create buffer
    NSMutableString*    html;
    html = [NSMutableString stringWithString:errorTemplate];
    
    // Replace keys
    NSEnumerator*   enumerator;
    NSString*       templateKey;
    enumerator = [_templateKeys objectEnumerator];
    while (templateKey = [enumerator nextObject]) {
        // Find in buffer
        if ([html rangeOfString:templateKey].location == NSNotFound) {
            continue;
        }
        
        // Get value for template key
        NSString*   value;
        value = [self _valueForTemplateKey:templateKey error:error frame:frame];
        if (!value) {
            value = @"";
        }
        
        // Replace it
        [html replaceOccurrencesOfString:templateKey 
                withString:value options:0 range:NSMakeRange(0, [html length])];
    }
    
    return html;
}

- (void)_certificateAllowingAlertDidEnd:(NSAlert*)alert 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
#if 0
    // 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];
    }
#endif
}

- (void)_soundEffectForWebView:(WebView*)webView error:(NSError*)error frame:(WebFrame*)frame
{
#if 0
    // 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);
            }
        }
    }
#endif
}

- (void)_webView:(WebView*)webView 
        didFailLoadWithError:(NSError*)error 
        forFrame:(WebFrame*)frame
        dataSource:(WebDataSource*)dataSource
{
    // Check error
    if ([error code] == NSURLErrorCancelled) {
        return;
    }
    
    // Get URL string
    NSString*   URLString;
    URLString = [[[dataSource request] URL] absoluteString];
    
    // Create HTML for error
    NSString*   html;
    html = [self _createHTMLWithError:error frame:frame];
    
    // Load HTML
    NSString*   path;
    path = HMApplicationSupportFolder(@"Shiira");
    path = [path stringByAppendingPathComponent:@"template/tmpError.html"];
    //path = [path stringByAppendingFormat:@"&%@", URLString];
    [[html dataUsingEncoding:NSUTF8StringEncoding] writeToFile:path atomically:YES];
    [frame loadRequest:
            [NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]];
}

- (void)webView:(WebView*)webView 
        didFailProvisionalLoadWithError:(NSError*)error 
        forFrame:(WebFrame*)frame
{
#if 0
    // 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];
        }
    }
#endif
    
    // Shoe error
    [self _webView:webView didFailLoadWithError:error forFrame:frame dataSource:[frame provisionalDataSource]];
}

- (void)webView:(WebView*)webView 
        didFailLoadWithError:(NSError*)error 
        forFrame:(WebFrame*)frame
{
#if 0
    // 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];
    }
#endif
    
    // Shoe error
    [self _webView:webView didFailLoadWithError:error forFrame:frame dataSource:[frame dataSource]];
}

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

- (void)webView:(WebView*)webView 
        willPerformClientRedirectToURL:(NSURL*)URL 
        delay:(NSTimeInterval)seconds 
        fireDate:(NSDate*)date 
        forFrame:(WebFrame*)frame
{
#if 0
    // 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]];
    }
#endif
}

- (void)webView:(WebView*)webView 
        didReceiveServerRedirectForProvisionalLoadForFrame:(WebFrame*)frame
{
    // 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 label
        [_tabViewItem setLabel:URLString];
        [_tabViewItem setSubLabel:URLString];
        
        // Notify change
        [[NSNotificationCenter defaultCenter] 
                postNotificationName:SRPageDidReceiveServerRedirect object:self];
    }
}

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

- (BOOL)_webView:(WebView*)webView 
        decidePolicyForNavigationByBrowseModeAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        frameName:(NSString*)frameName 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check modifier key flag
    unsigned int    modifierFlags = 0;
    modifierFlags = [[info objectForKey:WebActionModifierFlagsKey] unsignedIntValue];
    if ((modifierFlags & (NSAlphaShiftKeyMask | 
                          NSShiftKeyMask | 
                          NSControlKeyMask | 
                          NSAlternateKeyMask | 
                          NSCommandKeyMask)) != 0)
    {
        // Ignore browse mode
        return NO;
    }
    
    // Get browse mode
    int browseMode;
    browseMode = [defaults integerForKey:SRBrowseMode];
    switch (browseMode) {
    case SRSingleWindowMode: {
        // Open in same web view
        [listener use];
        return YES;
    }
    case SRSmartBrowseMode: {
        // Get domain name
        NSString*   initialDomain;
        NSString*   currentDomain;
        NSString*   newDomain;
        initialDomain = [[[[[webView mainFrame] dataSource] initialRequest] URL] host];
        currentDomain = [[[[[webView mainFrame] dataSource] request] URL] host];
        newDomain = [[request URL] host];
        
        // When domain is not decided yet
        if (!currentDomain || [currentDomain length] == 0 || 
            !newDomain || [newDomain length] == 0)
        {
            [listener use];
            return YES;
        }
        
        // Open in same web view when host name is same
//        if ([currentDomain isEqualToString:newDomain]) {
        if ([initialDomain isEqualToString:newDomain]) {
            [listener use];
            return YES;
        }
        
        // Open in new tab
        [_browserController openInNewTabRequest:request frameName:frameName groupName:nil select:YES];
        [listener ignore];
        return YES;
    }
    }
    
    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, YES, YES);
    
    // Check open action
    switch (openAction) {
    case SROpenAction: {
        // Do nothing
        break;
    }
    // For opening with tab or window
    case SROpenInNewTabAction:
    case SROpenInNewBackgroundTabAction:
    case SROpenInNewWindowAction:
    case SROpenInNewBackgroundWindowAction: {
        [_browserController 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];
    
    // Check full screen mode
    if ([_browserController isFullScreenMode]) {
        [listener use];
        return;
    }
    
    // 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];
            scheme = [linkURL scheme];
        }
    }
    
    // For about scheme
    if ([scheme isEqualToString:@"about"]) {
        // Just use
        [listener use];
        return;
    }
    
#if 0
    // For other schemes
    if (![self isSchemeSupported:scheme]) {
        // only if it's mailto, or if the default is set to allow all schemes
        if ([scheme isEqualToString:@"mailto"] || 
            [defaults boolForKey:SRSecurityAllowAllURLSchemes])
        {
            // Launch external viewer
            [[NSWorkspace sharedWorkspace] openURL:linkURL];
            [listener ignore];
            return;
        }
    }
#endif
    
    // For link click
    if (navigationType == WebNavigationTypeLinkClicked) {
        // Check browse mode
        if ([self _webView:webView decidePolicyForNavigationByBrowseModeAction: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) {
    }
    // For other
    if (navigationType == WebNavigationTypeOther) {
        // Check browse mode
        if ([self _webView:webView decidePolicyForNavigationByBrowseModeAction:info 
                request:request frameName:linkTargetFrame decisionListener:listener])
        {
            return;
        }
        
        if ([self _webView:webView decideTabPolicyAction:nil 
                request:request frameName:nil decisionListener:listener])
        {
            return;
        }
    }
    
    // If listener is nil, it should come from representation view
    if (!listener) {
        [self openRequest:request];
    }
    
    [listener use];
}

- (BOOL)_webView:(WebView*)webView 
        decidePolicyForNewWindowByBrowseModeAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        frameName:(NSString*)frameName 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check modifier key flag
    unsigned int    modifierFlags = 0;
    modifierFlags = [[info objectForKey:WebActionModifierFlagsKey] unsignedIntValue];
    if ((modifierFlags & (NSAlphaShiftKeyMask | 
                          NSShiftKeyMask | 
                          NSControlKeyMask | 
                          NSAlternateKeyMask | 
                          NSCommandKeyMask)) != 0)
    {
        // Ignore browse mode
        return NO;
    }
    
    // Get browse mode
    int browseMode;
    browseMode = [defaults integerForKey:SRBrowseMode];
    switch (browseMode) {
    case SRSingleWindowMode: {
        // Open in same web view
        [self openRequest:request];
        [listener ignore];
        return YES;
    }
    case SRSmartBrowseMode: {
        // Get domain name
        NSString*   currentDomain;
        NSString*   newDomain;
        currentDomain = [[[[[webView mainFrame] dataSource] request] URL] host];
        newDomain = [[request URL] host];
        
        // Open in same web view when host name is same
        if ([currentDomain isEqualToString:newDomain]) {
            [self openRequest:request];
            [listener ignore];
            return YES;
        }
        
        // Open in new tab
        [_browserController openInNewTabRequest:request frameName:frameName groupName:nil select:YES];
        [listener ignore];
        return YES;
    }
    }
    
    return NO;
}

- (void)webView:(WebView*)webView 
        decidePolicyForNewWindowAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        newFrameName:(NSString*)frameName 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check full screen mode
    if ([_browserController isFullScreenMode]) {
        [self openRequest:request];
        [listener ignore];
        return;
    }
    
    // Check browse mode
    if ([self _webView:webView decidePolicyForNewWindowByBrowseModeAction:info 
            request:request frameName:frameName decisionListener:listener])
    {
        return;
    }
    
    // For tabbed browsing
    if ([self _webView:webView 
            decideTabPolicyAction: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:[SRBrowserDocument class]]) {
            continue;
        }
        
        // Get brrowser controller
        SRBrowserController*    browserController;
        browserController = [document browserController];
        if (!browserController) {
            continue;
        }
        
#if 0
        // 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;
            }
        }
#endif
    }
    
    // Open target link in tab
    if ([defaults boolForKey:SRTabTargetLinkUseTab]) {
        [_browserController openRequest:request frameName:frameName groupName:nil 
                withOpenAction:SROpenActionTypeFromModifierFlags(NSCommandKeyMask, YES, YES)];
        
        [listener ignore];
        return;
    }
    
    // Always ignore
    [listener ignore];
    
    // Open in new background
    [_browserController openInNewWindowRequest:request frameName:frameName groupName:nil];
}

- (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];
}

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

- (WebView*)webView:(WebView*)webView
        createWebViewWithRequest:(NSURLRequest*)request
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get browse mode
    int browseMode;
    browseMode = [defaults integerForKey:SRBrowseMode];
    
    // For single window mode
    if (browseMode == SRSingleWindowMode) {
        // Open request by itself
        [self openRequest:request];
        return webView;
    }
    
    // Check target link opening way
    BOOL    doseTargetLinkUseTab;
    doseTargetLinkUseTab = [defaults boolForKey:SRTabTargetLinkUseTab];
    if (doseTargetLinkUseTab) {
        WebView*    webView;
        webView = [_browserController openInNewTabRequest:request frameName:nil groupName:nil select:YES];
        return webView;
    }
    
    // Create new document
    SRDocumentController*   documentController;
    SRBrowserDocument*      document;
    documentController = [NSDocumentController sharedDocumentController];
    document = [documentController makeUntitledDocumentOfType:SRHTMLDocumentType];
    [documentController addDocument:document];
    
    // Make window controllers
    [document makeWindowControllers];
    
    // Get browser controller
    SRBrowserController*    browserController;
    browserController = [document browserController];
    
    // Open request in new window, 
    // but not show it yet
    [browserController openRequest:request];
    
    return [[browserController selectedPageController] webView];
}

- (void)webViewClose:(WebView*)webView
{
    // Close page
    [_browserController closePageController:self];
}

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

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

- (void)webView:(WebView*)webView 
        makeFirstResponder:(NSResponder*)responder
{
    [[webView 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    selectNewTabs;
            selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
            
            // 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;
                }
            }
            
            // 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: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) {
        [[_browserController browserContent] setValue:[NSString stringWithFormat:status, URLString]
                forKey:@"status"];
    }
    else {
        [[_browserController browserContent] setValue:@"" forKey:@"status"];
    }
}

- (NSMenuItem*)_createSerachMenuItemByEngine:(SRSearchEngine*)engine 
        searchString:(NSString*)searchString
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get menu item for finding by search engine
    NSMenu*     menu;
    NSMenuItem* menuItem;
    menu = [SRWebViewContextMenu contextMenu];
    menuItem = HMCopyMenuItemWithTag(menu, SRFindBySearchEngineTag, nil);
    
    // Get engine name
    NSString*   engineName;
    engineName = [engine valueForKey:@"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
    NSMutableDictionary*    searchItem;
    searchItem = [NSMutableDictionary 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
    NSString*   templateURL;
    templateURL = [engine valueForKey:@"templateURL"];
    
    NSImage*    icon;
    icon = [[WebIconDatabase sharedIconDatabase] iconForURL:templateURL 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;
    WebFrame*   webFrame;
    BOOL        isSelected;
    linkURL = [element objectForKey:WebElementLinkURLKey];
    imageURL = [element objectForKey:WebElementImageURLKey];
    webFrame = [element objectForKey:WebElementFrameKey];
    isSelected = [[element objectForKey:WebElementIsSelectedKey] boolValue];
    
    // Check tab browsing
    BOOL    selectNewTabs;
    selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
    
    // Create array
    NSMutableArray* items;
    items = [NSMutableArray array];
    
    // Get web view context menu
    NSMenu*     menu;
    NSMenuItem* menuItem;
    NSMenuItem* altMenuItem;
    menu = [SRWebViewContextMenu contextMenu];
    
    // Get selected string
    NSString*   selectedString = nil;
    if (isSelected) {
        NSView* documentView;
        documentView = [[[element objectForKey:WebElementFrameKey] frameView] documentView];
        if ([documentView respondsToSelector:@selector(selectedString)]) {
            selectedString = [documentView performSelector:@selector(selectedString)];
        }
    }
    
    // Get selected links
    DOMRange*       range;
    DOMNode*        startContainer = nil;
    NSMutableArray* linkURLStrings;
    range = [webView selectedDOMRange];
    startContainer = [range startContainer];
    linkURLStrings = [NSMutableArray array];
    if (startContainer) {
        HMDOMRangeFilter*   filter;
        DOMNodeIterator*    iterator;
        DOMNode*            node;
        filter = [[HMDOMRangeFilter 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];
    }
    
    // When link is contained
    if (linkURL) {
        if ([WebView _canHandleRequest:[NSURLRequest requestWithURL:linkURL]]) {
            // 'Open Link in New Tab' and 'Open Link in Background Tab'
            if (selectNewTabs) {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewTabTag, nil);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewBackgroundTabTag, nil);
            }
            else {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewBackgroundTabTag, nil);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewTabTag, nil);
            }
            [menuItem setRepresentedObject:linkURL];
            [items addObject:menuItem];
            
            [altMenuItem setRepresentedObject:linkURL];
            [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
            [altMenuItem setAlternate:YES];
            [items addObject:altMenuItem];
            
            // 'Open Link in New Window' and 'Open Link in Background Window'
            if (selectNewTabs) {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewWindowTag, nil);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewBackgroundWindowTag, nil);
            }
            else {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewBackgroundWindowTag, nil);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewWindowTag, nil);
            }
            [menuItem setRepresentedObject:linkURL];
            [items addObject:menuItem];
            
            [altMenuItem setRepresentedObject:linkURL];
            [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
            [altMenuItem setAlternate:YES];
            [items addObject:altMenuItem];
        }
    }
    
    // 'Open All Links in New Tabs' and 'Open All Links in Background Tabs'
    if ([linkURLStrings count] > 1) {
        if (selectNewTabs) {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenAllLinksInNewTabsTag, nil);
            altMenuItem = HMCopyMenuItemWithTag(menu, SROpenAllLinksInNewBackgroundTabsTag, nil);
        }
        else {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenAllLinksInNewBackgroundTabsTag, nil);
            altMenuItem = HMCopyMenuItemWithTag(menu, SROpenAllLinksInNewTabsTag, nil);
        }
        
        [menuItem setTitle:[NSString stringWithFormat:[menuItem title], [linkURLStrings count]]];
        [menuItem setRepresentedObject:linkURLStrings];
        [items addObject:menuItem];
        
        [altMenuItem setTitle:[NSString stringWithFormat:[menuItem title], [linkURLStrings count]]];
        [altMenuItem setRepresentedObject:linkURLStrings];
        [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
        [altMenuItem setAlternate:YES];
        [items addObject:altMenuItem];
    }
    
    // When link is contained
    if (linkURL) {
        // 'Download Linked File' and 'Download Link File to Location'
        menuItem = HMCopyMenuItemWithTag(menu, SRDownloadLinkedFileTag, nil);
        [menuItem setRepresentedObject:linkURL];
        [items addObject:menuItem];
        //menuItem = HMCopyMenuItemWithTag(menu, SRDownloadLinkedFileToLocationTag, nil);
        //[menuItem setRepresentedObject:linkURL];
        //[items addObject:menuItem];
        
        // 'Add Link to Bookmarks'
        menuItem = HMCopyMenuItemWithTag(menu, SRAddLinkToBookmarksTag, nil);
        [menuItem setRepresentedObject:element];
        [items addObject:menuItem];
        
        // 'Copy Link'
        menuItem = HMCopyMenuItemWithTag(menu, SRCopyLinkTag, nil);
        [menuItem setRepresentedObject:element];
        [items addObject:menuItem];
    }
    
    // When image is contained
    if (imageURL) {
        // Add separator
        if ([items count] > 0) {
            [items addObject:[NSMenuItem separatorItem]];
        }
        
        // 'Open Image in New Tab' and 'Open Image in Background Tab'
        if (selectNewTabs) {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewTabTag, nil);
            altMenuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewBackgroundTabTag, nil);
        }
        else {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewBackgroundTabTag, nil);
            altMenuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewTabTag, nil);
        }
        [menuItem setRepresentedObject:imageURL];
        [items addObject:menuItem];
        
        [altMenuItem setRepresentedObject:imageURL];
        [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
        [altMenuItem setAlternate:YES];
        [items addObject:altMenuItem];
        
        // 'Open Image in New Window' and 'Open Image in Background Window'
        if (selectNewTabs) {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewWindowTag, nil);
            altMenuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewBackgroundWindowTag, nil);
        }
        else {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewBackgroundWindowTag, nil);
            altMenuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewWindowTag, nil);
        }
        [menuItem setRepresentedObject:imageURL];
        [items addObject:menuItem];
        
        [altMenuItem setRepresentedObject:imageURL];
        [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
        [altMenuItem setAlternate:YES];
        [items addObject:altMenuItem];
        
        // 'Download Image' and 'Download Image to Location'
        menuItem = HMCopyMenuItemWithTag(menu, SRDownloadImageTag, nil);
        [menuItem setRepresentedObject:imageURL];
        [items addObject:menuItem];
        //menuItem = HMCopyMenuItemWithTag(menu, SRDownloadImageToLocationTag, nil);
        //[items addObject:menuItem];
        
        // 'Copy Image to Clipboard' and 'Copy Image Location to Clipboard'
        menuItem = HMCopyMenuItemWithTag(menu, SRCopyImageTag, nil);
        [menuItem setRepresentedObject:element];
        [items addObject:menuItem];
        menuItem = HMCopyMenuItemWithTag(menu, SRCopyImageURLTag, nil);
        [menuItem setRepresentedObject:element];
        [items addObject:menuItem];
    }
    
    // When text is selected
    if (isSelected) {
        // Add separator
        if ([items count] > 0) {
            [items addObject:[NSMenuItem separatorItem]];
        }
        
        // 'Copy'
        menuItem = HMCopyMenuItemWithTag(menu, SRWebTextCopyTag, nil);
        [items addObject:menuItem];
        
        // 'Open Text As URL'
        if (selectedString) {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenTextAsURLTag, nil);
            [menuItem setRepresentedObject:selectedString];
            [items addObject:menuItem];
        }
    }
    
    // When text is selected
    if (isSelected) {
        // Add separator
        if ([items count] > 0) {
            [items addObject:[NSMenuItem separatorItem]];
        }
        
        // 'Search'
        menuItem = HMCopyMenuItemWithTag(menu, SRFindBySearchEngineTag, nil);
        [items addObject:menuItem];
        
        // 'Search By Other'
        menuItem = HMCopyMenuItemWithTag(menu, SRFindByOtherSearchEngineTag, nil);
        [items addObject:menuItem];
    }
    
    // When text is selcted
    if (selectedString) {
        // Get current search engine
        SRSearchEngine* currentEngine;
        currentEngine = [[_browserController searchFieldController] currentSearchEngine];
        
        if (currentEngine) {
            // Add separator
            if ([items count] > 0) {
                [items addObject:[NSMenuItem separatorItem]];
            }
            
            // 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;
            SRSearchEngine* engine;
            otherEngines = [NSMutableArray array];
            engines = [[SRAppController sharedInstance] searchEngines];
            enumerator = [engines objectEnumerator];
            while (engine = [enumerator nextObject]) {
                if ([[engine valueForKey:@"isUsing"] boolValue] && 
                    ![[engine valueForKey:@"title"] isEqualToString:[currentEngine valueForKey:@"title"]])
                {
                    [otherEngines addObject:engine];
                }
            }
            
            // Add search by other engines menu
            if ([otherEngines count] > 0) {
                // Add 'Search by other'
                menuItem = HMCopyMenuItemWithTag(menu, SRFindByOtherSearchEngineTag, 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];
                }
            }
        }
    }
                
    // When nothing is selected
    if (!isSelected && !linkURL && !imageURL) {
        // 'Go Back'
        if ([webView canGoBack]) {
            menuItem = HMCopyMenuItemWithTag(menu, SRWebViewGoBackTag, nil);
            [items addObject:menuItem];
        }
        // 'Go Forward'
        if ([webView canGoForward]) {
            menuItem = HMCopyMenuItemWithTag(menu, SRWebViewGoForwardTag, nil);
            [items addObject:menuItem];
        }
        // 'Reload Page'
        if (![webView isLoading]) {
            menuItem = HMCopyMenuItemWithTag(menu, SRWebViewReloadPageTag, nil);
            [items addObject:menuItem];
        }
        // 'Stop Loading'
        if ([webView isLoading]) {
            menuItem = HMCopyMenuItemWithTag(menu, SRWebViewStopLoadingTag, nil);
            [items addObject:menuItem];
        }
        
        // Frame and sub frame
        if ([[webView mainFrame] dataSource] || webFrame != [webView mainFrame]) {
            // Add separator
            if ([items count] > 0) {
                [items addObject:[NSMenuItem separatorItem]];
            }
            
            // When in sub frame
            if (webFrame != [webView mainFrame]) {
                // Get frame URL
                WebDataSource*  dataSource;
                NSURL*          frameURL;
                dataSource = [[element objectForKey:WebElementFrameKey] dataSource];
                frameURL = [dataSource unreachableURL];
                if (!frameURL) {
                    frameURL = [[dataSource request] URL];
                }
                
                // 'Open Frame in New Tab' and 'Open Frame in Background Tab'
                if (selectNewTabs) {
                    menuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewTabTag, nil);
                    altMenuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewBackgroundTabTag, nil);
                }
                else {
                    menuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewBackgroundTabTag, nil);
                    altMenuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewTabTag, nil);
                }
                [menuItem setRepresentedObject:frameURL];
                [items addObject:menuItem];
                
                [altMenuItem setRepresentedObject:frameURL];
                [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
                [altMenuItem setAlternate:YES];
                [items addObject:altMenuItem];
                
                // 'Open Frame in New Window' and 'Open Frame in Background Window'
                if (selectNewTabs) {
                    menuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewWindowTag, nil);
                    altMenuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewBackgroundWindowTag, nil);
                }
                else {
                    menuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewBackgroundWindowTag, nil);
                    altMenuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewWindowTag, nil);
                }
                [menuItem setRepresentedObject:frameURL];
                [items addObject:menuItem];
                
                [altMenuItem setRepresentedObject:frameURL];
                [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
                [altMenuItem setAlternate:YES];
                [items addObject:altMenuItem];
            }
            
            // 'View Source'
            if ([[webView mainFrame] dataSource]) {
                menuItem = HMCopyMenuItemWithTag(menu, SRViewSourceTag, nil);
                [items addObject:menuItem];
            }
            
            // 'View Frame Source'
            if (webFrame != [webView mainFrame]) {
                menuItem = HMCopyMenuItemWithTag(menu, SRViewFrameSourceTag, nil);
                [items addObject:menuItem];
            }
        }
    }
    
    return items;
}

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

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

#if 0
- (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;
    }
}
#endif

- (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 
        setFrame:(NSRect)frame
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
#if 0
    // Get browse mode
    int browseMode;
    browseMode = [defaults integerForKey:SRBrowseMode];
    if (browseMode != SRDefaultBrowseMode) {
        // Ignore it
        return;
    }
#endif
    
    // Check opened in tab
    BOOL    doseTargetLinkUseTab;
    doseTargetLinkUseTab = [defaults boolForKey:SRTabTargetLinkUseTab];
    if (doseTargetLinkUseTab) {
        // Ignore it
        return;
    }
    if ([[_browserController tabView] numberOfTabViewItems] > 1) {
        // Ignore it
        return;
    }
    
    // Set flag
    [_browserController setResizedByJavaScript:YES];
    
    // Resize window
    [[_browserController window] setFrame:frame display:NO];
}

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

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

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

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

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

- (void)webViewProgressStarted:(NSNotification*)notification
{
    // Clear loading time and set start time
    _loadingTime = -1;
    _startTime = CFAbsoluteTimeGetCurrent();
    _documentViewNeedsUpdate = YES;
    _webViewNeedsUpdate = YES;
    
    // Clear page image
    [self clearPageImage];
    
    // Set tab status
    [_tabViewItem setWorking:YES];
    [_tabViewItem setEstimatedProgress:0];
    
    // Notify progress
    [[NSNotificationCenter defaultCenter] 
            postNotificationName:SRPageProgressStart object:self];
}

- (void)webViewProgressEstimateChanged:(NSNotification*)notification
{
    // Get progress
    float   progress;
    progress = [[self webView] estimatedProgress];
    
    // Set image needs update
    _documentViewNeedsUpdate = YES;
    _webViewNeedsUpdate = YES;
    
    // Set tab thumbnail image
    HMTabView*  tabView;
    tabView = [_tabViewItem tabView];
    if ([tabView hasThumbnail] && [tabView isTabItemViewShown]) {
        [self updateDocumentViewImage];
    }
    
    // Set tab status
    [_tabViewItem setWorking:YES];
    [_tabViewItem setEstimatedProgress:progress];
    
    // Update web view image
    if (progress == 1.0f) {
        [self webViewImage];
    }
    
    // Notify progress
    [[NSNotificationCenter defaultCenter] 
            postNotificationName:SRPageProgressEstimateChanged object:self];
}

- (void)webViewProgressFinished:(NSNotification*)notification
{
    // Set loading time
    _loadingTime = CFAbsoluteTimeGetCurrent() - _startTime;
    
    // Set image needs update
    _documentViewNeedsUpdate = YES;
    _webViewNeedsUpdate = YES;
    
    // Set tab thumbnail image
    HMTabView*  tabView;
    tabView = [_tabViewItem tabView];
    if ([tabView hasThumbnail] && [tabView isTabItemViewShown]) {
        [self updateDocumentViewImage];
    }
    
    // Set tab status
    [_tabViewItem setWorking:NO];
    [_tabViewItem setEstimatedProgress:0.0];
    
    // Notify progress
    [[NSNotificationCenter defaultCenter] 
            postNotificationName:SRPageProgressFinish object:self];
}

@end
