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