71c4cc7fdda68709557ebc90c93ab1a794b15c81
[WebKit-https.git] / Source / WebKit / mac / Misc / WebElementDictionary.mm
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
48 using namespace WebCore;
49
50 static CFMutableDictionaryRef lookupTable = NULL;
51
52 static void addLookupKey(NSString *key, SEL selector)
53 {
54     CFDictionaryAddValue(lookupTable, key, selector);
55 }
56
57 static 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.
185 static 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