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