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