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