1/* 2 * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#import "WebElementDictionary.h" 30 31#import "DOMNodeInternal.h" 32#import "WebDOMOperations.h" 33#import "WebFrame.h" 34#import "WebFrameInternal.h" 35#import "WebKitLogging.h" 36#import "WebTypesInternal.h" 37#import "WebView.h" 38#import "WebViewPrivate.h" 39#import <WebCore/Frame.h> 40#import <WebCore/HitTestResult.h> 41#import <WebCore/Image.h> 42#import <WebCore/WebCoreObjCExtras.h> 43#import <WebKit/DOMCore.h> 44#import <WebKit/DOMExtensions.h> 45#import <runtime/InitializeThreading.h> 46 47using namespace WebCore; 48 49static CFMutableDictionaryRef lookupTable = NULL; 50 51static void addLookupKey(NSString *key, SEL selector) 52{ 53 CFDictionaryAddValue(lookupTable, key, selector); 54} 55 56static void cacheValueForKey(const void *key, const void *value, void *self) 57{ 58 // calling objectForKey will cache the value in our _cache dictionary 59 [(WebElementDictionary *)self objectForKey:(NSString *)key]; 60} 61 62@implementation WebElementDictionary 63 64+ (void)initialize 65{ 66 JSC::initializeThreading(); 67#ifndef BUILDING_ON_TIGER 68 WebCoreObjCFinalizeOnMainThread(self); 69#endif 70} 71 72+ (void)initializeLookupTable 73{ 74 if (lookupTable) 75 return; 76 77 lookupTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL); 78 79 addLookupKey(WebElementDOMNodeKey, @selector(_domNode)); 80 addLookupKey(WebElementFrameKey, @selector(_webFrame)); 81 addLookupKey(WebElementImageAltStringKey, @selector(_altDisplayString)); 82 addLookupKey(WebElementImageKey, @selector(_image)); 83 addLookupKey(WebElementImageRectKey, @selector(_imageRect)); 84 addLookupKey(WebElementImageURLKey, @selector(_absoluteImageURL)); 85 addLookupKey(WebElementIsSelectedKey, @selector(_isSelected)); 86 addLookupKey(WebElementSpellingToolTipKey, @selector(_spellingToolTip)); 87 addLookupKey(WebElementTitleKey, @selector(_title)); 88 addLookupKey(WebElementLinkURLKey, @selector(_absoluteLinkURL)); 89 addLookupKey(WebElementLinkTargetFrameKey, @selector(_targetWebFrame)); 90 addLookupKey(WebElementLinkTitleKey, @selector(_titleDisplayString)); 91 addLookupKey(WebElementLinkLabelKey, @selector(_textContent)); 92 addLookupKey(WebElementLinkIsLiveKey, @selector(_isLiveLink)); 93 addLookupKey(WebElementIsContentEditableKey, @selector(_isContentEditable)); 94 addLookupKey(WebElementIsInScrollBarKey, @selector(_isInScrollBar)); 95} 96 97- (id)initWithHitTestResult:(const HitTestResult&)result 98{ 99 [[self class] initializeLookupTable]; 100 [super init]; 101 _result = new HitTestResult(result); 102 return self; 103} 104 105- (void)dealloc 106{ 107 if (WebCoreObjCScheduleDeallocateOnMainThread([WebElementDictionary class], self)) 108 return; 109 110 delete _result; 111 [_cache release]; 112 [_nilValues release]; 113 [super dealloc]; 114} 115 116- (void)finalize 117{ 118 ASSERT_MAIN_THREAD(); 119 delete _result; 120 [super finalize]; 121} 122 123- (void)_fillCache 124{ 125 CFDictionaryApplyFunction(lookupTable, cacheValueForKey, self); 126 _cacheComplete = YES; 127} 128 129- (NSUInteger)count 130{ 131 if (!_cacheComplete) 132 [self _fillCache]; 133 return [_cache count]; 134} 135 136- (NSEnumerator *)keyEnumerator 137{ 138 if (!_cacheComplete) 139 [self _fillCache]; 140 return [_cache keyEnumerator]; 141} 142 143- (id)objectForKey:(id)key 144{ 145 id value = [_cache objectForKey:key]; 146 if (value || _cacheComplete || [_nilValues containsObject:key]) 147 return value; 148 149 SEL selector = (SEL)CFDictionaryGetValue(lookupTable, key); 150 if (!selector) 151 return nil; 152 value = [self performSelector:selector]; 153 154 unsigned lookupTableCount = CFDictionaryGetCount(lookupTable); 155 if (value) { 156 if (!_cache) 157 _cache = [[NSMutableDictionary alloc] initWithCapacity:lookupTableCount]; 158 [_cache setObject:value forKey:key]; 159 } else { 160 if (!_nilValues) 161 _nilValues = [[NSMutableSet alloc] initWithCapacity:lookupTableCount]; 162 [_nilValues addObject:key]; 163 } 164 165 _cacheComplete = ([_cache count] + [_nilValues count]) == lookupTableCount; 166 167 return value; 168} 169 170- (DOMNode *)_domNode 171{ 172 return kit(_result->innerNonSharedNode()); 173} 174 175- (WebFrame *)_webFrame 176{ 177 return [[[self _domNode] ownerDocument] webFrame]; 178} 179 180// String's NSString* operator converts null Strings to empty NSStrings for compatibility 181// with AppKit. We need to work around that here. 182static NSString* NSStringOrNil(String coreString) 183{ 184 if (coreString.isNull()) 185 return nil; 186 return coreString; 187} 188 189- (NSString *)_altDisplayString 190{ 191 return NSStringOrNil(_result->altDisplayString()); 192} 193 194- (NSString *)_spellingToolTip 195{ 196 TextDirection dir; 197 return NSStringOrNil(_result->spellingToolTip(dir)); 198} 199 200- (NSImage *)_image 201{ 202 Image* image = _result->image(); 203 return image ? image->getNSImage() : nil; 204} 205 206- (NSValue *)_imageRect 207{ 208 IntRect rect = _result->imageRect(); 209 return rect.isEmpty() ? nil : [NSValue valueWithRect:rect]; 210} 211 212- (NSURL *)_absoluteImageURL 213{ 214 return _result->absoluteImageURL(); 215} 216 217- (NSNumber *)_isSelected 218{ 219 return [NSNumber numberWithBool:_result->isSelected()]; 220} 221 222- (NSString *)_title 223{ 224 TextDirection dir; 225 return NSStringOrNil(_result->title(dir)); 226} 227 228- (NSURL *)_absoluteLinkURL 229{ 230 return _result->absoluteLinkURL(); 231} 232 233- (WebFrame *)_targetWebFrame 234{ 235 return kit(_result->targetFrame()); 236} 237 238- (NSString *)_titleDisplayString 239{ 240 return NSStringOrNil(_result->titleDisplayString()); 241} 242 243- (NSString *)_textContent 244{ 245 return NSStringOrNil(_result->textContent()); 246} 247 248- (NSNumber *)_isLiveLink 249{ 250 return [NSNumber numberWithBool:_result->isLiveLink()]; 251} 252 253- (NSNumber *)_isContentEditable 254{ 255 return [NSNumber numberWithBool:_result->isContentEditable()]; 256} 257 258- (NSNumber *)_isInScrollBar 259{ 260 return [NSNumber numberWithBool:_result->scrollbar() != 0]; 261} 262 263@end 264