WebKit/mac/History/WebHistoryItem.mm
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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     // 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()) {
       
   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 
       
   258 HistoryItem* 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 
       
   268 WebHistoryItem *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 
       
   285 static 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