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