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