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