|
1 /* |
|
2 * Copyright (C) 2005, 2007, 2008 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 "WebHistoryItemInternal.h" |
|
30 #import "WebHistoryItemPrivate.h" |
|
31 |
|
32 #import "WebFrameInternal.h" |
|
33 #import "WebFrameView.h" |
|
34 #import "WebHTMLViewInternal.h" |
|
35 #import "WebIconDatabase.h" |
|
36 #import "WebKitLogging.h" |
|
37 #import "WebKitNSStringExtras.h" |
|
38 #import "WebNSArrayExtras.h" |
|
39 #import "WebNSDictionaryExtras.h" |
|
40 #import "WebNSObjectExtras.h" |
|
41 #import "WebNSURLExtras.h" |
|
42 #import "WebNSURLRequestExtras.h" |
|
43 #import "WebNSViewExtras.h" |
|
44 #import "WebPluginController.h" |
|
45 #import "WebTypesInternal.h" |
|
46 #import <WebCore/HistoryItem.h> |
|
47 #import <WebCore/Image.h> |
|
48 #import <WebCore/KURL.h> |
|
49 #import <WebCore/PageCache.h> |
|
50 #import <WebCore/PlatformString.h> |
|
51 #import <WebCore/ThreadCheck.h> |
|
52 #import <WebCore/WebCoreObjCExtras.h> |
|
53 #import <runtime/InitializeThreading.h> |
|
54 #import <wtf/Assertions.h> |
|
55 #import <wtf/StdLibExtras.h> |
|
56 #import <wtf/Threading.h> |
|
57 |
|
58 // Private keys used in the WebHistoryItem's dictionary representation. |
|
59 // see 3245793 for explanation of "lastVisitedDate" |
|
60 static NSString *lastVisitedTimeIntervalKey = @"lastVisitedDate"; |
|
61 static NSString *visitCountKey = @"visitCount"; |
|
62 static NSString *titleKey = @"title"; |
|
63 static NSString *childrenKey = @"children"; |
|
64 static NSString *displayTitleKey = @"displayTitle"; |
|
65 static NSString *lastVisitWasFailureKey = @"lastVisitWasFailure"; |
|
66 static NSString *lastVisitWasHTTPNonGetKey = @"lastVisitWasHTTPNonGet"; |
|
67 static NSString *redirectURLsKey = @"redirectURLs"; |
|
68 static NSString *dailyVisitCountKey = @"D"; // short key to save space |
|
69 static NSString *weeklyVisitCountKey = @"W"; // short key to save space |
|
70 |
|
71 // Notification strings. |
|
72 NSString *WebHistoryItemChangedNotification = @"WebHistoryItemChangedNotification"; |
|
73 |
|
74 using namespace WebCore; |
|
75 using namespace std; |
|
76 |
|
77 typedef HashMap<HistoryItem*, WebHistoryItem*> HistoryItemMap; |
|
78 |
|
79 static inline WebHistoryItemPrivate* kitPrivate(WebCoreHistoryItem* list) { return (WebHistoryItemPrivate*)list; } |
|
80 static inline WebCoreHistoryItem* core(WebHistoryItemPrivate* list) { return (WebCoreHistoryItem*)list; } |
|
81 |
|
82 static HistoryItemMap& historyItemWrappers() |
|
83 { |
|
84 DEFINE_STATIC_LOCAL(HistoryItemMap, historyItemWrappers, ()); |
|
85 return historyItemWrappers; |
|
86 } |
|
87 |
|
88 void WKNotifyHistoryItemChanged(HistoryItem*) |
|
89 { |
|
90 [[NSNotificationCenter defaultCenter] |
|
91 postNotificationName:WebHistoryItemChangedNotification object:nil userInfo:nil]; |
|
92 } |
|
93 |
|
94 @implementation WebHistoryItem |
|
95 |
|
96 + (void)initialize |
|
97 { |
|
98 JSC::initializeThreading(); |
|
99 WTF::initializeMainThreadToProcessMainThread(); |
|
100 #ifndef BUILDING_ON_TIGER |
|
101 WebCoreObjCFinalizeOnMainThread(self); |
|
102 #endif |
|
103 } |
|
104 |
|
105 - (id)init |
|
106 { |
|
107 return [self initWithWebCoreHistoryItem:HistoryItem::create()]; |
|
108 } |
|
109 |
|
110 - (id)initWithURLString:(NSString *)URLString title:(NSString *)title lastVisitedTimeInterval:(NSTimeInterval)time |
|
111 { |
|
112 WebCoreThreadViolationCheckRoundOne(); |
|
113 return [self initWithWebCoreHistoryItem:HistoryItem::create(URLString, title, time)]; |
|
114 } |
|
115 |
|
116 - (void)dealloc |
|
117 { |
|
118 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHistoryItem class], self)) |
|
119 return; |
|
120 |
|
121 if (_private) { |
|
122 HistoryItem* coreItem = core(_private); |
|
123 coreItem->deref(); |
|
124 historyItemWrappers().remove(coreItem); |
|
125 } |
|
126 [super dealloc]; |
|
127 } |
|
128 |
|
129 - (void)finalize |
|
130 { |
|
131 WebCoreThreadViolationCheckRoundOne(); |
|
132 // FIXME: ~HistoryItem is what releases the history item's icon from the icon database |
|
133 // It's probably not good to release icons from the database only when the object is garbage-collected. |
|
134 // Need to change design so this happens at a predictable time. |
|
135 if (_private) { |
|
136 HistoryItem* coreItem = core(_private); |
|
137 coreItem->deref(); |
|
138 historyItemWrappers().remove(coreItem); |
|
139 } |
|
140 [super finalize]; |
|
141 } |
|
142 |
|
143 - (id)copyWithZone:(NSZone *)zone |
|
144 { |
|
145 WebCoreThreadViolationCheckRoundOne(); |
|
146 WebHistoryItem *copy = (WebHistoryItem *)NSCopyObject(self, 0, zone); |
|
147 RefPtr<HistoryItem> item = core(_private)->copy(); |
|
148 copy->_private = kitPrivate(item.get()); |
|
149 historyItemWrappers().set(item.release().releaseRef(), copy); |
|
150 |
|
151 return copy; |
|
152 } |
|
153 |
|
154 // FIXME: Need to decide if this class ever returns URLs and decide on the name of this method |
|
155 - (NSString *)URLString |
|
156 { |
|
157 ASSERT_MAIN_THREAD(); |
|
158 return nsStringNilIfEmpty(core(_private)->urlString()); |
|
159 } |
|
160 |
|
161 // The first URL we loaded to get to where this history item points. Includes both client |
|
162 // and server redirects. |
|
163 - (NSString *)originalURLString |
|
164 { |
|
165 ASSERT_MAIN_THREAD(); |
|
166 return nsStringNilIfEmpty(core(_private)->originalURLString()); |
|
167 } |
|
168 |
|
169 - (NSString *)title |
|
170 { |
|
171 ASSERT_MAIN_THREAD(); |
|
172 return nsStringNilIfEmpty(core(_private)->title()); |
|
173 } |
|
174 |
|
175 - (void)setAlternateTitle:(NSString *)alternateTitle |
|
176 { |
|
177 core(_private)->setAlternateTitle(alternateTitle); |
|
178 } |
|
179 |
|
180 - (NSString *)alternateTitle |
|
181 { |
|
182 return nsStringNilIfEmpty(core(_private)->alternateTitle()); |
|
183 } |
|
184 |
|
185 - (NSImage *)icon |
|
186 { |
|
187 return [[WebIconDatabase sharedIconDatabase] iconForURL:[self URLString] withSize:WebIconSmallSize]; |
|
188 |
|
189 // FIXME: Ideally, this code should simply be the following - |
|
190 // return core(_private)->icon()->getNSImage(); |
|
191 // Once radar - |
|
192 // <rdar://problem/4906567> - NSImage returned from WebCore::Image may be incorrect size |
|
193 // is resolved |
|
194 } |
|
195 |
|
196 - (NSTimeInterval)lastVisitedTimeInterval |
|
197 { |
|
198 ASSERT_MAIN_THREAD(); |
|
199 return core(_private)->lastVisitedTime(); |
|
200 } |
|
201 |
|
202 - (NSUInteger)hash |
|
203 { |
|
204 return [(NSString*)core(_private)->urlString() hash]; |
|
205 } |
|
206 |
|
207 - (BOOL)isEqual:(id)anObject |
|
208 { |
|
209 ASSERT_MAIN_THREAD(); |
|
210 if (![anObject isMemberOfClass:[WebHistoryItem class]]) { |
|
211 return NO; |
|
212 } |
|
213 |
|
214 return core(_private)->urlString() == core(((WebHistoryItem*)anObject)->_private)->urlString(); |
|
215 } |
|
216 |
|
217 - (NSString *)description |
|
218 { |
|
219 ASSERT_MAIN_THREAD(); |
|
220 HistoryItem* coreItem = core(_private); |
|
221 NSMutableString *result = [NSMutableString stringWithFormat:@"%@ %@", [super description], (NSString*)coreItem->urlString()]; |
|
222 if (coreItem->target()) { |
|
223 NSString *target = coreItem->target(); |
|
224 [result appendFormat:@" in \"%@\"", target]; |
|
225 } |
|
226 if (coreItem->isTargetItem()) { |
|
227 [result appendString:@" *target*"]; |
|
228 } |
|
229 if (coreItem->formData()) { |
|
230 [result appendString:@" *POST*"]; |
|
231 } |
|
232 |
|
233 if (coreItem->children().size()) { |
|
234 const HistoryItemVector& children = coreItem->children(); |
|
235 int currPos = [result length]; |
|
236 unsigned size = children.size(); |
|
237 for (unsigned i = 0; i < size; ++i) { |
|
238 WebHistoryItem *child = kit(children[i].get()); |
|
239 [result appendString:@"\n"]; |
|
240 [result appendString:[child description]]; |
|
241 } |
|
242 // shift all the contents over. A bit slow, but hey, this is for debugging. |
|
243 NSRange replRange = {currPos, [result length]-currPos}; |
|
244 [result replaceOccurrencesOfString:@"\n" withString:@"\n " options:0 range:replRange]; |
|
245 } |
|
246 |
|
247 return result; |
|
248 } |
|
249 |
|
250 @end |
|
251 |
|
252 @interface WebWindowWatcher : NSObject |
|
253 @end |
|
254 |
|
255 |
|
256 @implementation WebHistoryItem (WebInternal) |
|
257 |
|
258 HistoryItem* core(WebHistoryItem *item) |
|
259 { |
|
260 if (!item) |
|
261 return 0; |
|
262 |
|
263 ASSERT(historyItemWrappers().get(core(item->_private)) == item); |
|
264 |
|
265 return core(item->_private); |
|
266 } |
|
267 |
|
268 WebHistoryItem *kit(HistoryItem* item) |
|
269 { |
|
270 if (!item) |
|
271 return nil; |
|
272 |
|
273 WebHistoryItem *kitItem = historyItemWrappers().get(item); |
|
274 if (kitItem) |
|
275 return kitItem; |
|
276 |
|
277 return [[[WebHistoryItem alloc] initWithWebCoreHistoryItem:item] autorelease]; |
|
278 } |
|
279 |
|
280 + (WebHistoryItem *)entryWithURL:(NSURL *)URL |
|
281 { |
|
282 return [[[self alloc] initWithURL:URL title:nil] autorelease]; |
|
283 } |
|
284 |
|
285 static WebWindowWatcher *_windowWatcher = nil; |
|
286 |
|
287 + (void)initWindowWatcherIfNecessary |
|
288 { |
|
289 if (_windowWatcher) |
|
290 return; |
|
291 _windowWatcher = [[WebWindowWatcher alloc] init]; |
|
292 [[NSNotificationCenter defaultCenter] addObserver:_windowWatcher selector:@selector(windowWillClose:) |
|
293 name:NSWindowWillCloseNotification object:nil]; |
|
294 } |
|
295 |
|
296 - (id)initWithURL:(NSURL *)URL target:(NSString *)target parent:(NSString *)parent title:(NSString *)title |
|
297 { |
|
298 return [self initWithWebCoreHistoryItem:HistoryItem::create(URL, target, parent, title)]; |
|
299 } |
|
300 |
|
301 - (id)initWithURLString:(NSString *)URLString title:(NSString *)title displayTitle:(NSString *)displayTitle lastVisitedTimeInterval:(NSTimeInterval)time |
|
302 { |
|
303 return [self initWithWebCoreHistoryItem:HistoryItem::create(URLString, title, displayTitle, time)]; |
|
304 } |
|
305 |
|
306 - (id)initWithWebCoreHistoryItem:(PassRefPtr<HistoryItem>)item |
|
307 { |
|
308 WebCoreThreadViolationCheckRoundOne(); |
|
309 // Need to tell WebCore what function to call for the |
|
310 // "History Item has Changed" notification - no harm in doing this |
|
311 // everytime a WebHistoryItem is created |
|
312 // Note: We also do this in [WebFrameView initWithFrame:] where we do |
|
313 // other "init before WebKit is used" type things |
|
314 WebCore::notifyHistoryItemChanged = WKNotifyHistoryItemChanged; |
|
315 |
|
316 self = [super init]; |
|
317 |
|
318 _private = kitPrivate(item.releaseRef()); |
|
319 ASSERT(!historyItemWrappers().get(core(_private))); |
|
320 historyItemWrappers().set(core(_private), self); |
|
321 return self; |
|
322 } |
|
323 |
|
324 - (void)setTitle:(NSString *)title |
|
325 { |
|
326 core(_private)->setTitle(title); |
|
327 } |
|
328 |
|
329 - (void)setVisitCount:(int)count |
|
330 { |
|
331 core(_private)->setVisitCount(count); |
|
332 } |
|
333 |
|
334 - (void)setViewState:(id)statePList |
|
335 { |
|
336 core(_private)->setViewState(statePList); |
|
337 } |
|
338 |
|
339 - (void)_mergeAutoCompleteHints:(WebHistoryItem *)otherItem |
|
340 { |
|
341 ASSERT_ARG(otherItem, otherItem); |
|
342 core(_private)->mergeAutoCompleteHints(core(otherItem->_private)); |
|
343 } |
|
344 |
|
345 - (id)initFromDictionaryRepresentation:(NSDictionary *)dict |
|
346 { |
|
347 ASSERT_MAIN_THREAD(); |
|
348 NSString *URLString = [dict _webkit_stringForKey:@""]; |
|
349 NSString *title = [dict _webkit_stringForKey:titleKey]; |
|
350 |
|
351 // Do an existence check to avoid calling doubleValue on a nil string. Leave |
|
352 // time interval at 0 if there's no value in dict. |
|
353 NSString *timeIntervalString = [dict _webkit_stringForKey:lastVisitedTimeIntervalKey]; |
|
354 NSTimeInterval lastVisited = timeIntervalString == nil ? 0 : [timeIntervalString doubleValue]; |
|
355 |
|
356 self = [self initWithURLString:URLString title:title displayTitle:[dict _webkit_stringForKey:displayTitleKey] lastVisitedTimeInterval:lastVisited]; |
|
357 |
|
358 // Check if we've read a broken URL from the file that has non-Latin1 chars. If so, try to convert |
|
359 // as if it was from user typing. |
|
360 if (![URLString canBeConvertedToEncoding:NSISOLatin1StringEncoding]) { |
|
361 NSURL *tempURL = [NSURL _web_URLWithUserTypedString:URLString]; |
|
362 ASSERT(tempURL); |
|
363 NSString *newURLString = [tempURL _web_originalDataAsString]; |
|
364 core(_private)->setURLString(newURLString); |
|
365 core(_private)->setOriginalURLString(newURLString); |
|
366 } |
|
367 |
|
368 int visitCount = [dict _webkit_intForKey:visitCountKey]; |
|
369 |
|
370 // Can't trust data on disk, and we've had at least one report of this (<rdar://6572300>). |
|
371 if (visitCount < 0) { |
|
372 LOG_ERROR("visit count for history item \"%@\" is negative (%d), will be reset to 1", URLString, visitCount); |
|
373 visitCount = 1; |
|
374 } |
|
375 core(_private)->setVisitCount(visitCount); |
|
376 |
|
377 if ([dict _webkit_boolForKey:lastVisitWasFailureKey]) |
|
378 core(_private)->setLastVisitWasFailure(true); |
|
379 |
|
380 BOOL lastVisitWasHTTPNonGet = [dict _webkit_boolForKey:lastVisitWasHTTPNonGetKey]; |
|
381 NSString *tempURLString = [URLString lowercaseString]; |
|
382 if (lastVisitWasHTTPNonGet && ([tempURLString hasPrefix:@"http:"] || [tempURLString hasPrefix:@"https:"])) |
|
383 core(_private)->setLastVisitWasHTTPNonGet(lastVisitWasHTTPNonGet); |
|
384 |
|
385 if (NSArray *redirectURLs = [dict _webkit_arrayForKey:redirectURLsKey]) { |
|
386 NSUInteger size = [redirectURLs count]; |
|
387 OwnPtr<Vector<String> > redirectURLsVector(new Vector<String>(size)); |
|
388 for (NSUInteger i = 0; i < size; ++i) |
|
389 (*redirectURLsVector)[i] = String([redirectURLs _webkit_stringAtIndex:i]); |
|
390 core(_private)->setRedirectURLs(redirectURLsVector.release()); |
|
391 } |
|
392 |
|
393 NSArray *dailyCounts = [dict _webkit_arrayForKey:dailyVisitCountKey]; |
|
394 NSArray *weeklyCounts = [dict _webkit_arrayForKey:weeklyVisitCountKey]; |
|
395 if (dailyCounts || weeklyCounts) { |
|
396 Vector<int> coreDailyCounts([dailyCounts count]); |
|
397 Vector<int> coreWeeklyCounts([weeklyCounts count]); |
|
398 |
|
399 // Daily and weekly counts < 0 are errors in the data read from disk, so reset to 0. |
|
400 for (size_t i = 0; i < coreDailyCounts.size(); ++i) |
|
401 coreDailyCounts[i] = max([[dailyCounts _webkit_numberAtIndex:i] intValue], 0); |
|
402 for (size_t i = 0; i < coreWeeklyCounts.size(); ++i) |
|
403 coreWeeklyCounts[i] = max([[weeklyCounts _webkit_numberAtIndex:i] intValue], 0); |
|
404 |
|
405 core(_private)->adoptVisitCounts(coreDailyCounts, coreWeeklyCounts); |
|
406 } |
|
407 |
|
408 NSArray *childDicts = [dict objectForKey:childrenKey]; |
|
409 if (childDicts) { |
|
410 for (int i = [childDicts count] - 1; i >= 0; i--) { |
|
411 WebHistoryItem *child = [[WebHistoryItem alloc] initFromDictionaryRepresentation:[childDicts objectAtIndex:i]]; |
|
412 core(_private)->addChildItem(core(child->_private)); |
|
413 [child release]; |
|
414 } |
|
415 } |
|
416 |
|
417 return self; |
|
418 } |
|
419 |
|
420 - (NSPoint)scrollPoint |
|
421 { |
|
422 ASSERT_MAIN_THREAD(); |
|
423 return core(_private)->scrollPoint(); |
|
424 } |
|
425 |
|
426 - (void)_visitedWithTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount |
|
427 { |
|
428 core(_private)->visited(title, [NSDate timeIntervalSinceReferenceDate], increaseVisitCount ? IncreaseVisitCount : DoNotIncreaseVisitCount); |
|
429 } |
|
430 |
|
431 - (void)_recordInitialVisit |
|
432 { |
|
433 core(_private)->recordInitialVisit(); |
|
434 } |
|
435 |
|
436 @end |
|
437 |
|
438 @implementation WebHistoryItem (WebPrivate) |
|
439 |
|
440 - (id)initWithURL:(NSURL *)URL title:(NSString *)title |
|
441 { |
|
442 return [self initWithURLString:[URL _web_originalDataAsString] title:title lastVisitedTimeInterval:0]; |
|
443 } |
|
444 |
|
445 - (NSDictionary *)dictionaryRepresentation |
|
446 { |
|
447 ASSERT_MAIN_THREAD(); |
|
448 NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:8]; |
|
449 |
|
450 HistoryItem* coreItem = core(_private); |
|
451 |
|
452 if (!coreItem->urlString().isEmpty()) |
|
453 [dict setObject:(NSString*)coreItem->urlString() forKey:@""]; |
|
454 if (!coreItem->title().isEmpty()) |
|
455 [dict setObject:(NSString*)coreItem->title() forKey:titleKey]; |
|
456 if (!coreItem->alternateTitle().isEmpty()) |
|
457 [dict setObject:(NSString*)coreItem->alternateTitle() forKey:displayTitleKey]; |
|
458 if (coreItem->lastVisitedTime() != 0.0) { |
|
459 // Store as a string to maintain backward compatibility. (See 3245793) |
|
460 [dict setObject:[NSString stringWithFormat:@"%.1lf", coreItem->lastVisitedTime()] |
|
461 forKey:lastVisitedTimeIntervalKey]; |
|
462 } |
|
463 if (coreItem->visitCount()) |
|
464 [dict setObject:[NSNumber numberWithInt:coreItem->visitCount()] forKey:visitCountKey]; |
|
465 if (coreItem->lastVisitWasFailure()) |
|
466 [dict setObject:[NSNumber numberWithBool:YES] forKey:lastVisitWasFailureKey]; |
|
467 if (coreItem->lastVisitWasHTTPNonGet()) { |
|
468 ASSERT(coreItem->urlString().startsWith("http:", false) || coreItem->urlString().startsWith("https:", false)); |
|
469 [dict setObject:[NSNumber numberWithBool:YES] forKey:lastVisitWasHTTPNonGetKey]; |
|
470 } |
|
471 if (Vector<String>* redirectURLs = coreItem->redirectURLs()) { |
|
472 size_t size = redirectURLs->size(); |
|
473 ASSERT(size); |
|
474 NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:size]; |
|
475 for (size_t i = 0; i < size; ++i) |
|
476 [result addObject:(NSString*)redirectURLs->at(i)]; |
|
477 [dict setObject:result forKey:redirectURLsKey]; |
|
478 [result release]; |
|
479 } |
|
480 |
|
481 const Vector<int>& dailyVisitCounts = coreItem->dailyVisitCounts(); |
|
482 if (dailyVisitCounts.size()) { |
|
483 NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:13]; |
|
484 for (size_t i = 0; i < dailyVisitCounts.size(); ++i) |
|
485 [array addObject:[NSNumber numberWithInt:dailyVisitCounts[i]]]; |
|
486 [dict setObject:array forKey:dailyVisitCountKey]; |
|
487 [array release]; |
|
488 } |
|
489 |
|
490 const Vector<int>& weeklyVisitCounts = coreItem->weeklyVisitCounts(); |
|
491 if (weeklyVisitCounts.size()) { |
|
492 NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:5]; |
|
493 for (size_t i = 0; i < weeklyVisitCounts.size(); ++i) |
|
494 [array addObject:[NSNumber numberWithInt:weeklyVisitCounts[i]]]; |
|
495 [dict setObject:array forKey:weeklyVisitCountKey]; |
|
496 [array release]; |
|
497 } |
|
498 |
|
499 if (coreItem->children().size()) { |
|
500 const HistoryItemVector& children = coreItem->children(); |
|
501 NSMutableArray *childDicts = [NSMutableArray arrayWithCapacity:children.size()]; |
|
502 |
|
503 for (int i = children.size() - 1; i >= 0; i--) |
|
504 [childDicts addObject:[kit(children[i].get()) dictionaryRepresentation]]; |
|
505 [dict setObject: childDicts forKey:childrenKey]; |
|
506 } |
|
507 |
|
508 return dict; |
|
509 } |
|
510 |
|
511 - (NSString *)target |
|
512 { |
|
513 ASSERT_MAIN_THREAD(); |
|
514 return nsStringNilIfEmpty(core(_private)->target()); |
|
515 } |
|
516 |
|
517 - (BOOL)isTargetItem |
|
518 { |
|
519 return core(_private)->isTargetItem(); |
|
520 } |
|
521 |
|
522 - (int)visitCount |
|
523 { |
|
524 ASSERT_MAIN_THREAD(); |
|
525 return core(_private)->visitCount(); |
|
526 } |
|
527 |
|
528 - (NSString *)RSSFeedReferrer |
|
529 { |
|
530 return nsStringNilIfEmpty(core(_private)->referrer()); |
|
531 } |
|
532 |
|
533 - (void)setRSSFeedReferrer:(NSString *)referrer |
|
534 { |
|
535 core(_private)->setReferrer(referrer); |
|
536 } |
|
537 |
|
538 - (NSArray *)children |
|
539 { |
|
540 ASSERT_MAIN_THREAD(); |
|
541 const HistoryItemVector& children = core(_private)->children(); |
|
542 if (!children.size()) |
|
543 return nil; |
|
544 |
|
545 unsigned size = children.size(); |
|
546 NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:size] autorelease]; |
|
547 |
|
548 for (unsigned i = 0; i < size; ++i) |
|
549 [result addObject:kit(children[i].get())]; |
|
550 |
|
551 return result; |
|
552 } |
|
553 |
|
554 - (void)setAlwaysAttemptToUsePageCache:(BOOL)flag |
|
555 { |
|
556 // Safari 2.0 uses this for SnapBack, so we stub it out to avoid a crash. |
|
557 } |
|
558 |
|
559 - (NSURL *)URL |
|
560 { |
|
561 ASSERT_MAIN_THREAD(); |
|
562 const KURL& url = core(_private)->url(); |
|
563 if (url.isEmpty()) |
|
564 return nil; |
|
565 return url; |
|
566 } |
|
567 |
|
568 // This should not be called directly for WebHistoryItems that are already included |
|
569 // in WebHistory. Use -[WebHistory setLastVisitedTimeInterval:forItem:] instead. |
|
570 - (void)_setLastVisitedTimeInterval:(NSTimeInterval)time |
|
571 { |
|
572 core(_private)->setLastVisitedTime(time); |
|
573 } |
|
574 |
|
575 // FIXME: <rdar://problem/4880065> - Push Global History into WebCore |
|
576 // Once that task is complete, this accessor can go away |
|
577 - (NSCalendarDate *)_lastVisitedDate |
|
578 { |
|
579 ASSERT_MAIN_THREAD(); |
|
580 return [[[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:core(_private)->lastVisitedTime()] autorelease]; |
|
581 } |
|
582 |
|
583 - (WebHistoryItem *)targetItem |
|
584 { |
|
585 ASSERT_MAIN_THREAD(); |
|
586 return kit(core(_private)->targetItem()); |
|
587 } |
|
588 |
|
589 + (void)_releaseAllPendingPageCaches |
|
590 { |
|
591 pageCache()->releaseAutoreleasedPagesNow(); |
|
592 } |
|
593 |
|
594 - (id)_transientPropertyForKey:(NSString *)key |
|
595 { |
|
596 return core(_private)->getTransientProperty(key); |
|
597 } |
|
598 |
|
599 - (void)_setTransientProperty:(id)property forKey:(NSString *)key |
|
600 { |
|
601 core(_private)->setTransientProperty(key, property); |
|
602 } |
|
603 |
|
604 - (BOOL)lastVisitWasFailure |
|
605 { |
|
606 return core(_private)->lastVisitWasFailure(); |
|
607 } |
|
608 |
|
609 - (void)_setLastVisitWasFailure:(BOOL)failure |
|
610 { |
|
611 core(_private)->setLastVisitWasFailure(failure); |
|
612 } |
|
613 |
|
614 - (BOOL)_lastVisitWasHTTPNonGet |
|
615 { |
|
616 return core(_private)->lastVisitWasHTTPNonGet(); |
|
617 } |
|
618 |
|
619 - (NSArray *)_redirectURLs |
|
620 { |
|
621 Vector<String>* redirectURLs = core(_private)->redirectURLs(); |
|
622 if (!redirectURLs) |
|
623 return nil; |
|
624 |
|
625 size_t size = redirectURLs->size(); |
|
626 ASSERT(size); |
|
627 NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:size]; |
|
628 for (size_t i = 0; i < size; ++i) |
|
629 [result addObject:(NSString*)redirectURLs->at(i)]; |
|
630 return [result autorelease]; |
|
631 } |
|
632 |
|
633 - (size_t)_getDailyVisitCounts:(const int**)counts |
|
634 { |
|
635 HistoryItem* coreItem = core(_private); |
|
636 *counts = coreItem->dailyVisitCounts().data(); |
|
637 return coreItem->dailyVisitCounts().size(); |
|
638 } |
|
639 |
|
640 - (size_t)_getWeeklyVisitCounts:(const int**)counts |
|
641 { |
|
642 HistoryItem* coreItem = core(_private); |
|
643 *counts = coreItem->weeklyVisitCounts().data(); |
|
644 return coreItem->weeklyVisitCounts().size(); |
|
645 } |
|
646 |
|
647 @end |
|
648 |
|
649 |
|
650 // FIXME: <rdar://problem/4886761>. |
|
651 // This is a bizarre policy. We flush the page caches ANY time ANY window is closed? |
|
652 |
|
653 @implementation WebWindowWatcher |
|
654 |
|
655 - (void)windowWillClose:(NSNotification *)notification |
|
656 { |
|
657 if (!pthread_main_np()) { |
|
658 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; |
|
659 return; |
|
660 } |
|
661 |
|
662 pageCache()->releaseAutoreleasedPagesNow(); |
|
663 } |
|
664 |
|
665 @end |