Reviewed by Darin.
[WebKit-https.git] / WebKit / Misc / WebElementDictionary.mm
1 /*
2  * Copyright (C) 2006 Apple Computer, 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 "WebDOMOperations.h"
32 #import "WebFrame.h"
33 #import "WebFrameBridge.h"
34 #import "WebFrameInternal.h"
35 #import "WebView.h"
36 #import "WebViewPrivate.h"
37
38 #import <WebKit/DOMCore.h>
39 #import <WebKit/DOMExtensions.h>
40 #import <WebCore/FrameMac.h>
41 #import <WebCore/HitTestResult.h>
42 #import <WebCore/Image.h>
43 #import <WebCore/KURL.h>
44
45 using namespace WebCore;
46
47 static CFMutableDictionaryRef lookupTable = NULL;
48
49 static void addLookupKey(NSString *key, SEL selector)
50 {
51     CFDictionaryAddValue(lookupTable, key, selector);
52 }
53
54 static void cacheValueForKey(const void *key, const void *value, void *self)
55 {
56     // calling objectForKey will cache the value in our _cache dictionary
57     [(WebElementDictionary *)self objectForKey:(NSString *)key];
58 }
59
60 @implementation WebElementDictionary
61
62 + (void)initializeLookupTable
63 {
64     if (lookupTable)
65         return;
66
67     lookupTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL);
68
69     addLookupKey(WebElementDOMNodeKey, @selector(_domNode));
70     addLookupKey(WebElementFrameKey, @selector(_webFrame));
71     addLookupKey(WebElementImageAltStringKey, @selector(_altDisplayString));
72     addLookupKey(WebElementImageKey, @selector(_image));
73     addLookupKey(WebElementImageRectKey, @selector(_imageRect));
74     addLookupKey(WebElementImageURLKey, @selector(_absoluteImageURL));
75     addLookupKey(WebElementIsSelectedKey, @selector(_isSelected));
76     addLookupKey(WebElementSpellingToolTipKey, @selector(_spellingToolTip));
77     addLookupKey(WebElementTitleKey, @selector(_title));
78     addLookupKey(WebElementLinkURLKey, @selector(_absoluteLinkURL));
79     addLookupKey(WebElementLinkTargetFrameKey, @selector(_targetWebFrame));
80     addLookupKey(WebElementLinkTitleKey, @selector(_titleDisplayString));
81     addLookupKey(WebElementLinkLabelKey, @selector(_textContent));
82     addLookupKey(WebElementLinkIsLiveKey, @selector(_isLiveLink));
83     addLookupKey(WebElementIsContentEditableKey, @selector(_isContentEditable));
84 }
85
86 - (id)initWithHitTestResult:(const HitTestResult&)result
87 {
88     [[self class] initializeLookupTable];
89     [super init];
90     _result = new HitTestResult(result);
91     return self;
92 }
93
94 - (void)dealloc
95 {
96     delete _result;
97     [_cache release];
98     [_nilValues release];
99     [super dealloc];
100 }
101
102 - (void)finalize
103 {
104     delete _result;
105     [super finalize];
106 }
107
108 - (void)_fillCache
109 {
110     CFDictionaryApplyFunction(lookupTable, cacheValueForKey, self);
111     _cacheComplete = YES;
112 }
113
114 - (unsigned)count
115 {
116     if (!_cacheComplete)
117         [self _fillCache];
118     return [_cache count];
119 }
120
121 - (NSEnumerator *)keyEnumerator
122 {
123     if (!_cacheComplete)
124         [self _fillCache];
125     return [_cache keyEnumerator];
126 }
127
128 - (id)objectForKey:(id)key
129 {
130     id value = [_cache objectForKey:key];
131     if (value || _cacheComplete || [_nilValues containsObject:key])
132         return value;
133
134     SEL selector = (SEL)CFDictionaryGetValue(lookupTable, key);
135     if (!selector)
136         return nil;
137     value = [self performSelector:selector];
138
139     unsigned lookupTableCount = CFDictionaryGetCount(lookupTable);
140     if (value) {
141         if (!_cache)
142             _cache = [[NSMutableDictionary alloc] initWithCapacity:lookupTableCount];
143         [_cache setObject:value forKey:key];
144     } else {
145         if (!_nilValues)
146             _nilValues = [[NSMutableSet alloc] initWithCapacity:lookupTableCount];
147         [_nilValues addObject:key];
148     }
149
150     _cacheComplete = ([_cache count] + [_nilValues count]) == lookupTableCount;
151
152     return value;
153 }
154
155 - (DOMNode *)_domNode
156 {
157     return kit(_result->innerNonSharedNode());
158 }
159
160 - (WebFrame *)_webFrame
161 {
162     return [[[self _domNode] ownerDocument] webFrame];
163 }
164
165 // String's NSString* operator converts null Strings to empty NSStrings for compatibility
166 // with AppKit. We need to work around that here.
167 static NSString* NSStringOrNil(String coreString)
168 {
169     if (coreString.isNull())
170         return nil;
171     return coreString;
172 }
173
174 - (NSString *)_altDisplayString
175 {
176     return NSStringOrNil(_result->altDisplayString());
177 }
178
179 - (NSString *)_spellingToolTip
180 {
181     return NSStringOrNil(_result->spellingToolTip());
182 }
183
184 - (NSImage *)_image
185 {
186     Image* image = _result->image();
187     return image ? image->getNSImage() : nil;
188 }
189
190 - (NSValue *)_imageRect
191 {
192     if ([self objectForKey:WebElementImageURLKey])
193         return [NSValue valueWithRect:_result->boundingBox()];
194     return nil;
195 }
196
197 - (NSURL *)_absoluteImageURL
198 {
199     return _result->absoluteImageURL().getNSURL();
200 }
201
202 - (NSNumber *)_isSelected
203 {
204     return [NSNumber numberWithBool:_result->isSelected()];
205 }
206
207 - (NSString *)_title
208 {
209     return NSStringOrNil(_result->title());
210 }
211
212 - (NSURL *)_absoluteLinkURL
213 {
214     return _result->absoluteLinkURL().getNSURL();
215 }
216
217 - (WebFrame *)_targetWebFrame
218 {
219     return kit(_result->targetFrame());
220 }
221
222 - (NSString *)_titleDisplayString
223 {
224     return NSStringOrNil(_result->titleDisplayString());
225 }
226
227 - (NSString *)_textContent
228 {
229     return NSStringOrNil(_result->textContent());
230 }
231
232 - (NSNumber *)_isLiveLink
233 {
234     return [NSNumber numberWithBool:_result->isLiveLink()];
235 }
236
237 - (NSNumber *)_isContentEditable
238 {
239     return [NSNumber numberWithBool:_result->isContentEditable()];
240 }
241
242 @end