Move lastVisitWasHTTPNonGet out to WebHistoryItem
[WebKit-https.git] / Source / WebKit / mac / History / WebHistoryItem.mm
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/URL.h>
49 #import <WebCore/PageCache.h>
50 #import <WebCore/ThreadCheck.h>
51 #import <WebCore/WebCoreObjCExtras.h>
52 #import <runtime/InitializeThreading.h>
53 #import <wtf/Assertions.h>
54 #import <wtf/MainThread.h>
55 #import <wtf/RunLoop.h>
56 #import <wtf/StdLibExtras.h>
57 #import <wtf/text/WTFString.h>
58
59 #if PLATFORM(IOS)
60 #import <WebCore/WebCoreThreadMessage.h>
61
62 NSString *WebViewportInitialScaleKey = @"initial-scale";
63 NSString *WebViewportMinimumScaleKey = @"minimum-scale";
64 NSString *WebViewportMaximumScaleKey = @"maximum-scale";
65 NSString *WebViewportUserScalableKey = @"user-scalable";
66 NSString *WebViewportWidthKey        = @"width";
67 NSString *WebViewportHeightKey       = @"height";
68 NSString *WebViewportMinimalUIKey    = @"minimal-ui";
69
70 static NSString *scaleKey = @"scale";
71 static NSString *scaleIsInitialKey = @"scaleIsInitial";
72 static NSString *scrollPointXKey = @"scrollPointX";
73 static NSString *scrollPointYKey = @"scrollPointY";
74
75 static NSString * const bookmarkIDKey = @"bookmarkID";
76 static NSString * const sharedLinkUniqueIdentifierKey = @"sharedLinkUniqueIdentifier";
77 #endif
78
79 // Private keys used in the WebHistoryItem's dictionary representation.
80 // see 3245793 for explanation of "lastVisitedDate"
81 static NSString *lastVisitedTimeIntervalKey = @"lastVisitedDate";
82 static NSString *visitCountKey = @"visitCount";
83 static NSString *titleKey = @"title";
84 static NSString *childrenKey = @"children";
85 static NSString *displayTitleKey = @"displayTitle";
86 static NSString *lastVisitWasFailureKey = @"lastVisitWasFailure";
87 static NSString *lastVisitWasHTTPNonGetKey = @"lastVisitWasHTTPNonGet";
88 static NSString *redirectURLsKey = @"redirectURLs";
89 static NSString *dailyVisitCountKey = @"D"; // short key to save space
90 static NSString *weeklyVisitCountKey = @"W"; // short key to save space
91
92 // Notification strings.
93 NSString *WebHistoryItemChangedNotification = @"WebHistoryItemChangedNotification";
94
95 using namespace WebCore;
96
97 @implementation WebHistoryItemPrivate
98
99 @end
100
101 typedef HashMap<HistoryItem*, WebHistoryItem*> HistoryItemMap;
102
103 static inline WebCoreHistoryItem* core(WebHistoryItemPrivate* itemPrivate)
104 {
105     return itemPrivate->_historyItem.get();
106 }
107
108 static HistoryItemMap& historyItemWrappers()
109 {
110     DEFINE_STATIC_LOCAL(HistoryItemMap, historyItemWrappers, ());
111     return historyItemWrappers;
112 }
113
114 void WKNotifyHistoryItemChanged(HistoryItem*)
115 {
116 #if !PLATFORM(IOS)
117     [[NSNotificationCenter defaultCenter]
118         postNotificationName:WebHistoryItemChangedNotification object:nil userInfo:nil];
119 #else
120     WebThreadPostNotification(WebHistoryItemChangedNotification, nil, nil);
121 #endif
122 }
123
124 @implementation WebHistoryItem
125
126 + (void)initialize
127 {
128 #if !PLATFORM(IOS)
129     JSC::initializeThreading();
130     WTF::initializeMainThreadToProcessMainThread();
131     RunLoop::initializeMainRunLoop();
132 #endif
133     WebCoreObjCFinalizeOnMainThread(self);
134 }
135
136 - (instancetype)init
137 {
138     return [self initWithWebCoreHistoryItem:HistoryItem::create()];
139 }
140
141 - (instancetype)initWithURLString:(NSString *)URLString title:(NSString *)title lastVisitedTimeInterval:(NSTimeInterval)time
142 {
143     WebCoreThreadViolationCheckRoundOne();
144     return [self initWithWebCoreHistoryItem:HistoryItem::create(URLString, title, time)];
145 }
146
147 - (void)dealloc
148 {
149     if (WebCoreObjCScheduleDeallocateOnMainThread([WebHistoryItem class], self))
150         return;
151
152     historyItemWrappers().remove(_private->_historyItem.get());
153     [_private release];
154
155     [super dealloc];
156 }
157
158 - (void)finalize
159 {
160     WebCoreThreadViolationCheckRoundOne();
161
162     // FIXME: ~HistoryItem is what releases the history item's icon from the icon database
163     // It's probably not good to release icons from the database only when the object is garbage-collected. 
164     // Need to change design so this happens at a predictable time.
165     historyItemWrappers().remove(_private->_historyItem.get());
166
167     [super finalize];
168 }
169
170 - (id)copyWithZone:(NSZone *)zone
171 {
172     WebCoreThreadViolationCheckRoundOne();
173     WebHistoryItem *copy = [[[self class] alloc] initWithWebCoreHistoryItem:core(_private)->copy()];
174
175     copy->_private->_lastVisitWasHTTPNonGet = _private->_lastVisitWasHTTPNonGet;
176
177     historyItemWrappers().set(core(copy->_private), copy);
178
179     return copy;
180 }
181
182 // FIXME: Need to decide if this class ever returns URLs and decide on the name of this method
183 - (NSString *)URLString
184 {
185     ASSERT_MAIN_THREAD();
186     return nsStringNilIfEmpty(core(_private)->urlString());
187 }
188
189 // The first URL we loaded to get to where this history item points.  Includes both client
190 // and server redirects.
191 - (NSString *)originalURLString
192 {
193     ASSERT_MAIN_THREAD();
194     return nsStringNilIfEmpty(core(_private)->originalURLString());
195 }
196
197 - (NSString *)title
198 {
199     ASSERT_MAIN_THREAD();
200     return nsStringNilIfEmpty(core(_private)->title());
201 }
202
203 - (void)setAlternateTitle:(NSString *)alternateTitle
204 {
205     core(_private)->setAlternateTitle(alternateTitle);
206 }
207
208 - (NSString *)alternateTitle
209 {
210     return nsStringNilIfEmpty(core(_private)->alternateTitle());
211 }
212
213 #if !PLATFORM(IOS)
214 - (NSImage *)icon
215 {
216     return [[WebIconDatabase sharedIconDatabase] iconForURL:[self URLString] withSize:WebIconSmallSize];
217 }
218 #endif
219
220 - (NSTimeInterval)lastVisitedTimeInterval
221 {
222     ASSERT_MAIN_THREAD();
223     return core(_private)->lastVisitedTime();
224 }
225
226 - (NSUInteger)hash
227 {
228     return [(NSString*)core(_private)->urlString() hash];
229 }
230
231 - (BOOL)isEqual:(id)anObject
232 {
233     ASSERT_MAIN_THREAD();
234     if (![anObject isMemberOfClass:[WebHistoryItem class]]) {
235         return NO;
236     }
237     
238     return core(_private)->urlString() == core(((WebHistoryItem*)anObject)->_private)->urlString();
239 }
240
241 - (NSString *)description
242 {
243     ASSERT_MAIN_THREAD();
244     HistoryItem* coreItem = core(_private);
245     NSMutableString *result = [NSMutableString stringWithFormat:@"%@ %@", [super description], (NSString*)coreItem->urlString()];
246     if (!coreItem->target().isEmpty()) {
247         NSString *target = coreItem->target();
248         [result appendFormat:@" in \"%@\"", target];
249     }
250     if (coreItem->isTargetItem()) {
251         [result appendString:@" *target*"];
252     }
253     if (coreItem->formData()) {
254         [result appendString:@" *POST*"];
255     }
256     
257     if (coreItem->children().size()) {
258         const HistoryItemVector& children = coreItem->children();
259         int currPos = [result length];
260         unsigned size = children.size();        
261         for (unsigned i = 0; i < size; ++i) {
262             WebHistoryItem *child = kit(children[i].get());
263             [result appendString:@"\n"];
264             [result appendString:[child description]];
265         }
266         // shift all the contents over.  A bit slow, but hey, this is for debugging.
267         NSRange replRange = { static_cast<NSUInteger>(currPos), [result length] - currPos };
268         [result replaceOccurrencesOfString:@"\n" withString:@"\n    " options:0 range:replRange];
269     }
270     
271     return result;
272 }
273
274 @end
275
276 @implementation WebHistoryItem (WebInternal)
277
278 HistoryItem* core(WebHistoryItem *item)
279 {
280     if (!item)
281         return 0;
282     
283     ASSERT(historyItemWrappers().get(core(item->_private)) == item);
284
285     return core(item->_private);
286 }
287
288 WebHistoryItem *kit(HistoryItem* item)
289 {
290     if (!item)
291         return nil;
292         
293     WebHistoryItem *kitItem = historyItemWrappers().get(item);
294     if (kitItem)
295         return kitItem;
296     
297     return [[[WebHistoryItem alloc] initWithWebCoreHistoryItem:item] autorelease];
298 }
299
300 + (WebHistoryItem *)entryWithURL:(NSURL *)URL
301 {
302     return [[[self alloc] initWithURL:URL title:nil] autorelease];
303 }
304
305 - (id)initWithURL:(NSURL *)URL target:(NSString *)target parent:(NSString *)parent title:(NSString *)title
306 {
307     return [self initWithWebCoreHistoryItem:HistoryItem::create(URL, target, parent, title)];
308 }
309
310 - (id)initWithURLString:(NSString *)URLString title:(NSString *)title displayTitle:(NSString *)displayTitle lastVisitedTimeInterval:(NSTimeInterval)time
311 {
312     return [self initWithWebCoreHistoryItem:HistoryItem::create(URLString, title, displayTitle, time)];
313 }
314
315 - (id)initWithWebCoreHistoryItem:(PassRefPtr<HistoryItem>)item
316 {   
317     WebCoreThreadViolationCheckRoundOne();
318     // Need to tell WebCore what function to call for the 
319     // "History Item has Changed" notification - no harm in doing this
320     // everytime a WebHistoryItem is created
321     // Note: We also do this in [WebFrameView initWithFrame:] where we do
322     // other "init before WebKit is used" type things
323     WebCore::notifyHistoryItemChanged = WKNotifyHistoryItemChanged;
324     
325     if (!(self = [super init]))
326         return nil;
327
328     _private = [[WebHistoryItemPrivate alloc] init];
329     _private->_historyItem = item;
330
331     ASSERT(!historyItemWrappers().get(core(_private)));
332     historyItemWrappers().set(core(_private), self);
333     return self;
334 }
335
336 - (void)setTitle:(NSString *)title
337 {
338     core(_private)->setTitle(title);
339 }
340
341 - (void)setVisitCount:(int)count
342 {
343     core(_private)->setVisitCount(count);
344 }
345
346 - (void)setViewState:(id)statePList
347 {
348     core(_private)->setViewState(statePList);
349 }
350
351 - (void)_mergeAutoCompleteHints:(WebHistoryItem *)otherItem
352 {
353     ASSERT_ARG(otherItem, otherItem);
354     core(_private)->mergeAutoCompleteHints(core(otherItem->_private));
355 }
356
357 - (id)initFromDictionaryRepresentation:(NSDictionary *)dict
358 {
359     ASSERT_MAIN_THREAD();
360     NSString *URLString = [dict _webkit_stringForKey:@""];
361     NSString *title = [dict _webkit_stringForKey:titleKey];
362
363     // Do an existence check to avoid calling doubleValue on a nil string. Leave
364     // time interval at 0 if there's no value in dict.
365     NSString *timeIntervalString = [dict _webkit_stringForKey:lastVisitedTimeIntervalKey];
366     NSTimeInterval lastVisited = timeIntervalString == nil ? 0 : [timeIntervalString doubleValue];
367
368     self = [self initWithURLString:URLString title:title displayTitle:[dict _webkit_stringForKey:displayTitleKey] lastVisitedTimeInterval:lastVisited];
369     
370     // Check if we've read a broken URL from the file that has non-Latin1 chars.  If so, try to convert
371     // as if it was from user typing.
372     if (![URLString canBeConvertedToEncoding:NSISOLatin1StringEncoding]) {
373         NSURL *tempURL = [NSURL _web_URLWithUserTypedString:URLString];
374         ASSERT(tempURL);
375         NSString *newURLString = [tempURL _web_originalDataAsString];
376         core(_private)->setURLString(newURLString);
377         core(_private)->setOriginalURLString(newURLString);
378     } 
379
380     int visitCount = [dict _webkit_intForKey:visitCountKey];
381     
382     // Can't trust data on disk, and we've had at least one report of this (<rdar://6572300>).
383     if (visitCount < 0) {
384         LOG_ERROR("visit count for history item \"%@\" is negative (%d), will be reset to 1", URLString, visitCount);
385         visitCount = 1;
386     }
387     core(_private)->setVisitCount(visitCount);
388
389     if ([dict _webkit_boolForKey:lastVisitWasFailureKey])
390         core(_private)->setLastVisitWasFailure(true);
391     
392     BOOL lastVisitWasHTTPNonGet = [dict _webkit_boolForKey:lastVisitWasHTTPNonGetKey];
393     NSString *tempURLString = [URLString lowercaseString];
394     if (lastVisitWasHTTPNonGet && ([tempURLString hasPrefix:@"http:"] || [tempURLString hasPrefix:@"https:"]))
395         _private->_lastVisitWasHTTPNonGet = lastVisitWasHTTPNonGet;
396
397     if (NSArray *redirectURLs = [dict _webkit_arrayForKey:redirectURLsKey]) {
398         NSUInteger size = [redirectURLs count];
399         auto redirectURLsVector = std::make_unique<Vector<String>>(size);
400         for (NSUInteger i = 0; i < size; ++i)
401             (*redirectURLsVector)[i] = String([redirectURLs _webkit_stringAtIndex:i]);
402         core(_private)->setRedirectURLs(std::move(redirectURLsVector));
403     }
404
405     NSArray *dailyCounts = [dict _webkit_arrayForKey:dailyVisitCountKey];
406     NSArray *weeklyCounts = [dict _webkit_arrayForKey:weeklyVisitCountKey];
407     if (dailyCounts || weeklyCounts) {
408         Vector<int> coreDailyCounts([dailyCounts count]);
409         Vector<int> coreWeeklyCounts([weeklyCounts count]);
410
411         // Daily and weekly counts < 0 are errors in the data read from disk, so reset to 0.
412         for (size_t i = 0; i < coreDailyCounts.size(); ++i)
413             coreDailyCounts[i] = std::max([[dailyCounts _webkit_numberAtIndex:i] intValue], 0);
414         for (size_t i = 0; i < coreWeeklyCounts.size(); ++i)
415             coreWeeklyCounts[i] = std::max([[weeklyCounts _webkit_numberAtIndex:i] intValue], 0);
416     
417         core(_private)->adoptVisitCounts(coreDailyCounts, coreWeeklyCounts);
418     }
419
420     NSArray *childDicts = [dict objectForKey:childrenKey];
421     if (childDicts) {
422         for (int i = [childDicts count] - 1; i >= 0; i--) {
423             WebHistoryItem *child = [[WebHistoryItem alloc] initFromDictionaryRepresentation:[childDicts objectAtIndex:i]];
424             core(_private)->addChildItem(core(child->_private));
425             [child release];
426         }
427     }
428
429 #if PLATFORM(IOS)
430     NSNumber *scaleValue = [dict objectForKey:scaleKey];
431     NSNumber *scaleIsInitialValue = [dict objectForKey:scaleIsInitialKey];
432     if (scaleValue && scaleIsInitialValue)
433         core(_private)->setScale([scaleValue floatValue], [scaleIsInitialValue boolValue]);
434
435     if (id viewportArguments = [dict objectForKey:@"WebViewportArguments"])
436         [self _setViewportArguments:viewportArguments];
437
438     NSNumber *scrollPointXValue = [dict objectForKey:scrollPointXKey];
439     NSNumber *scrollPointYValue = [dict objectForKey:scrollPointYKey];
440     if (scrollPointXValue && scrollPointYValue)
441         core(_private)->setScrollPoint(IntPoint([scrollPointXValue intValue], [scrollPointYValue intValue]));
442
443     uint32_t bookmarkIDValue = [[dict objectForKey:bookmarkIDKey] unsignedIntValue];
444     if (bookmarkIDValue)
445         core(_private)->setBookmarkID(bookmarkIDValue);
446
447     NSString *sharedLinkUniqueIdentifierValue = [dict objectForKey:sharedLinkUniqueIdentifierKey];
448     if (sharedLinkUniqueIdentifierValue)
449         core(_private)->setSharedLinkUniqueIdentifier(sharedLinkUniqueIdentifierValue);
450 #endif
451
452     return self;
453 }
454
455 - (NSPoint)scrollPoint
456 {
457     ASSERT_MAIN_THREAD();
458     return core(_private)->scrollPoint();
459 }
460
461 - (void)_visitedWithTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount
462 {
463     core(_private)->visited(title, [NSDate timeIntervalSinceReferenceDate], increaseVisitCount ? IncreaseVisitCount : DoNotIncreaseVisitCount);
464 }
465
466 - (void)_recordInitialVisit
467 {
468     core(_private)->recordInitialVisit();
469 }
470
471 @end
472
473 @implementation WebHistoryItem (WebPrivate)
474
475 - (id)initWithURL:(NSURL *)URL title:(NSString *)title
476 {
477     return [self initWithURLString:[URL _web_originalDataAsString] title:title lastVisitedTimeInterval:0];
478 }
479
480 // FIXME: The only iOS difference here should be whether YES or NO is passed to dictionaryRepresentationIncludingChildren:
481 #if PLATFORM(IOS)
482 - (NSDictionary *)dictionaryRepresentation
483 {
484     return [self dictionaryRepresentationIncludingChildren:YES];
485 }
486
487 - (NSDictionary *)dictionaryRepresentationIncludingChildren:(BOOL)includesChildren
488 #else
489 - (NSDictionary *)dictionaryRepresentation
490 #endif
491 {
492     ASSERT_MAIN_THREAD();
493     NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:8];
494
495     HistoryItem* coreItem = core(_private);
496     
497     if (!coreItem->urlString().isEmpty())
498         [dict setObject:(NSString*)coreItem->urlString() forKey:@""];
499     if (!coreItem->title().isEmpty())
500         [dict setObject:(NSString*)coreItem->title() forKey:titleKey];
501     if (!coreItem->alternateTitle().isEmpty())
502         [dict setObject:(NSString*)coreItem->alternateTitle() forKey:displayTitleKey];
503     if (coreItem->lastVisitedTime() != 0.0) {
504         // Store as a string to maintain backward compatibility. (See 3245793)
505         [dict setObject:[NSString stringWithFormat:@"%.1lf", coreItem->lastVisitedTime()]
506                  forKey:lastVisitedTimeIntervalKey];
507     }
508     if (coreItem->visitCount())
509         [dict setObject:[NSNumber numberWithInt:coreItem->visitCount()] forKey:visitCountKey];
510     if (coreItem->lastVisitWasFailure())
511         [dict setObject:[NSNumber numberWithBool:YES] forKey:lastVisitWasFailureKey];
512     if (coreItem->lastVisitWasHTTPNonGet()) {
513         ASSERT(coreItem->urlString().startsWith("http:", false) || coreItem->urlString().startsWith("https:", false));
514         [dict setObject:[NSNumber numberWithBool:YES] forKey:lastVisitWasHTTPNonGetKey];
515     }
516     if (Vector<String>* redirectURLs = coreItem->redirectURLs()) {
517         size_t size = redirectURLs->size();
518         ASSERT(size);
519         NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:size];
520         for (size_t i = 0; i < size; ++i)
521             [result addObject:(NSString*)redirectURLs->at(i)];
522         [dict setObject:result forKey:redirectURLsKey];
523         [result release];
524     }
525     
526     const Vector<int>& dailyVisitCounts = coreItem->dailyVisitCounts();
527     if (dailyVisitCounts.size()) {
528         NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:13];
529         for (size_t i = 0; i < dailyVisitCounts.size(); ++i)
530             [array addObject:[NSNumber numberWithInt:dailyVisitCounts[i]]];
531         [dict setObject:array forKey:dailyVisitCountKey];
532         [array release];
533     }
534     
535     const Vector<int>& weeklyVisitCounts = coreItem->weeklyVisitCounts();
536     if (weeklyVisitCounts.size()) {
537         NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:5];
538         for (size_t i = 0; i < weeklyVisitCounts.size(); ++i)
539             [array addObject:[NSNumber numberWithInt:weeklyVisitCounts[i]]];
540         [dict setObject:array forKey:weeklyVisitCountKey];
541         [array release];
542     }    
543
544 #if PLATFORM(IOS)
545     if (includesChildren && coreItem->children().size()) {
546 #else
547     if (coreItem->children().size()) {
548 #endif
549         const HistoryItemVector& children = coreItem->children();
550         NSMutableArray *childDicts = [NSMutableArray arrayWithCapacity:children.size()];
551         
552         for (int i = children.size() - 1; i >= 0; i--)
553             [childDicts addObject:[kit(children[i].get()) dictionaryRepresentation]];
554         [dict setObject: childDicts forKey:childrenKey];
555     }
556
557 #if PLATFORM(IOS)
558     [dict setObject:[NSNumber numberWithFloat:core(_private)->scale()] forKey:scaleKey];
559     [dict setObject:[NSNumber numberWithBool:core(_private)->scaleIsInitial()] forKey:scaleIsInitialKey];
560
561     NSDictionary *viewportArguments = [self _viewportArguments];
562     if (viewportArguments)
563         [dict setObject:viewportArguments forKey:@"WebViewportArguments"];
564
565     IntPoint scrollPoint = core(_private)->scrollPoint();
566     [dict setObject:[NSNumber numberWithInt:scrollPoint.x()] forKey:scrollPointXKey];
567     [dict setObject:[NSNumber numberWithInt:scrollPoint.y()] forKey:scrollPointYKey];
568
569     uint32_t bookmarkID = core(_private)->bookmarkID();
570     if (bookmarkID)
571         [dict setObject:[NSNumber numberWithUnsignedInt:bookmarkID] forKey:bookmarkIDKey];
572
573     NSString *sharedLinkUniqueIdentifier = [self _sharedLinkUniqueIdentifier];
574     if (sharedLinkUniqueIdentifier)
575         [dict setObject:sharedLinkUniqueIdentifier forKey:sharedLinkUniqueIdentifierKey];
576 #endif
577
578     return dict;
579 }
580
581 - (NSString *)target
582 {
583     ASSERT_MAIN_THREAD();
584     return nsStringNilIfEmpty(core(_private)->target());
585 }
586
587 - (BOOL)isTargetItem
588 {
589     return core(_private)->isTargetItem();
590 }
591
592 - (int)visitCount
593 {
594     ASSERT_MAIN_THREAD();
595     return core(_private)->visitCount();
596 }
597
598 - (NSString *)RSSFeedReferrer
599 {
600     return nsStringNilIfEmpty(core(_private)->referrer());
601 }
602
603 - (void)setRSSFeedReferrer:(NSString *)referrer
604 {
605     core(_private)->setReferrer(referrer);
606 }
607
608 - (NSArray *)children
609 {
610     ASSERT_MAIN_THREAD();
611     const HistoryItemVector& children = core(_private)->children();
612     if (!children.size())
613         return nil;
614
615     unsigned size = children.size();
616     NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:size] autorelease];
617     
618     for (unsigned i = 0; i < size; ++i)
619         [result addObject:kit(children[i].get())];
620     
621     return result;
622 }
623
624 - (void)setAlwaysAttemptToUsePageCache:(BOOL)flag
625 {
626     // Safari 2.0 uses this for SnapBack, so we stub it out to avoid a crash.
627 }
628
629 - (NSURL *)URL
630 {
631     ASSERT_MAIN_THREAD();
632     const URL& url = core(_private)->url();
633     if (url.isEmpty())
634         return nil;
635     return url;
636 }
637
638 // This should not be called directly for WebHistoryItems that are already included
639 // in WebHistory. Use -[WebHistory setLastVisitedTimeInterval:forItem:] instead.
640 - (void)_setLastVisitedTimeInterval:(NSTimeInterval)time
641 {
642     core(_private)->setLastVisitedTime(time);
643 }
644
645 - (WebHistoryItem *)targetItem
646 {    
647     ASSERT_MAIN_THREAD();
648     return kit(core(_private)->targetItem());
649 }
650
651 #if !PLATFORM(IOS)
652 + (void)_releaseAllPendingPageCaches
653 {
654 }
655 #endif
656
657 - (id)_transientPropertyForKey:(NSString *)key
658 {
659     return core(_private)->getTransientProperty(key);
660 }
661
662 - (void)_setTransientProperty:(id)property forKey:(NSString *)key
663 {
664     core(_private)->setTransientProperty(key, property);
665 }
666
667 - (BOOL)lastVisitWasFailure
668 {
669     return core(_private)->lastVisitWasFailure();
670 }
671
672 - (void)_setLastVisitWasFailure:(BOOL)failure
673 {
674     core(_private)->setLastVisitWasFailure(failure);
675 }
676
677 - (BOOL)_lastVisitWasHTTPNonGet
678 {
679     return _private->_lastVisitWasHTTPNonGet;
680 }
681
682 - (NSArray *)_redirectURLs
683 {
684     Vector<String>* redirectURLs = core(_private)->redirectURLs();
685     if (!redirectURLs)
686         return nil;
687
688     size_t size = redirectURLs->size();
689     ASSERT(size);
690     NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:size];
691     for (size_t i = 0; i < size; ++i)
692         [result addObject:(NSString*)redirectURLs->at(i)];
693     return [result autorelease];
694 }
695
696 - (size_t)_getDailyVisitCounts:(const int**)counts
697 {
698     HistoryItem* coreItem = core(_private);
699     *counts = coreItem->dailyVisitCounts().data();
700     return coreItem->dailyVisitCounts().size();
701 }
702
703 - (size_t)_getWeeklyVisitCounts:(const int**)counts
704 {
705     HistoryItem* coreItem = core(_private);
706     *counts = coreItem->weeklyVisitCounts().data();
707     return coreItem->weeklyVisitCounts().size();
708 }
709
710 #if PLATFORM(IOS)
711 - (void)_setScale:(float)scale isInitial:(BOOL)aFlag
712 {
713     core(_private)->setScale(scale, aFlag);
714 }
715
716 - (float)_scale
717 {
718     return core(_private)->scale();
719 }
720
721 - (BOOL)_scaleIsInitial
722 {
723     return core(_private)->scaleIsInitial();
724 }
725
726 - (NSDictionary *)_viewportArguments
727 {
728     const ViewportArguments& viewportArguments = core(_private)->viewportArguments();
729     NSMutableDictionary *argumentsDictionary = [NSMutableDictionary dictionary];
730     [argumentsDictionary setObject:[NSNumber numberWithFloat:viewportArguments.zoom] forKey:WebViewportInitialScaleKey];
731     [argumentsDictionary setObject:[NSNumber numberWithFloat:viewportArguments.minZoom] forKey:WebViewportMinimumScaleKey];
732     [argumentsDictionary setObject:[NSNumber numberWithFloat:viewportArguments.maxZoom] forKey:WebViewportMaximumScaleKey];
733     [argumentsDictionary setObject:[NSNumber numberWithFloat:viewportArguments.width] forKey:WebViewportWidthKey];
734     [argumentsDictionary setObject:[NSNumber numberWithFloat:viewportArguments.height] forKey:WebViewportHeightKey];
735     [argumentsDictionary setObject:[NSNumber numberWithFloat:viewportArguments.userZoom] forKey:WebViewportUserScalableKey];
736     [argumentsDictionary setObject:[NSNumber numberWithBool:viewportArguments.minimalUI] forKey:WebViewportMinimalUIKey];
737     return argumentsDictionary;
738 }
739
740 - (void)_setViewportArguments:(NSDictionary *)arguments
741 {
742     ViewportArguments viewportArguments;
743     viewportArguments.zoom = [[arguments objectForKey:WebViewportInitialScaleKey] floatValue];
744     viewportArguments.minZoom = [[arguments objectForKey:WebViewportMinimumScaleKey] floatValue];
745     viewportArguments.maxZoom = [[arguments objectForKey:WebViewportMaximumScaleKey] floatValue];
746     viewportArguments.width = [[arguments objectForKey:WebViewportWidthKey] floatValue];
747     viewportArguments.height = [[arguments objectForKey:WebViewportHeightKey] floatValue];
748     viewportArguments.userZoom = [[arguments objectForKey:WebViewportUserScalableKey] floatValue];
749     viewportArguments.minimalUI = [[arguments objectForKey:WebViewportMinimalUIKey] boolValue];
750     core(_private)->setViewportArguments(viewportArguments);
751 }
752
753 - (CGPoint)_scrollPoint
754 {
755     return core(_private)->scrollPoint();
756 }
757
758 - (void)_setScrollPoint:(CGPoint)scrollPoint
759 {
760     core(_private)->setScrollPoint(IntPoint(scrollPoint));
761 }
762
763 - (uint32_t)_bookmarkID
764 {
765     return core(_private)->bookmarkID();
766 }
767
768 - (void)_setBookmarkID:(uint32_t)bookmarkID
769 {
770     core(_private)->setBookmarkID(bookmarkID);
771 }
772
773 - (NSString *)_sharedLinkUniqueIdentifier
774 {
775     return nsStringNilIfEmpty(core(_private)->sharedLinkUniqueIdentifier());
776 }
777
778 - (void)_setSharedLinkUniqueIdentifier:(NSString *)identifier
779 {
780     core(_private)->setSharedLinkUniqueIdentifier(identifier);
781 }
782 #endif // PLATFORM(IOS)
783
784 - (BOOL)_isInPageCache
785 {
786     return core(_private)->isInPageCache();
787 }
788
789 - (BOOL)_hasCachedPageExpired
790 {
791     return core(_private)->hasCachedPageExpired();
792 }
793
794 @end