a2e49ed63843a277f1601cd1f6d3a7308cdce8f6
[WebKit-https.git] / Source / WebKit / mac / History / WebHistoryItem.mm
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
190 - (NSTimeInterval)lastVisitedTimeInterval
191 {
192     ASSERT_MAIN_THREAD();
193     return core(_private)->lastVisitedTime();
194 }
195
196 - (NSUInteger)hash
197 {
198     return [(NSString*)core(_private)->urlString() hash];
199 }
200
201 - (BOOL)isEqual:(id)anObject
202 {
203     ASSERT_MAIN_THREAD();
204     if (![anObject isMemberOfClass:[WebHistoryItem class]]) {
205         return NO;
206     }
207     
208     return core(_private)->urlString() == core(((WebHistoryItem*)anObject)->_private)->urlString();
209 }
210
211 - (NSString *)description
212 {
213     ASSERT_MAIN_THREAD();
214     HistoryItem* coreItem = core(_private);
215     NSMutableString *result = [NSMutableString stringWithFormat:@"%@ %@", [super description], (NSString*)coreItem->urlString()];
216     if (!coreItem->target().isEmpty()) {
217         NSString *target = coreItem->target();
218         [result appendFormat:@" in \"%@\"", target];
219     }
220     if (coreItem->isTargetItem()) {
221         [result appendString:@" *target*"];
222     }
223     if (coreItem->formData()) {
224         [result appendString:@" *POST*"];
225     }
226     
227     if (coreItem->children().size()) {
228         const HistoryItemVector& children = coreItem->children();
229         int currPos = [result length];
230         unsigned size = children.size();        
231         for (unsigned i = 0; i < size; ++i) {
232             WebHistoryItem *child = kit(children[i].get());
233             [result appendString:@"\n"];
234             [result appendString:[child description]];
235         }
236         // shift all the contents over.  A bit slow, but hey, this is for debugging.
237         NSRange replRange = {currPos, [result length]-currPos};
238         [result replaceOccurrencesOfString:@"\n" withString:@"\n    " options:0 range:replRange];
239     }
240     
241     return result;
242 }
243
244 @end
245
246 @interface WebWindowWatcher : NSObject
247 @end
248
249
250 @implementation WebHistoryItem (WebInternal)
251
252 HistoryItem* core(WebHistoryItem *item)
253 {
254     if (!item)
255         return 0;
256     
257     ASSERT(historyItemWrappers().get(core(item->_private)) == item);
258
259     return core(item->_private);
260 }
261
262 WebHistoryItem *kit(HistoryItem* item)
263 {
264     if (!item)
265         return nil;
266         
267     WebHistoryItem *kitItem = historyItemWrappers().get(item);
268     if (kitItem)
269         return kitItem;
270     
271     return [[[WebHistoryItem alloc] initWithWebCoreHistoryItem:item] autorelease];
272 }
273
274 + (WebHistoryItem *)entryWithURL:(NSURL *)URL
275 {
276     return [[[self alloc] initWithURL:URL title:nil] autorelease];
277 }
278
279 static WebWindowWatcher *_windowWatcher = nil;
280
281 + (void)initWindowWatcherIfNecessary
282 {
283     if (_windowWatcher)
284         return;
285     _windowWatcher = [[WebWindowWatcher alloc] init];
286     [[NSNotificationCenter defaultCenter] addObserver:_windowWatcher selector:@selector(windowWillClose:)
287         name:NSWindowWillCloseNotification object:nil];
288 }
289
290 - (id)initWithURL:(NSURL *)URL target:(NSString *)target parent:(NSString *)parent title:(NSString *)title
291 {
292     return [self initWithWebCoreHistoryItem:HistoryItem::create(URL, target, parent, title)];
293 }
294
295 - (id)initWithURLString:(NSString *)URLString title:(NSString *)title displayTitle:(NSString *)displayTitle lastVisitedTimeInterval:(NSTimeInterval)time
296 {
297     return [self initWithWebCoreHistoryItem:HistoryItem::create(URLString, title, displayTitle, time)];
298 }
299
300 - (id)initWithWebCoreHistoryItem:(PassRefPtr<HistoryItem>)item
301 {   
302     WebCoreThreadViolationCheckRoundOne();
303     // Need to tell WebCore what function to call for the 
304     // "History Item has Changed" notification - no harm in doing this
305     // everytime a WebHistoryItem is created
306     // Note: We also do this in [WebFrameView initWithFrame:] where we do
307     // other "init before WebKit is used" type things
308     WebCore::notifyHistoryItemChanged = WKNotifyHistoryItemChanged;
309     
310     self = [super init];
311     
312     _private = kitPrivate(item.releaseRef());
313     ASSERT(!historyItemWrappers().get(core(_private)));
314     historyItemWrappers().set(core(_private), self);
315     return self;
316 }
317
318 - (void)setTitle:(NSString *)title
319 {
320     core(_private)->setTitle(title);
321 }
322
323 - (void)setVisitCount:(int)count
324 {
325     core(_private)->setVisitCount(count);
326 }
327
328 - (void)setViewState:(id)statePList
329 {
330     core(_private)->setViewState(statePList);
331 }
332
333 - (void)_mergeAutoCompleteHints:(WebHistoryItem *)otherItem
334 {
335     ASSERT_ARG(otherItem, otherItem);
336     core(_private)->mergeAutoCompleteHints(core(otherItem->_private));
337 }
338
339 - (id)initFromDictionaryRepresentation:(NSDictionary *)dict
340 {
341     ASSERT_MAIN_THREAD();
342     NSString *URLString = [dict _webkit_stringForKey:@""];
343     NSString *title = [dict _webkit_stringForKey:titleKey];
344
345     // Do an existence check to avoid calling doubleValue on a nil string. Leave
346     // time interval at 0 if there's no value in dict.
347     NSString *timeIntervalString = [dict _webkit_stringForKey:lastVisitedTimeIntervalKey];
348     NSTimeInterval lastVisited = timeIntervalString == nil ? 0 : [timeIntervalString doubleValue];
349
350     self = [self initWithURLString:URLString title:title displayTitle:[dict _webkit_stringForKey:displayTitleKey] lastVisitedTimeInterval:lastVisited];
351     
352     // Check if we've read a broken URL from the file that has non-Latin1 chars.  If so, try to convert
353     // as if it was from user typing.
354     if (![URLString canBeConvertedToEncoding:NSISOLatin1StringEncoding]) {
355         NSURL *tempURL = [NSURL _web_URLWithUserTypedString:URLString];
356         ASSERT(tempURL);
357         NSString *newURLString = [tempURL _web_originalDataAsString];
358         core(_private)->setURLString(newURLString);
359         core(_private)->setOriginalURLString(newURLString);
360     } 
361
362     int visitCount = [dict _webkit_intForKey:visitCountKey];
363     
364     // Can't trust data on disk, and we've had at least one report of this (<rdar://6572300>).
365     if (visitCount < 0) {
366         LOG_ERROR("visit count for history item \"%@\" is negative (%d), will be reset to 1", URLString, visitCount);
367         visitCount = 1;
368     }
369     core(_private)->setVisitCount(visitCount);
370
371     if ([dict _webkit_boolForKey:lastVisitWasFailureKey])
372         core(_private)->setLastVisitWasFailure(true);
373     
374     BOOL lastVisitWasHTTPNonGet = [dict _webkit_boolForKey:lastVisitWasHTTPNonGetKey];
375     NSString *tempURLString = [URLString lowercaseString];
376     if (lastVisitWasHTTPNonGet && ([tempURLString hasPrefix:@"http:"] || [tempURLString hasPrefix:@"https:"]))
377         core(_private)->setLastVisitWasHTTPNonGet(lastVisitWasHTTPNonGet);
378
379     if (NSArray *redirectURLs = [dict _webkit_arrayForKey:redirectURLsKey]) {
380         NSUInteger size = [redirectURLs count];
381         OwnPtr<Vector<String> > redirectURLsVector = adoptPtr(new Vector<String>(size));
382         for (NSUInteger i = 0; i < size; ++i)
383             (*redirectURLsVector)[i] = String([redirectURLs _webkit_stringAtIndex:i]);
384         core(_private)->setRedirectURLs(redirectURLsVector.release());
385     }
386
387     NSArray *dailyCounts = [dict _webkit_arrayForKey:dailyVisitCountKey];
388     NSArray *weeklyCounts = [dict _webkit_arrayForKey:weeklyVisitCountKey];
389     if (dailyCounts || weeklyCounts) {
390         Vector<int> coreDailyCounts([dailyCounts count]);
391         Vector<int> coreWeeklyCounts([weeklyCounts count]);
392
393         // Daily and weekly counts < 0 are errors in the data read from disk, so reset to 0.
394         for (size_t i = 0; i < coreDailyCounts.size(); ++i)
395             coreDailyCounts[i] = max([[dailyCounts _webkit_numberAtIndex:i] intValue], 0);
396         for (size_t i = 0; i < coreWeeklyCounts.size(); ++i)
397             coreWeeklyCounts[i] = max([[weeklyCounts _webkit_numberAtIndex:i] intValue], 0);
398     
399         core(_private)->adoptVisitCounts(coreDailyCounts, coreWeeklyCounts);
400     }
401
402     NSArray *childDicts = [dict objectForKey:childrenKey];
403     if (childDicts) {
404         for (int i = [childDicts count] - 1; i >= 0; i--) {
405             WebHistoryItem *child = [[WebHistoryItem alloc] initFromDictionaryRepresentation:[childDicts objectAtIndex:i]];
406             core(_private)->addChildItem(core(child->_private));
407             [child release];
408         }
409     }
410
411     return self;
412 }
413
414 - (NSPoint)scrollPoint
415 {
416     ASSERT_MAIN_THREAD();
417     return core(_private)->scrollPoint();
418 }
419
420 - (void)_visitedWithTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount
421 {
422     core(_private)->visited(title, [NSDate timeIntervalSinceReferenceDate], increaseVisitCount ? IncreaseVisitCount : DoNotIncreaseVisitCount);
423 }
424
425 - (void)_recordInitialVisit
426 {
427     core(_private)->recordInitialVisit();
428 }
429
430 @end
431
432 @implementation WebHistoryItem (WebPrivate)
433
434 - (id)initWithURL:(NSURL *)URL title:(NSString *)title
435 {
436     return [self initWithURLString:[URL _web_originalDataAsString] title:title lastVisitedTimeInterval:0];
437 }
438
439 - (NSDictionary *)dictionaryRepresentation
440 {
441     ASSERT_MAIN_THREAD();
442     NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:8];
443
444     HistoryItem* coreItem = core(_private);
445     
446     if (!coreItem->urlString().isEmpty())
447         [dict setObject:(NSString*)coreItem->urlString() forKey:@""];
448     if (!coreItem->title().isEmpty())
449         [dict setObject:(NSString*)coreItem->title() forKey:titleKey];
450     if (!coreItem->alternateTitle().isEmpty())
451         [dict setObject:(NSString*)coreItem->alternateTitle() forKey:displayTitleKey];
452     if (coreItem->lastVisitedTime() != 0.0) {
453         // Store as a string to maintain backward compatibility. (See 3245793)
454         [dict setObject:[NSString stringWithFormat:@"%.1lf", coreItem->lastVisitedTime()]
455                  forKey:lastVisitedTimeIntervalKey];
456     }
457     if (coreItem->visitCount())
458         [dict setObject:[NSNumber numberWithInt:coreItem->visitCount()] forKey:visitCountKey];
459     if (coreItem->lastVisitWasFailure())
460         [dict setObject:[NSNumber numberWithBool:YES] forKey:lastVisitWasFailureKey];
461     if (coreItem->lastVisitWasHTTPNonGet()) {
462         ASSERT(coreItem->urlString().startsWith("http:", false) || coreItem->urlString().startsWith("https:", false));
463         [dict setObject:[NSNumber numberWithBool:YES] forKey:lastVisitWasHTTPNonGetKey];
464     }
465     if (Vector<String>* redirectURLs = coreItem->redirectURLs()) {
466         size_t size = redirectURLs->size();
467         ASSERT(size);
468         NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:size];
469         for (size_t i = 0; i < size; ++i)
470             [result addObject:(NSString*)redirectURLs->at(i)];
471         [dict setObject:result forKey:redirectURLsKey];
472         [result release];
473     }
474     
475     const Vector<int>& dailyVisitCounts = coreItem->dailyVisitCounts();
476     if (dailyVisitCounts.size()) {
477         NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:13];
478         for (size_t i = 0; i < dailyVisitCounts.size(); ++i)
479             [array addObject:[NSNumber numberWithInt:dailyVisitCounts[i]]];
480         [dict setObject:array forKey:dailyVisitCountKey];
481         [array release];
482     }
483     
484     const Vector<int>& weeklyVisitCounts = coreItem->weeklyVisitCounts();
485     if (weeklyVisitCounts.size()) {
486         NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:5];
487         for (size_t i = 0; i < weeklyVisitCounts.size(); ++i)
488             [array addObject:[NSNumber numberWithInt:weeklyVisitCounts[i]]];
489         [dict setObject:array forKey:weeklyVisitCountKey];
490         [array release];
491     }    
492     
493     if (coreItem->children().size()) {
494         const HistoryItemVector& children = coreItem->children();
495         NSMutableArray *childDicts = [NSMutableArray arrayWithCapacity:children.size()];
496         
497         for (int i = children.size() - 1; i >= 0; i--)
498             [childDicts addObject:[kit(children[i].get()) dictionaryRepresentation]];
499         [dict setObject: childDicts forKey:childrenKey];
500     }
501
502     return dict;
503 }
504
505 - (NSString *)target
506 {
507     ASSERT_MAIN_THREAD();
508     return nsStringNilIfEmpty(core(_private)->target());
509 }
510
511 - (BOOL)isTargetItem
512 {
513     return core(_private)->isTargetItem();
514 }
515
516 - (int)visitCount
517 {
518     ASSERT_MAIN_THREAD();
519     return core(_private)->visitCount();
520 }
521
522 - (NSString *)RSSFeedReferrer
523 {
524     return nsStringNilIfEmpty(core(_private)->referrer());
525 }
526
527 - (void)setRSSFeedReferrer:(NSString *)referrer
528 {
529     core(_private)->setReferrer(referrer);
530 }
531
532 - (NSArray *)children
533 {
534     ASSERT_MAIN_THREAD();
535     const HistoryItemVector& children = core(_private)->children();
536     if (!children.size())
537         return nil;
538
539     unsigned size = children.size();
540     NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:size] autorelease];
541     
542     for (unsigned i = 0; i < size; ++i)
543         [result addObject:kit(children[i].get())];
544     
545     return result;
546 }
547
548 - (void)setAlwaysAttemptToUsePageCache:(BOOL)flag
549 {
550     // Safari 2.0 uses this for SnapBack, so we stub it out to avoid a crash.
551 }
552
553 - (NSURL *)URL
554 {
555     ASSERT_MAIN_THREAD();
556     const KURL& url = core(_private)->url();
557     if (url.isEmpty())
558         return nil;
559     return url;
560 }
561
562 // This should not be called directly for WebHistoryItems that are already included
563 // in WebHistory. Use -[WebHistory setLastVisitedTimeInterval:forItem:] instead.
564 - (void)_setLastVisitedTimeInterval:(NSTimeInterval)time
565 {
566     core(_private)->setLastVisitedTime(time);
567 }
568
569 // FIXME: <rdar://problem/4880065> - Push Global History into WebCore
570 // Once that task is complete, this accessor can go away
571 - (NSCalendarDate *)_lastVisitedDate
572 {
573     ASSERT_MAIN_THREAD();
574     return [[[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:core(_private)->lastVisitedTime()] autorelease];
575 }
576
577 - (WebHistoryItem *)targetItem
578 {    
579     ASSERT_MAIN_THREAD();
580     return kit(core(_private)->targetItem());
581 }
582
583 + (void)_releaseAllPendingPageCaches
584 {
585     pageCache()->releaseAutoreleasedPagesNow();
586 }
587
588 - (id)_transientPropertyForKey:(NSString *)key
589 {
590     return core(_private)->getTransientProperty(key);
591 }
592
593 - (void)_setTransientProperty:(id)property forKey:(NSString *)key
594 {
595     core(_private)->setTransientProperty(key, property);
596 }
597
598 - (BOOL)lastVisitWasFailure
599 {
600     return core(_private)->lastVisitWasFailure();
601 }
602
603 - (void)_setLastVisitWasFailure:(BOOL)failure
604 {
605     core(_private)->setLastVisitWasFailure(failure);
606 }
607
608 - (BOOL)_lastVisitWasHTTPNonGet
609 {
610     return core(_private)->lastVisitWasHTTPNonGet();
611 }
612
613 - (NSArray *)_redirectURLs
614 {
615     Vector<String>* redirectURLs = core(_private)->redirectURLs();
616     if (!redirectURLs)
617         return nil;
618
619     size_t size = redirectURLs->size();
620     ASSERT(size);
621     NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:size];
622     for (size_t i = 0; i < size; ++i)
623         [result addObject:(NSString*)redirectURLs->at(i)];
624     return [result autorelease];
625 }
626
627 - (size_t)_getDailyVisitCounts:(const int**)counts
628 {
629     HistoryItem* coreItem = core(_private);
630     *counts = coreItem->dailyVisitCounts().data();
631     return coreItem->dailyVisitCounts().size();
632 }
633
634 - (size_t)_getWeeklyVisitCounts:(const int**)counts
635 {
636     HistoryItem* coreItem = core(_private);
637     *counts = coreItem->weeklyVisitCounts().data();
638     return coreItem->weeklyVisitCounts().size();
639 }
640
641 @end
642
643
644 // FIXME: <rdar://problem/4886761>.
645 // This is a bizarre policy. We flush the page caches ANY time ANY window is closed?
646
647 @implementation WebWindowWatcher
648
649 - (void)windowWillClose:(NSNotification *)notification
650 {
651     if (!pthread_main_np()) {
652         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
653         return;
654     }
655
656     pageCache()->releaseAutoreleasedPagesNow();
657 }
658
659 @end