1/* 2 * Copyright (C) 2006, 2007, 2008 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} 95 96- (id)initWithHitTestResult:(const HitTestResult&)result 97{ 98 [[self class] initializeLookupTable]; 99 [super init]; 100 _result = new HitTestResult(result); 101 return self; 102} 103 104- (void)dealloc 105{ 106 if (WebCoreObjCScheduleDeallocateOnMainThread([WebElementDictionary class], self)) 107 return; 108 109 delete _result; 110 [_cache release]; 111 [_nilValues release]; 112 [super dealloc]; 113} 114 115- (void)finalize 116{ 117 ASSERT_MAIN_THREAD(); 118 delete _result; 119 [super finalize]; 120} 121 122- (void)_fillCache 123{ 124 CFDictionaryApplyFunction(lookupTable, cacheValueForKey, self); 125 _cacheComplete = YES; 126} 127 128- (NSUInteger)count 129{ 130 if (!_cacheComplete) 131 [self _fillCache]; 132 return [_cache count]; 133} 134 135- (NSEnumerator *)keyEnumerator 136{ 137 if (!_cacheComplete) 138 [self _fillCache]; 139 return [_cache keyEnumerator]; 140} 141 142- (id)objectForKey:(id)key 143{ 144 id value = [_cache objectForKey:key]; 145 if (value || _cacheComplete || [_nilValues containsObject:key]) 146 return value; 147 148 SEL selector = (SEL)CFDictionaryGetValue(lookupTable, key); 149 if (!selector) 150 return nil; 151 value = [self performSelector:selector]; 152 153 unsigned lookupTableCount = CFDictionaryGetCount(lookupTable); 154 if (value) { 155 if (!_cache) 156 _cache = [[NSMutableDictionary alloc] initWithCapacity:lookupTableCount]; 157 [_cache setObject:value forKey:key]; 158 } else { 159 if (!_nilValues) 160 _nilValues = [[NSMutableSet alloc] initWithCapacity:lookupTableCount]; 161 [_nilValues addObject:key]; 162 } 163 164 _cacheComplete = ([_cache count] + [_nilValues count]) == lookupTableCount; 165 166 return value; 167} 168 169- (DOMNode *)_domNode 170{ 171 return kit(_result->innerNonSharedNode()); 172} 173 174- (WebFrame *)_webFrame 175{ 176 return [[[self _domNode] ownerDocument] webFrame]; 177} 178 179// String's NSString* operator converts null Strings to empty NSStrings for compatibility 180// with AppKit. We need to work around that here. 181static NSString* NSStringOrNil(String coreString) 182{ 183 if (coreString.isNull()) 184 return nil; 185 return coreString; 186} 187 188- (NSString *)_altDisplayString 189{ 190 return NSStringOrNil(_result->altDisplayString()); 191} 192 193- (NSString *)_spellingToolTip 194{ 195 TextDirection dir; 196 return NSStringOrNil(_result->spellingToolTip(dir)); 197} 198 199- (NSImage *)_image 200{ 201 Image* image = _result->image(); 202 return image ? image->getNSImage() : nil; 203} 204 205- (NSValue *)_imageRect 206{ 207 IntRect rect = _result->imageRect(); 208 return rect.isEmpty() ? nil : [NSValue valueWithRect:rect]; 209} 210 211- (NSURL *)_absoluteImageURL 212{ 213 return _result->absoluteImageURL(); 214} 215 216- (NSNumber *)_isSelected 217{ 218 return [NSNumber numberWithBool:_result->isSelected()]; 219} 220 221- (NSString *)_title 222{ 223 TextDirection dir; 224 return NSStringOrNil(_result->title(dir)); 225} 226 227- (NSURL *)_absoluteLinkURL 228{ 229 return _result->absoluteLinkURL(); 230} 231 232- (WebFrame *)_targetWebFrame 233{ 234 return kit(_result->targetFrame()); 235} 236 237- (NSString *)_titleDisplayString 238{ 239 return NSStringOrNil(_result->titleDisplayString()); 240} 241 242- (NSString *)_textContent 243{ 244 return NSStringOrNil(_result->textContent()); 245} 246 247- (NSNumber *)_isLiveLink 248{ 249 return [NSNumber numberWithBool:_result->isLiveLink()]; 250} 251 252- (NSNumber *)_isContentEditable 253{ 254 return [NSNumber numberWithBool:_result->isContentEditable()]; 255} 256 257@end 258