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