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#import <wtf/Threading.h> 47 48using namespace WebCore; 49 50static CFMutableDictionaryRef lookupTable = NULL; 51 52static void addLookupKey(NSString *key, SEL selector) 53{ 54 CFDictionaryAddValue(lookupTable, key, selector); 55} 56 57static void cacheValueForKey(const void *key, const void *value, void *self) 58{ 59 // calling objectForKey will cache the value in our _cache dictionary 60 [(WebElementDictionary *)self objectForKey:(NSString *)key]; 61} 62 63@implementation WebElementDictionary 64 65+ (void)initialize 66{ 67 JSC::initializeThreading(); 68 WTF::initializeMainThreadToProcessMainThread(); 69#ifndef BUILDING_ON_TIGER 70 WebCoreObjCFinalizeOnMainThread(self); 71#endif 72} 73 74+ (void)initializeLookupTable 75{ 76 if (lookupTable) 77 return; 78 79 lookupTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL); 80 81 addLookupKey(WebElementDOMNodeKey, @selector(_domNode)); 82 addLookupKey(WebElementFrameKey, @selector(_webFrame)); 83 addLookupKey(WebElementImageAltStringKey, @selector(_altDisplayString)); 84 addLookupKey(WebElementImageKey, @selector(_image)); 85 addLookupKey(WebElementImageRectKey, @selector(_imageRect)); 86 addLookupKey(WebElementImageURLKey, @selector(_absoluteImageURL)); 87 addLookupKey(WebElementIsSelectedKey, @selector(_isSelected)); 88 addLookupKey(WebElementMediaURLKey, @selector(_absoluteMediaURL)); 89 addLookupKey(WebElementSpellingToolTipKey, @selector(_spellingToolTip)); 90 addLookupKey(WebElementTitleKey, @selector(_title)); 91 addLookupKey(WebElementLinkURLKey, @selector(_absoluteLinkURL)); 92 addLookupKey(WebElementLinkTargetFrameKey, @selector(_targetWebFrame)); 93 addLookupKey(WebElementLinkTitleKey, @selector(_titleDisplayString)); 94 addLookupKey(WebElementLinkLabelKey, @selector(_textContent)); 95 addLookupKey(WebElementLinkIsLiveKey, @selector(_isLiveLink)); 96 addLookupKey(WebElementIsContentEditableKey, @selector(_isContentEditable)); 97 addLookupKey(WebElementIsInScrollBarKey, @selector(_isInScrollBar)); 98} 99 100- (id)initWithHitTestResult:(const HitTestResult&)result 101{ 102 [[self class] initializeLookupTable]; 103 [super init]; 104 _result = new HitTestResult(result); 105 return self; 106} 107 108- (void)dealloc 109{ 110 if (WebCoreObjCScheduleDeallocateOnMainThread([WebElementDictionary class], self)) 111 return; 112 113 delete _result; 114 [_cache release]; 115 [_nilValues release]; 116 [super dealloc]; 117} 118 119- (void)finalize 120{ 121 ASSERT_MAIN_THREAD(); 122 delete _result; 123 [super finalize]; 124} 125 126- (void)_fillCache 127{ 128 CFDictionaryApplyFunction(lookupTable, cacheValueForKey, self); 129 _cacheComplete = YES; 130} 131 132- (NSUInteger)count 133{ 134 if (!_cacheComplete) 135 [self _fillCache]; 136 return [_cache count]; 137} 138 139- (NSEnumerator *)keyEnumerator 140{ 141 if (!_cacheComplete) 142 [self _fillCache]; 143 return [_cache keyEnumerator]; 144} 145 146- (id)objectForKey:(id)key 147{ 148 id value = [_cache objectForKey:key]; 149 if (value || _cacheComplete || [_nilValues containsObject:key]) 150 return value; 151 152 SEL selector = (SEL)CFDictionaryGetValue(lookupTable, key); 153 if (!selector) 154 return nil; 155 value = [self performSelector:selector]; 156 157 unsigned lookupTableCount = CFDictionaryGetCount(lookupTable); 158 if (value) { 159 if (!_cache) 160 _cache = [[NSMutableDictionary alloc] initWithCapacity:lookupTableCount]; 161 [_cache setObject:value forKey:key]; 162 } else { 163 if (!_nilValues) 164 _nilValues = [[NSMutableSet alloc] initWithCapacity:lookupTableCount]; 165 [_nilValues addObject:key]; 166 } 167 168 _cacheComplete = ([_cache count] + [_nilValues count]) == lookupTableCount; 169 170 return value; 171} 172 173- (DOMNode *)_domNode 174{ 175 return kit(_result->innerNonSharedNode()); 176} 177 178- (WebFrame *)_webFrame 179{ 180 return [[[self _domNode] ownerDocument] webFrame]; 181} 182 183// String's NSString* operator converts null Strings to empty NSStrings for compatibility 184// with AppKit. We need to work around that here. 185static NSString* NSStringOrNil(String coreString) 186{ 187 if (coreString.isNull()) 188 return nil; 189 return coreString; 190} 191 192- (NSString *)_altDisplayString 193{ 194 return NSStringOrNil(_result->altDisplayString()); 195} 196 197- (NSString *)_spellingToolTip 198{ 199 TextDirection dir; 200 return NSStringOrNil(_result->spellingToolTip(dir)); 201} 202 203- (NSImage *)_image 204{ 205 Image* image = _result->image(); 206 return image ? image->getNSImage() : nil; 207} 208 209- (NSValue *)_imageRect 210{ 211 IntRect rect = _result->imageRect(); 212 return rect.isEmpty() ? nil : [NSValue valueWithRect:rect]; 213} 214 215- (NSURL *)_absoluteImageURL 216{ 217 return _result->absoluteImageURL(); 218} 219 220- (NSURL *)_absoluteMediaURL 221{ 222 return _result->absoluteMediaURL(); 223} 224 225- (NSNumber *)_isSelected 226{ 227 return [NSNumber numberWithBool:_result->isSelected()]; 228} 229 230- (NSString *)_title 231{ 232 TextDirection dir; 233 return NSStringOrNil(_result->title(dir)); 234} 235 236- (NSURL *)_absoluteLinkURL 237{ 238 return _result->absoluteLinkURL(); 239} 240 241- (WebFrame *)_targetWebFrame 242{ 243 return kit(_result->targetFrame()); 244} 245 246- (NSString *)_titleDisplayString 247{ 248 return NSStringOrNil(_result->titleDisplayString()); 249} 250 251- (NSString *)_textContent 252{ 253 return NSStringOrNil(_result->textContent()); 254} 255 256- (NSNumber *)_isLiveLink 257{ 258 return [NSNumber numberWithBool:_result->isLiveLink()]; 259} 260 261- (NSNumber *)_isContentEditable 262{ 263 return [NSNumber numberWithBool:_result->isContentEditable()]; 264} 265 266- (NSNumber *)_isInScrollBar 267{ 268 return [NSNumber numberWithBool:_result->scrollbar() != 0]; 269} 270 271@end 272