WebHistoryItem.mm revision 65f03d4f644ce73618e5f4f50dd694b26f55ae12
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"
60static NSString *lastVisitedTimeIntervalKey = @"lastVisitedDate";
61static NSString *visitCountKey = @"visitCount";
62static NSString *titleKey = @"title";
63static NSString *childrenKey = @"children";
64static NSString *displayTitleKey = @"displayTitle";
65static NSString *lastVisitWasFailureKey = @"lastVisitWasFailure";
66static NSString *lastVisitWasHTTPNonGetKey = @"lastVisitWasHTTPNonGet";
67static NSString *redirectURLsKey = @"redirectURLs";
68static NSString *dailyVisitCountKey = @"D"; // short key to save space
69static NSString *weeklyVisitCountKey = @"W"; // short key to save space
70
71// Notification strings.
72NSString *WebHistoryItemChangedNotification = @"WebHistoryItemChangedNotification";
73
74using namespace WebCore;
75using namespace std;
76
77typedef HashMap<HistoryItem*, WebHistoryItem*> HistoryItemMap;
78
79static inline WebHistoryItemPrivate* kitPrivate(WebCoreHistoryItem* list) { return (WebHistoryItemPrivate*)list; }
80static inline WebCoreHistoryItem* core(WebHistoryItemPrivate* list) { return (WebCoreHistoryItem*)list; }
81
82static HistoryItemMap& historyItemWrappers()
83{
84    DEFINE_STATIC_LOCAL(HistoryItemMap, historyItemWrappers, ());
85    return historyItemWrappers;
86}
87
88void 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().isEmpty()) {
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
258HistoryItem* 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
268WebHistoryItem *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
285static 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
666