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