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