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 47 using namespace WebCore; 48 49 static CFMutableDictionaryRef lookupTable = NULL; 50 51 static void addLookupKey(NSString *key, SEL selector) 52 { 53 CFDictionaryAddValue(lookupTable, key, selector); 54 } 55 56 static 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. 182 static 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