/*
WebKitEx.h

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 <objc/objc-runtime.h>

#import "SRDefaultsKey.h"
#import "SRMainWindowController.h"
#import "SRBookmark.h"
#import "SRBookmarkStorage.h"
#import "SRPasteboard.h"
#import "SRWebView.h"
#import "WebKitEx.h"

// Flag for wheel scroll
// This value is used by WebViewEx and WebHTMLViewEx
static BOOL _isWheelTabScroll = NO;

@implementation WebViewEx

+ (void)load
{
    NSAutoreleasePool*  pool;
    pool = [[NSAutoreleasePool alloc] init];
    [self poseAsClass:[WebView class]];
    [pool release];
}

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (!self) {
        return self;
    }
    
    // Register bookmark type for dragging
    NSArray*    types;
    types = [NSArray arrayWithObjects:SRBookmarkPboardType, nil];
    [self registerForDraggedTypes:types];
    
    return self;
}

- (void)mouseDown:(NSEvent*)event
{
    _isWheelTabScroll = NO;
    
    [super mouseDown:event];
}

- (void)rightMouseDown:(NSEvent*)event
{
    _isWheelTabScroll = NO;
    
    [super rightMouseDown:event];
}

- (void)otherMouseDown:(NSEvent*)event
{
    if ([event clickCount] > 1) {
        // Wheel tab scroll mode
        _isWheelTabScroll = YES;
        return;
    }
    _isWheelTabScroll = NO;
    
    [super otherMouseDown:event];
}

- (void)scrollWheel:(NSEvent*)event
{
    if (_isWheelTabScroll) {
        SRMainWindowController* windowController;
        windowController = [[self window] windowController];
        
        // Scroll tab
        float   deltaY;
        deltaY = [event deltaY];
        if (deltaY < 0) {
            [windowController selectLeftTab];
        }
        if (deltaY > 0) {
            [windowController selectRightTab];
        }
        
        return;
    }
    
    [super scrollWheel:event];
}

- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info
{
    // Check dragged data type
    NSPasteboard*   pboard;
    NSArray*        types;
    pboard = [info draggingPasteboard];
    types = [pboard types];
    
    // For bookmark type
    if ([types containsObject:SRBookmarkPboardType]) {
        return NSDragOperationCopy;
    }
    
    return [super draggingEntered:info];
}

- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info
{
    // Check dragged data type
    NSPasteboard*   pboard;
    NSArray*        types;
    pboard = [info draggingPasteboard];
    types = [pboard types];
    
    // For bookmark type
    if ([types containsObject:SRBookmarkPboardType]) {
        return NSDragOperationCopy;
    }
    
    return [super draggingUpdated:info];
}

- (BOOL)performDragOperation:(id<NSDraggingInfo>)info
{
    // Check dragged data type
    NSPasteboard*   pboard;
    NSArray*        types;
    pboard = [info draggingPasteboard];
    types = [pboard types];
    
    // For bookmark type
    if ([types containsObject:SRBookmarkPboardType]) {
        // Read bookmakrs
        NSArray*    bookmarks;
        bookmarks = SRReadBookmarksFromPasteboard(pboard);
        if (bookmarks) {
            // Get all bookmarks in folder
            NSMutableArray* extractedBookmarks;
            extractedBookmarks = [NSMutableArray array];
            
            NSEnumerator*   enumerator;
            SRBookmark*     bookmark;
            enumerator = [bookmarks objectEnumerator];
            while (bookmark = [enumerator nextObject]) {
                if ([bookmark isFolderType]) {
                    [extractedBookmarks addObjectsFromArray:[bookmark children]];
                }
                else {
                    [extractedBookmarks addObject:bookmark];
                }
            }
            
            // Open bookmarks in tabs
            if ([extractedBookmarks count] > 1) {
                id  windowController;
                windowController = [[self window] windowController];
                if ([windowController isKindOfClass:[SRMainWindowController class]]) {
                    [windowController openInTabsBookmarks:extractedBookmarks];
                }
                
                return YES;
            }
            
            // Open bookmark
            if ([extractedBookmarks count] == 1) {
                id  windowController;
                windowController = [[self window] windowController];
                if ([windowController isKindOfClass:[SRMainWindowController class]]) {
                    [windowController openBookmark:[extractedBookmarks objectAtIndex:0]];
                }
                
                return YES;
            }
        }
    }
    
    return [super performDragOperation:info];
}

- (void)concludeDragOperation:(id<NSDraggingInfo>)info
{
    // Check dragged data type
    NSPasteboard*   pboard;
    NSArray*        types;
    pboard = [info draggingPasteboard];
    types = [pboard types];
    
    // For bookmark type
    if ([types containsObject:SRBookmarkPboardType]) {
        // Do nothing
        // To avoid WebView default performance
        return;
    }
    
    [super concludeDragOperation:info];
}

@end

#pragma mark -

@implementation WebView (DocumentText)

- (NSString*)_selectedStringOfChildFrame:(WebFrame*)frame
{
    // Get document view
    id  documentView;
    documentView = [[frame frameView] documentView];
    if (documentView) {
        // Get selected string
        if ([documentView respondsToSelector:@selector(selectedString)]) {
            NSString*   selectedString;
            selectedString = [documentView selectedString];
            if (selectedString && [selectedString length] > 0) {
                return selectedString;
            }
        }
    }
    
    // Find in child frames
    NSArray*    childFrames;
    childFrames = [frame childFrames];
    if (!childFrames) {
        return nil;
    }
    
    NSEnumerator*   enumerator;
    WebFrame*       childFrame;
    enumerator = [childFrames objectEnumerator];
    while (childFrame = [enumerator nextObject]) {
        NSString*   selectedString;
        selectedString = [self _selectedStringOfChildFrame:childFrame];
        if (selectedString && [selectedString length] > 0) {
            return selectedString;
        }
    }
    
    return nil;
}

- (NSString*)selectedString
{
    return [self _selectedStringOfChildFrame:[self mainFrame]];
}

@end

#pragma mark -
//--------------------------------------------------------------//
// WebHTMLViewEx
//--------------------------------------------------------------//

@implementation WebHTMLViewEx

+ (void)load
{
    NSAutoreleasePool*  pool;
    pool = [[NSAutoreleasePool alloc] init];
    [self poseAsClass:[WebHTMLView class]];
    [pool release];
}

- (BOOL)_isEventInSubframe:(NSEvent*)event
{
	// Check frame page and form element
	NSArray*        subviews;
    NSEnumerator*   enumerator;
    NSView*         subview;
    
    subviews = [self subviews];
    enumerator = [subviews objectEnumerator];
    while (subview = [enumerator nextObject]) {
		NSPoint point;
		point = [self convertPoint:[event locationInWindow] fromView:nil];
        
        if (NSPointInRect(point, [subview frame])) {
            return YES;
        }
    }
    
    return NO;
}

- (void)mouseDown:(NSEvent*)event
{
    // Clear tab scrll by wheel scroll flag
    _isWheelTabScroll = NO;
    
	// Check frame page and form element
    if ([self _isEventInSubframe:event]) {
        // Ignore it
        [super mouseDown:event];
        return;
    }
    
    // Show context menu by mouse press holding
    if (SRShowContextMenuByMousePressHolding(self, event)) {
        return;
    }
    
    [super mouseDown:event];
}

- (void)rightMouseDown:(NSEvent*)event
{
    _isWheelTabScroll = NO;
    
    [super rightMouseDown:event];
}

- (void)otherMouseDown:(NSEvent*)event
{
    if ([event clickCount] > 1) {
        // Wheel tab scroll mode
        _isWheelTabScroll = YES;
        return;
    }
    _isWheelTabScroll = NO;
    
    // Create left mouse down with command modifier event
    NSEvent*    leftDownEvent;
    leftDownEvent = [NSEvent mouseEventWithType:NSLeftMouseDown 
            location:[event locationInWindow] 
            modifierFlags:([event modifierFlags] | NSCommandKeyMask) 
            timestamp:[event timestamp] 
            windowNumber:[event windowNumber] 
            context:[event context] 
            eventNumber:[event eventNumber] 
            clickCount:[event clickCount] 
            pressure:[event pressure]];
    
    [super mouseDown:leftDownEvent];
}

- (void)otherMouseUp:(NSEvent*)event
{
    // Create left mouse up with command modifier event
    NSEvent*    leftUpEvent;
    leftUpEvent = [NSEvent mouseEventWithType:NSLeftMouseUp 
            location:[event locationInWindow] 
            modifierFlags:([event modifierFlags] | NSCommandKeyMask) 
            timestamp:[event timestamp] 
            windowNumber:[event windowNumber] 
            context:[event context] 
            eventNumber:[event eventNumber] 
            clickCount:[event clickCount] 
            pressure:[event pressure]];
    
    [self mouseUp:leftUpEvent];
}

- (void)scrollWheel:(NSEvent*)event
{
    if (_isWheelTabScroll) {
        SRMainWindowController* windowController;
        windowController = [[self window] windowController];
        
        // Scroll tab
        float   deltaY;
        deltaY = [event deltaY];
        if (deltaY > 0) {
            [windowController selectLeftTab];
        }
        if (deltaY < 0) {
            [windowController selectRightTab];
        }
        
        return;
    }
    
    [super scrollWheel:event];
}

@end

#pragma mark -
//--------------------------------------------------------------//
// WebClipViewEx
//--------------------------------------------------------------//

#if 0
@implementation WebClipViewEx

+ (void)load
{
    NSAutoreleasePool*  pool;
    pool = [[NSAutoreleasePool alloc] init];
    [self poseAsClass:[WebClipView class]];
    [pool release];
}

- (NSMenu*)menuForEvent:(NSEvent*)event
{
    // Get UI delegate of web view
    NSView*     view = self;
    WebView*    webView = nil;
    id          delegate = nil;
    while (view) {
        view = [view superview];
        if ([view isKindOfClass:[WebView class]]) {
            webView = (WebView*)view;
            delegate = [webView UIDelegate];
            break;
        }
    }
    if (delegate) {
        NSArray*    items;
        items = [delegate webView:webView 
                contextMenuItemsForElement:[NSDictionary dictionary] 
                defaultMenuItems:[NSArray array]];
        if (!items || [items count] == 0) {
            return nil;
        }
        
        // Create menu
        NSMenu* menu;
        menu = [[NSMenu alloc] initWithTitle:@""];
        [menu autorelease];
        
        NSEnumerator*   enumerator;
        NSMenuItem*     item;
        enumerator = [items objectEnumerator];
        while (item = [enumerator nextObject]) {
            [menu addItem:item];
        }
        
        return menu;
    }
    
    return [super menuForEvent:event];
}

@end
#endif

#pragma mark -
//--------------------------------------------------------------//
// WebImageViewEx_pseudo
//--------------------------------------------------------------//

NSString*    SRWebImageSizeChangeNotification = @"SRWebImageSizeChangeNotification";

@implementation WebImageViewEx_pseudo

static NSMutableDictionary* _webImageViewAutoResizeModeDict = nil;

+ (void)load
{
    NSAutoreleasePool*  pool;
    pool = [[NSAutoreleasePool alloc] init];
    
    struct objc_class*  newClass;
    struct objc_class*  rootClass;
    struct objc_class*  superClass;
    struct objc_class*  metaClass;
    
    // Get super class WebImageView
    superClass = (struct objc_class*)objc_lookUpClass("WebImageView");
    if (superClass && objc_lookUpClass("WebImageViewEx") == NULL) {
        // Get root class
        rootClass = superClass;
        while (rootClass->super_class != nil) {
            rootClass = rootClass->super_class;
        }
        
        // Allocate memory for new class
        newClass = calloc(2, sizeof(struct objc_class));
        metaClass = &newClass[1];
        
        // Setup class
        newClass->isa = metaClass;
        newClass->info = CLS_CLASS;
        metaClass->info = CLS_META;
        newClass->name = "WebImageViewEx";
        metaClass->name = newClass->name;
        
        // Setup methods
        newClass->methodLists = calloc(1, sizeof(struct objc_method_list*));
        metaClass->methodLists = calloc(1, sizeof(struct objc_method_list*));
        
        void*   iterator = 0;
        struct objc_method_list*    methodList;
        
        // Add instance methods
        methodList = class_nextMethodList(self, &iterator);
        while (methodList != NULL) {
            class_addMethods(newClass, methodList);
            methodList = class_nextMethodList(self, &iterator);
        }
        
        // Add class methods
        methodList = class_nextMethodList([self class]->isa, &iterator);
        while (methodList != NULL) {
            class_addMethods(metaClass, methodList);
            methodList = class_nextMethodList(self, &iterator);
        }
        
        // Setup super class
        newClass->super_class = superClass;
        metaClass->super_class = superClass->isa;
        metaClass->isa = (void*)rootClass->isa;
        
        newClass->instance_size = superClass->instance_size;
        newClass->ivars = NULL;
        metaClass->instance_size = metaClass->instance_size;
        metaClass->ivars = NULL;
        
        // Register new class
        objc_addClass(newClass);
        
        // Pose as WebViewImage
        class_poseAs(newClass, superClass);
    }
    
    [pool release];
}

+ (void)initialize
{
    if (!_webImageViewAutoResizeModeDict) {
        _webImageViewAutoResizeModeDict = [[NSMutableDictionary dictionary] retain];
    }
}

- (WebView*)_webView
{
    return [self performSelector:@selector(webView)];
}

+ (NSArray*)_supportedImageMIMETypes
{
    return [self performSelector:@selector(supportedImageMIMETypes)];
}

- (WebImageRepresentation*)_rep
{
    id  value;
    object_getInstanceVariable(self, "rep", (void**)&value);
    return value;
}

- (BOOL)isViewingImageFile
{
    // Get MIME type from response
    WebDataSource*  mainDataSource;
    NSString*       MIMEType;
    mainDataSource = [[[self _webView] mainFrame] dataSource];
    if (!mainDataSource) {
        return NO;
    }
    MIMEType = [[mainDataSource response] MIMEType];
    if (!MIMEType) {
        return NO;
    }
    
    // Check MIME type
    return [[[self class] _supportedImageMIMETypes] containsObject:MIMEType];
}

- (NSSize)_imageSize
{
    // Get original image size
    WebImageRenderer*   webImageRenderer;
    webImageRenderer = [[self _rep] image];
    
    // Get WebImageData for Tiger
    WebImageData*   webImageData;
    object_getInstanceVariable(webImageRenderer, "imageData", (void**)&webImageData);
    if (webImageData) {
        CGSize  size;
        size = [webImageData size];
        return NSMakeSize(size.width, size.height);
    }
    
    // Get WebInternalImage for Panther
    id  webInternalImage;
    webInternalImage = [webImageRenderer image];
    if (webInternalImage) {
        NSImageRep* imageRep;
        imageRep = [webInternalImage bestRepresentationForDevice:nil];
        if (imageRep) {
            return NSMakeSize([imageRep pixelsWide], [imageRep pixelsHigh]);
        }
        //return [webInternalImage size];
    }
    
    return NSZeroSize;
}

- (BOOL)_isImageSizeLargerThanVisibleSize
{
    NSSize      imageSize;
    NSSize      visibleSize;
    imageSize = [self _imageSize];
    visibleSize = [[self _webView] visibleRect].size;
    
    return imageSize.width > visibleSize.width || 
        imageSize.height > visibleSize.height;
}

- (void)_setFrameSizeToWindow
{
    // Get original image size
    NSSize  imageSize;
    imageSize = [self _imageSize];
    
    // Get web view visible rect size
    NSSize  visibleSize;
    visibleSize = [[self _webView] visibleRect].size;
    
    // Calculate scaled size
    NSSize  scaledSize = imageSize;
    if (imageSize.width > visibleSize.width || 
        imageSize.height > visibleSize.height)
    {
        float   ratioW, ratioH;
        ratioW = visibleSize.width / imageSize.width;
        ratioH = visibleSize.height / imageSize.height;
        if (ratioW < ratioH) {
            scaledSize.width = visibleSize.width;
            scaledSize.height = imageSize.height * ratioW;
        }
        else {
            scaledSize.width = imageSize.width * ratioH;
            scaledSize.height = visibleSize.height;
        }
    }
    
    // Set Size
    NSSize  frameSize;
    frameSize = [self frame].size;
    if (!NSEqualSizes(scaledSize, imageSize) || 
        !NSEqualSizes(scaledSize, frameSize))
    {
        id  webImageRenderer;
        webImageRenderer = [[self _rep] image];
        [webImageRenderer resize:scaledSize];
        [self setFrameSize:scaledSize];
        [self setNeedsDisplay:YES];
        
        // Post notification
        [[NSNotificationCenter defaultCenter] 
                postNotificationName:SRWebImageSizeChangeNotification object:self];
    }
}

- (void)_resetFrameSize
{
    // Get original image size
    NSSize  imageSize;
    imageSize = [self _imageSize];
    
    // Reset size
    id  webImageRenderer;
    webImageRenderer = [[self _rep] image];
    [webImageRenderer resize:imageSize];
    [self setFrameSize:imageSize];
    [self setNeedsDisplay:YES];
    
    // Post notification
    [[NSNotificationCenter defaultCenter] 
            postNotificationName:SRWebImageSizeChangeNotification object:self];
}

- (BOOL)autoResizeMode
{
    NSNumber*   mode;
    mode = [_webImageViewAutoResizeModeDict 
            objectForKey:[NSNumber numberWithUnsignedInt:(unsigned int)self]];
    if (!mode) {
        // Default is YES
        return YES;
    }
    
    return [mode boolValue];
}

- (void)setAutoResizeMode:(BOOL)flag
{
    [_webImageViewAutoResizeModeDict setObject:[NSNumber numberWithBool:flag] 
            forKey:[NSNumber numberWithUnsignedInt:(unsigned int)self]];
    
    // Set frame size
    if (flag) {
        [self _setFrameSizeToWindow];
    }
    else {
        [self _resetFrameSize];
    }
}

- (void)mouseDown:(NSEvent*)event
{
    // Toggle auto resize mode
    BOOL    mode;
    mode = [self autoResizeMode];
    mode = !mode;
    [self setAutoResizeMode:mode];
    
    // Get super method
    struct  objc_method*    superMethod;
    superMethod = class_getInstanceMethod([self superclass], _cmd);
    
    // Instaed of [super mouseDown:event], use IMP directly
    superMethod->method_imp(self, _cmd, event);
}

- (void)drawRect:(NSRect)rect
{
    // Get super method
    struct  objc_method*    superMethod;
    superMethod = class_getInstanceMethod([self superclass], _cmd);
    
    // Check auto resize mode
    BOOL    mode;
    mode = [self autoResizeMode];
    if (!mode || ![self _isImageSizeLargerThanVisibleSize]) {
        // Instaed of [super drawRect:rect], use IMP directly
        superMethod->method_imp(self, _cmd, rect);
        
        return;
    }
    
    // Resize itself
    [self _setFrameSizeToWindow];
    
    // Instaed of [super drawRect:rect], use IMP directly
    superMethod->method_imp(self, _cmd, rect);
}

@end

#pragma mark -

@implementation WebFrameEx

+ (void)load
{
    NSAutoreleasePool*  pool;
    pool = [[NSAutoreleasePool alloc] init];
    [self poseAsClass:[WebFrame class]];
    [pool release];
}

- (void)_transitionToCommitted:(NSDictionary*)pageCache
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    if (![defaults boolForKey:SRUsePageTransitionEffect]) {
        [super _transitionToCommitted:pageCache];
        return;
    }
    
    id  webView;
    webView = [self webView];
    
    if ([webView mainFrame] == self) {
        if ([self _state] == WebFrameStateProvisional) {
            if ([webView respondsToSelector:@selector(prepareForTransition)]) {
                [webView prepareForTransition];
            }
        }
    }
    
    [super _transitionToCommitted:pageCache];
}

- (void)_goToItem:(WebHistoryItem*)item withLoadType:(WebFrameLoadType)type
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    if (![defaults boolForKey:SRUsePageTransitionEffect]) {
        [super _goToItem:item withLoadType:type];
        return;
    }
    
    id  webView;
    webView = [self webView];
    
    if ([webView respondsToSelector:@selector(setBackOrForward:) ]) {
        // Find item in the back and forward list
        WebBackForwardList* list;
        NSArray*            items;
        int                 backOrForward = 0;
        list = [[self webView] backForwardList];
        items = [list backListWithLimit:[list backListCount]];
        if ([items containsObject:item]) {
            // This is back
            backOrForward = -1;
        }
        else {
            items = [list forwardListWithLimit:[list forwardListCount]];
            if ([items containsObject:item]) {
                // This is forward
                backOrForward = 1;
            }
        }
        [webView setBackOrForward:backOrForward];
    }
    
    [super _goToItem:item withLoadType:type];
}

- (void)_transitionToLayoutAcceptable
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    if (![defaults boolForKey:SRUsePageTransitionEffect]) {
        [super _transitionToLayoutAcceptable];
        return;
    }
    
    id  webView;
    webView = [self webView];
    
    if ([webView mainFrame] == self) {
        if ([self _state] == WebFrameStateCommittedPage) {
            int transitionType = SRWebViewNoneTransition;
            WebFrameLoadType    loadType;
            loadType = (WebFrameLoadType)[self _loadType];
            switch (loadType) {
            case WebFrameLoadTypeStandard:
            case WebFrameLoadTypeForward: {
                transitionType = SRWebViewPageCurlTransition;
                break;
            }
            case WebFrameLoadTypeBack: {
                transitionType = SRWebViewPareCurlBackTransition;
                break;
            }
            case WebFrameLoadTypeIndexedBackForward: {
                if ([webView respondsToSelector:@selector(backOrForward)]) {
                    int backOrForward;
                    backOrForward = [webView backOrForward];
                    if (backOrForward == -1) {
                        transitionType = SRWebViewPareCurlBackTransition;
                    }
                    else {
                        transitionType = SRWebViewPageCurlTransition;
                    }
                }
                break;
            }
            case WebFrameLoadTypeReload: {
                transitionType = SRWebViewCopyMachineTransition;
                break;
            }
            }
            [super _transitionToLayoutAcceptable];
            
            if ([webView respondsToSelector:@selector(startTransitionOfType:)]) {
                [webView startTransitionOfType:transitionType];
            }
            return;
        }
    }
    [super _transitionToLayoutAcceptable];
}

@end

#pragma mark -

@implementation WebFrame (FindFrame)

- (WebFrame*)srFindFrameNamed:(NSString*)frameName
{
    // Get name
    NSString*   name;
    name = [self name];
    
    // Compare name
    if ([name isEqualToString:frameName]) {
        return self;
    }
    
    // Find in children
    NSArray*        children;
    NSEnumerator*   enumerator;
    WebFrame*       frame;
    children = [self childFrames];
    enumerator = [children objectEnumerator];
    while (frame = [enumerator nextObject]) {
        WebFrame*   childFrame;
        childFrame = [frame srFindFrameNamed:frameName];
        if (childFrame) {
            return childFrame;
        }
    }
    
    return nil;
}

@end

#pragma mark -
//--------------------------------------------------------------//
// WebURLsWithTitles
//--------------------------------------------------------------//

@implementation WebURLsWithTitles (NSURLExtras)

+ (id)_web_userVisibleURLStringsFromPasteboard:(NSPasteboard*)pboard
{
    // Get property list
    id  propertyList;
    propertyList = [pboard propertyListForType:WebURLsWithTitlesPboardType];
    if (!propertyList || ![propertyList isKindOfClass:[NSArray class]]) {
        return nil;
    }
    
    // Get URL strings
    NSArray*    URLStrings;
    URLStrings = [propertyList objectAtIndex:0];
    if (!URLStrings || ![URLStrings isKindOfClass:[NSArray class]]) {
        return nil;
    }
    
    return URLStrings;
}

@end

#pragma mark -

@implementation WebIconDatabase (IconURL)

- (NSString*)iconURLOfURL:(NSString*)URLString
{
    // Get web icon database
    WebIconDatabase*    database;
    database = [WebIconDatabase sharedIconDatabase];
    
    // Get icon database private
    void*   databasePrivate = NULL;
    object_getInstanceVariable(database, "_private", &databasePrivate);
    if (!databasePrivate) {
        return nil;
    }
    
    // Get icon URLs
    NSDictionary*   URLToIconURL = nil;
    object_getInstanceVariable(databasePrivate, "URLToIconURL", (void**)(&URLToIconURL));
    if (!URLToIconURL) {
        return nil;
    }
    
    return [URLToIconURL objectForKey:URLString];
}

@end

#pragma mark -

@implementation WebPreferences (WebPreferencesKVCSupport)

- (BOOL)isPlugInsEnabled
{
    return [self arePlugInsEnabled];
}

@end

#pragma mark -

@implementation DOMNode (SRExtension)

- (DOMNode*)firstChildOfType:(unsigned int)type
{
    return nil;
}

- (DOMNode*)firstChildOfNodeName:(NSString*)nodeName
{
    DOMNodeList*    children;
    children = [self childNodes];
    if (!children) {
        return nil;
    }
    
    unsigned long   i;
    for (i = 0; i < [children length]; i++) {
        DOMNode*    child;
        child = [children item:i];
        if (child && [[child nodeName] isEqualToString:nodeName]) {
            return child;
        }
    }
    
    return nil;
}

- (DOMNode*)firstChildOfClass:(Class)nodeClass
{
    DOMNodeList*    children;
    children = [self childNodes];
    if (!children) {
        return nil;
    }
    
    unsigned long   i;
    for (i = 0; i < [children length]; i++) {
        DOMNode*    child;
        child = [children item:i];
        if (child && [child isKindOfClass:nodeClass]) {
            return child;
        }
    }
    
    return nil;
}

- (BOOL)isDescendant:(DOMNode*)node
{
    while (node = [node parentNode]) {
        if (node == self) {
            return YES;
        }
    }
    
    return NO;
}

@end
