/*
SRSBPageHolder.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 "SRMainWindowController.h"
#import "SRPreferencesController.h"
#import "SRSourceWindowController.h"

#import "SRSBPageHolderController.h"

#import "SRXMLDocument.h"

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

NSString*    SRSBPageHolderEnableToAdd = @"enableToAdd";
NSString*    SRSBPageHolderEnableToRemove = @"enableToRemove";
NSString*    SRSBPageHolderEnableToReload = @"enableToReload";
NSString*    SRSBPageHolderEnableToStop = @"enableToStop";
NSString*    SRSBPageHolderEnableToMakeTextSmaller = @"canMakeTextSmaller";
NSString*    SRSBPageHolderEnableToMakeTextStandard = @"canMakeTextStandard";
NSString*    SRSBPageHolderEnableToMakeTextLarger = @"canMakeTextLarger";
NSString*    SRSBPageHolderIsLoading = @"isLoading";

@interface SRSBPageHolderController (private)
- (void)_addPageWithURL:(NSString*)URLString title:(NSString*)title select:(BOOL)select;
- (void)_updateControlButtons;
@end

@implementation SRSBPageHolderController

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

- (id)initWithMainWindowController:(SRMainWindowController*)mainWindowController 
        sideBarController:(SRSideBarController*)sideBarController
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize instance variables
    _mainWindowController = mainWindowController;
    _sideBarController = sideBarController;
    
    // Register notifications
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self 
            selector:@selector(webViewProgressStarted:) name:WebViewProgressStartedNotification object:nil];
    [center addObserver:self 
            selector:@selector(webViewProgressFinished:) name:WebViewProgressFinishedNotification object:nil];
    
    return self;
}

- (void)dealloc
{
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self];
    
    [super dealloc];
}

- (void)awakeFromNib
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Configure tab view
    [_tabView removeTabViewItem:[_tabView tabViewItemAtIndex:0]];
    
    // Get pages
    NSArray*    pages;
    pages = [defaults objectForKey:SRPageHolderPages];
    if (pages) {
        NSEnumerator*   enumerator;
        NSDictionary*   page;
        enumerator = [pages objectEnumerator];
        while (page = [enumerator nextObject]) {
            // Get URL and title
            NSString*   URLString;
            URLString = [page objectForKey:@"URLString"];
            if (!URLString) {
                continue;
            }
            
            NSString*   title;
            title = [page objectForKey:@"title"];
            if (!title) {
                title = NSLocalizedString(@"Untitled", nil);
            }
            
            // Add page
            [self _addPageWithURL:URLString title:title select:NO];
        }
    }
    
    // Configure control buttons
    [self _updateControlButtons];
}

//--------------------------------------------------------------//
#pragma mark -- First responder --
//--------------------------------------------------------------//

- (BOOL)acceptsFirstResponder
{
    return YES;
}

- (void)_updateNextResponder
{
#if 0
    // Get selected table
    id  table;
    table = [self selectedHistoryView];
    
    if ([table nextResponder] != self) {
        [self setNextResponder:[table nextResponder]];
        [table setNextResponder:self];
    }
#endif
}

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

- (SRMainWindowController*)mainWindowController
{
    return _mainWindowController;
}

- (void)setMainWindowController:(SRMainWindowController*)mainWindowController
{
    _mainWindowController = mainWindowController;
}

- (SRSideBarController*)sideBarController
{
    return _sideBarController;
}

- (void)setSidebarController:(SRSideBarController*)sideBarController
{
    _sideBarController = sideBarController;
}

//--------------------------------------------------------------//
#pragma mark -- View --
//--------------------------------------------------------------//

- (NSView*)view
{
    return _pageHolderView;
}

- (NSTabViewItem*)_tabViewItemOfWebView:(WebView*)webView
{
    int i;
    for (i = 0; i < [_tabView numberOfTabViewItems]; i++) {
        NSTabViewItem*  item;
        item = [_tabView tabViewItemAtIndex:i];
        if ([webView isDescendantOf:[item view]]) {
            return item;
        }
    }
    
    return nil;
}

- (void)_addPageWithURL:(NSString*)URLString title:(NSString*)title select:(BOOL)select
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Find tab view imte
    int             index;
    NSTabViewItem*  item;
    index = [_tabView indexOfTabViewItemWithIdentifier:URLString];
    if (index != NSNotFound && index < [_tabView numberOfTabViewItems]) {
        item = [_tabView tabViewItemAtIndex:index];
        if (item) {
            if (select) {
                [_tabView selectTabViewItem:item];
            }
            return;
        }
    }
    
    // Create tab view item
    item = [[NSTabViewItem alloc] initWithIdentifier:URLString];
    [item autorelease];
    [item setLabel:title];
    
    // Duplicate tab content view
    NSView* contentView;
    contentView = SRCopyView(_tabContentView);
    
    // Get web view
    WebView*    webView;
    webView = SRViewWithClass(contentView, [WebView class]);
    if (webView) {
        [webView setFrameLoadDelegate:self];
        [webView setResourceLoadDelegate:self];
        [webView setUIDelegate:self];
        [webView setPolicyDelegate:self];
        [webView setPreferencesIdentifier:SRWebPreferencesIdentifier];
    }
    
    // Set content view
    [item setView:contentView];
    [_tabView addTabViewItem:item];
    
    // Select tab and load URL
    if (select || [_tabView numberOfTabViewItems] == 1) {
        // Select tab
        [_tabView selectTabViewItem:item];
        
        if (URLString) {
            // Load request
            [[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:URLString]]];
        }
    }
    
    // Find page in default
    NSArray*    pages;
    pages = [defaults objectForKey:SRPageHolderPages];
    if (!pages) {
        pages = [NSArray array];
    }
    
    NSEnumerator*   enumerator;
    NSDictionary*   page;
    enumerator = [pages objectEnumerator];
    while (page = [enumerator nextObject]) {
        if ([URLString isEqualToString:[page objectForKey:@"URLString"]]) {
            break;
        }
    }
    
    if (!page) {
        // Create page
        NSDictionary*   page;
        page = [NSDictionary dictionaryWithObjectsAndKeys:
                title, @"title", 
                URLString, @"URLString", 
                nil];
        
        // Add page
        NSMutableArray* newPages;
        newPages = [NSMutableArray arrayWithArray:pages];
        [newPages addObject:page];
        
        [defaults setObject:newPages forKey:SRPageHolderPages];
    }
}

- (void)_updateControlButtons
{
    // Get front web view
    WebView*    webView;
    webView = SRViewWithClass([[_tabView selectedTabViewItem] view], [WebView class]);
    
    // Get data source and URL string
    WebDataSource*  dataSource;
    NSString*       URLString;
    dataSource = [[webView mainFrame] dataSource];
    if (!dataSource) {
        dataSource = [[webView mainFrame] provisionalDataSource];
    }
    URLString = [[[dataSource request] URL] absoluteString];
    
    // For remove button
    [_content setValue:[NSNumber numberWithBool:webView != nil] 
            forKey:SRSBPageHolderEnableToRemove];
    
    // For reload button
    if ([URLString isEqualToString:@"about:blank"]) {
        [_content setValue:[NSNumber numberWithBool:NO] 
                forKey:SRSBPageHolderEnableToReload];
    }
    else {
        [_content setValue:[NSNumber numberWithBool:dataSource != nil && ![dataSource isLoading]] 
                forKey:SRSBPageHolderEnableToReload];
    }
    
    // For stop button
    if ([URLString isEqualToString:@"about:blank"]) {
        [_content setValue:[NSNumber numberWithBool:NO] 
                forKey:SRSBPageHolderEnableToStop];
    }
    else {
        [_content setValue:[NSNumber numberWithBool:dataSource != nil && [dataSource isLoading]] 
                forKey:SRSBPageHolderEnableToStop];
    }
    
    // For make text smaller button
    if ([URLString isEqualToString:@"about:blank"]) {
        [_content setValue:[NSNumber numberWithBool:NO] 
                forKey:SRSBPageHolderEnableToMakeTextSmaller];
    }
    else {
        [_content setValue:[NSNumber numberWithBool:[webView canMakeTextSmaller]] 
                forKey:SRSBPageHolderEnableToMakeTextSmaller];
    }
    
#if 0
    // For make text standard button
    if ([URLString isEqualToString:@"about:blank"]) {
        [_content setValue:[NSNumber numberWithBool:NO] 
                forKey:SRSBPageHolderEnableToMakeTextStandard];
    }
    else {
        [_content setValue:[NSNumber numberWithBool:[webView canMakeTextStandardSize]] 
                forKey:SRSBPageHolderEnableToMakeTextLarger];
    }
#endif
    
    // For make text larger button
    if ([URLString isEqualToString:@"about:blank"]) {
        [_content setValue:[NSNumber numberWithBool:NO] 
                forKey:SRSBPageHolderEnableToMakeTextLarger];
    }
    else {
        [_content setValue:[NSNumber numberWithBool:[webView canMakeTextLarger]] 
                forKey:SRSBPageHolderEnableToMakeTextLarger];
    }
    
    // For progress
    if ([URLString isEqualToString:@"about:blank"]) {
        [_content setValue:[NSNumber numberWithBool:NO] 
                forKey:SRSBPageHolderIsLoading];
    }
    else {
        [_content setValue:[NSNumber numberWithBool:dataSource != nil && [dataSource isLoading]] 
                forKey:SRSBPageHolderIsLoading];
    }
}

//--------------------------------------------------------------//
#pragma mark -- Actions --
//--------------------------------------------------------------//

- (void)addPageAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get selected web view from main window controller
    WebView*    mainWebView;
    mainWebView = [_mainWindowController selectedWebView];
    
    // Get title
    NSString*   title;
    title = [[[mainWebView mainFrame] dataSource] pageTitle];
    if (!title) {
        title = NSLocalizedString(@"Untitled", nil);
    }
    
    // Get URL request
    NSURLRequest*   request;
    request = [[[mainWebView mainFrame] dataSource] request];
    
    // Add page
    [self _addPageWithURL:[[request URL] absoluteString] title:title select:YES];
}

- (void)removePageAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get selected tab view item
    NSTabViewItem*  item;
    NSString*       URLString;
    item = [_tabView selectedTabViewItem];
    URLString = [item identifier];
    
    // Remove page
    if (URLString) {
        NSArray*    pages;
        pages = [defaults objectForKey:SRPageHolderPages];
        if (pages && [pages count] > 0) {
            NSEnumerator*   enumerator;
            NSDictionary*   page;
            enumerator = [pages objectEnumerator];
            while (page = [enumerator nextObject]) {
                if ([URLString isEqualToString:[page objectForKey:@"URLString"]]) {
                    break;
                }
            }
            
            if (page) {
                NSMutableArray* newPages;
                newPages = [NSMutableArray arrayWithArray:pages];
                [newPages removeObject:page];
                
                [defaults setObject:newPages forKey:SRPageHolderPages];
            }
        }
    }
    
    // Remove tab view item
    [_tabView removeTabViewItem:item];
}

- (void)reloadAction:(id)sender
{
    // Get front web view
    WebView*    webView;
    webView = SRViewWithClass([[_tabView selectedTabViewItem] view], [WebView class]);
    
    // Realod page
    if (webView) {
        [webView reload:self];
    }
}

- (void)stopAction:(id)sender
{
    // Get front web view
    WebView*    webView;
    webView = SRViewWithClass([[_tabView selectedTabViewItem] view], [WebView class]);
    
    // Stop loading
    if (webView) {
        [webView stopLoading:self];
    }
}

- (void)makeTextSmallerAction:(id)sender
{
    // Get front web view
    WebView*    webView;
    webView = SRViewWithClass([[_tabView selectedTabViewItem] view], [WebView class]);
    
    // Stop loading
    if (webView) {
        [webView makeTextSmaller:self];
    }
}

- (void)makeTextLargerAction:(id)sender
{
    // Get front web view
    WebView*    webView;
    webView = SRViewWithClass([[_tabView selectedTabViewItem] view], [WebView class]);
    
    // Stop loading
    if (webView) {
        [webView makeTextLarger:self];
    }
}

- (void)makeTextStandardAction:(id)sender
{
#if 0
    [_webView makeTextStandardSize:self];
#endif
}

- (void)openLinkInNewTabAction:(id)sender
{
    // For NSMenuItem
    if ([sender respondsToSelector:@selector(representedObject)]) {
        id  representedObject;
        representedObject = [sender representedObject];
        
        // For NSURL
        if ([representedObject isKindOfClass:[NSURL class]]) {
            // Open request in new tab
            [_mainWindowController openInNewTabURL:representedObject select:YES];
        }
    }
}

- (void)openLinkInNewBackgroundTabAction:(id)sender
{
    // For NSMenuItem
    if ([sender respondsToSelector:@selector(representedObject)]) {
        id  representedObject;
        representedObject = [sender representedObject];
        
        // For NSURL
        if ([representedObject isKindOfClass:[NSURL class]]) {
            // Open request in new tab
            [_mainWindowController openInNewTabURL:representedObject select:NO];
        }
    }
}

- (void)openAllLinksInNewTabsAction:(id)sender
{
    // For NSMenuItem
    if ([sender respondsToSelector:@selector(representedObject)]) {
        id  representedObject;
        representedObject = [sender representedObject];
        
        // For NSArray
        if ([representedObject isKindOfClass:[NSArray class]]) {
            // Open request in new tabs
            [_mainWindowController openInNewTabsURLStrings:representedObject select:YES];
        }
    }
}

- (void)openAllLinksInNewBackgroundTabsAction:(id)sender
{
    // For NSMenuItem
    if ([sender respondsToSelector:@selector(representedObject)]) {
        id  representedObject;
        representedObject = [sender representedObject];
        
        // For NSArray
        if ([representedObject isKindOfClass:[NSArray class]]) {
            // Open request in background tabs
            [_mainWindowController openInNewTabsURLStrings:representedObject select:NO];
        }
    }
}

- (void)viewPageSourceAction:(id)sender
{
    // Get page source
    WebDataSource*  dataSource = nil;
    SRXMLDocument*  document = nil;
    
	// Check from context menu
	id  repObj;
    repObj = [sender representedObject];
	
    // For WebFrame
    if ([repObj isKindOfClass:[WebFrame class]]) {
		dataSource = [repObj dataSource];
    }
    // For XML document
    else if ([repObj isKindOfClass:NSClassFromString(@"SRXMLDocument")]) {
        document = repObj;
    }
    else {
        dataSource = [[[_mainWindowController selectedWebView] mainFrame] dataSource];
    }
    
    // View page source
    SRSourceWindowController*   sourceWindowController = nil;
    if (dataSource) {
        sourceWindowController = [SRSourceWindowController sourceWindowWithWebDataSource:dataSource];
    }
    else if (document) {
        sourceWindowController = [SRSourceWindowController sourceWindowWithXMLDocument:document];
    }
    
    if (sourceWindowController) {
        [sourceWindowController showWindow:self];
    }
}

- (void)copyImageURLToClipboardAction:(id)sender
{
    if ([sender respondsToSelector:@selector(representedObject)]) {
        id  representedObject;
        representedObject = [sender representedObject];
        
        // For NSURL
        if ([representedObject isKindOfClass:[NSURL class]]) {
            NSPasteboard*   pb;
            pb = [NSPasteboard generalPasteboard];
            
            [pb declareTypes:[NSArray arrayWithObjects:NSStringPboardType,NSURLPboardType,nil] owner:nil];
            [representedObject writeToPasteboard:pb];
            [pb setString:[representedObject _web_userVisibleString] forType:NSStringPboardType];
        }
    }
}

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

- (void)webView:(WebView*)webView 
        didStartProvisionalLoadForFrame:(WebFrame*)frame
{
    // Update appearance
    [self _updateControlButtons];
}

- (void)webView:(WebView*)webView 
        didCommitLoadForFrame:(WebFrame*)frame
{
    // Get URL
    NSString*   URLString;
    URLString = [[[[frame dataSource] request] URL] absoluteString];
    
    // Get tab view item of web view
    NSTabViewItem*  item;
    item = [self _tabViewItemOfWebView:webView];
    if (item) {
        if ([URLString isEqualToString:[item identifier]]) {
            // Set idnetifier
            [item setIdentifier:URLString];
        }
    }
}

- (void)webView:(WebView*)webView 
        didReceiveTitle:(NSString*)title 
        forFrame:(WebFrame*)frame
{
    if (frame != [webView mainFrame]) {
        return;
    }
    
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get URL
    NSString*   URLString;
    URLString = [[[[frame dataSource] request] URL] absoluteString];
    if (!URLString) {
        return;
    }
    
    // Get tab view item with URL
    int index;
    index = [_tabView indexOfTabViewItemWithIdentifier:URLString];
    if (index != NSNotFound) {
        NSTabViewItem*  item;
        item = [_tabView tabViewItemAtIndex:index];
        
        // Set label
        [item setLabel:title];
    }
    
    // Get page
    NSArray*    pages;
    pages = [defaults objectForKey:SRPageHolderPages];
    if (pages && [pages count] > 0) {
        NSEnumerator*   enumerator;
        NSDictionary*   page;
        enumerator = [pages objectEnumerator];
        while (page = [enumerator nextObject]) {
            // Get URL string and title
            NSString*   pageURLString;
            NSString*   pageTitle;
            pageURLString = [page objectForKey:@"URLString"];
            pageTitle = [page objectForKey:@"title"];
            
            if ([URLString isEqualToString:pageURLString] && 
                ![title isEqualToString:pageTitle])
            {
                break;
            }
        }
        
        if (page) {
            NSDictionary*   newPage;
            NSMutableArray* newPages;
            newPage = [NSDictionary dictionaryWithObjectsAndKeys:
                    title, @"title", 
                    URLString, @"URLString", 
                    nil];
            newPages = [NSMutableArray arrayWithArray:pages];
            [newPages replaceObjectAtIndex:[newPages indexOfObject:page] withObject:newPage];
            
            [defaults setObject:newPages forKey:SRPageHolderPages];
        }
    }
}

- (void)webView:(WebView*)webView 
        didFinishLoadForFrame:(WebFrame*)frame
{
    // Update appearance
    [self _updateControlButtons];
}

- (void)webView:(WebView*)webView 
        didFailProvisionalLoadWithError:(NSError*)error 
        forFrame:(WebFrame*)frame
{
    // Update appearance
    [self _updateControlButtons];
}

- (void)webView:(WebView*)webView 
        didFailLoadWithError:(NSError*)error 
        forFrame:(WebFrame*)frame
{
    // Update appearance
    [self _updateControlButtons];
}

//--------------------------------------------------------------//
#pragma mark -- NSTabView delegate --
//--------------------------------------------------------------//

- (void)tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem
{
    // Get web view
    WebView*    webView;
    webView = SRViewWithClass([tabViewItem view], [WebView class]);
    if (webView) {
        // Get data source
        if (![[webView mainFrame] dataSource]) {
            // Load URL
            NSString*   URLString;
            URLString = [tabViewItem identifier];
            if (URLString) {
                [[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:URLString]]];
            }
        }
    }
    
    // Update appearance
    [self _updateControlButtons];
}

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

- (void)webView:(WebView*)webView 
        decidePolicyForNavigationAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        frame:(WebFrame*)frame 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    // Get navigation type
    int navigationType;
    navigationType = [[info objectForKey:WebActionNavigationTypeKey] intValue];
    
    // For link click
    if (navigationType == WebNavigationTypeLinkClicked) {
        // Open in main window controller
        [_mainWindowController openURL:[request URL]];
        
        // Cancel this reqeust
        [listener ignore];
        
        return;
    }
    
    [listener use];
}

- (void)webView:(WebView*)webView 
        decidePolicyForNewWindowAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        newFrameName:(NSString*)frameName 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    // Get navigation type
    int navigationType;
    navigationType = [[info objectForKey:WebActionNavigationTypeKey] intValue];
    
    // For link click
    if (navigationType == WebNavigationTypeLinkClicked) {
        // Open in main window controller
        [_mainWindowController openURL:[request URL]];
        
        // Cancel this reqeust
        [listener ignore];
        
        return;
    }
    
    // Ignore anyway
    [listener ignore];
}

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

- (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];
    
    static int  _allowedMenuItemTags[] = {
        WebMenuItemTagCopyLinkToClipboard, WebMenuItemTagCopyImageToClipboard, 
        WebMenuItemTagCopy, WebMenuItemTagStop, 
        WebMenuItemTagReload, 
    };
    
    // Collect allowed menu items
    NSEnumerator*   enumerator;
    enumerator = [defaultMenuItems objectEnumerator];
    while (menuItem = [enumerator nextObject]) {
        int tag;
        tag = [menuItem tag];
        
        // For separator
        if ([menuItem isSeparatorItem]) {
            if ([allowedDefaultMenuItems count] != 0 && 
                ![[allowedDefaultMenuItems lastObject] isSeparatorItem])
            {
                [allowedDefaultMenuItems addObject:menuItem];
            }
            continue;
        }
        
        int i;
        for (i = 0; i < sizeof(_allowedMenuItemTags) / sizeof(int); i++) {
            if (tag == _allowedMenuItemTags[i]) {
                [allowedDefaultMenuItems addObject:menuItem];
                continue;
            }
        }
    }
    
    // Get selected links
    NSArray*    linkURLStrings;
    linkURLStrings = [webView selectedLinks];
    
    // Link is contained
    if (linkURL) {
        // Copy default menu item
        [items addObjectsFromArray:allowedDefaultMenuItems];
        
        // 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:self];
                    [menuItem setRepresentedObject:linkURL];
                    [items insertObject:menuItem atIndex:0];
                    
                    // Add 'Open All Links in New Tabs'
                    if ([linkURLStrings count] > 1) {
                        menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenAllLinksInNewTabsTag target:self];
                        [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 Tab'
                    menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenLinkInNewBackgroundTabTag target:self];
                    [menuItem setRepresentedObject:linkURL];
                    [items insertObject:menuItem atIndex:0];
                    
                    // Add 'Open All Links in New Background Tabs'
                    if ([linkURLStrings count] > 1) {
                        menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenAllLinksInNewBackgroundTabsTag target:self];
                        [menuItem setTitle:[NSString stringWithFormat:[menuItem title], [linkURLStrings count]]];
                        [menuItem setRepresentedObject:linkURLStrings];
                        [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:SROpenLinkInNewBackgroundTabTag];
                    }
                }
            }
        }
    }
    
    // Link is not contained
    else {
        // Text is selected
        if (isSelected) {
            // Copy default menu item
            [items addObjectsFromArray:allowedDefaultMenuItems];
            
            // 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:self];
                    [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:self];
                    [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:self];
                [menuItem setRepresentedObject:[element objectForKey:WebElementFrameKey]]; // WebFrame
                [items addObject:menuItem];
            }
        }
    }
    
    // Image is selected
    if (imageURL) {
        // Add 'Copy Image Location to Clipboard'
        menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SRCopyImageURLTag target:self];
        [menuItem setRepresentedObject:imageURL];
        [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:WebMenuItemTagCopyImageToClipboard];
        
        // For tabbed browsing
        if (enableTabbedBrowsing) {
            if ((selectNewTabs && !shiftFlag) || 
                (!selectNewTabs && shiftFlag))
            {
                // Add 'Open Image in New Tab'
                menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenImageInNewTabTag target:self];
                [menuItem setRepresentedObject:imageURL];
                [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:WebMenuItemTagOpenImageInNewWindow];
            }
            else {
                // Add 'Open Image in New Background Tab'
                menuItem = [SRContextMenu copyMenuItemFrom:menu ofTag:SROpenImageInNewBackgroundTabTag target:self];
                [menuItem setRepresentedObject:imageURL];
                [SRContextMenu insertMenuItem:menuItem inItems:items afterTag:SROpenImageInNewBackgroundWindowTag];
            }
        }
    }
    
    return items;
}

//--------------------------------------------------------------//
#pragma mark -- WebView notifications --
//--------------------------------------------------------------//

- (void)webViewProgressStarted:(NSNotification*)notification
{
    if ([[notification object] isDescendantOf:_tabView]) {
        // Update appearance
        [self _updateControlButtons];
    }
}

- (void)webViewProgressFinished:(NSNotification*)notification
{
    // Update appearance
    [self _updateControlButtons];
}

@end
